Fossil

Check-in [8eccd9a2]
Login

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

Overview
Comment:Begin adding style to the forum display.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | forum-v2
Files: files | file ages | folders
SHA3-256:8eccd9a221fd165563ad23c3e976171b8ae5c0b6f26274dd2e587bcb3220a4cc
User & Date: drh 2018-07-25 22:34:27
Context
2018-07-26
01:02
Fixes to the forum hierarchical display algorithm. check-in: 9757488a user: drh tags: forum-v2
2018-07-25
22:34
Begin adding style to the forum display. check-in: 8eccd9a2 user: drh tags: forum-v2
21:00
Hierarchical display of forum threads. check-in: 7da12996 user: drh tags: forum-v2
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/default_css.txt.

674
675
676
677
678
679
680











  vertical-align: top;
  text-align: right;
}
.debug {
  background-color: #ffc;
  border: 2px solid #ff0;
}


















>
>
>
>
>
>
>
>
>
>
>
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
  vertical-align: top;
  text-align: right;
}
.debug {
  background-color: #ffc;
  border: 2px solid #ff0;
}
div.forumEdit {
  border: 1px solid black;
  padding-left: 1ex;
  padding-right: 1ex;
}
div.forumHier, div.forumTime {
  border: 1px solid black;
  padding-left: 1ex;
  padding-right: 1ex;
  margin-top: 1ex;
}

Changes to src/forum.c.

59
60
61
62
63
64
65

66
67
68
69
70
71
72

73
74
75
76
77
78
79
...
219
220
221
222
223
224
225
226
227
228

229
230



231
232
233
234
235
236
237
...
240
241
242
243
244
245
246

247

248
249
250
251
252
253
254
255
256
257
258
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
...
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
353
354
...
370
371
372
373
374
375
376
377






378

379
380
381
382
383
384
385
...
405
406
407
408
409
410
411



412
413
414


415



416
417
418
419
420
421
422
...
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
...
642
643
644
645
646
647
648
649

650
651
652
653
654
655
656
657
658
...
665
666
667
668
669
670
671
672

673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
  for(pEntry=pThread->pFirst; pEntry; pEntry = pNext){
    pNext = pEntry->pNext;
    fossil_free(pEntry);
  }
  fossil_free(pThread);
}


/*
** Search a ForumEntry list forwards looking for the entry with fpid
*/
static ForumEntry *forumentry_forward(ForumEntry *p, int fpid){
  while( p && p->fpid!=fpid ) p = p->pNext;
  return p;
}


/*
** Search backwards for a ForumEntry
*/
static ForumEntry *forumentry_backward(ForumEntry *p, int fpid){
  while( p && p->fpid!=fpid ) p = p->pPrev;
  return p;
................................................................................
  forumthread_delete(pThread);
}

/*
** Render a forum post for display
*/
void forum_render(
  const char *zTitle,
  const char *zMimetype,
  const char *zContent

){
  @ <div style='border: 1px solid black;padding: 1ex;'>



  if( zTitle ){
    if( zTitle[0] ){
      @ <h1>%h(zTitle)</h1>
    }else{
      @ <h1><i>Deleted</i></h1>
    }
  }
................................................................................
    blob_init(&x, 0, 0);
    blob_append(&x, zContent, -1);
    wiki_render_by_mimetype(&x, zMimetype);
    blob_reset(&x);
  }else{
    @ <i>Deleted</i>
  }

  @ </div>

}

