Fossil

Check-in [449f06c2]
Login

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

Overview
Comment:Forum posts appear in the timeline. The /info command displays posts.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | forum-v2
Files: files | file ages | folders
SHA3-256: 449f06c296f883393b1b772b84237c93f25115088d19d3c64e6e9958f99f17b5
User & Date: drh 2018-07-24 23:37:43.231
Context
2018-07-25
11:25
Enhance the webpage_error() routine to show the complete calling environment to authorized users. Also, continuing work on forum. ... (check-in: 5fcf49f1 user: drh tags: forum-v2)
2018-07-24
23:37
Forum posts appear in the timeline. The /info command displays posts. ... (check-in: 449f06c2 user: drh tags: forum-v2)
22:21
Fix describe_artifact so that it understands forum posts. ... (check-in: 318b7c5a user: drh tags: forum-v2)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/forum.c.
16
17
18
19
20
21
22










23
24
25
26
27
28

29



30
31
32




33
















34
35
36
37
38
39
40
*******************************************************************************
**
** This file contains code used to generate the user forum.
*/
#include "config.h"
#include <assert.h>
#include "forum.h"











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

  db_prepare(&q, "SELECT fpid FROM forumpost WHERE froot=%d"



                 " ORDER BY fmtime", froot);
  while( db_step(&q)==SQLITE_ROW ){
    int fpid = db_column_int(&q, 0);




    Manifest *pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
















    if( pPost==0 ) continue;
    manifest_destroy(pPost);
  }
  db_finalize(&q);
}

/*







>
>
>
>
>
>
>
>
>
>






>
|
>
>
>
|


>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
*******************************************************************************
**
** This file contains code used to generate the user forum.
*/
#include "config.h"
#include <assert.h>
#include "forum.h"

/*
** Render a forum post for display
*/
void forum_render(const char *zMimetype, const char *zContent){
  Blob x;
  blob_init(&x, zContent, -1);
  wiki_render_by_mimetype(&x, zMimetype);
  blob_reset(&x);
}

/*
** Display all posts in a forum thread in chronological order
*/
static void forum_thread_chronological(int froot){
  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( i==0 ){
      @ <hr>
    }
    i++;
    @ <p>%d(fpid) %h(zUuid)<br>
    @ By %h(pPost->zUser) on %h(zDate)
    if( fprev ){
      @ edit of %d(fprev) %h(pPost->azParent[0])
    }
    if( firt ){
      @ in reply to %d(firt) %h(pPost->zInReplyTo)
    }
    if( pPost->zThreadTitle ){
      @ <h1>%h(pPost->zThreadTitle)</h1>
    }
    forum_render(pPost->zMimetype, pPost->zWiki);
    if( pPost==0 ) continue;
    manifest_destroy(pPost);
  }
  db_finalize(&q);
}

/*
84
85
86
87
88
89
90


91
92
93
94
95
96
97
98
99
*/
static int forum_need_moderation(void){
  return !g.perm.WrTForum && !g.perm.ModForum && P("domod")==0;
}

