Fossil SCM

Improvements to forum thread display.

drh 2018-07-27 13:29 forum-v2
Commit fd06544c04a3a13af800c4232e378fe387f4b7a3367b88291b3e6e0d55729baf
--- src/default_css.txt
+++ src/default_css.txt
@@ -689,6 +689,9 @@
689689
padding-right: 1ex;
690690
margin-top: 1ex;
691691
}
692692
div.forumSel {
693693
background-color: #cef;
694
+}
695
+div.forumObs {
696
+ color: #bbb;
694697
}
695698
--- src/default_css.txt
+++ src/default_css.txt
@@ -689,6 +689,9 @@
689 padding-right: 1ex;
690 margin-top: 1ex;
691 }
692 div.forumSel {
693 background-color: #cef;
 
 
 
694 }
695
--- src/default_css.txt
+++ src/default_css.txt
@@ -689,6 +689,9 @@
689 padding-right: 1ex;
690 margin-top: 1ex;
691 }
692 div.forumSel {
693 background-color: #cef;
694 }
695 div.forumObs {
696 color: #bbb;
697 }
698
+153 -133
--- src/forum.c
+++ src/forum.c
@@ -30,10 +30,11 @@
3030
struct ForumEntry {
3131
int fpid; /* rid for this entry */
3232
int fprev; /* zero if initial entry. non-zero if an edit */
3333
int firt; /* This entry replies to firt */
3434
int mfirt; /* Root in-reply-to */
35
+ char *zUuid; /* Artifact hash */
3536
ForumEntry *pLeaf; /* Most recent edit for this entry */
3637
ForumEntry *pEdit; /* This entry is an edit of pEditee */
3738
ForumEntry *pNext; /* Next in chronological order */
3839
ForumEntry *pPrev; /* Previous in chronological order */
3940
ForumEntry *pDisplay; /* Next in display order */
@@ -56,10 +57,11 @@
5657
*/
5758
static void forumthread_delete(ForumThread *pThread){
5859
ForumEntry *pEntry, *pNext;
5960
for(pEntry=pThread->pFirst; pEntry; pEntry = pNext){
6061
pNext = pEntry->pNext;
62
+ fossil_free(pEntry->zUuid);
6163
fossil_free(pEntry);
6264
}
6365
fossil_free(pThread);
6466
}
6567
@@ -115,24 +117,29 @@
115117
}
116118
117119
/*
118120
** Construct a ForumThread object given the root record id.
119121
*/
120
-static ForumThread *forumthread_create(int froot){
122
+static ForumThread *forumthread_create(int froot, int computeHierarchy){
121123
ForumThread *pThread;
122124
ForumEntry *pEntry;
123125
Stmt q;
124126
pThread = fossil_malloc( sizeof(*pThread) );
125127
memset(pThread, 0, sizeof(*pThread));
126
- db_prepare(&q, "SELECT fpid, firt, fprev FROM forumpost"
127
- " WHERE froot=%d ORDER BY fmtime", froot);
128
+ db_prepare(&q,
129
+ "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)"
130
+ " FROM forumpost"
131
+ " WHERE froot=%d ORDER BY fmtime",
132
+ froot
133
+ );
128134
while( db_step(&q)==SQLITE_ROW ){
129135
pEntry = fossil_malloc( sizeof(*pEntry) );
130136
memset(pEntry, 0, sizeof(*pEntry));
131137
pEntry->fpid = db_column_int(&q, 0);
132138
pEntry->firt = db_column_int(&q, 1);
133139
pEntry->fprev = db_column_int(&q, 2);
140
+ pEntry->zUuid = fossil_strdup(db_column_text(&q,3));
134141
pEntry->mfirt = pEntry->firt;
135142
pEntry->pPrev = pThread->pLast;
136143
pEntry->pNext = 0;
137144
if( pThread->pLast==0 ){
138145
pThread->pFirst = pEntry;
@@ -161,15 +168,17 @@
161168
if( p->mfirt==pEntry->fpid ) p->mfirt = pBase->fpid;
162169
}
163170
}
164171
}
165172
166
- /* Compute the display order */
167
- pEntry = pThread->pFirst;
168
- pEntry->nIndent = 1;
169
- forumentry_add_to_display(pThread, pEntry);
170
- forumthread_display_order(pThread, pEntry, pEntry->fpid, 2);
173
+ if( computeHierarchy ){
174
+ /* Compute the hierarchical display order */
175
+ pEntry = pThread->pFirst;
176
+ pEntry->nIndent = 1;
177
+ forumentry_add_to_display(pThread, pEntry);
178
+ forumthread_display_order(pThread, pEntry, pEntry->fpid, 2);
179
+ }
171180
172181
/* Return the result */
173182
return pThread;
174183
}
175184
@@ -199,11 +208,11 @@
199208
if( froot==0 ){
200209
fossil_fatal("Not a forum post: \"%s\"", zName);
201210
}
202211
fossil_print("fpid = %d\n", fpid);
203212
fossil_print("froot = %d\n", froot);
204
- pThread = forumthread_create(froot);
213
+ pThread = forumthread_create(froot, 1);
205214
fossil_print("Chronological:\n");
206215
/* 123456789 123456789 123456789 123456789 123456789 */
207216
fossil_print(" fpid firt fprev mfirt pLeaf\n");
208217
for(p=pThread->pFirst; p; p=p->pNext){
209218
fossil_print("%9d %9d %9d %9d %9d\n",
@@ -256,42 +265,151 @@
256265
257266
/*
258267
** Display all posts in a forum thread in chronological order
259268
*/
260269
static void forum_display_chronological(int froot, int target){
261
- Stmt q;
262
- db_prepare(&q,
263
- "SELECT fpid, fprev, firt, uuid, datetime(fmtime,'unixepoch')\n"
264
- " FROM forumpost, blob\n"
265
- " WHERE froot=%d AND rid=fpid\n"
266
- " ORDER BY fmtime", froot);
267
- while( db_step(&q)==SQLITE_ROW ){
268
- int fpid = db_column_int(&q, 0);
269
- int fprev = db_column_int(&q, 1);
270
- int firt = db_column_int(&q, 2);
271
- const char *zUuid = db_column_text(&q, 3);
272
- const char *zDate = db_column_text(&q, 4);
273
- Manifest *pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
270
+ ForumThread *pThread = forumthread_create(froot, 0);
271
+ ForumEntry *p;
272
+ for(p=pThread->pFirst; p; p=p->pNext){
273
+ char *zDate;
274
+ Manifest *pPost;
275
+
276
+ pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
274277
if( pPost==0 ) continue;
275
- if( fpid==target ){
276
- @ <div id="forum%d(fpid)" class="forumTime forumSel">
278
+ if( p->fpid==target ){
279
+ @ <div id="forum%d(p->fpid)" class="forumTime forumSel">
280
+ }else if( p->pLeaf!=0 ){
281
+ @ <div id="forum%d(p->fpid)" class="forumTime forumObs">
282
+ }else{
283
+ @ <div id="forum%d(p->fpid)" class="forumTime">
284
+ }
285
+ if( pPost->zThreadTitle ){
286
+ @ <h1>%h(pPost->zThreadTitle)</h1>
287
+ }
288
+ zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
289
+ @ <p>By %h(pPost->zUser) on %h(zDate) (%d(p->fpid))
290
+ fossil_free(zDate);
291
+ if( p->pEdit ){
292
+ @ edit of %z(href("%R/forumthread/%S?t",p->pEdit->zUuid))%d(p->fprev)</a>
293
+ }
294
+ if( p->firt ){
295
+ ForumEntry *pIrt = p->pPrev;
296
+ while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev;
297
+ if( pIrt ){
298
+ @ reply to %z(href("%R/forumthread/%S?t",pIrt->zUuid))%d(p->firt)</a>
299
+ }
300
+ }
301
+ if( p->pLeaf ){
302
+ @ updated by %z(href("%R/forumthread/%S?t",p->pLeaf->zUuid))\
303
+ @ %d(p->pLeaf->fpid)</a>
304
+ }
305
+ if( g.perm.Debug ){
306
+ @ <span class="debug">\
307
+ @ <a href="%R/artifact/%h(p->zUuid)">artifact</a></span>
308
+ }
309
+ if( p->fpid!=target ){
310
+ @ %z(href("%R/forumthread/%S?t",p->zUuid))[link]</a>
311
+ }
312
+ forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
313
+ if( g.perm.WrForum && p->pLeaf==0 ){
314
+ int sameUser = login_is_individual()
315
+ && fossil_strcmp(pPost->zUser, g.zLogin)==0;
316
+ int isPrivate = content_is_private(p->fpid);
317
+ @ <p><form action="%R/forumedit" method="POST">
318
+ @ <input type="hidden" name="fpid" value="%s(p->zUuid)">
319
+ if( !isPrivate ){
320
+ /* Reply and Edit are only available if the post has already
321
+ ** been approved */
322
+ @ <input type="submit" name="reply" value="Reply">
323
+ if( g.perm.Admin || sameUser ){
324
+ @ <input type="submit" name="edit" value="Edit">
325
+ @ <input type="submit" name="nullout" value="Delete">
326
+ }
327
+ }else if( g.perm.ModForum ){
328
+ /* Provide moderators with moderation buttons for posts that
329
+ ** are pending moderation */
330
+ @ <input type="submit" name="approve" value="Approve">
331
+ @ <input type="submit" name="reject" value="Reject">
332
+ }else if( sameUser ){
333
+ /* A post that is pending moderation can be deleted by the
334
+ ** person who originally submitted the post */
335
+ @ <input type="submit" name="reject" value="Delete">
336
+ }
337
+ @ </form></p>
338
+ }
339
+ manifest_destroy(pPost);
340
+ @ </div>
341
+ }
342
+ forumthread_delete(pThread);
343
+}
344
+
345
+/*
346
+** Display all messages in a forumthread with indentation.
347
+*/
348
+static int forum_display_hierarchical(int froot, int target){
349
+ ForumThread *pThread;
350
+ ForumEntry *p;
351
+ Manifest *pPost, *pOPost;
352
+ int fpid;
353
+ const char *zUuid;
354
+ char *zDate;
355
+ const char *zSel;
356
+
357
+ pThread = forumthread_create(froot, 1);
358
+ for(p=pThread->pFirst; p; p=p->pNext){
359
+ if( p->fpid==target ){
360
+ while( p->pEdit ) p = p->pEdit;
361
+ target = p->fpid;
362
+ break;
363
+ }
364
+ }
365
+ for(p=pThread->pDisplay; p; p=p->pDisplay){
366
+ pOPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
367
+ if( p->pLeaf ){
368
+ fpid = p->pLeaf->fpid;
369
+ zUuid = p->pLeaf->zUuid;
370
+ pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
371
+ }else{
372
+ fpid = p->fpid;
373
+ zUuid = p->zUuid;
374
+ pPost = pOPost;
375
+ }
376
+ zSel = p->fpid==target ? " forumSel" : "";
377
+ if( p->nIndent==1 ){
378
+ @ <div id='forum(%d(fpid)' class='forumHierRoot%s(zSel)'>
277379
}else{
278
- @ <div id="forum%d(fpid)" class="forumTime">
380
+ @ <div id='forum%d(fpid)' class='forumHier%s(zSel)' \
381
+ @ style='margin-left: %d((p->nIndent-1)*3)ex;'>
279382
}
383
+ pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
384
+ if( pPost==0 ) continue;
280385
if( pPost->zThreadTitle ){
281386
@ <h1>%h(pPost->zThreadTitle)</h1>
282387
}
283
- @ <p>By %h(pPost->zUser) on %h(zDate) (%d(fpid))
284
- if( fprev ){
285
- @ edit of %d(fprev)
286
- }
287
- if( firt ){
288
- @ reply to %d(firt)
289
- }
388
+ zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate);
389
+ @ <p>By %h(pOPost->zUser) on %h(zDate)
390
+ fossil_free(zDate);
290391
if( g.perm.Debug ){
291392
@ <span class="debug">\
292
- @ <a href="%R/artifact/%h(zUuid)">artifact</a></span>
393
+ @ <a href="%R/artifact/%h(p->zUuid)">(%d(p->fpid))</a></span>
394
+ }
395
+ if( p->pLeaf ){
396
+ zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
397
+ if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){
398
+ @ and edited on %h(zDate)
399
+ }else{
400
+ @ as edited by %h(pPost->zUser) on %h(zDate)
401
+ }
402
+ fossil_free(zDate);
403
+ if( g.perm.Debug ){
404
+ @ <span class="debug">\
405
+ @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">(%d(fpid))</a></span>
406
+ }
407
+ manifest_destroy(pOPost);
408
+ }
409
+ if( fpid!=target ){
410
+ @ %z(href("%R/forumthread/%S",zUuid))[link]</a>
293411
}
294412
forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
295413
if( g.perm.WrForum ){
296414
int sameUser = login_is_individual()
297415
&& fossil_strcmp(pPost->zUser, g.zLogin)==0;
@@ -319,109 +437,10 @@
319437
@ </form></p>
320438
}
321439
manifest_destroy(pPost);
322440
@ </div>
323441
}
324
- db_finalize(&q);
325
-}
326
-
327
-/*
328
-** Display all messages in a forumthread with indentation.
329
-*/
330
-static int forum_display_hierarchical(int froot, int target){
331
- ForumThread *pThread;
332
- ForumEntry *p;
333
- Manifest *pPost, *pOPost;
334
- int fpid;
335
- char *zDate;
336
- char *zUuid;
337
- const char *zSel;
338
-
339
- pThread = forumthread_create(froot);
340
- for(p=pThread->pFirst; p; p=p->pNext){
341
- if( p->fpid==target ){
342
- while( p->pEdit ) p = p->pEdit;
343
- target = p->fpid;
344
- break;
345
- }
346
- }
347
- for(p=pThread->pDisplay; p; p=p->pDisplay){
348
- pOPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
349
- if( p->pLeaf ){
350
- fpid = p->pLeaf->fpid;
351
- pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
352
- }else{
353
- fpid = p->fpid;
354
- pPost = pOPost;
355
- }
356
- zSel = p->fpid==target ? " forumSel" : "";
357
- if( p->nIndent==1 ){
358
- @ <div id='forum(%d(fpid)' class='forumHierRoot%s(zSel)'>
359
- }else{
360
- @ <div id='forum%d(fpid)' class='forumHier%s(zSel)' \
361
- @ style='margin-left: %d((p->nIndent-1)*3)ex;'>
362
- }
363
- pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
364
- if( pPost==0 ) continue;
365
- if( pPost->zThreadTitle ){
366
- @ <h1>%h(pPost->zThreadTitle)</h1>
367
- }
368
- zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate);
369
- @ <p>By %h(pOPost->zUser) on %h(zDate)
370
- fossil_free(zDate);
371
- zUuid = rid_to_uuid(p->fpid);
372
- if( g.perm.Debug ){
373
- @ <span class="debug">\
374
- @ <a href="%R/artifact/%h(zUuid)">(%d(p->fpid))</a></span>
375
- }
376
- if( p->pLeaf ){
377
- zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
378
- if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){
379
- @ and edited on %h(zDate)
380
- }else{
381
- @ as edited by %h(pPost->zUser) on %h(zDate)
382
- }
383
- fossil_free(zDate);
384
- fossil_free(zUuid);
385
- zUuid = rid_to_uuid(fpid);
386
- if( g.perm.Debug ){
387
- @ <span class="debug">\
388
- @ <a href="%R/artifact/%h(zUuid)">(%d(fpid))</a></span>
389
- }
390
- manifest_destroy(pOPost);
391
- }
392
- forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
393
- if( g.perm.WrForum ){
394
- int sameUser = login_is_individual()
395
- && fossil_strcmp(pPost->zUser, g.zLogin)==0;
396
- int isPrivate = content_is_private(fpid);
397
- @ <p><form action="%R/forumedit" method="POST">
398
- @ <input type="hidden" name="fpid" value="%s(zUuid)">
399
- if( !isPrivate ){
400
- /* Reply and Edit are only available if the post has already
401
- ** been approved */
402
- @ <input type="submit" name="reply" value="Reply">
403
- if( g.perm.Admin || sameUser ){
404
- @ <input type="submit" name="edit" value="Edit">
405
- @ <input type="submit" name="nullout" value="Delete">
406
- }
407
- }else if( g.perm.ModForum ){
408
- /* Provide moderators with moderation buttons for posts that
409
- ** are pending moderation */
410
- @ <input type="submit" name="approve" value="Approve">
411
- @ <input type="submit" name="reject" value="Reject">
412
- }else if( sameUser ){
413
- /* A post that is pending moderation can be deleted by the
414
- ** person who originally submitted the post */
415
- @ <input type="submit" name="reject" value="Delete">
416
- }
417
- @ </form></p>
418
- }
419
- manifest_destroy(pPost);
420
- fossil_free(zUuid);
421
- @ </div>
422
- }
423442
forumthread_delete(pThread);
424443
return target;
425444
}
426445
427446
/*
@@ -430,10 +449,11 @@
430449
** Show all forum messages associated with a particular message thread.
431450
**
432451
** Query parameters:
433452
**
434453
** name=X The hash of the first post of the thread. REQUIRED
454
+** t Show a chronologic listing instead of hierarchical
435455
*/
436456
void forumthread_page(void){
437457
int fpid;
438458
int froot;
439459
const char *zName = P("name");
@@ -461,11 +481,11 @@
461481
forum_display_chronological(froot, fpid);
462482
}else{
463483
if( g.perm.Debug ){
464484
style_submenu_element("Chronological", "%R/forumthread/%s?t", zName);
465485
}
466
- fpid = forum_display_hierarchical(froot, fpid);
486
+ forum_display_hierarchical(froot, fpid);
467487
}
468488
style_load_js("forum.js");
469489
style_footer();
470490
}
471491
472492
--- src/forum.c
+++ src/forum.c
@@ -30,10 +30,11 @@
30 struct ForumEntry {
31 int fpid; /* rid for this entry */
32 int fprev; /* zero if initial entry. non-zero if an edit */
33 int firt; /* This entry replies to firt */
34 int mfirt; /* Root in-reply-to */
 
35 ForumEntry *pLeaf; /* Most recent edit for this entry */
36 ForumEntry *pEdit; /* This entry is an edit of pEditee */
37 ForumEntry *pNext; /* Next in chronological order */
38 ForumEntry *pPrev; /* Previous in chronological order */
39 ForumEntry *pDisplay; /* Next in display order */
@@ -56,10 +57,11 @@
56 */
57 static void forumthread_delete(ForumThread *pThread){
58 ForumEntry *pEntry, *pNext;
59 for(pEntry=pThread->pFirst; pEntry; pEntry = pNext){
60 pNext = pEntry->pNext;
 
61 fossil_free(pEntry);
62 }
63 fossil_free(pThread);
64 }
65
@@ -115,24 +117,29 @@
115 }
116
117 /*
118 ** Construct a ForumThread object given the root record id.
119 */
120 static ForumThread *forumthread_create(int froot){
121 ForumThread *pThread;
122 ForumEntry *pEntry;
123 Stmt q;
124 pThread = fossil_malloc( sizeof(*pThread) );
125 memset(pThread, 0, sizeof(*pThread));
126 db_prepare(&q, "SELECT fpid, firt, fprev FROM forumpost"
127 " WHERE froot=%d ORDER BY fmtime", froot);
 
 
 
 
128 while( db_step(&q)==SQLITE_ROW ){
129 pEntry = fossil_malloc( sizeof(*pEntry) );
130 memset(pEntry, 0, sizeof(*pEntry));
131 pEntry->fpid = db_column_int(&q, 0);
132 pEntry->firt = db_column_int(&q, 1);
133 pEntry->fprev = db_column_int(&q, 2);
 
134 pEntry->mfirt = pEntry->firt;
135 pEntry->pPrev = pThread->pLast;
136 pEntry->pNext = 0;
137 if( pThread->pLast==0 ){
138 pThread->pFirst = pEntry;
@@ -161,15 +168,17 @@
161 if( p->mfirt==pEntry->fpid ) p->mfirt = pBase->fpid;
162 }
163 }
164 }
165
166 /* Compute the display order */
167 pEntry = pThread->pFirst;
168 pEntry->nIndent = 1;
169 forumentry_add_to_display(pThread, pEntry);
170 forumthread_display_order(pThread, pEntry, pEntry->fpid, 2);
 
 
171
172 /* Return the result */
173 return pThread;
174 }
175
@@ -199,11 +208,11 @@
199 if( froot==0 ){
200 fossil_fatal("Not a forum post: \"%s\"", zName);
201 }
202 fossil_print("fpid = %d\n", fpid);
203 fossil_print("froot = %d\n", froot);
204 pThread = forumthread_create(froot);
205 fossil_print("Chronological:\n");
206 /* 123456789 123456789 123456789 123456789 123456789 */
207 fossil_print(" fpid firt fprev mfirt pLeaf\n");
208 for(p=pThread->pFirst; p; p=p->pNext){
209 fossil_print("%9d %9d %9d %9d %9d\n",
@@ -256,42 +265,151 @@
256
257 /*
258 ** Display all posts in a forum thread in chronological order
259 */
260 static void forum_display_chronological(int froot, int target){
261 Stmt q;
262 db_prepare(&q,
263 "SELECT fpid, fprev, firt, uuid, datetime(fmtime,'unixepoch')\n"
264 " FROM forumpost, blob\n"
265 " WHERE froot=%d AND rid=fpid\n"
266 " ORDER BY fmtime", froot);
267 while( db_step(&q)==SQLITE_ROW ){
268 int fpid = db_column_int(&q, 0);
269 int fprev = db_column_int(&q, 1);
270 int firt = db_column_int(&q, 2);
271 const char *zUuid = db_column_text(&q, 3);
272 const char *zDate = db_column_text(&q, 4);
273 Manifest *pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
274 if( pPost==0 ) continue;
275 if( fpid==target ){
276 @ <div id="forum%d(fpid)" class="forumTime forumSel">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277 }else{
278 @ <div id="forum%d(fpid)" class="forumTime">
 
279 }
 
 
280 if( pPost->zThreadTitle ){
281 @ <h1>%h(pPost->zThreadTitle)</h1>
282 }
283 @ <p>By %h(pPost->zUser) on %h(zDate) (%d(fpid))
284 if( fprev ){
285 @ edit of %d(fprev)
286 }
287 if( firt ){
288 @ reply to %d(firt)
289 }
290 if( g.perm.Debug ){
291 @ <span class="debug">\
292 @ <a href="%R/artifact/%h(zUuid)">artifact</a></span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293 }
294 forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
295 if( g.perm.WrForum ){
296 int sameUser = login_is_individual()
297 && fossil_strcmp(pPost->zUser, g.zLogin)==0;
@@ -319,109 +437,10 @@
319 @ </form></p>
320 }
321 manifest_destroy(pPost);
322 @ </div>
323 }
324 db_finalize(&q);
325 }
326
327 /*
328 ** Display all messages in a forumthread with indentation.
329 */
330 static int forum_display_hierarchical(int froot, int target){
331 ForumThread *pThread;
332 ForumEntry *p;
333 Manifest *pPost, *pOPost;
334 int fpid;
335 char *zDate;
336 char *zUuid;
337 const char *zSel;
338
339 pThread = forumthread_create(froot);
340 for(p=pThread->pFirst; p; p=p->pNext){
341 if( p->fpid==target ){
342 while( p->pEdit ) p = p->pEdit;
343 target = p->fpid;
344 break;
345 }
346 }
347 for(p=pThread->pDisplay; p; p=p->pDisplay){
348 pOPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
349 if( p->pLeaf ){
350 fpid = p->pLeaf->fpid;
351 pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
352 }else{
353 fpid = p->fpid;
354 pPost = pOPost;
355 }
356 zSel = p->fpid==target ? " forumSel" : "";
357 if( p->nIndent==1 ){
358 @ <div id='forum(%d(fpid)' class='forumHierRoot%s(zSel)'>
359 }else{
360 @ <div id='forum%d(fpid)' class='forumHier%s(zSel)' \
361 @ style='margin-left: %d((p->nIndent-1)*3)ex;'>
362 }
363 pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
364 if( pPost==0 ) continue;
365 if( pPost->zThreadTitle ){
366 @ <h1>%h(pPost->zThreadTitle)</h1>
367 }
368 zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate);
369 @ <p>By %h(pOPost->zUser) on %h(zDate)
370 fossil_free(zDate);
371 zUuid = rid_to_uuid(p->fpid);
372 if( g.perm.Debug ){
373 @ <span class="debug">\
374 @ <a href="%R/artifact/%h(zUuid)">(%d(p->fpid))</a></span>
375 }
376 if( p->pLeaf ){
377 zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
378 if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){
379 @ and edited on %h(zDate)
380 }else{
381 @ as edited by %h(pPost->zUser) on %h(zDate)
382 }
383 fossil_free(zDate);
384 fossil_free(zUuid);
385 zUuid = rid_to_uuid(fpid);
386 if( g.perm.Debug ){
387 @ <span class="debug">\
388 @ <a href="%R/artifact/%h(zUuid)">(%d(fpid))</a></span>
389 }
390 manifest_destroy(pOPost);
391 }
392 forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
393 if( g.perm.WrForum ){
394 int sameUser = login_is_individual()
395 && fossil_strcmp(pPost->zUser, g.zLogin)==0;
396 int isPrivate = content_is_private(fpid);
397 @ <p><form action="%R/forumedit" method="POST">
398 @ <input type="hidden" name="fpid" value="%s(zUuid)">
399 if( !isPrivate ){
400 /* Reply and Edit are only available if the post has already
401 ** been approved */
402 @ <input type="submit" name="reply" value="Reply">
403 if( g.perm.Admin || sameUser ){
404 @ <input type="submit" name="edit" value="Edit">
405 @ <input type="submit" name="nullout" value="Delete">
406 }
407 }else if( g.perm.ModForum ){
408 /* Provide moderators with moderation buttons for posts that
409 ** are pending moderation */
410 @ <input type="submit" name="approve" value="Approve">
411 @ <input type="submit" name="reject" value="Reject">
412 }else if( sameUser ){
413 /* A post that is pending moderation can be deleted by the
414 ** person who originally submitted the post */
415 @ <input type="submit" name="reject" value="Delete">
416 }
417 @ </form></p>
418 }
419 manifest_destroy(pPost);
420 fossil_free(zUuid);
421 @ </div>
422 }
423 forumthread_delete(pThread);
424 return target;
425 }
426
427 /*
@@ -430,10 +449,11 @@
430 ** Show all forum messages associated with a particular message thread.
431 **
432 ** Query parameters:
433 **
434 ** name=X The hash of the first post of the thread. REQUIRED
 
435 */
436 void forumthread_page(void){
437 int fpid;
438 int froot;
439 const char *zName = P("name");
@@ -461,11 +481,11 @@
461 forum_display_chronological(froot, fpid);
462 }else{
463 if( g.perm.Debug ){
464 style_submenu_element("Chronological", "%R/forumthread/%s?t", zName);
465 }
466 fpid = forum_display_hierarchical(froot, fpid);
467 }
468 style_load_js("forum.js");
469 style_footer();
470 }
471
472
--- src/forum.c
+++ src/forum.c
@@ -30,10 +30,11 @@
30 struct ForumEntry {
31 int fpid; /* rid for this entry */
32 int fprev; /* zero if initial entry. non-zero if an edit */
33 int firt; /* This entry replies to firt */
34 int mfirt; /* Root in-reply-to */
35 char *zUuid; /* Artifact hash */
36 ForumEntry *pLeaf; /* Most recent edit for this entry */
37 ForumEntry *pEdit; /* This entry is an edit of pEditee */
38 ForumEntry *pNext; /* Next in chronological order */
39 ForumEntry *pPrev; /* Previous in chronological order */
40 ForumEntry *pDisplay; /* Next in display order */
@@ -56,10 +57,11 @@
57 */
58 static void forumthread_delete(ForumThread *pThread){
59 ForumEntry *pEntry, *pNext;
60 for(pEntry=pThread->pFirst; pEntry; pEntry = pNext){
61 pNext = pEntry->pNext;
62 fossil_free(pEntry->zUuid);
63 fossil_free(pEntry);
64 }
65 fossil_free(pThread);
66 }
67
@@ -115,24 +117,29 @@
117 }
118
119 /*
120 ** Construct a ForumThread object given the root record id.
121 */
122 static ForumThread *forumthread_create(int froot, int computeHierarchy){
123 ForumThread *pThread;
124 ForumEntry *pEntry;
125 Stmt q;
126 pThread = fossil_malloc( sizeof(*pThread) );
127 memset(pThread, 0, sizeof(*pThread));
128 db_prepare(&q,
129 "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)"
130 " FROM forumpost"
131 " WHERE froot=%d ORDER BY fmtime",
132 froot
133 );
134 while( db_step(&q)==SQLITE_ROW ){
135 pEntry = fossil_malloc( sizeof(*pEntry) );
136 memset(pEntry, 0, sizeof(*pEntry));
137 pEntry->fpid = db_column_int(&q, 0);
138 pEntry->firt = db_column_int(&q, 1);
139 pEntry->fprev = db_column_int(&q, 2);
140 pEntry->zUuid = fossil_strdup(db_column_text(&q,3));
141 pEntry->mfirt = pEntry->firt;
142 pEntry->pPrev = pThread->pLast;
143 pEntry->pNext = 0;
144 if( pThread->pLast==0 ){
145 pThread->pFirst = pEntry;
@@ -161,15 +168,17 @@
168 if( p->mfirt==pEntry->fpid ) p->mfirt = pBase->fpid;
169 }
170 }
171 }
172
173 if( computeHierarchy ){
174 /* Compute the hierarchical display order */
175 pEntry = pThread->pFirst;
176 pEntry->nIndent = 1;
177 forumentry_add_to_display(pThread, pEntry);
178 forumthread_display_order(pThread, pEntry, pEntry->fpid, 2);
179 }
180
181 /* Return the result */
182 return pThread;
183 }
184
@@ -199,11 +208,11 @@
208 if( froot==0 ){
209 fossil_fatal("Not a forum post: \"%s\"", zName);
210 }
211 fossil_print("fpid = %d\n", fpid);
212 fossil_print("froot = %d\n", froot);
213 pThread = forumthread_create(froot, 1);
214 fossil_print("Chronological:\n");
215 /* 123456789 123456789 123456789 123456789 123456789 */
216 fossil_print(" fpid firt fprev mfirt pLeaf\n");
217 for(p=pThread->pFirst; p; p=p->pNext){
218 fossil_print("%9d %9d %9d %9d %9d\n",
@@ -256,42 +265,151 @@
265
266 /*
267 ** Display all posts in a forum thread in chronological order
268 */
269 static void forum_display_chronological(int froot, int target){
270 ForumThread *pThread = forumthread_create(froot, 0);
271 ForumEntry *p;
272 for(p=pThread->pFirst; p; p=p->pNext){
273 char *zDate;
274 Manifest *pPost;
275
276 pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
 
 
 
 
 
 
277 if( pPost==0 ) continue;
278 if( p->fpid==target ){
279 @ <div id="forum%d(p->fpid)" class="forumTime forumSel">
280 }else if( p->pLeaf!=0 ){
281 @ <div id="forum%d(p->fpid)" class="forumTime forumObs">
282 }else{
283 @ <div id="forum%d(p->fpid)" class="forumTime">
284 }
285 if( pPost->zThreadTitle ){
286 @ <h1>%h(pPost->zThreadTitle)</h1>
287 }
288 zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
289 @ <p>By %h(pPost->zUser) on %h(zDate) (%d(p->fpid))
290 fossil_free(zDate);
291 if( p->pEdit ){
292 @ edit of %z(href("%R/forumthread/%S?t",p->pEdit->zUuid))%d(p->fprev)</a>
293 }
294 if( p->firt ){
295 ForumEntry *pIrt = p->pPrev;
296 while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev;
297 if( pIrt ){
298 @ reply to %z(href("%R/forumthread/%S?t",pIrt->zUuid))%d(p->firt)</a>
299 }
300 }
301 if( p->pLeaf ){
302 @ updated by %z(href("%R/forumthread/%S?t",p->pLeaf->zUuid))\
303 @ %d(p->pLeaf->fpid)</a>
304 }
305 if( g.perm.Debug ){
306 @ <span class="debug">\
307 @ <a href="%R/artifact/%h(p->zUuid)">artifact</a></span>
308 }
309 if( p->fpid!=target ){
310 @ %z(href("%R/forumthread/%S?t",p->zUuid))[link]</a>
311 }
312 forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
313 if( g.perm.WrForum && p->pLeaf==0 ){
314 int sameUser = login_is_individual()
315 && fossil_strcmp(pPost->zUser, g.zLogin)==0;
316 int isPrivate = content_is_private(p->fpid);
317 @ <p><form action="%R/forumedit" method="POST">
318 @ <input type="hidden" name="fpid" value="%s(p->zUuid)">
319 if( !isPrivate ){
320 /* Reply and Edit are only available if the post has already
321 ** been approved */
322 @ <input type="submit" name="reply" value="Reply">
323 if( g.perm.Admin || sameUser ){
324 @ <input type="submit" name="edit" value="Edit">
325 @ <input type="submit" name="nullout" value="Delete">
326 }
327 }else if( g.perm.ModForum ){
328 /* Provide moderators with moderation buttons for posts that
329 ** are pending moderation */
330 @ <input type="submit" name="approve" value="Approve">
331 @ <input type="submit" name="reject" value="Reject">
332 }else if( sameUser ){
333 /* A post that is pending moderation can be deleted by the
334 ** person who originally submitted the post */
335 @ <input type="submit" name="reject" value="Delete">
336 }
337 @ </form></p>
338 }
339 manifest_destroy(pPost);
340 @ </div>
341 }
342 forumthread_delete(pThread);
343 }
344
345 /*
346 ** Display all messages in a forumthread with indentation.
347 */
348 static int forum_display_hierarchical(int froot, int target){
349 ForumThread *pThread;
350 ForumEntry *p;
351 Manifest *pPost, *pOPost;
352 int fpid;
353 const char *zUuid;
354 char *zDate;
355 const char *zSel;
356
357 pThread = forumthread_create(froot, 1);
358 for(p=pThread->pFirst; p; p=p->pNext){
359 if( p->fpid==target ){
360 while( p->pEdit ) p = p->pEdit;
361 target = p->fpid;
362 break;
363 }
364 }
365 for(p=pThread->pDisplay; p; p=p->pDisplay){
366 pOPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
367 if( p->pLeaf ){
368 fpid = p->pLeaf->fpid;
369 zUuid = p->pLeaf->zUuid;
370 pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
371 }else{
372 fpid = p->fpid;
373 zUuid = p->zUuid;
374 pPost = pOPost;
375 }
376 zSel = p->fpid==target ? " forumSel" : "";
377 if( p->nIndent==1 ){
378 @ <div id='forum(%d(fpid)' class='forumHierRoot%s(zSel)'>
379 }else{
380 @ <div id='forum%d(fpid)' class='forumHier%s(zSel)' \
381 @ style='margin-left: %d((p->nIndent-1)*3)ex;'>
382 }
383 pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
384 if( pPost==0 ) continue;
385 if( pPost->zThreadTitle ){
386 @ <h1>%h(pPost->zThreadTitle)</h1>
387 }
388 zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate);
389 @ <p>By %h(pOPost->zUser) on %h(zDate)
390 fossil_free(zDate);
 
 
 
 
391 if( g.perm.Debug ){
392 @ <span class="debug">\
393 @ <a href="%R/artifact/%h(p->zUuid)">(%d(p->fpid))</a></span>
394 }
395 if( p->pLeaf ){
396 zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
397 if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){
398 @ and edited on %h(zDate)
399 }else{
400 @ as edited by %h(pPost->zUser) on %h(zDate)
401 }
402 fossil_free(zDate);
403 if( g.perm.Debug ){
404 @ <span class="debug">\
405 @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">(%d(fpid))</a></span>
406 }
407 manifest_destroy(pOPost);
408 }
409 if( fpid!=target ){
410 @ %z(href("%R/forumthread/%S",zUuid))[link]</a>
411 }
412 forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
413 if( g.perm.WrForum ){
414 int sameUser = login_is_individual()
415 && fossil_strcmp(pPost->zUser, g.zLogin)==0;
@@ -319,109 +437,10 @@
437 @ </form></p>
438 }
439 manifest_destroy(pPost);
440 @ </div>
441 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442 forumthread_delete(pThread);
443 return target;
444 }
445
446 /*
@@ -430,10 +449,11 @@
449 ** Show all forum messages associated with a particular message thread.
450 **
451 ** Query parameters:
452 **
453 ** name=X The hash of the first post of the thread. REQUIRED
454 ** t Show a chronologic listing instead of hierarchical
455 */
456 void forumthread_page(void){
457 int fpid;
458 int froot;
459 const char *zName = P("name");
@@ -461,11 +481,11 @@
481 forum_display_chronological(froot, fpid);
482 }else{
483 if( g.perm.Debug ){
484 style_submenu_element("Chronological", "%R/forumthread/%s?t", zName);
485 }
486 forum_display_hierarchical(froot, fpid);
487 }
488 style_load_js("forum.js");
489 style_footer();
490 }
491
492
+1 -3
--- src/forum.js
+++ src/forum.js
@@ -11,11 +11,9 @@
1111
var x = document.getElementsByClassName('forumSel');
1212
if(x[0]){
1313
var w = window.innerHeight;
1414
var h = x[0].scrollHeight;
1515
var y = absoluteY(x[0]);
16
- if( w<h ){
17
- y = y + (h-w)/2;
18
- }
16
+ if( w>h ) y = y + (h-w)/2;
1917
if( y>0 ) window.scrollTo(0, y);
2018
}
2119
}())
2220
--- src/forum.js
+++ src/forum.js
@@ -11,11 +11,9 @@
11 var x = document.getElementsByClassName('forumSel');
12 if(x[0]){
13 var w = window.innerHeight;
14 var h = x[0].scrollHeight;
15 var y = absoluteY(x[0]);
16 if( w<h ){
17 y = y + (h-w)/2;
18 }
19 if( y>0 ) window.scrollTo(0, y);
20 }
21 }())
22
--- src/forum.js
+++ src/forum.js
@@ -11,11 +11,9 @@
11 var x = document.getElementsByClassName('forumSel');
12 if(x[0]){
13 var w = window.innerHeight;
14 var h = x[0].scrollHeight;
15 var y = absoluteY(x[0]);
16 if( w>h ) y = y + (h-w)/2;
 
 
17 if( y>0 ) window.scrollTo(0, y);
18 }
19 }())
20

Keyboard Shortcuts

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