Fossil SCM
Add the forum-close-policy boolean config setting. If true, forum moderators may close/re-open forum posts, as well as reply to closed posts.
Commit
162fc1e6aa1c6f3c54c5c39769b0af0d39f9cc617903f1169f9e42eaaad35dcf
Parent
8e7de26aa2680f2…
2 files changed
+7
+56
-16
M
src/db.c
+7
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -4650,10 +4650,17 @@ | ||
| 4650 | 4650 | ** to obtain a check-in lock during auto-sync, the server will |
| 4651 | 4651 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4652 | 4652 | ** which will cause the client to avoid generating a delta |
| 4653 | 4653 | ** manifest. |
| 4654 | 4654 | */ |
| 4655 | +/* | |
| 4656 | +** SETTING: forum-close-policy boolean default=off | |
| 4657 | +** If true, forum moderators may close forum posts, else only | |
| 4658 | +** administrators may do so. Note that this only affects the forum web | |
| 4659 | +** UI, not post-closing tags which arrive via the command-line or from | |
| 4660 | +** synchronization with a remote. | |
| 4661 | +*/ | |
| 4655 | 4662 | /* |
| 4656 | 4663 | ** SETTING: gdiff-command width=40 default=gdiff sensitive |
| 4657 | 4664 | ** The value is an external command to run when performing a graphical |
| 4658 | 4665 | ** diff. If undefined, text diff will be used. |
| 4659 | 4666 | */ |
| 4660 | 4667 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -4650,10 +4650,17 @@ | |
| 4650 | ** to obtain a check-in lock during auto-sync, the server will |
| 4651 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4652 | ** which will cause the client to avoid generating a delta |
| 4653 | ** manifest. |
| 4654 | */ |
| 4655 | /* |
| 4656 | ** SETTING: gdiff-command width=40 default=gdiff sensitive |
| 4657 | ** The value is an external command to run when performing a graphical |
| 4658 | ** diff. If undefined, text diff will be used. |
| 4659 | */ |
| 4660 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -4650,10 +4650,17 @@ | |
| 4650 | ** to obtain a check-in lock during auto-sync, the server will |
| 4651 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4652 | ** which will cause the client to avoid generating a delta |
| 4653 | ** manifest. |
| 4654 | */ |
| 4655 | /* |
| 4656 | ** SETTING: forum-close-policy boolean default=off |
| 4657 | ** If true, forum moderators may close forum posts, else only |
| 4658 | ** administrators may do so. Note that this only affects the forum web |
| 4659 | ** UI, not post-closing tags which arrive via the command-line or from |
| 4660 | ** synchronization with a remote. |
| 4661 | */ |
| 4662 | /* |
| 4663 | ** SETTING: gdiff-command width=40 default=gdiff sensitive |
| 4664 | ** The value is an external command to run when performing a graphical |
| 4665 | ** diff. If undefined, text diff will be used. |
| 4666 | */ |
| 4667 |
+56
-16
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -261,20 +261,56 @@ | ||
| 261 | 261 | db_end_transaction(0); |
| 262 | 262 | return 1; |
| 263 | 263 | } |
| 264 | 264 | |
| 265 | 265 | /* |
| 266 | -** If iClosed is true and the current user has admin privileges, this | |
| 267 | -** renders either a checkbox to unlock forum post fpid (if iClosed>0) | |
| 268 | -** or a SPAN.warning element that the given post inherits the CLOSED | |
| 269 | -** status from a parent post (if iClosed<0). If neither of the initial | |
| 270 | -** conditions is true, this is a no-op. | |
| 266 | +** Returns true if the forum-close-policy setting is true, else false, | |
| 267 | +** caching the result for subsequent calls. | |
| 268 | +*/ | |
| 269 | +static int forumpost_close_policy(void){ | |
| 270 | + static int closePolicy = -99; | |
| 271 | + | |
| 272 | + if( closePolicy==-99 ){ | |
| 273 | + closePolicy = db_get_boolean("forum-close-policy",0)>0; | |
| 274 | + } | |
| 275 | + return closePolicy; | |
| 276 | +} | |
| 277 | + | |
| 278 | +/* | |
| 279 | +** Returns 1 if the current user is an admin, -1 if the current user | |
| 280 | +** is a forum moderator and the forum-close-policy setting is true, | |
| 281 | +** else returns 0. The value is cached for subsequent calls. | |
| 282 | +*/ | |
| 283 | +static int forumpost_may_close(void){ | |
| 284 | + static int permClose = -99; | |
| 285 | + if( permClose!=-99 ){ | |
| 286 | + return permClose; | |
| 287 | + }else if( g.perm.Admin ){ | |
| 288 | + return permClose = 1; | |
| 289 | + }else if( g.perm.ModForum ){ | |
| 290 | + return permClose = forumpost_close_policy()>0 ? -1 : 0; | |
| 291 | + }else{ | |
| 292 | + return permClose = 0; | |
| 293 | + } | |
| 294 | +} | |
| 295 | + | |
| 296 | +/* | |
| 297 | +** If iClosed is true and the current user forumpost-close privileges, | |
| 298 | +** this renders either a checkbox to unlock forum post fpid (if | |
| 299 | +** iClosed>0) or a SPAN.warning element that the given post inherits | |
| 300 | +** the CLOSED status from a parent post (if iClosed<0). If neither of | |
| 301 | +** the initial conditions is true, this is a no-op. | |
| 271 | 302 | */ |
| 272 | 303 | static void forumpost_emit_closed_state(int fpid, int iClosed){ |
| 273 | - const char *zCommon = | |
| 274 | - "Only admins may edit or respond to closed posts."; | |
| 304 | + const char *zCommon; | |
| 275 | 305 | int iHead = forumpost_head_rid(fpid); |
| 306 | + const int permClose = forumpost_may_close(); | |
| 307 | + | |
| 308 | + zCommon = forumpost_close_policy()==0 | |
| 309 | + ? "Admins may close or re-open posts, or respond to closed posts." | |
| 310 | + : "Admins or moderators " | |
| 311 | + "may close or re-open posts, or respond to closed posts."; | |
| 276 | 312 | /*@ forumpost_emit_closed_state(%d(fpid), %d(iClosed))<br/>*/ |
| 277 | 313 | if( iHead != fpid ){ |
| 278 | 314 | iClosed = forum_rid_is_closed(iHead, 1); |
| 279 | 315 | /*@ forumpost_emit_closed_state() %d(iHead), %d(iClosed)*/ |
| 280 | 316 | } |
| @@ -283,28 +319,32 @@ | ||
| 283 | 319 | @ This post is CLOSED via a parent post. %s(zCommon)\ |
| 284 | 320 | @ </div> |
| 285 | 321 | return; |
| 286 | 322 | } |
| 287 | 323 | else if( iClosed==0 ){ |
| 288 | - if( g.perm.Admin==0 ) return; | |
| 324 | + if( permClose==0 ) return; | |
| 289 | 325 | @ <div class="warning forumpost-closure-warning"> |
| 290 | 326 | @ <form method="post" action="%R/forumpost_close"> |
| 291 | 327 | @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" /> |
| 292 | 328 | @ <input type="submit" value="CLOSE this post and its responses" /> |
| 293 | - @ %s(zCommon) | |
| 329 | + @ <span>%s(zCommon)</span> | |
| 330 | + @ <span>This does NOT save any pending changes in | |
| 331 | + @ the editor!</span> | |
| 294 | 332 | @ </form></div> |
| 295 | 333 | return; |
| 296 | 334 | } |
| 297 | 335 | assert( iClosed>0 ); |
| 298 | - /* Only show the "unlock" checkbox on a post which is actually | |
| 336 | + /* Only show the "unlock" option on a post which is actually | |
| 299 | 337 | ** closed, not on a post which inherits that state. */ |
| 300 | 338 | @ <div class="warning forumpost-closure-warning">\ |
| 301 | 339 | @ This post is CLOSED. %s(zCommon) |
| 302 | - if( g.perm.Admin ){ | |
| 340 | + if( permClose ){ | |
| 303 | 341 | @ <form method="post" action="%R/forumpost_reopen"> |
| 304 | 342 | @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" /> |
| 305 | 343 | @ <input type="submit" value="Re-open this post and its responses" /> |
| 344 | + @ <span>This does NOT save any pending changes in | |
| 345 | + @ the editor!</span> | |
| 306 | 346 | @ </form> |
| 307 | 347 | } |
| 308 | 348 | @ </div> |
| 309 | 349 | } |
| 310 | 350 | |
| @@ -884,13 +924,13 @@ | ||
| 884 | 924 | @ <form action="%R/forumedit" method="POST"> |
| 885 | 925 | @ <input type="hidden" name="fpid" value="%s(p->zUuid)"> |
| 886 | 926 | if( !bPrivate ){ |
| 887 | 927 | /* Reply and Edit are only available if the post has been |
| 888 | 928 | ** approved. Closed threads can only be edited or replied to |
| 889 | - ** by an admin but a user may delete their own posts even if | |
| 890 | - ** they are closed. */ | |
| 891 | - if( g.perm.Admin || !iClosed ){ | |
| 929 | + ** if forumpost_may_close() is true but a user may delete | |
| 930 | + ** their own posts even if they are closed. */ | |
| 931 | + if( forumpost_may_close() || !iClosed ){ | |
| 892 | 932 | @ <input type="submit" name="reply" value="Reply"> |
| 893 | 933 | if( g.perm.Admin || (bSameUser && !iClosed) ){ |
| 894 | 934 | @ <input type="submit" name="edit" value="Edit"> |
| 895 | 935 | } |
| 896 | 936 | if( g.perm.Admin || bSameUser ){ |
| @@ -913,11 +953,11 @@ | ||
| 913 | 953 | }else if( bSameUser ){ |
| 914 | 954 | /* Allow users to delete (reject) their own pending posts. */ |
| 915 | 955 | @ <input type="submit" name="reject" value="Delete"> |
| 916 | 956 | } |
| 917 | 957 | @ </form> |
| 918 | - if( bSelect && g.perm.Admin && iClosed>=0 ){ | |
| 958 | + if( bSelect && forumpost_may_close() && iClosed>=0 ){ | |
| 919 | 959 | int iHead = forumpost_head_rid(p->fpid); |
| 920 | 960 | @ <form method="post" \ |
| 921 | 961 | @ action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'> |
| 922 | 962 | @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" /> |
| 923 | 963 | @ <input type="submit" value='%s(iClosed ? "Re-open" : "Close")' /> |
| @@ -1430,11 +1470,11 @@ | ||
| 1430 | 1470 | const char *zReason = 0; |
| 1431 | 1471 | int fClose; |
| 1432 | 1472 | int fpid; |
| 1433 | 1473 | |
| 1434 | 1474 | login_check_credentials(); |
| 1435 | - if( !g.perm.Admin ){ | |
| 1475 | + if( forumpost_may_close()==0 ){ | |
| 1436 | 1476 | login_needed(g.anon.Admin); |
| 1437 | 1477 | return; |
| 1438 | 1478 | } |
| 1439 | 1479 | fpid = symbolic_name_to_rid(zFpid, "f"); |
| 1440 | 1480 | if( fpid<=0 ){ |
| 1441 | 1481 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -261,20 +261,56 @@ | |
| 261 | db_end_transaction(0); |
| 262 | return 1; |
| 263 | } |
| 264 | |
| 265 | /* |
| 266 | ** If iClosed is true and the current user has admin privileges, this |
| 267 | ** renders either a checkbox to unlock forum post fpid (if iClosed>0) |
| 268 | ** or a SPAN.warning element that the given post inherits the CLOSED |
| 269 | ** status from a parent post (if iClosed<0). If neither of the initial |
| 270 | ** conditions is true, this is a no-op. |
| 271 | */ |
| 272 | static void forumpost_emit_closed_state(int fpid, int iClosed){ |
| 273 | const char *zCommon = |
| 274 | "Only admins may edit or respond to closed posts."; |
| 275 | int iHead = forumpost_head_rid(fpid); |
| 276 | /*@ forumpost_emit_closed_state(%d(fpid), %d(iClosed))<br/>*/ |
| 277 | if( iHead != fpid ){ |
| 278 | iClosed = forum_rid_is_closed(iHead, 1); |
| 279 | /*@ forumpost_emit_closed_state() %d(iHead), %d(iClosed)*/ |
| 280 | } |
| @@ -283,28 +319,32 @@ | |
| 283 | @ This post is CLOSED via a parent post. %s(zCommon)\ |
| 284 | @ </div> |
| 285 | return; |
| 286 | } |
| 287 | else if( iClosed==0 ){ |
| 288 | if( g.perm.Admin==0 ) return; |
| 289 | @ <div class="warning forumpost-closure-warning"> |
| 290 | @ <form method="post" action="%R/forumpost_close"> |
| 291 | @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" /> |
| 292 | @ <input type="submit" value="CLOSE this post and its responses" /> |
| 293 | @ %s(zCommon) |
| 294 | @ </form></div> |
| 295 | return; |
| 296 | } |
| 297 | assert( iClosed>0 ); |
| 298 | /* Only show the "unlock" checkbox on a post which is actually |
| 299 | ** closed, not on a post which inherits that state. */ |
| 300 | @ <div class="warning forumpost-closure-warning">\ |
| 301 | @ This post is CLOSED. %s(zCommon) |
| 302 | if( g.perm.Admin ){ |
| 303 | @ <form method="post" action="%R/forumpost_reopen"> |
| 304 | @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" /> |
| 305 | @ <input type="submit" value="Re-open this post and its responses" /> |
| 306 | @ </form> |
| 307 | } |
| 308 | @ </div> |
| 309 | } |
| 310 | |
| @@ -884,13 +924,13 @@ | |
| 884 | @ <form action="%R/forumedit" method="POST"> |
| 885 | @ <input type="hidden" name="fpid" value="%s(p->zUuid)"> |
| 886 | if( !bPrivate ){ |
| 887 | /* Reply and Edit are only available if the post has been |
| 888 | ** approved. Closed threads can only be edited or replied to |
| 889 | ** by an admin but a user may delete their own posts even if |
| 890 | ** they are closed. */ |
| 891 | if( g.perm.Admin || !iClosed ){ |
| 892 | @ <input type="submit" name="reply" value="Reply"> |
| 893 | if( g.perm.Admin || (bSameUser && !iClosed) ){ |
| 894 | @ <input type="submit" name="edit" value="Edit"> |
| 895 | } |
| 896 | if( g.perm.Admin || bSameUser ){ |
| @@ -913,11 +953,11 @@ | |
| 913 | }else if( bSameUser ){ |
| 914 | /* Allow users to delete (reject) their own pending posts. */ |
| 915 | @ <input type="submit" name="reject" value="Delete"> |
| 916 | } |
| 917 | @ </form> |
| 918 | if( bSelect && g.perm.Admin && iClosed>=0 ){ |
| 919 | int iHead = forumpost_head_rid(p->fpid); |
| 920 | @ <form method="post" \ |
| 921 | @ action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'> |
| 922 | @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" /> |
| 923 | @ <input type="submit" value='%s(iClosed ? "Re-open" : "Close")' /> |
| @@ -1430,11 +1470,11 @@ | |
| 1430 | const char *zReason = 0; |
| 1431 | int fClose; |
| 1432 | int fpid; |
| 1433 | |
| 1434 | login_check_credentials(); |
| 1435 | if( !g.perm.Admin ){ |
| 1436 | login_needed(g.anon.Admin); |
| 1437 | return; |
| 1438 | } |
| 1439 | fpid = symbolic_name_to_rid(zFpid, "f"); |
| 1440 | if( fpid<=0 ){ |
| 1441 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -261,20 +261,56 @@ | |
| 261 | db_end_transaction(0); |
| 262 | return 1; |
| 263 | } |
| 264 | |
| 265 | /* |
| 266 | ** Returns true if the forum-close-policy setting is true, else false, |
| 267 | ** caching the result for subsequent calls. |
| 268 | */ |
| 269 | static int forumpost_close_policy(void){ |
| 270 | static int closePolicy = -99; |
| 271 | |
| 272 | if( closePolicy==-99 ){ |
| 273 | closePolicy = db_get_boolean("forum-close-policy",0)>0; |
| 274 | } |
| 275 | return closePolicy; |
| 276 | } |
| 277 | |
| 278 | /* |
| 279 | ** Returns 1 if the current user is an admin, -1 if the current user |
| 280 | ** is a forum moderator and the forum-close-policy setting is true, |
| 281 | ** else returns 0. The value is cached for subsequent calls. |
| 282 | */ |
| 283 | static int forumpost_may_close(void){ |
| 284 | static int permClose = -99; |
| 285 | if( permClose!=-99 ){ |
| 286 | return permClose; |
| 287 | }else if( g.perm.Admin ){ |
| 288 | return permClose = 1; |
| 289 | }else if( g.perm.ModForum ){ |
| 290 | return permClose = forumpost_close_policy()>0 ? -1 : 0; |
| 291 | }else{ |
| 292 | return permClose = 0; |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | /* |
| 297 | ** If iClosed is true and the current user forumpost-close privileges, |
| 298 | ** this renders either a checkbox to unlock forum post fpid (if |
| 299 | ** iClosed>0) or a SPAN.warning element that the given post inherits |
| 300 | ** the CLOSED status from a parent post (if iClosed<0). If neither of |
| 301 | ** the initial conditions is true, this is a no-op. |
| 302 | */ |
| 303 | static void forumpost_emit_closed_state(int fpid, int iClosed){ |
| 304 | const char *zCommon; |
| 305 | int iHead = forumpost_head_rid(fpid); |
| 306 | const int permClose = forumpost_may_close(); |
| 307 | |
| 308 | zCommon = forumpost_close_policy()==0 |
| 309 | ? "Admins may close or re-open posts, or respond to closed posts." |
| 310 | : "Admins or moderators " |
| 311 | "may close or re-open posts, or respond to closed posts."; |
| 312 | /*@ forumpost_emit_closed_state(%d(fpid), %d(iClosed))<br/>*/ |
| 313 | if( iHead != fpid ){ |
| 314 | iClosed = forum_rid_is_closed(iHead, 1); |
| 315 | /*@ forumpost_emit_closed_state() %d(iHead), %d(iClosed)*/ |
| 316 | } |
| @@ -283,28 +319,32 @@ | |
| 319 | @ This post is CLOSED via a parent post. %s(zCommon)\ |
| 320 | @ </div> |
| 321 | return; |
| 322 | } |
| 323 | else if( iClosed==0 ){ |
| 324 | if( permClose==0 ) return; |
| 325 | @ <div class="warning forumpost-closure-warning"> |
| 326 | @ <form method="post" action="%R/forumpost_close"> |
| 327 | @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" /> |
| 328 | @ <input type="submit" value="CLOSE this post and its responses" /> |
| 329 | @ <span>%s(zCommon)</span> |
| 330 | @ <span>This does NOT save any pending changes in |
| 331 | @ the editor!</span> |
| 332 | @ </form></div> |
| 333 | return; |
| 334 | } |
| 335 | assert( iClosed>0 ); |
| 336 | /* Only show the "unlock" option on a post which is actually |
| 337 | ** closed, not on a post which inherits that state. */ |
| 338 | @ <div class="warning forumpost-closure-warning">\ |
| 339 | @ This post is CLOSED. %s(zCommon) |
| 340 | if( permClose ){ |
| 341 | @ <form method="post" action="%R/forumpost_reopen"> |
| 342 | @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" /> |
| 343 | @ <input type="submit" value="Re-open this post and its responses" /> |
| 344 | @ <span>This does NOT save any pending changes in |
| 345 | @ the editor!</span> |
| 346 | @ </form> |
| 347 | } |
| 348 | @ </div> |
| 349 | } |
| 350 | |
| @@ -884,13 +924,13 @@ | |
| 924 | @ <form action="%R/forumedit" method="POST"> |
| 925 | @ <input type="hidden" name="fpid" value="%s(p->zUuid)"> |
| 926 | if( !bPrivate ){ |
| 927 | /* Reply and Edit are only available if the post has been |
| 928 | ** approved. Closed threads can only be edited or replied to |
| 929 | ** if forumpost_may_close() is true but a user may delete |
| 930 | ** their own posts even if they are closed. */ |
| 931 | if( forumpost_may_close() || !iClosed ){ |
| 932 | @ <input type="submit" name="reply" value="Reply"> |
| 933 | if( g.perm.Admin || (bSameUser && !iClosed) ){ |
| 934 | @ <input type="submit" name="edit" value="Edit"> |
| 935 | } |
| 936 | if( g.perm.Admin || bSameUser ){ |
| @@ -913,11 +953,11 @@ | |
| 953 | }else if( bSameUser ){ |
| 954 | /* Allow users to delete (reject) their own pending posts. */ |
| 955 | @ <input type="submit" name="reject" value="Delete"> |
| 956 | } |
| 957 | @ </form> |
| 958 | if( bSelect && forumpost_may_close() && iClosed>=0 ){ |
| 959 | int iHead = forumpost_head_rid(p->fpid); |
| 960 | @ <form method="post" \ |
| 961 | @ action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'> |
| 962 | @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" /> |
| 963 | @ <input type="submit" value='%s(iClosed ? "Re-open" : "Close")' /> |
| @@ -1430,11 +1470,11 @@ | |
| 1470 | const char *zReason = 0; |
| 1471 | int fClose; |
| 1472 | int fpid; |
| 1473 | |
| 1474 | login_check_credentials(); |
| 1475 | if( forumpost_may_close()==0 ){ |
| 1476 | login_needed(g.anon.Admin); |
| 1477 | return; |
| 1478 | } |
| 1479 | fpid = symbolic_name_to_rid(zFpid, "f"); |
| 1480 | if( fpid<=0 ){ |
| 1481 |