Fossil

Check-in [162fc1e6aa]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment: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.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | forumpost-locking
Files: files | file ages | folders
SHA3-256: 162fc1e6aa1c6f3c54c5c39769b0af0d39f9cc617903f1169f9e42eaaad35dcf
User & Date: stephan 2023-06-06 19:38:32.092
Context
2023-06-07
12:07
Initial version of the /setup_forum page. Closed-Leaf check-in: b370c189bc user: stephan tags: forumpost-locking
2023-06-06
19:38
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. check-in: 162fc1e6aa user: stephan tags: forumpost-locking
2023-06-03
08:49
Merge trunk into forumpost-locking branch. check-in: 8e7de26aa2 user: stephan tags: forumpost-locking
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/db.c.
4648
4649
4650
4651
4652
4653
4654







4655
4656
4657
4658
4659
4660
4661
** If enabled on a client, new delta manifests are prohibited on
** commits.  If enabled on a server, whenever a client attempts
** to obtain a check-in lock during auto-sync, the server will 
** send the "pragma avoid-delta-manifests" statement in its reply,
** which will cause the client to avoid generating a delta
** manifest.
*/







/*
** SETTING: gdiff-command    width=40 default=gdiff sensitive
** The value is an external command to run when performing a graphical
** diff. If undefined, text diff will be used.
*/
/*
** SETTING: gmerge-command   width=40 sensitive







>
>
>
>
>
>
>







4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
** If enabled on a client, new delta manifests are prohibited on
** commits.  If enabled on a server, whenever a client attempts
** to obtain a check-in lock during auto-sync, the server will 
** send the "pragma avoid-delta-manifests" statement in its reply,
** which will cause the client to avoid generating a delta
** manifest.
*/
/*
** SETTING: forum-close-policy    boolean default=off
** If true, forum moderators may close forum posts, else only
** administrators may do so. Note that this only affects the forum web
** UI, not post-closing tags which arrive via the command-line or from
** synchronization with a remote.
*/
/*
** SETTING: gdiff-command    width=40 default=gdiff sensitive
** The value is an external command to run when performing a graphical
** diff. If undefined, text diff will be used.
*/
/*
** SETTING: gmerge-command   width=40 sensitive
Changes to src/forum.c.
259
260
261
262
263
264
265































266
267
268
269
270
271
272
273
274
275






276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293


294
295
296
297
298
299
300
301
302
303
304
305


306
307
308
309
310
311
312
  ** size of these artifacts, however, that would save little space,
  ** if any. */
  db_end_transaction(0);
  return 1;
}

/*































** If iClosed is true and the current user has admin privileges, this
** renders either a checkbox to unlock forum post fpid (if iClosed>0)
** or a SPAN.warning element that the given post inherits the CLOSED
** status from a parent post (if iClosed<0). If neither of the initial
** conditions is true, this is a no-op.
*/
static void forumpost_emit_closed_state(int fpid, int iClosed){
  const char *zCommon =
    "Only admins may edit or respond to closed posts.";
  int iHead = forumpost_head_rid(fpid);






  /*@ forumpost_emit_closed_state(%d(fpid), %d(iClosed))<br/>*/
  if( iHead != fpid ){
    iClosed = forum_rid_is_closed(iHead, 1);
    /*@ forumpost_emit_closed_state() %d(iHead), %d(iClosed)*/
  }
  if( iClosed<0 ){
    @ <div class="warning forumpost-closure-warning">\
    @ This post is CLOSED via a parent post. %s(zCommon)\
    @ </div>
    return;
  }
  else if( iClosed==0 ){
    if( g.perm.Admin==0 ) return;
    @ <div class="warning forumpost-closure-warning">
    @ <form method="post" action="%R/forumpost_close">
    @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" />
    @ <input type="submit" value="CLOSE this post and its responses" />
    @ %s(zCommon)


    @ </form></div>
    return;
  }
  assert( iClosed>0 );
  /* Only show the "unlock" checkbox on a post which is actually
  ** closed, not on a post which inherits that state. */
  @ <div class="warning forumpost-closure-warning">\
  @ This post is CLOSED. %s(zCommon)
  if( g.perm.Admin ){
    @ <form method="post" action="%R/forumpost_reopen">
    @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" />
    @ <input type="submit" value="Re-open this post and its responses" />


    @ </form>
  }
  @ </div>
}

/*
** Emits a warning that the current forum post is CLOSED and can only







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|


|
<

>
>
>
>
>
>












|




|
>
>




|



|



>
>







259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
  ** size of these artifacts, however, that would save little space,
  ** if any. */
  db_end_transaction(0);
  return 1;
}

/*
** Returns true if the forum-close-policy setting is true, else false,
** caching the result for subsequent calls.
*/
static int forumpost_close_policy(void){
  static int closePolicy = -99;

  if( closePolicy==-99 ){
    closePolicy = db_get_boolean("forum-close-policy",0)>0;
  }
  return closePolicy;
}

/*
** Returns 1 if the current user is an admin, -1 if the current user
** is a forum moderator and the forum-close-policy setting is true,
** else returns 0. The value is cached for subsequent calls.
*/
static int forumpost_may_close(void){
  static int permClose = -99;
  if( permClose!=-99 ){
    return permClose;
  }else if( g.perm.Admin ){
    return permClose = 1;
  }else if( g.perm.ModForum ){
    return permClose = forumpost_close_policy()>0 ? -1 : 0;
  }else{
    return permClose = 0;
  }  
}