/*
** Add a new Forum Post artifact to the repository.


*/
static void forum_post(
  const char *zTitle,          /* Title.  NULL for replies */
  int iInReplyTo,              /* Post replying to.  0 for new threads */
  int iEdit,                   /* Post being edited, or zero for a new post */
  const char *zUser,           /* Username.  NULL means use login name */
  const char *zMimetype,       /* Mimetype of content. */
  const char *zContent         /* Content */
){







>
>

|







118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
*/
static int forum_need_moderation(void){
  return !g.perm.WrTForum && !g.perm.ModForum && P("domod")==0;
}

/*
** Add a new Forum Post artifact to the repository.
**
** Return true if a redirect occurs.
*/
static int forum_post(
  const char *zTitle,          /* Title.  NULL for replies */
  int iInReplyTo,              /* Post replying to.  0 for new threads */
  int iEdit,                   /* Post being edited, or zero for a new post */
  const char *zUser,           /* Username.  NULL means use login name */
  const char *zMimetype,       /* Mimetype of content. */
  const char *zContent         /* Content */
){
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  blob_appendf(&x, "W %d\n%s\n", strlen(zContent), zContent);
  md5sum_blob(&x, &cksum);
  blob_appendf(&x, "Z %b\n", &cksum);
  blob_reset(&cksum);
  if( P("dryrun") ){
    @ <pre>%h(blob_str(&x))</pre><hr>
  }else{
    wiki_put(&x, 0, forum_need_moderation());

    return;
  }

forum_post_error:
  blob_reset(&x);
}

/*
** Render a forum post for display
*/
void forum_render(const char *zMimetype, const char *zContent){
  Blob x;
  blob_init(&x, zContent, -1);
  wiki_render_by_mimetype(&x, zMimetype);
  blob_reset(&x);
}

/*
** WEBPAGE: forumnew
** WEBPAGE: test-forumnew
**
** Start a new forum thread.  The /test-forumnew works just like
** /forumnew except that it provides additional controls for testing
** and debugging.
*/
void forumnew_page(void){
  const char *zTitle = PDT("t","");
  const char *zMimetype = PD("mt","text/x-fossil-wiki");
  const char *zContent = PDT("x","");
  login_check_credentials();
  if( !g.perm.WrForum ){
    login_needed(g.anon.WrForum);
    return;
  }
  if( P("submit") ){
    forum_post(zTitle, 0, 0, 0, zMimetype, zContent);
  }
  if( P("preview") ){
    @ <h1>%h(zTitle)</h1>
    forum_render(zMimetype, zContent);
    @ <hr>
  }
  style_header("New Forum Thread");







|
>
|




<
|
<
<
<
<
<
<
<
<




















|







176
177
178
179
180
181
182
183
184
185
186
187
188
189

190








191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
  blob_appendf(&x, "W %d\n%s\n", strlen(zContent), zContent);
  md5sum_blob(&x, &cksum);
  blob_appendf(&x, "Z %b\n", &cksum);
  blob_reset(&cksum);
  if( P("dryrun") ){
    @ <pre>%h(blob_str(&x))</pre><hr>
  }else{
    int nrid = wiki_put(&x, 0, forum_need_moderation());
    cgi_redirectf("%R/forumthread/%S", rid_to_uuid(nrid));
    return 1;
  }

forum_post_error:
  blob_reset(&x);

  return 0;








}

/*
** WEBPAGE: forumnew
** WEBPAGE: test-forumnew
**
** Start a new forum thread.  The /test-forumnew works just like
** /forumnew except that it provides additional controls for testing
** and debugging.
*/
void forumnew_page(void){
  const char *zTitle = PDT("t","");
  const char *zMimetype = PD("mt","text/x-fossil-wiki");
  const char *zContent = PDT("x","");
  login_check_credentials();
  if( !g.perm.WrForum ){
    login_needed(g.anon.WrForum);
    return;
  }
  if( P("submit") ){
    if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return;
  }
  if( P("preview") ){
    @ <h1>%h(zTitle)</h1>
    forum_render(zMimetype, zContent);
    @ <hr>
  }
  style_header("New Forum Thread");
Changes to src/info.c.
2362
2363
2364
2365
2366
2367
2368





2369
2370
2371
2372
2373
2374
2375
    ci_page();
  }else
  if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
    ci_page();
  }else
  if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
    ainfo_page();





  }else
  {
    artifact_page();
  }
}

