Fossil SCM

Improved titles for forum posts that show the original poster and the latest editor if they are different people.

drh 2020-08-25 14:34 trunk
Commit 9543ddbef25dcb49fd6fcfb290494449f008c1532065042cdb41793cbb9ed2df
1 file changed +83 -54
+83 -54
--- src/forum.c
+++ src/forum.c
@@ -35,10 +35,12 @@
3535
struct ForumPost {
3636
int fpid; /* rid for this post */
3737
int sid; /* Serial ID number */
3838
int rev; /* Revision number */
3939
char *zUuid; /* Artifact hash */
40
+ char *zDisplayName; /* Name of user who wrote this post */
41
+ double rDate; /* Date for this post */
4042
ForumPost *pIrt; /* This post replies to pIrt */
4143
ForumPost *pEditHead; /* Original, unedited post */
4244
ForumPost *pEditTail; /* Most recent edit for this post */
4345
ForumPost *pEditNext; /* This post is edited by pEditNext */
4446
ForumPost *pEditPrev; /* This post is an edit of pEditPrev */
@@ -84,10 +86,11 @@
8486
static void forumthread_delete(ForumThread *pThread){
8587
ForumPost *pPost, *pNext;
8688
for(pPost=pThread->pFirst; pPost; pPost = pNext){
8789
pNext = pPost->pNext;
8890
fossil_free(pPost->zUuid);
91
+ fossil_free(pPost->zDisplayName);
8992
fossil_free(pPost);
9093
}
9194
fossil_free(pThread);
9295
}
9396
@@ -163,11 +166,11 @@
163166
int sid = 1;
164167
int firt, fprev;
165168
pThread = fossil_malloc( sizeof(*pThread) );
166169
memset(pThread, 0, sizeof(*pThread));
167170
db_prepare(&q,
168
- "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)"
171
+ "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid), fmtime"
169172
" FROM forumpost"
170173
" WHERE froot=%d ORDER BY fmtime",
171174
froot
172175
);
173176
while( db_step(&q)==SQLITE_ROW ){
@@ -175,10 +178,11 @@
175178
memset(pPost, 0, sizeof(*pPost));
176179
pPost->fpid = db_column_int(&q, 0);
177180
firt = db_column_int(&q, 1);
178181
fprev = db_column_int(&q, 2);
179182
pPost->zUuid = fossil_strdup(db_column_text(&q,3));
183
+ pPost->rDate = db_column_double(&q,4);
180184
if( !fprev ) pPost->sid = sid++;
181185
pPost->pPrev = pThread->pLast;
182186
pPost->pNext = 0;
183187
if( pThread->pLast==0 ){
184188
pThread->pFirst = pPost;
@@ -367,11 +371,11 @@
367371
** name just be the login.
368372
**
369373
** Space to hold the returned name is obtained from fossil_strdup() or
370374
** mprintf() and should be freed by the caller.
371375
*/
372
-char *display_name_from_login(const char *zLogin){
376
+static char *display_name_from_login(const char *zLogin){
373377
static Stmt q;
374378
char *zResult;
375379
db_static_prepare(&q,
376380
"SELECT display_name(info) FROM user WHERE login=$login"
377381
);
@@ -387,10 +391,34 @@
387391
zResult = fossil_strdup(zLogin);
388392
}
389393
db_reset(&q);
390394
return zResult;
391395
}
396
+
397
+/*
398
+** Compute and return the display name for a ForumPost. If
399
+** pManifest is not NULL, then it is a Manifest object for the post.
400
+** if pManifest is NULL, this routine has to fetch and parse the
401
+** Manifest object for itself.
402
+**
403
+** Memory to hold the display name is attached to p->zDisplayName
404
+** and will be freed together with the ForumPost object p when it
405
+** is freed.
406
+*/
407
+static char *forum_post_display_name(ForumPost *p, Manifest *pManifest){
408
+ Manifest *pToFree = 0;
409
+ if( p->zDisplayName ) return p->zDisplayName;
410
+ if( pManifest==0 ){
411
+ pManifest = pToFree = manifest_get(p->fpid, CFTYPE_FORUM, 0);
412
+ if( pManifest==0 ) return "(unknown)";
413
+ }
414
+ p->zDisplayName = display_name_from_login(pManifest->zUser);
415
+ if( pToFree ) manifest_destroy(pToFree);
416
+ if( p->zDisplayName==0 ) return "(unknown)";
417
+ return p->zDisplayName;
418
+}
419
+
392420
393421
/*
394422
** Display a single post in a forum thread.
395423
*/
396424
static void forum_display_post(
@@ -400,29 +428,23 @@
400428
int bUnf, /* True to leave the post unformatted */
401429
int bHist, /* True if showing edit history */
402430
int bSelect, /* True if this is the selected post */
403431
char *zQuery /* Common query string */
404432
){
405
- char *zDisplayName; /* The display name */
433
+ char *zPosterName; /* Name of user who originally made this post */
434
+ char *zEditorName; /* Name of user who provided the current edit */
406435
char *zDate; /* The time/date string */
407436
char *zHist; /* History query string */
408
- Manifest *pOriginal; /* Original post artifact */
409
- Manifest *pRevised; /* Revised post artifact (may be same as pOriginal) */
437
+ Manifest *pManifest; /* Manifest comprising the current post */
410438
int bPrivate; /* True for posts awaiting moderation */
411439
int bSameUser; /* True if author is also the reader */
412440
int iIndent; /* Indent level */
413441
const char *zMimetype;/* Formatting MIME type */
414442
415
- /* Get the original and revised artifacts for the post. Abort if either is
416
- ** not found (e.g. shunned). */
417
- if( p->pEditHead ){
418
- pOriginal = manifest_get(p->pEditHead->fpid, CFTYPE_FORUM, 0);
419
- pRevised = manifest_get(p->fpid, CFTYPE_FORUM, 0);
420
- }else{
421
- pOriginal = pRevised = manifest_get(p->fpid, CFTYPE_FORUM, 0);
422
- }
423
- if( !pOriginal || !pRevised ) return;
443
+ /* Get the manifest for the post. Abort if not found (e.g. shunned). */
444
+ pManifest = manifest_get(p->fpid, CFTYPE_FORUM, 0);
445
+ if( !pManifest ) return;
424446
425447
/* When not in raw mode, create the border around the post. */
426448
if( !bRaw ){
427449
/* Open the <div> enclosing the post. Set the class string to mark the post
428450
** as selected and/or obsolete. */
@@ -434,55 +456,72 @@
434456
@ style='margin-left: %d(iIndent*iIndentScale)ex'
435457
}
436458
@ >
437459
438460
/* If this is the first post (or an edit thereof), emit the thread title. */
439
- if( pRevised->zThreadTitle ){
440
- @ <h1>%h(pRevised->zThreadTitle)</h1>
461
+ if( pManifest->zThreadTitle ){
462
+ @ <h1>%h(pManifest->zThreadTitle)</h1>
441463
}
442464
443
- /* Emit the serial number, revision number, author, and date. */
444
- zDisplayName = display_name_from_login(pOriginal->zUser);
445
- zDate = db_text(0, "SELECT datetime(%.17g)", pOriginal->rDate);
446
- @ <h3 class='forumPostHdr'>(%d(p->sid)\
447
- if( p->nEdit ){
448
- @ .%.*d(fossil_num_digits(p->nEdit))(p->rev)\
449
- }
450
- @ ) By %h(zDisplayName) on %h(zDate)
451
- fossil_free(zDisplayName);
465
+ /* Begin emitting the header line. The forum of the title
466
+ ** varies depending on whether:
467
+ ** * The post is uneditted
468
+ ** * The post was last editted by the original author
469
+ ** * The post was last editted by a different person
470
+ */
471
+ if( p->pEditHead ){
472
+ zDate = db_text(0, "SELECT datetime(%.17g)", p->pEditHead->rDate);
473
+ }else{
474
+ zPosterName = forum_post_display_name(p, pManifest);
475
+ zEditorName = zPosterName;
476
+ }
477
+ zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate);
478
+ if( p->pEditPrev ){
479
+ zPosterName = forum_post_display_name(p->pEditHead, 0);
480
+ zEditorName = forum_post_display_name(p, pManifest);
481
+ zHist = bHist ? "" : "&hist";
482
+ @ <h3 class='forumPostHdr'>(%d(p->sid)\
483
+ @ .%0*d(fossil_num_digits(p->nEdit))(p->rev)) \
484
+ if( fossil_strcmp(zPosterName, zEditorName)==0 ){
485
+ @ By %h(zPosterName) on %h(zDate) editted from \
486
+ @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
487
+ @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
488
+ }else{
489
+ @ Originally by %h(zPosterName) \
490
+ @ with edits by %h(zEditorName) on %h(zDate) from \
491
+ @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
492
+ @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
493
+ }
494
+ }else{
495
+ zPosterName = forum_post_display_name(p, pManifest);
496
+ @ <h3 class='forumPostHdr'>(%d(p->sid)) \
497
+ @ By %h(zPosterName) on %h(zDate)
498
+ }
452499
fossil_free(zDate);
500
+
453501
454502
/* If debugging is enabled, link to the artifact page. */
455503
if( g.perm.Debug ){
456504
@ <span class="debug">\
457505
@ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
458506
}
459507
460
- /* If this is an edit, refer back to the old version. Be sure "hist" is in
461
- ** the query string so the old version will actually be shown. */
462
- if( p->pEditPrev ){
463
- zHist = bHist ? "" : "&hist";
464
- @ edit of \
465
- @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
466
- @ %d(p->sid).%.*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
467
- }
468
-
469508
/* If this is a reply, refer back to the parent post. */
470509
if( p->pIrt ){
471510
@ in reply to %z(href("%R/forumpost/%S?%s",p->pIrt->zUuid,zQuery))\
472511
@ %d(p->pIrt->sid)\
473512
if( p->pIrt->nEdit ){
474
- @ .%.*d(fossil_num_digits(p->pIrt->nEdit))(p->pIrt->rev)\
513
+ @ .%0*d(fossil_num_digits(p->pIrt->nEdit))(p->pIrt->rev)\
475514
}
476515
@ </a>
477516
}
478517
479518
/* If this post was later edited, refer forward to the next edit. */
480519
if( p->pEditNext ){
481520
@ updated by %z(href("%R/forumpost/%S?%s",p->pEditNext->zUuid,zQuery))\
482521
@ %d(p->pEditNext->sid)\
483
- @ .%.*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a>
522
+ @ .%0*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a>
484523
}
485524
486525
/* Provide a link to select the individual post. */
487526
if( !bSelect ){
488527
@ %z(href("%R/forumpost/%S?%s",p->zUuid,zQuery))[link]</a>
@@ -490,37 +529,28 @@
490529
491530
/* Provide a link to the raw source code. */
492531
if( !bUnf ){
493532
@ %z(href("%R/forumpost/%S?raw",p->zUuid))[source]</a>
494533
}
495
-
496
- /* If this is an edit, identify the editor and date. */
497
- if( p->pEditPrev ){
498
- zDisplayName = display_name_from_login(pRevised->zUser);
499
- zDate = db_text(0, "SELECT datetime(%.17g)", pRevised->rDate);
500
- @ <br>Edited by %h(zDisplayName) on %h(zDate)
501
- fossil_free(zDisplayName);
502
- fossil_free(zDate);
503
- }
504534
@ </h3>
505535
}
506536
507537
/* Check if this post is approved, also if it's by the current user. */
508538
bPrivate = content_is_private(p->fpid);
509539
bSameUser = login_is_individual()
510
- && fossil_strcmp(pOriginal->zUser, g.zLogin)==0;
540
+ && fossil_strcmp(pManifest->zUser, g.zLogin)==0;
511541
512542
/* Render the post if the user is able to see it. */
513543
if( bPrivate && !g.perm.ModForum && !bSameUser ){
514544
@ <p><span class="modpending">Awaiting Moderator Approval</span></p>
515545
}else{
516546
if( bRaw || bUnf || p->pEditTail ){
517547
zMimetype = "text/plain";
518548
}else{
519
- zMimetype = pRevised->zMimetype;
549
+ zMimetype = pManifest->zMimetype;
520550
}
521
- forum_render(0, zMimetype, pRevised->zWiki, 0, !bRaw);
551
+ forum_render(0, zMimetype, pManifest->zWiki, 0, !bRaw);
522552
}
523553
524554
/* When not in raw mode, finish creating the border around the post. */
525555
if( !bRaw ){
526556
/* If the user is able to write to the forum and if this post has not been
@@ -539,16 +569,16 @@
539569
/* Allow moderators to approve or reject pending posts. Also allow
540570
** forum supervisors to mark non-special users as trusted and therefore
541571
** able to post unmoderated. */
542572
@ <input type="submit" name="approve" value="Approve">
543573
@ <input type="submit" name="reject" value="Reject">
544
- if( g.perm.AdminForum && !login_is_special(pOriginal->zUser) ){
574
+ if( g.perm.AdminForum && !login_is_special(pManifest->zUser) ){
545575
@ <br><label><input type="checkbox" name="trust">
546
- @ Trust user "%h(pOriginal->zUser)" so that future posts by \
547
- @ "%h(pOriginal->zUser)" do not require moderation.
576
+ @ Trust user "%h(pManifest->zUser)" so that future posts by \
577
+ @ "%h(pManifest->zUser)" do not require moderation.
548578
@ </label>
549
- @ <input type="hidden" name="trustuser" value="%h(pOriginal->zUser)">
579
+ @ <input type="hidden" name="trustuser" value="%h(pManifest->zUser)">
550580
}
551581
}else if( bSameUser ){
552582
/* Allow users to delete (reject) their own pending posts. */
553583
@ <input type="submit" name="reject" value="Delete">
554584
}
@@ -556,12 +586,11 @@
556586
}
557587
@ </div>
558588
}
559589
560590
/* Clean up. */
561
- if( pRevised!=pOriginal ) manifest_destroy(pRevised);
562
- manifest_destroy(pOriginal);
591
+ manifest_destroy(pManifest);
563592
}
564593
565594
/*
566595
** Possible display modes for forum_display_thread().
567596
*/
568597
--- src/forum.c
+++ src/forum.c
@@ -35,10 +35,12 @@
35 struct ForumPost {
36 int fpid; /* rid for this post */
37 int sid; /* Serial ID number */
38 int rev; /* Revision number */
39 char *zUuid; /* Artifact hash */
 
 
40 ForumPost *pIrt; /* This post replies to pIrt */
41 ForumPost *pEditHead; /* Original, unedited post */
42 ForumPost *pEditTail; /* Most recent edit for this post */
43 ForumPost *pEditNext; /* This post is edited by pEditNext */
44 ForumPost *pEditPrev; /* This post is an edit of pEditPrev */
@@ -84,10 +86,11 @@
84 static void forumthread_delete(ForumThread *pThread){
85 ForumPost *pPost, *pNext;
86 for(pPost=pThread->pFirst; pPost; pPost = pNext){
87 pNext = pPost->pNext;
88 fossil_free(pPost->zUuid);
 
89 fossil_free(pPost);
90 }
91 fossil_free(pThread);
92 }
93
@@ -163,11 +166,11 @@
163 int sid = 1;
164 int firt, fprev;
165 pThread = fossil_malloc( sizeof(*pThread) );
166 memset(pThread, 0, sizeof(*pThread));
167 db_prepare(&q,
168 "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)"
169 " FROM forumpost"
170 " WHERE froot=%d ORDER BY fmtime",
171 froot
172 );
173 while( db_step(&q)==SQLITE_ROW ){
@@ -175,10 +178,11 @@
175 memset(pPost, 0, sizeof(*pPost));
176 pPost->fpid = db_column_int(&q, 0);
177 firt = db_column_int(&q, 1);
178 fprev = db_column_int(&q, 2);
179 pPost->zUuid = fossil_strdup(db_column_text(&q,3));
 
180 if( !fprev ) pPost->sid = sid++;
181 pPost->pPrev = pThread->pLast;
182 pPost->pNext = 0;
183 if( pThread->pLast==0 ){
184 pThread->pFirst = pPost;
@@ -367,11 +371,11 @@
367 ** name just be the login.
368 **
369 ** Space to hold the returned name is obtained from fossil_strdup() or
370 ** mprintf() and should be freed by the caller.
371 */
372 char *display_name_from_login(const char *zLogin){
373 static Stmt q;
374 char *zResult;
375 db_static_prepare(&q,
376 "SELECT display_name(info) FROM user WHERE login=$login"
377 );
@@ -387,10 +391,34 @@
387 zResult = fossil_strdup(zLogin);
388 }
389 db_reset(&q);
390 return zResult;
391 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
393 /*
394 ** Display a single post in a forum thread.
395 */
396 static void forum_display_post(
@@ -400,29 +428,23 @@
400 int bUnf, /* True to leave the post unformatted */
401 int bHist, /* True if showing edit history */
402 int bSelect, /* True if this is the selected post */
403 char *zQuery /* Common query string */
404 ){
405 char *zDisplayName; /* The display name */
 
406 char *zDate; /* The time/date string */
407 char *zHist; /* History query string */
408 Manifest *pOriginal; /* Original post artifact */
409 Manifest *pRevised; /* Revised post artifact (may be same as pOriginal) */
410 int bPrivate; /* True for posts awaiting moderation */
411 int bSameUser; /* True if author is also the reader */
412 int iIndent; /* Indent level */
413 const char *zMimetype;/* Formatting MIME type */
414
415 /* Get the original and revised artifacts for the post. Abort if either is
416 ** not found (e.g. shunned). */
417 if( p->pEditHead ){
418 pOriginal = manifest_get(p->pEditHead->fpid, CFTYPE_FORUM, 0);
419 pRevised = manifest_get(p->fpid, CFTYPE_FORUM, 0);
420 }else{
421 pOriginal = pRevised = manifest_get(p->fpid, CFTYPE_FORUM, 0);
422 }
423 if( !pOriginal || !pRevised ) return;
424
425 /* When not in raw mode, create the border around the post. */
426 if( !bRaw ){
427 /* Open the <div> enclosing the post. Set the class string to mark the post
428 ** as selected and/or obsolete. */
@@ -434,55 +456,72 @@
434 @ style='margin-left: %d(iIndent*iIndentScale)ex'
435 }
436 @ >
437
438 /* If this is the first post (or an edit thereof), emit the thread title. */
439 if( pRevised->zThreadTitle ){
440 @ <h1>%h(pRevised->zThreadTitle)</h1>
441 }
442
443 /* Emit the serial number, revision number, author, and date. */
444 zDisplayName = display_name_from_login(pOriginal->zUser);
445 zDate = db_text(0, "SELECT datetime(%.17g)", pOriginal->rDate);
446 @ <h3 class='forumPostHdr'>(%d(p->sid)\
447 if( p->nEdit ){
448 @ .%.*d(fossil_num_digits(p->nEdit))(p->rev)\
449 }
450 @ ) By %h(zDisplayName) on %h(zDate)
451 fossil_free(zDisplayName);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452 fossil_free(zDate);
 
453
454 /* If debugging is enabled, link to the artifact page. */
455 if( g.perm.Debug ){
456 @ <span class="debug">\
457 @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
458 }
459
460 /* If this is an edit, refer back to the old version. Be sure "hist" is in
461 ** the query string so the old version will actually be shown. */
462 if( p->pEditPrev ){
463 zHist = bHist ? "" : "&hist";
464 @ edit of \
465 @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
466 @ %d(p->sid).%.*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
467 }
468
469 /* If this is a reply, refer back to the parent post. */
470 if( p->pIrt ){
471 @ in reply to %z(href("%R/forumpost/%S?%s",p->pIrt->zUuid,zQuery))\
472 @ %d(p->pIrt->sid)\
473 if( p->pIrt->nEdit ){
474 @ .%.*d(fossil_num_digits(p->pIrt->nEdit))(p->pIrt->rev)\
475 }
476 @ </a>
477 }
478
479 /* If this post was later edited, refer forward to the next edit. */
480 if( p->pEditNext ){
481 @ updated by %z(href("%R/forumpost/%S?%s",p->pEditNext->zUuid,zQuery))\
482 @ %d(p->pEditNext->sid)\
483 @ .%.*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a>
484 }
485
486 /* Provide a link to select the individual post. */
487 if( !bSelect ){
488 @ %z(href("%R/forumpost/%S?%s",p->zUuid,zQuery))[link]</a>
@@ -490,37 +529,28 @@
490
491 /* Provide a link to the raw source code. */
492 if( !bUnf ){
493 @ %z(href("%R/forumpost/%S?raw",p->zUuid))[source]</a>
494 }
495
496 /* If this is an edit, identify the editor and date. */
497 if( p->pEditPrev ){
498 zDisplayName = display_name_from_login(pRevised->zUser);
499 zDate = db_text(0, "SELECT datetime(%.17g)", pRevised->rDate);
500 @ <br>Edited by %h(zDisplayName) on %h(zDate)
501 fossil_free(zDisplayName);
502 fossil_free(zDate);
503 }
504 @ </h3>
505 }
506
507 /* Check if this post is approved, also if it's by the current user. */
508 bPrivate = content_is_private(p->fpid);
509 bSameUser = login_is_individual()
510 && fossil_strcmp(pOriginal->zUser, g.zLogin)==0;
511
512 /* Render the post if the user is able to see it. */
513 if( bPrivate && !g.perm.ModForum && !bSameUser ){
514 @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
515 }else{
516 if( bRaw || bUnf || p->pEditTail ){
517 zMimetype = "text/plain";
518 }else{
519 zMimetype = pRevised->zMimetype;
520 }
521 forum_render(0, zMimetype, pRevised->zWiki, 0, !bRaw);
522 }
523
524 /* When not in raw mode, finish creating the border around the post. */
525 if( !bRaw ){
526 /* If the user is able to write to the forum and if this post has not been
@@ -539,16 +569,16 @@
539 /* Allow moderators to approve or reject pending posts. Also allow
540 ** forum supervisors to mark non-special users as trusted and therefore
541 ** able to post unmoderated. */
542 @ <input type="submit" name="approve" value="Approve">
543 @ <input type="submit" name="reject" value="Reject">
544 if( g.perm.AdminForum && !login_is_special(pOriginal->zUser) ){
545 @ <br><label><input type="checkbox" name="trust">
546 @ Trust user "%h(pOriginal->zUser)" so that future posts by \
547 @ "%h(pOriginal->zUser)" do not require moderation.
548 @ </label>
549 @ <input type="hidden" name="trustuser" value="%h(pOriginal->zUser)">
550 }
551 }else if( bSameUser ){
552 /* Allow users to delete (reject) their own pending posts. */
553 @ <input type="submit" name="reject" value="Delete">
554 }
@@ -556,12 +586,11 @@
556 }
557 @ </div>
558 }
559
560 /* Clean up. */
561 if( pRevised!=pOriginal ) manifest_destroy(pRevised);
562 manifest_destroy(pOriginal);
563 }
564
565 /*
566 ** Possible display modes for forum_display_thread().
567 */
568
--- src/forum.c
+++ src/forum.c
@@ -35,10 +35,12 @@
35 struct ForumPost {
36 int fpid; /* rid for this post */
37 int sid; /* Serial ID number */
38 int rev; /* Revision number */
39 char *zUuid; /* Artifact hash */
40 char *zDisplayName; /* Name of user who wrote this post */
41 double rDate; /* Date for this post */
42 ForumPost *pIrt; /* This post replies to pIrt */
43 ForumPost *pEditHead; /* Original, unedited post */
44 ForumPost *pEditTail; /* Most recent edit for this post */
45 ForumPost *pEditNext; /* This post is edited by pEditNext */
46 ForumPost *pEditPrev; /* This post is an edit of pEditPrev */
@@ -84,10 +86,11 @@
86 static void forumthread_delete(ForumThread *pThread){
87 ForumPost *pPost, *pNext;
88 for(pPost=pThread->pFirst; pPost; pPost = pNext){
89 pNext = pPost->pNext;
90 fossil_free(pPost->zUuid);
91 fossil_free(pPost->zDisplayName);
92 fossil_free(pPost);
93 }
94 fossil_free(pThread);
95 }
96
@@ -163,11 +166,11 @@
166 int sid = 1;
167 int firt, fprev;
168 pThread = fossil_malloc( sizeof(*pThread) );
169 memset(pThread, 0, sizeof(*pThread));
170 db_prepare(&q,
171 "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid), fmtime"
172 " FROM forumpost"
173 " WHERE froot=%d ORDER BY fmtime",
174 froot
175 );
176 while( db_step(&q)==SQLITE_ROW ){
@@ -175,10 +178,11 @@
178 memset(pPost, 0, sizeof(*pPost));
179 pPost->fpid = db_column_int(&q, 0);
180 firt = db_column_int(&q, 1);
181 fprev = db_column_int(&q, 2);
182 pPost->zUuid = fossil_strdup(db_column_text(&q,3));
183 pPost->rDate = db_column_double(&q,4);
184 if( !fprev ) pPost->sid = sid++;
185 pPost->pPrev = pThread->pLast;
186 pPost->pNext = 0;
187 if( pThread->pLast==0 ){
188 pThread->pFirst = pPost;
@@ -367,11 +371,11 @@
371 ** name just be the login.
372 **
373 ** Space to hold the returned name is obtained from fossil_strdup() or
374 ** mprintf() and should be freed by the caller.
375 */
376 static char *display_name_from_login(const char *zLogin){
377 static Stmt q;
378 char *zResult;
379 db_static_prepare(&q,
380 "SELECT display_name(info) FROM user WHERE login=$login"
381 );
@@ -387,10 +391,34 @@
391 zResult = fossil_strdup(zLogin);
392 }
393 db_reset(&q);
394 return zResult;
395 }
396
397 /*
398 ** Compute and return the display name for a ForumPost. If
399 ** pManifest is not NULL, then it is a Manifest object for the post.
400 ** if pManifest is NULL, this routine has to fetch and parse the
401 ** Manifest object for itself.
402 **
403 ** Memory to hold the display name is attached to p->zDisplayName
404 ** and will be freed together with the ForumPost object p when it
405 ** is freed.
406 */
407 static char *forum_post_display_name(ForumPost *p, Manifest *pManifest){
408 Manifest *pToFree = 0;
409 if( p->zDisplayName ) return p->zDisplayName;
410 if( pManifest==0 ){
411 pManifest = pToFree = manifest_get(p->fpid, CFTYPE_FORUM, 0);
412 if( pManifest==0 ) return "(unknown)";
413 }
414 p->zDisplayName = display_name_from_login(pManifest->zUser);
415 if( pToFree ) manifest_destroy(pToFree);
416 if( p->zDisplayName==0 ) return "(unknown)";
417 return p->zDisplayName;
418 }
419
420
421 /*
422 ** Display a single post in a forum thread.
423 */
424 static void forum_display_post(
@@ -400,29 +428,23 @@
428 int bUnf, /* True to leave the post unformatted */
429 int bHist, /* True if showing edit history */
430 int bSelect, /* True if this is the selected post */
431 char *zQuery /* Common query string */
432 ){
433 char *zPosterName; /* Name of user who originally made this post */
434 char *zEditorName; /* Name of user who provided the current edit */
435 char *zDate; /* The time/date string */
436 char *zHist; /* History query string */
437 Manifest *pManifest; /* Manifest comprising the current post */
 
438 int bPrivate; /* True for posts awaiting moderation */
439 int bSameUser; /* True if author is also the reader */
440 int iIndent; /* Indent level */
441 const char *zMimetype;/* Formatting MIME type */
442
443 /* Get the manifest for the post. Abort if not found (e.g. shunned). */
444 pManifest = manifest_get(p->fpid, CFTYPE_FORUM, 0);
445 if( !pManifest ) return;
 
 
 
 
 
 
446
447 /* When not in raw mode, create the border around the post. */
448 if( !bRaw ){
449 /* Open the <div> enclosing the post. Set the class string to mark the post
450 ** as selected and/or obsolete. */
@@ -434,55 +456,72 @@
456 @ style='margin-left: %d(iIndent*iIndentScale)ex'
457 }
458 @ >
459
460 /* If this is the first post (or an edit thereof), emit the thread title. */
461 if( pManifest->zThreadTitle ){
462 @ <h1>%h(pManifest->zThreadTitle)</h1>
463 }
464
465 /* Begin emitting the header line. The forum of the title
466 ** varies depending on whether:
467 ** * The post is uneditted
468 ** * The post was last editted by the original author
469 ** * The post was last editted by a different person
470 */
471 if( p->pEditHead ){
472 zDate = db_text(0, "SELECT datetime(%.17g)", p->pEditHead->rDate);
473 }else{
474 zPosterName = forum_post_display_name(p, pManifest);
475 zEditorName = zPosterName;
476 }
477 zDate = db_text(0, "SELECT datetime(%.17g)", p->rDate);
478 if( p->pEditPrev ){
479 zPosterName = forum_post_display_name(p->pEditHead, 0);
480 zEditorName = forum_post_display_name(p, pManifest);
481 zHist = bHist ? "" : "&hist";
482 @ <h3 class='forumPostHdr'>(%d(p->sid)\
483 @ .%0*d(fossil_num_digits(p->nEdit))(p->rev)) \
484 if( fossil_strcmp(zPosterName, zEditorName)==0 ){
485 @ By %h(zPosterName) on %h(zDate) editted from \
486 @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
487 @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
488 }else{
489 @ Originally by %h(zPosterName) \
490 @ with edits by %h(zEditorName) on %h(zDate) from \
491 @ %z(href("%R/forumpost/%S?%s%s",p->pEditPrev->zUuid,zQuery,zHist))\
492 @ %d(p->sid).%0*d(fossil_num_digits(p->nEdit))(p->pEditPrev->rev)</a>
493 }
494 }else{
495 zPosterName = forum_post_display_name(p, pManifest);
496 @ <h3 class='forumPostHdr'>(%d(p->sid)) \
497 @ By %h(zPosterName) on %h(zDate)
498 }
499 fossil_free(zDate);
500
501
502 /* If debugging is enabled, link to the artifact page. */
503 if( g.perm.Debug ){
504 @ <span class="debug">\
505 @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
506 }
507
 
 
 
 
 
 
 
 
 
508 /* If this is a reply, refer back to the parent post. */
509 if( p->pIrt ){
510 @ in reply to %z(href("%R/forumpost/%S?%s",p->pIrt->zUuid,zQuery))\
511 @ %d(p->pIrt->sid)\
512 if( p->pIrt->nEdit ){
513 @ .%0*d(fossil_num_digits(p->pIrt->nEdit))(p->pIrt->rev)\
514 }
515 @ </a>
516 }
517
518 /* If this post was later edited, refer forward to the next edit. */
519 if( p->pEditNext ){
520 @ updated by %z(href("%R/forumpost/%S?%s",p->pEditNext->zUuid,zQuery))\
521 @ %d(p->pEditNext->sid)\
522 @ .%0*d(fossil_num_digits(p->nEdit))(p->pEditNext->rev)</a>
523 }
524
525 /* Provide a link to select the individual post. */
526 if( !bSelect ){
527 @ %z(href("%R/forumpost/%S?%s",p->zUuid,zQuery))[link]</a>
@@ -490,37 +529,28 @@
529
530 /* Provide a link to the raw source code. */
531 if( !bUnf ){
532 @ %z(href("%R/forumpost/%S?raw",p->zUuid))[source]</a>
533 }
 
 
 
 
 
 
 
 
 
534 @ </h3>
535 }
536
537 /* Check if this post is approved, also if it's by the current user. */
538 bPrivate = content_is_private(p->fpid);
539 bSameUser = login_is_individual()
540 && fossil_strcmp(pManifest->zUser, g.zLogin)==0;
541
542 /* Render the post if the user is able to see it. */
543 if( bPrivate && !g.perm.ModForum && !bSameUser ){
544 @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
545 }else{
546 if( bRaw || bUnf || p->pEditTail ){
547 zMimetype = "text/plain";
548 }else{
549 zMimetype = pManifest->zMimetype;
550 }
551 forum_render(0, zMimetype, pManifest->zWiki, 0, !bRaw);
552 }
553
554 /* When not in raw mode, finish creating the border around the post. */
555 if( !bRaw ){
556 /* If the user is able to write to the forum and if this post has not been
@@ -539,16 +569,16 @@
569 /* Allow moderators to approve or reject pending posts. Also allow
570 ** forum supervisors to mark non-special users as trusted and therefore
571 ** able to post unmoderated. */
572 @ <input type="submit" name="approve" value="Approve">
573 @ <input type="submit" name="reject" value="Reject">
574 if( g.perm.AdminForum && !login_is_special(pManifest->zUser) ){
575 @ <br><label><input type="checkbox" name="trust">
576 @ Trust user "%h(pManifest->zUser)" so that future posts by \
577 @ "%h(pManifest->zUser)" do not require moderation.
578 @ </label>
579 @ <input type="hidden" name="trustuser" value="%h(pManifest->zUser)">
580 }
581 }else if( bSameUser ){
582 /* Allow users to delete (reject) their own pending posts. */
583 @ <input type="submit" name="reject" value="Delete">
584 }
@@ -556,12 +586,11 @@
586 }
587 @ </div>
588 }
589
590 /* Clean up. */
591 manifest_destroy(pManifest);
 
592 }
593
594 /*
595 ** Possible display modes for forum_display_thread().
596 */
597

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button