/*
** Display all posts in a forum thread in chronological order
*/
static void forum_display_chronological(int froot, int target){
  Stmt q;
  int i = 0;
  db_prepare(&q,
      "SELECT fpid, fprev, firt, uuid, datetime(fmtime,'unixepoch')\n"
      " FROM forumpost, blob\n"
      " WHERE froot=%d AND rid=fpid\n"
      " ORDER BY fmtime", froot);
  while( db_step(&q)==SQLITE_ROW ){
    int fpid = db_column_int(&q, 0);
    int fprev = db_column_int(&q, 1);
    int firt = db_column_int(&q, 2);
    const char *zUuid = db_column_text(&q, 3);
    const char *zDate = db_column_text(&q, 4);
    Manifest *pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
    if( pPost==0 ) continue;
    if( i>0 ){
      @ <hr>
    }
    i++;
    if( pPost->zThreadTitle ){
      @ <h1>%h(pPost->zThreadTitle)</h1>
    }
    @ <p>By %h(pPost->zUser) on %h(zDate) (%d(fpid))
    if( fprev ){
      @ edit of %d(fprev)
    }
................................................................................
    if( firt ){
      @ reply to %d(firt)
    }
    if( g.perm.Debug ){
      @ <span class="debug">\
      @ <a href="%R/artifact/%h(zUuid)">artifact</a></span>
    }
    forum_render(0, pPost->zMimetype, pPost->zWiki);
    if( g.perm.WrForum ){
      int sameUser = login_is_individual()
                     && fossil_strcmp(pPost->zUser, g.zLogin)==0;
      int isPrivate = content_is_private(fpid);
      @ <p><form action="%R/forumedit" method="POST">
      @ <input type="hidden" name="fpid" value="%s(zUuid)">
      if( !isPrivate ){
................................................................................
        /* A post that is pending moderation can be deleted by the
        ** person who originally submitted the post */
        @ <input type="submit" name="reject" value="Delete">
      }
      @ </form></p>
    }
    manifest_destroy(pPost);

  }
  db_finalize(&q);
}

/*
** Display all messages in a forumthread with indentation.
*/
static void forum_display(int froot, int target){
  ForumThread *pThread;
  ForumEntry *p;
  Manifest *pPost;
  int fpid;
  char *zDate;
  char *zUuid;

  pThread = forumthread_create(froot);
  for(p=pThread->pDisplay; p; p=p->pDisplay){
    @ <div style='margin-left: %d((p->nIndent-1)*3)ex;'>
    fpid = p->pLeaf ? p->pLeaf->fpid : p->fpid;






    pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
    if( pPost==0 ) continue;
    if( pPost->zThreadTitle ){
      @ <h1>%h(pPost->zThreadTitle)</h1>
    }
    zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
    @ <p>By %h(pPost->zUser) on %h(zDate) (%d(fpid))
    fossil_free(zDate);
    zUuid = rid_to_uuid(fpid);
    if( g.perm.Debug ){
      @ <span class="debug">\
      @ <a href="%R/artifact/%h(zUuid)">artifact</a></span>
    }
    forum_render(0, pPost->zMimetype, pPost->zWiki);
    if( g.perm.WrForum ){
      int sameUser = login_is_individual()
                     && fossil_strcmp(pPost->zUser, g.zLogin)==0;
      int isPrivate = content_is_private(fpid);
      @ <p><form action="%R/forumedit" method="POST">
      @ <input type="hidden" name="fpid" value="%s(zUuid)">
      if( !isPrivate ){
................................................................................
        @ <input type="submit" name="reject" value="Delete">
      }
      @ </form></p>
    }
    manifest_destroy(pPost);
    fossil_free(zUuid);
    @ </div>
  }






  forumthread_delete(pThread);

}