/*







>
>
>
>
>







2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
    ci_page();
  }else
  if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
    ci_page();
  }else
  if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
    ainfo_page();
  }else
  if( db_table_exists("repository","forumpost")
   && db_exists("SELECT 1 FROM forumpost WHERE fpid=%d", rid)
  ){
    forumthread_page();
  }else
  {
    artifact_page();
  }
}

/*
Changes to src/manifest.c.
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478




















2479
2480
2481
2482
2483
2484
2485
      p->rDate, rid, p->zUser, blob_str(&comment)+1
    );
    blob_reset(&comment);
  }
  if( p->type==CFTYPE_FORUM ){
    int froot, fprev, firt;
    schema_forum();
    froot = p->zThreadRoot ? uuid_to_rid(p->zThreadRoot, 1) : p->rid;
    fprev = p->nParent ? uuid_to_rid(p->azParent[0],1) : 0;
    firt = p->zInReplyTo ? uuid_to_rid(p->zInReplyTo,1) : 0;
    db_multi_exec(
      "INSERT INTO forumpost(fpid,froot,fprev,firt,fmtime)"
      "VALUES(%d,%d,nullif(%d,0),nullif(%d,0),%.17g)",
      p->rid, froot, fprev, firt, p->rDate
    );




















  }
  db_end_transaction(0);
  if( permitHooks ){
    rc = xfer_run_common_script();
    if( rc==TH_OK ){
      rc = xfer_run_script(zScript, zUuid, 0);
    }







|







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
      p->rDate, rid, p->zUser, blob_str(&comment)+1
    );
    blob_reset(&comment);
  }
  if( p->type==CFTYPE_FORUM ){
    int froot, fprev, firt;
    schema_forum();
    froot = p->zThreadRoot ? uuid_to_rid(p->zThreadRoot, 1) : rid;
    fprev = p->nParent ? uuid_to_rid(p->azParent[0],1) : 0;
    firt = p->zInReplyTo ? uuid_to_rid(p->zInReplyTo,1) : 0;
    db_multi_exec(
      "INSERT INTO forumpost(fpid,froot,fprev,firt,fmtime)"
      "VALUES(%d,%d,nullif(%d,0),nullif(%d,0),%.17g)",
      p->rid, froot, fprev, firt, p->rDate
    );
    if( froot!=p->rid ){
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('f',%.17g,%d,%Q,'Reply to:'||"
        "coalesce(substr((SELECT comment FROM event"
                        " WHERE objid=%d),12),' post '||substr(%Q,1,14)))",
        p->rDate, rid, p->zUser, froot, zUuid
      );
    }else{
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('f',%.17g,%d,%Q,'Forum post: %q')",
        p->rDate, rid, p->zUser, p->zThreadTitle
      );
      db_multi_exec(
        "UPDATE event SET comment='Reply to: %q' WHERE objid IN"
        " (SELECT fpid FROM forumpost WHERE froot=%d AND fpid!=froot)",
        p->zThreadTitle, rid
      );
    }
  }
  db_end_transaction(0);
  if( permitHooks ){
    rc = xfer_run_common_script();
    if( rc==TH_OK ){
      rc = xfer_run_script(zScript, zUuid, 0);
    }
Changes to src/wiki.c.
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443

444
445
446
447
448
449
450
  manifest_destroy(pWiki);
  style_footer();
}

/*
** Write a wiki artifact into the repository
*/
void wiki_put(Blob *pWiki, int parent, int needMod){
  int nrid;
  if( !needMod ){
    nrid = content_put_ex(pWiki, 0, 0, 0, 0);
    if( parent) content_deltify(parent, &nrid, 1, 0);
  }else{
    nrid = content_put_ex(pWiki, 0, 0, 0, 1);
    moderation_table_create();
    db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
  db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid);
  manifest_crosslink(nrid, pWiki, MC_NONE);

}

/*
** Output a selection box from which the user can select the
** wiki mimetype.
*/
void mimetype_option_menu(const char *zMimetype){







|












>







424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
  manifest_destroy(pWiki);
  style_footer();
}

/*
** Write a wiki artifact into the repository
*/
int wiki_put(Blob *pWiki, int parent, int needMod){
  int nrid;
  if( !needMod ){
    nrid = content_put_ex(pWiki, 0, 0, 0, 0);
    if( parent) content_deltify(parent, &nrid, 1, 0);
  }else{
    nrid = content_put_ex(pWiki, 0, 0, 0, 1);
    moderation_table_create();
    db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid);
  }
  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
  db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid);
  manifest_crosslink(nrid, pWiki, MC_NONE);
  return nrid;
}

/*
** Output a selection box from which the user can select the
** wiki mimetype.
*/
void mimetype_option_menu(const char *zMimetype){