/*
** If iClosed is true and the current user forumpost-close privileges,
** this renders either a checkbox to unlock forum post fpid (if
** iClosed>0) or a SPAN.warning element that the given post inherits
** the CLOSED status from a parent post (if iClosed<0). If neither of
** the initial conditions is true, this is a no-op.
*/
static void forumpost_emit_closed_state(int fpid, int iClosed){
  const char *zCommon;

  int iHead = forumpost_head_rid(fpid);
  const int permClose = forumpost_may_close();

  zCommon = forumpost_close_policy()==0
    ? "Admins may close or re-open posts, or respond to closed posts."
    : "Admins or moderators "
      "may close or re-open posts, or respond to closed posts.";
  /*@ forumpost_emit_closed_state(%d(fpid), %d(iClosed))<br/>*/
  if( iHead != fpid ){
    iClosed = forum_rid_is_closed(iHead, 1);
    /*@ forumpost_emit_closed_state() %d(iHead), %d(iClosed)*/
  }
  if( iClosed<0 ){
    @ <div class="warning forumpost-closure-warning">\
    @ This post is CLOSED via a parent post. %s(zCommon)\
    @ </div>
    return;
  }
  else if( iClosed==0 ){
    if( permClose==0 ) return;
    @ <div class="warning forumpost-closure-warning">
    @ <form method="post" action="%R/forumpost_close">
    @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" />
    @ <input type="submit" value="CLOSE this post and its responses" />
    @ <span>%s(zCommon)</span>
    @ <span>This does NOT save any pending changes in
    @ the editor!</span>
    @ </form></div>
    return;
  }
  assert( iClosed>0 );
  /* Only show the "unlock" option on a post which is actually
  ** closed, not on a post which inherits that state. */
  @ <div class="warning forumpost-closure-warning">\
  @ This post is CLOSED. %s(zCommon)
  if( permClose ){
    @ <form method="post" action="%R/forumpost_reopen">
    @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" />
    @ <input type="submit" value="Re-open this post and its responses" />
    @ <span>This does NOT save any pending changes in
    @ the editor!</span>
    @ </form>
  }
  @ </div>
}

/*
** Emits a warning that the current forum post is CLOSED and can only
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
    if( g.perm.WrForum && !p->pEditTail ){
      @ <div class="forumpost-single-controls">\
      @ <form action="%R/forumedit" method="POST">
      @ <input type="hidden" name="fpid" value="%s(p->zUuid)">
      if( !bPrivate ){
        /* Reply and Edit are only available if the post has been
        ** approved.  Closed threads can only be edited or replied to
        ** by an admin but a user may delete their own posts even if
        ** they are closed. */
        if( g.perm.Admin || !iClosed ){
          @ <input type="submit" name="reply" value="Reply">
          if( g.perm.Admin || (bSameUser && !iClosed) ){
            @ <input type="submit" name="edit" value="Edit">
          }
          if( g.perm.Admin || bSameUser ){
            @ <input type="submit" name="nullout" value="Delete">
          }







|
|
|







922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
    if( g.perm.WrForum && !p->pEditTail ){
      @ <div class="forumpost-single-controls">\
      @ <form action="%R/forumedit" method="POST">
      @ <input type="hidden" name="fpid" value="%s(p->zUuid)">
      if( !bPrivate ){
        /* Reply and Edit are only available if the post has been
        ** approved.  Closed threads can only be edited or replied to
        ** if forumpost_may_close() is true but a user may delete
        ** their own posts even if they are closed. */
        if( forumpost_may_close() || !iClosed ){
          @ <input type="submit" name="reply" value="Reply">
          if( g.perm.Admin || (bSameUser && !iClosed) ){
            @ <input type="submit" name="edit" value="Edit">
          }
          if( g.perm.Admin || bSameUser ){
            @ <input type="submit" name="nullout" value="Delete">
          }
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
          @ <input type="hidden" name="trustuser" value="%h(pManifest->zUser)">
        }
      }else if( bSameUser ){
        /* Allow users to delete (reject) their own pending posts. */
        @ <input type="submit" name="reject" value="Delete">
      }
      @ </form>
      if( bSelect && g.perm.Admin && iClosed>=0 ){
        int iHead = forumpost_head_rid(p->fpid);
        @ <form method="post" \
        @  action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'>
        @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" />
        @ <input type="submit" value='%s(iClosed ? "Re-open" : "Close")' />
        @ </form>
      }







|







951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
          @ <input type="hidden" name="trustuser" value="%h(pManifest->zUser)">
        }
      }else if( bSameUser ){
        /* Allow users to delete (reject) their own pending posts. */
        @ <input type="submit" name="reject" value="Delete">
      }
      @ </form>
      if( bSelect && forumpost_may_close() && iClosed>=0 ){
        int iHead = forumpost_head_rid(p->fpid);
        @ <form method="post" \
        @  action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'>
        @ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" />
        @ <input type="submit" value='%s(iClosed ? "Re-open" : "Close")' />
        @ </form>
      }
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
void forum_page_close(void){
  const char *zFpid = PD("fpid","");
  const char *zReason = 0;
  int fClose;
  int fpid;

  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(g.anon.Admin);
    return;
  }
  fpid = symbolic_name_to_rid(zFpid, "f");
  if( fpid<=0 ){
    webpage_error("Missing or invalid fpid query parameter");
  }







|







1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
void forum_page_close(void){
  const char *zFpid = PD("fpid","");
  const char *zReason = 0;
  int fClose;
  int fpid;

  login_check_credentials();
  if( forumpost_may_close()==0 ){
    login_needed(g.anon.Admin);
    return;
  }
  fpid = symbolic_name_to_rid(zFpid, "f");
  if( fpid<=0 ){
    webpage_error("Missing or invalid fpid query parameter");
  }