/*
** WEBPAGE: forumthread
**
** Show all forum messages associated with a particular message thread.
**
................................................................................
  }
  style_header("Forum");
  froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
  if( froot==0 ){
    webpage_error("Not a forum post: \"%s\"", zName);
  }
  if( P("t") ){



    forum_display_chronological(froot, fpid);
  }else{
    forum_display(froot, fpid);


  }



  style_footer();
}

/*
** Return true if a forum post should be moderated.
*/
static int forum_need_moderation(void){
................................................................................
    return;
  }
  if( P("submit") ){
    if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return;
  }
  if( P("preview") ){
    @ <h1>Preview:</h1>
    forum_render(zTitle, zMimetype, zContent);
  }
  style_header("New Forum Thread");
  @ <form action="%R/%s(g.zPath)" method="POST">
  forum_entry_widget(zTitle, zMimetype, zContent);
  @ <input type="submit" name="preview" value="Preview">
  if( P("preview") ){
    @ <input type="submit" name="submit" value="Submit">
................................................................................
  }
  if( isDelete ){
    zMimetype = "text/x-fossil-wiki";
    zContent = "";
    if( pPost->zThreadTitle ) zTitle = "";
    style_header("Delete %s", zTitle ? "Post" : "Reply");
    @ <h1>Original Post:</h1>
    forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki);

    @ <h1>Change Into:</h1>
    forum_render(zTitle, zMimetype, zContent);
    @ <form action="%R/forumedit" method="POST">
    @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
    @ <input type="hidden" name="nullout" value="1">
    @ <input type="hidden" name="mimetype" value="%h(zMimetype)">
    @ <input type="hidden" name="content" value="%h(zContent)">
    if( zTitle ){
      @ <input type="hidden" name="title" value="%h(zTitle)">
................................................................................
    if( zContent==0 ) zContent = fossil_strdup(pPost->zWiki);
    if( zMimetype==0 ) zMimetype = fossil_strdup(pPost->zMimetype);
    if( zTitle==0 && pPost->zThreadTitle!=0 ){
      zTitle = fossil_strdup(pPost->zThreadTitle);
    }
    style_header("Edit %s", zTitle ? "Post" : "Reply");
    @ <h1>Original Post:</h1>
    forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki);

    if( P("preview") ){
      @ <h1>Preview Of Editted Post:</h1>
      forum_render(zTitle, zMimetype, zContent);
    }
    @ <h1>Enter A Reply:</h1>
    @ <form action="%R/forumedit" method="POST">
    @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
    @ <input type="hidden" name="edit" value="1">
    forum_entry_widget(zTitle, zMimetype, zContent);
  }else{
    /* Reply */
    zMimetype = PD("mimetype","text/x-fossil-wiki");
    zContent = PDT("content","");
    style_header("Reply");
    @ <h1>Replying To:</h1>
    forum_render(0, pPost->zMimetype, pPost->zWiki);
    if( P("preview") ){
      @ <h1>Preview:</h1>
      forum_render(0, zMimetype,zContent);
    }
    @ <h1>Enter A Reply:</h1>
    @ <form action="%R/forumedit" method="POST">
    @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
    @ <input type="hidden" name="reply" value="1">
    forum_entry_widget(0, zMimetype, zContent);
  }







>







>







 







|
|
|
>

<
>
>
>







 







>
|
>







<













|
<
<
<







 







|







 







>







|









<

>
>
>
>
>
>













|







 








>
>
>
>
>
>

>







 







>
>
>


<
>
>
|
>
>
>







 







|







 







|
>

|







 







|
>


|












|


|







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
...
221
222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240
241
242
...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
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
...
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
353
354
355
356
357
358
359
360
361
362
363
...
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
...
421
422
423
424
425
426
427
428
429
430
431
432

433
434
435
436
437
438
439
440
441
442
443
444
445
...
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
...
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
...
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
  for(pEntry=pThread->pFirst; pEntry; pEntry = pNext){
    pNext = pEntry->pNext;
    fossil_free(pEntry);
  }
  fossil_free(pThread);
}

#if 0 /* not used */
/*
** Search a ForumEntry list forwards looking for the entry with fpid
*/
static ForumEntry *forumentry_forward(ForumEntry *p, int fpid){
  while( p && p->fpid!=fpid ) p = p->pNext;
  return p;
}
#endif

/*
** Search backwards for a ForumEntry
*/
static ForumEntry *forumentry_backward(ForumEntry *p, int fpid){
  while( p && p->fpid!=fpid ) p = p->pPrev;
  return p;
................................................................................
  forumthread_delete(pThread);
}

/*
** Render a forum post for display
*/
void forum_render(
  const char *zTitle,         /* The title.  Might be NULL for no title */
  const char *zMimetype,      /* Mimetype of the message */
  const char *zContent,       /* Content of the message */
  const char *zClass          /* Put in a <div> if not NULL */
){

  if( zClass ){
    @ <div class='%s(zClass)'>
  }
  if( zTitle ){
    if( zTitle[0] ){
      @ <h1>%h(zTitle)</h1>
    }else{
      @ <h1><i>Deleted</i></h1>
    }
  }
................................................................................
    blob_init(&x, 0, 0);
    blob_append(&x, zContent, -1);
    wiki_render_by_mimetype(&x, zMimetype);
    blob_reset(&x);
  }else{
    @ <i>Deleted</i>
  }
  if( zClass ){
    @ </div>
  }
}

