Fossil

Check-in [c35fe945]
Login

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

Overview
Comment:Initial implementation of the forum reply screen.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | forum-v2
Files: files | file ages | folders
SHA3-256:c35fe945d2b3a2fe1572b8757d4fb01c58cfe62d16f6dfd2c8af3a41b9922f6e
User & Date: drh 2018-07-25 13:09:44
Context
2018-07-25
13:21
Merge enhancements from trunk. check-in: 94262a8c user: drh tags: forum-v2
13:09
Initial implementation of the forum reply screen. check-in: c35fe945 user: drh tags: forum-v2
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
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/cgi.c.

341
342
343
344
345
346
347

348
349
350
351
352
353
354
  }
  fflush(g.httpOut);
  CGIDEBUG(("DONE\n"));

  /* After the webpage has been sent, do any useful background
  ** processing.
  */

  if( g.db!=0 && iReplyStatus==200 ){
    fclose(g.httpOut);
    g.httpOut = fossil_fopen("/dev/null", "wb");
    backoffice_run();
  }
}








>







341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
  }
  fflush(g.httpOut);
  CGIDEBUG(("DONE\n"));

  /* After the webpage has been sent, do any useful background
  ** processing.
  */
  g.cgiOutput = 2;
  if( g.db!=0 && iReplyStatus==200 ){
    fclose(g.httpOut);
    g.httpOut = fossil_fopen("/dev/null", "wb");
    backoffice_run();
  }
}

Changes to src/forum.c.

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

75
76


77
78




79
80
81
82
83
84
85
...
198
199
200
201
202
203
204

















205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
...
274
275
276
277
278
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293


294
295


296
297




...
298









299


300












301






302



303