/*
** Display all posts in a forum thread in chronological order
*/
static void forum_display_chronological(int froot, int target){
  Stmt q;

  db_prepare(&q,
      "SELECT fpid, fprev, firt, uuid, datetime(fmtime,'unixepoch')\n"
      " FROM forumpost, blob\n"
      " WHERE froot=%d AND rid=fpid\n"
      " ORDER BY fmtime", froot);
  while( db_step(&q)==SQLITE_ROW ){
    int fpid = db_column_int(&q, 0);
    int fprev = db_column_int(&q, 1);
    int firt = db_column_int(&q, 2);
    const char *zUuid = db_column_text(&q, 3);
    const char *zDate = db_column_text(&q, 4);
    Manifest *pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
    if( pPost==0 ) continue;
    @ <div id="forum%d(fpid)" class="forumTime">



    if( pPost->zThreadTitle ){
      @ <h1>%h(pPost->zThreadTitle)</h1>
    }
    @ <p>By %h(pPost->zUser) on %h(zDate) (%d(fpid))
    if( fprev ){
      @ edit of %d(fprev)
    }
................................................................................
    if( firt ){
      @ reply to %d(firt)
    }
    if( g.perm.Debug ){
      @ <span class="debug">\
      @ <a href="%R/artifact/%h(zUuid)">artifact</a></span>
    }
    forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
    if( g.perm.WrForum ){
      int sameUser = login_is_individual()
                     && fossil_strcmp(pPost->zUser, g.zLogin)==0;
      int isPrivate = content_is_private(fpid);
      @ <p><form action="%R/forumedit" method="POST">
      @ <input type="hidden" name="fpid" value="%s(zUuid)">
      if( !isPrivate ){
................................................................................
        /* A post that is pending moderation can be deleted by the
        ** person who originally submitted the post */
        @ <input type="submit" name="reject" value="Delete">
      }
      @ </form></p>
    }
    manifest_destroy(pPost);
    @ </div>
  }
  db_finalize(&q);
}

/*
** Display all messages in a forumthread with indentation.
*/
static int forum_display_hierarchical(int froot, int target){
  ForumThread *pThread;
  ForumEntry *p;
  Manifest *pPost;
  int fpid;
  char *zDate;
  char *zUuid;

  pThread = forumthread_create(froot);
  for(p=pThread->pDisplay; p; p=p->pDisplay){

    fpid = p->pLeaf ? p->pLeaf->fpid : p->fpid;
    if( p->nIndent==1 ){
      @ <div id='forum(%d(fpid)' class='forumHierRoot'>
    }else{
      @ <div id='forum%d(fpid)' class='forumHier' \
      @ style='margin-left: %d((p->nIndent-1)*3)ex;'>
    }
    pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
    if( pPost==0 ) continue;
    if( pPost->zThreadTitle ){
      @ <h1>%h(pPost->zThreadTitle)</h1>
    }
    zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
    @ <p>By %h(pPost->zUser) on %h(zDate) (%d(fpid))
    fossil_free(zDate);
    zUuid = rid_to_uuid(fpid);
    if( g.perm.Debug ){
      @ <span class="debug">\
      @ <a href="%R/artifact/%h(zUuid)">artifact</a></span>
    }
    forum_render(0, pPost->zMimetype, pPost->zWiki, 0);
    if( g.perm.WrForum ){
      int sameUser = login_is_individual()
                     && fossil_strcmp(pPost->zUser, g.zLogin)==0;
      int isPrivate = content_is_private(fpid);
      @ <p><form action="%R/forumedit" method="POST">
      @ <input type="hidden" name="fpid" value="%s(zUuid)">
      if( !isPrivate ){
................................................................................
        @ <input type="submit" name="reject" value="Delete">
      }
      @ </form></p>
    }
    manifest_destroy(pPost);
    fossil_free(zUuid);
    @ </div>
  }
  for(p=pThread->pFirst; p; p=p->pNext){
    if( p->fpid==target ){
      if( p->pLeaf ) target = p->pLeaf->fpid;
      break;
    }
  }
  forumthread_delete(pThread);
  return target;
}

/*
** WEBPAGE: forumthread
**
** Show all forum messages associated with a particular message thread.
**
................................................................................
  }
  style_header("Forum");
  froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
  if( froot==0 ){
    webpage_error("Not a forum post: \"%s\"", zName);
  }
  if( P("t") ){
    if( g.perm.Debug ){
      style_submenu_element("Hierarchical", "%R/forumthread/%s", zName);
    }                          
    forum_display_chronological(froot, fpid);
  }else{

    if( g.perm.Debug ){
      style_submenu_element("Chronological", "%R/forumthread/%s?t", zName);
    }                          
    fpid = forum_display_hierarchical(froot, fpid);
  }
  
  style_footer();
}

/*
** Return true if a forum post should be moderated.
*/
static int forum_need_moderation(void){
................................................................................
    return;
  }
  if( P("submit") ){
    if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return;
  }
  if( P("preview") ){
    @ <h1>Preview:</h1>
    forum_render(zTitle, zMimetype, zContent, "forumEdit");
  }
  style_header("New Forum Thread");
  @ <form action="%R/%s(g.zPath)" method="POST">
  forum_entry_widget(zTitle, zMimetype, zContent);
  @ <input type="submit" name="preview" value="Preview">
  if( P("preview") ){
    @ <input type="submit" name="submit" value="Submit">
................................................................................
  }
  if( isDelete ){
    zMimetype = "text/x-fossil-wiki";
    zContent = "";
    if( pPost->zThreadTitle ) zTitle = "";
    style_header("Delete %s", zTitle ? "Post" : "Reply");
    @ <h1>Original Post:</h1>
    forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki,
                 "forumEdit");
    @ <h1>Change Into:</h1>
    forum_render(zTitle, zMimetype, zContent,"forumEdit");
    @ <form action="%R/forumedit" method="POST">
    @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
    @ <input type="hidden" name="nullout" value="1">
    @ <input type="hidden" name="mimetype" value="%h(zMimetype)">
    @ <input type="hidden" name="content" value="%h(zContent)">
    if( zTitle ){
      @ <input type="hidden" name="title" value="%h(zTitle)">
................................................................................
    if( zContent==0 ) zContent = fossil_strdup(pPost->zWiki);
    if( zMimetype==0 ) zMimetype = fossil_strdup(pPost->zMimetype);
    if( zTitle==0 && pPost->zThreadTitle!=0 ){
      zTitle = fossil_strdup(pPost->zThreadTitle);
    }
    style_header("Edit %s", zTitle ? "Post" : "Reply");
    @ <h1>Original Post:</h1>
    forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki,
                 "forumEdit");
    if( P("preview") ){
      @ <h1>Preview Of Editted Post:</h1>
      forum_render(zTitle, zMimetype, zContent,"forumEdit");
    }
    @ <h1>Enter A Reply:</h1>
    @ <form action="%R/forumedit" method="POST">
    @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
    @ <input type="hidden" name="edit" value="1">
    forum_entry_widget(zTitle, zMimetype, zContent);
  }else{
    /* Reply */
    zMimetype = PD("mimetype","text/x-fossil-wiki");
    zContent = PDT("content","");
    style_header("Reply");
    @ <h1>Replying To:</h1>
    forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit");
    if( P("preview") ){
      @ <h1>Preview:</h1>
      forum_render(0, zMimetype,zContent, "forumEdit");
    }
    @ <h1>Enter A Reply:</h1>
    @ <form action="%R/forumedit" method="POST">
    @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
    @ <input type="hidden" name="reply" value="1">
    forum_entry_widget(0, zMimetype, zContent);
  }