304
305
306
307
    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++;
    @ <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( g.perm.WrForum ){



      @ <p><form action="%R/forumedit" method="POST">
      @ <input type="hidden" name="fpid" value="%s(zUuid)">



      @ <input type="submit" name="reply" value="Reply">
      if( g.perm.Admin || fossil_strcmp(pPost->zUser,g.zLogin)==0 ){
        @ <input type="submit" name="edit" value="Edit">

      }
      if( g.perm.ModForum && content_is_private(fpid) ){


        @ <input type="submit" name="approve" value="Approve">
        @ <input type="submit" name="reject" value="Reject">




      }
      @ </form></p>
    }
    manifest_destroy(pPost);
  }
  db_finalize(&q);
}
................................................................................
    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");
  @ <form action="%R/%s(g.zPath)" method="POST">
  @ Title: <input type="input" name="t" value="%h(zTitle)" size="50"><br>
  @ Markup style:
  mimetype_option_menu(zMimetype);
  @ <br><textarea name="x" class="wikiedit" cols="80" \
  @ rows="25" wrap="virtual">%h(zContent)</textarea><br>
  @ <input type="submit" name="preview" value="Preview">
  if( P("preview") ){
    @ <input type="submit" name="submit" value="Submit">
  }else{
    @ <input type="submit" name="submit" value="Submit" disabled>
  }
  if( g.zPath[0]=='t' ){
................................................................................
**
**   name=X        Hash of the post to be editted.  REQUIRED
*/
void forumedit_page(void){
  int fpid;
  Manifest *pPost;

  fpid = symbolic_name_to_rid(PD("fpid",""), "f");
  login_check_credentials();
  if( !g.perm.WrForum ){
    login_needed(g.anon.WrForum);
    return;
  }

  if( fpid<=0 || (pPost = manifest_get(fpid, CFTYPE_FORUM, 0))==0 ){
    webpage_error("Missing or invalid fpid query parameter");
    return;
  }
#if 0
  if( g.perm.ModForum ){
    if( P("approve") ){


    }
    if( P("reject") ){


    }
  }




................................................................................
  if( P("submit") ){









  }


  if( P("edit") ){












  }






  if( P("reply") ){



  }



  style_footer();
#endif
  webpage_error("Not yet implemented");
}







|
>
>
|






<
<
<


>
>
>


>
>
>
|
|
|
>
|
|
>
>


>
>
>
>







 







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










|
|
|







 







|
<
<
<
<







 







<





>




<


>
>


>
>


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

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

<
<

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
...
255
256
257
258
259
260
261
262




263
264
265
266
267
268
269
...
299
300
301
302
303
304
305

306
307
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
323
324
325
326
327
328
329
...
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371


372
    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)
    if( fprev ){
      @ edit of %d(fprev) %h(pPost->azParent[0])
    }
    if( firt ){
      @ in reply to %d(firt) %h(pPost->zInReplyTo)
    }



    forum_render(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 ){
        /* Reply and Edit are only available if the post has already
        ** been approved */
        @ <input type="submit" name="reply" value="Reply">
        if( g.perm.Admin || sameUser ){
          @ <input type="submit" name="edit" value="Edit">
          @ <input type="submit" name="nullout" value="Delete">
        }
      }else if( g.perm.ModForum ){
        /* Provide moderators with moderation buttons for posts that
        ** are pending moderation */
        @ <input type="submit" name="approve" value="Approve">
        @ <input type="submit" name="reject" value="Reject">
      }else if( sameUser ){
        /* 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);
}
................................................................................
    return 1;
  }

forum_post_error:
  blob_reset(&x);
  return 0;
}

/*
** Paint the form elements for entering a Forum post
*/
static void forum_entry_widget(
  const char *zTitle,
  const char *zMimetype,
  const char *zContent
){
  if( zTitle ){
    @ Title: <input type="input" name="title" value="%h(zTitle)" size="50"><br>
  }
  @ Markup style:
  mimetype_option_menu(zMimetype);
  @ <br><textarea name="content" class="wikiedit" cols="80" \
  @ rows="25" wrap="virtual">%h(zContent)</textarea><br>
}

/*
** 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("title","");
  const char *zMimetype = PD("mimetype","text/x-fossil-wiki");
  const char *zContent = PDT("content","");
  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");
  @ <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">
  }else{
    @ <input type="submit" name="submit" value="Submit" disabled>
  }
  if( g.zPath[0]=='t' ){
................................................................................
**
**   name=X        Hash of the post to be editted.  REQUIRED
*/
void forumedit_page(void){
  int fpid;
  Manifest *pPost;


  login_check_credentials();
  if( !g.perm.WrForum ){
    login_needed(g.anon.WrForum);
    return;
  }
  fpid = symbolic_name_to_rid(PD("fpid",""), "f");
  if( fpid<=0 || (pPost = manifest_get(fpid, CFTYPE_FORUM, 0))==0 ){
    webpage_error("Missing or invalid fpid query parameter");
    return;
  }

  if( g.perm.ModForum ){
    if( P("approve") ){
      webpage_not_yet_implemented();
      return;
    }
    if( P("reject") ){
      webpage_not_yet_implemented();
      return;
    }
  }
  if( P("submitdryrun") ){
    cgi_set_parameter_nocopy("dryrun","1",1);
    cgi_set_parameter_nocopy("submit","1",1);
  }
................................................................................
  if( P("submit") && cgi_csrf_safe(1) ){
    int done = 1;
    const char *zMimetype = PD("mimetype","text/x-fossil-wiki");
    const char *zContent = PDT("content","");
    if( P("reply") ){
      done = forum_post(0, fpid, 0, 0, zMimetype, zContent);
    }else if( P("edit") ){
      done = forum_post(0, 0, fpid, 0, zMimetype, zContent);
    }else{
      webpage_error("Need one of 'edit' or 'reply' query parameters");
    }
    if( done ) return;
  }
  if( P("edit") ){
    /* Provide an edit to the fpid post */
    webpage_not_yet_implemented();
    return;
  }else{
    const char *zMimetype = PD("mimetype","text/x-fossil-wiki");
    const char *zContent = PDT("content","");
    style_header("Forum Reply");
    @ <h1>Replying To:</h1>
    forum_render(pPost->zMimetype, pPost->zWiki);
    if( P("preview") ){
      @ <h1>Preview:</h1>
      forum_render(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);
    @ <input type="submit" name="preview" value="Preview">
    if( P("preview") ){
      @ <input type="submit" name="submit" value="Submit">
      if( g.perm.Setup ){
        @ <input type="submit" name="submitdryrun" value="Dry Run">
      }
    }
    @ </form>
  }
  style_footer();


}

Changes to src/main.c.

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  char *zHttpsURL;        /* zBaseURL translated to https: */
  char *zTop;             /* Parent directory of zPath */
  const char *zContentType;  /* The content type of the input HTTP request */
  int iErrPriority;       /* Priority of current error message */
  char *zErrMsg;          /* Text of an error message */
  int sslNotAvailable;    /* SSL is not available.  Do not redirect to https: */
  Blob cgiIn;             /* Input to an xfer www method */
  int cgiOutput;          /* 0: command-line 1: CGI. 2: CGI after an error */
  int xferPanic;          /* Write error messages in XFER protocol */
  int fullHttpReply;      /* True for full HTTP reply.  False for CGI reply */
  Th_Interp *interp;      /* The TH1 interpreter */
  char *th1Setup;         /* The TH1 post-creation setup script, if any */
  int th1Flags;           /* The TH1 integration state flags */
  FILE *httpIn;           /* Accept HTTP input from here */
  FILE *httpOut;          /* Send HTTP output here */







|







166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  char *zHttpsURL;        /* zBaseURL translated to https: */
  char *zTop;             /* Parent directory of zPath */
  const char *zContentType;  /* The content type of the input HTTP request */
  int iErrPriority;       /* Priority of current error message */
  char *zErrMsg;          /* Text of an error message */
  int sslNotAvailable;    /* SSL is not available.  Do not redirect to https: */
  Blob cgiIn;             /* Input to an xfer www method */
  int cgiOutput;          /* 0: command-line 1: CGI. 2: after CGI */
  int xferPanic;          /* Write error messages in XFER protocol */
  int fullHttpReply;      /* True for full HTTP reply.  False for CGI reply */
  Th_Interp *interp;      /* The TH1 interpreter */
  char *th1Setup;         /* The TH1 post-creation setup script, if any */
  int th1Flags;           /* The TH1 integration state flags */
  FILE *httpIn;           /* Accept HTTP input from here */
  FILE *httpOut;          /* Send HTTP output here */

Changes to src/printf.c.

1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
  fossil_errorlog("warning: %s", z);
#ifdef FOSSIL_ENABLE_JSON
  if(g.json.isJsonMode){
    json_warn( FSL_JSON_W_UNKNOWN, "%s", z );
  }else
#endif
  {
    if( g.cgiOutput ){
      cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
    }else{
      fossil_force_newline();
      fossil_trace("%s\n", z);
    }
  }
  free(z);







|







1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
  fossil_errorlog("warning: %s", z);
#ifdef FOSSIL_ENABLE_JSON
  if(g.json.isJsonMode){
    json_warn( FSL_JSON_W_UNKNOWN, "%s", z );
  }else
#endif
  {
    if( g.cgiOutput==1 ){
      cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
    }else{
      fossil_force_newline();
      fossil_trace("%s\n", z);
    }
  }
  free(z);

Changes to src/style.c.

1012
1013
1014
1015
1016
1017
1018







    @ <hr />
    @ <pre>
    @ %h(blob_str(&g.httpHeader))
    @ </pre>
  }
  style_footer();
}














>
>
>
>
>
>
>
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
    @ <hr />
    @ <pre>
    @ %h(blob_str(&g.httpHeader))
    @ </pre>
  }
  style_footer();
}

/*
** Generate a Not Yet Implemented error page.
*/
void webpage_not_yet_implemented(void){
  webpage_error("Not yet implemented");
}