Fossil SCM

Hierarchical display of forum threads.

drh 2018-07-25 21:00 forum-v2
Commit 7da12996859b46c1c5867dd0aa2aabbc35ee318ff90fb8e4c33fe2e243fd9a2a
1 file changed +266 -2
+266 -2
--- src/forum.c
+++ src/forum.c
@@ -18,10 +18,208 @@
1818
** This file contains code used to generate the user forum.
1919
*/
2020
#include "config.h"
2121
#include <assert.h>
2222
#include "forum.h"
23
+
24
+#if INTERFACE
25
+/*
26
+** Each instance of the following object represents a single message -
27
+** either the initial post, an edit to a post, a reply, or an edit to
28
+** a reply.
29
+*/
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 */
40
+ int nIndent; /* Number of levels of indentation for this entry */
41
+};
42
+
43
+/*
44
+** A single instance of the following tracks all entries for a thread.
45
+*/
46
+struct ForumThread {
47
+ ForumEntry *pFirst; /* First entry in chronological order */
48
+ ForumEntry *pLast; /* Last entry in chronological order */
49
+ ForumEntry *pDisplay; /* Entries in display order */
50
+ ForumEntry *pTail; /* Last on the display list */
51
+};
52
+#endif /* INTERFACE */
53
+
54
+/*
55
+** Delete a complete ForumThread and all its entries.
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
+
66
+/*
67
+** Search a ForumEntry list forwards looking for the entry with fpid
68
+*/
69
+static ForumEntry *forumentry_forward(ForumEntry *p, int fpid){
70
+ while( p && p->fpid!=fpid ) p = p->pNext;
71
+ return p;
72
+}
73
+
74
+/*
75
+** Search backwards for a ForumEntry
76
+*/
77
+static ForumEntry *forumentry_backward(ForumEntry *p, int fpid){
78
+ while( p && p->fpid!=fpid ) p = p->pPrev;
79
+ return p;
80
+}
81
+
82
+/*
83
+** Add an entry to the display list
84
+*/
85
+static void forumentry_add_to_display(ForumThread *pThread, ForumEntry *p){
86
+ if( pThread->pDisplay==0 ){
87
+ pThread->pDisplay = p;
88
+ }else{
89
+ pThread->pTail->pDisplay = p;
90
+ }
91
+ pThread->pTail = p;
92
+}
93
+
94
+/*
95
+** Extend the display list for pThread by adding all entries that
96
+** reference fpid. The first such entry will be no earlier then
97
+** entry "p".
98
+*/
99
+static void forumthread_display_order(
100
+ ForumThread *pThread,
101
+ ForumEntry *p,
102
+ int fpid,
103
+ int nIndent
104
+){
105
+ while( p ){
106
+ if( p->pEdit==0 && p->mfirt==fpid ){
107
+ p->nIndent = nIndent;
108
+ forumentry_add_to_display(pThread, p);
109
+ forumthread_display_order(pThread, p->pNext, p->fpid, nIndent+1);
110
+ }
111
+ p = p->pNext;
112
+ }
113
+}
114
+
115
+/*
116
+** Construct a ForumThread object given the root record id.
117
+*/
118
+static ForumThread *forumthread_create(int froot){
119
+ ForumThread *pThread;
120
+ ForumEntry *pEntry;
121
+ Stmt q;
122
+ pThread = fossil_malloc( sizeof(*pThread) );
123
+ memset(pThread, 0, sizeof(*pThread));
124
+ db_prepare(&q, "SELECT fpid, firt, fprev FROM forumpost"
125
+ " WHERE froot=%d ORDER BY fmtime", froot);
126
+ while( db_step(&q)==SQLITE_ROW ){
127
+ pEntry = fossil_malloc( sizeof(*pEntry) );
128
+ memset(pEntry, 0, sizeof(*pEntry));
129
+ pEntry->fpid = db_column_int(&q, 0);
130
+ pEntry->firt = db_column_int(&q, 1);
131
+ pEntry->fprev = db_column_int(&q, 2);
132
+ pEntry->mfirt = pEntry->firt;
133
+ pEntry->pPrev = pThread->pLast;
134
+ pEntry->pNext = 0;
135
+ if( pThread->pLast==0 ){
136
+ pThread->pFirst = pEntry;
137
+ }else{
138
+ pThread->pLast->pNext = pEntry;
139
+ }
140
+ pThread->pLast = pEntry;
141
+ }
142
+ db_finalize(&q);
143
+
144
+ /* Establish which entries are the latest edit. After this loop
145
+ ** completes, entries that have non-NULL pLeaf should not be
146
+ ** displayed.
147
+ */
148
+ for(pEntry=pThread->pFirst; pEntry; pEntry=pEntry->pNext){
149
+ if( pEntry->fprev ){
150
+ ForumEntry *pBase, *p;
151
+ pBase = p = forumentry_backward(pEntry->pPrev, pEntry->fprev);
152
+ pEntry->pEdit = p;
153
+ while( p ){
154
+ pBase = p;
155
+ p->pLeaf = pEntry;
156
+ p = pBase->pEdit;
157
+ }
158
+ for(p=pEntry->pNext; p; p=p->pNext){
159
+ if( p->mfirt==pEntry->fpid ) p->mfirt = pBase->mfirt;
160
+ }
161
+ }
162
+ }
163
+
164
+ /* Compute the display order */
165
+ pEntry = pThread->pFirst;
166
+ pEntry->nIndent = 1;
167
+ forumentry_add_to_display(pThread, pEntry);
168
+ forumthread_display_order(pThread, pEntry, pEntry->fpid, 2);
169
+
170
+ /* Return the result */
171
+ return pThread;
172
+}
173
+
174
+/*
175
+** COMMAND: test-forumthread
176
+**
177
+** Usage: %fossil test-forumthread THREADID
178
+**
179
+** Display a summary of all messages on a thread.
180
+*/
181
+void forumthread_cmd(void){
182
+ int fpid;
183
+ int froot;
184
+ const char *zName;
185
+ ForumThread *pThread;
186
+ ForumEntry *p;
187
+
188
+ db_find_and_open_repository(0,0);
189
+ verify_all_options();
190
+ if( g.argc!=3 ) usage("THREADID");
191
+ zName = g.argv[2];
192
+ fpid = symbolic_name_to_rid(zName, "f");
193
+ if( fpid<=0 ){
194
+ fossil_fatal("Unknown or ambiguous forum id: \"%s\"", zName);
195
+ }
196
+ froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
197
+ if( froot==0 ){
198
+ fossil_fatal("Not a forum post: \"%s\"", zName);
199
+ }
200
+ fossil_print("fpid = %d\n", fpid);
201
+ fossil_print("froot = %d\n", froot);
202
+ pThread = forumthread_create(froot);
203
+ fossil_print("Chronological:\n");
204
+ /* 123456789 123456789 123456789 123456789 123456789 */
205
+ fossil_print(" fpid firt fprev mfirt pLeaf\n");
206
+ for(p=pThread->pFirst; p; p=p->pNext){
207
+ fossil_print("%9d %9d %9d %9d %9d\n",
208
+ p->fpid, p->firt, p->fprev, p->mfirt, p->pLeaf ? p->pLeaf->fpid : 0);
209
+ }
210
+ fossil_print("\nDisplay\n");
211
+ for(p=pThread->pDisplay; p; p=p->pDisplay){
212
+ fossil_print("%*s", (p->nIndent-1)*3, "");
213
+ if( p->pLeaf ){
214
+ fossil_print("%d->%d\n", p->fpid, p->pLeaf->fpid);
215
+ }else{
216
+ fossil_print("%d\n", p->fpid);
217
+ }
218
+ }
219
+ forumthread_delete(pThread);
220
+}
23221
24222
/*
25223
** Render a forum post for display
26224
*/
27225
void forum_render(
@@ -50,11 +248,11 @@
50248
}
51249
52250
/*
53251
** Display all posts in a forum thread in chronological order
54252
*/
55
-static void forum_thread_chronological(int froot){
253
+static void forum_display_chronological(int froot, int target){
56254
Stmt q;
57255
int i = 0;
58256
db_prepare(&q,
59257
"SELECT fpid, fprev, firt, uuid, datetime(fmtime,'unixepoch')\n"
60258
" FROM forumpost, blob\n"
@@ -115,10 +313,72 @@
115313
}
116314
manifest_destroy(pPost);
117315
}
118316
db_finalize(&q);
119317
}
318
+
319
+/*
320
+** Display all messages in a forumthread with indentation.
321
+*/
322
+static void forum_display(int froot, int target){
323
+ ForumThread *pThread;
324
+ ForumEntry *p;
325
+ Manifest *pPost;
326
+ int fpid;
327
+ char *zDate;
328
+ char *zUuid;
329
+
330
+ pThread = forumthread_create(froot);
331
+ for(p=pThread->pDisplay; p; p=p->pDisplay){
332
+ @ <div style='margin-left: %d((p->nIndent-1)*3)ex;'>
333
+ fpid = p->pLeaf ? p->pLeaf->fpid : p->fpid;
334
+ pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
335
+ if( pPost==0 ) continue;
336
+ if( pPost->zThreadTitle ){
337
+ @ <h1>%h(pPost->zThreadTitle)</h1>
338
+ }
339
+ zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
340
+ @ <p>By %h(pPost->zUser) on %h(zDate) (%d(fpid))
341
+ fossil_free(zDate);
342
+ zUuid = rid_to_uuid(fpid);
343
+ if( g.perm.Debug ){
344
+ @ <span class="debug">\
345
+ @ <a href="%R/artifact/%h(zUuid)">artifact</a></span>
346
+ }
347
+ forum_render(0, pPost->zMimetype, pPost->zWiki);
348
+ if( g.perm.WrForum ){
349
+ int sameUser = login_is_individual()
350
+ && fossil_strcmp(pPost->zUser, g.zLogin)==0;
351
+ int isPrivate = content_is_private(fpid);
352
+ @ <p><form action="%R/forumedit" method="POST">
353
+ @ <input type="hidden" name="fpid" value="%s(zUuid)">
354
+ if( !isPrivate ){
355
+ /* Reply and Edit are only available if the post has already
356
+ ** been approved */
357
+ @ <input type="submit" name="reply" value="Reply">
358
+ if( g.perm.Admin || sameUser ){
359
+ @ <input type="submit" name="edit" value="Edit">
360
+ @ <input type="submit" name="nullout" value="Delete">
361
+ }
362
+ }else if( g.perm.ModForum ){
363
+ /* Provide moderators with moderation buttons for posts that
364
+ ** are pending moderation */
365
+ @ <input type="submit" name="approve" value="Approve">
366
+ @ <input type="submit" name="reject" value="Reject">
367
+ }else if( sameUser ){
368
+ /* A post that is pending moderation can be deleted by the
369
+ ** person who originally submitted the post */
370
+ @ <input type="submit" name="reject" value="Delete">
371
+ }
372
+ @ </form></p>
373
+ }
374
+ manifest_destroy(pPost);
375
+ fossil_free(zUuid);
376
+ @ </div>
377
+ }
378
+ forumthread_delete(pThread);
379
+}
120380
121381
/*
122382
** WEBPAGE: forumthread
123383
**
124384
** Show all forum messages associated with a particular message thread.
@@ -146,11 +406,15 @@
146406
style_header("Forum");
147407
froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
148408
if( froot==0 ){
149409
webpage_error("Not a forum post: \"%s\"", zName);
150410
}
151
- forum_thread_chronological(froot);
411
+ if( P("t") ){
412
+ forum_display_chronological(froot, fpid);
413
+ }else{
414
+ forum_display(froot, fpid);
415
+ }
152416
style_footer();
153417
}
154418
155419
/*
156420
** Return true if a forum post should be moderated.
157421
--- src/forum.c
+++ src/forum.c
@@ -18,10 +18,208 @@
18 ** This file contains code used to generate the user forum.
19 */
20 #include "config.h"
21 #include <assert.h>
22 #include "forum.h"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
24 /*
25 ** Render a forum post for display
26 */
27 void forum_render(
@@ -50,11 +248,11 @@
50 }
51
52 /*
53 ** Display all posts in a forum thread in chronological order
54 */
55 static void forum_thread_chronological(int froot){
56 Stmt q;
57 int i = 0;
58 db_prepare(&q,
59 "SELECT fpid, fprev, firt, uuid, datetime(fmtime,'unixepoch')\n"
60 " FROM forumpost, blob\n"
@@ -115,10 +313,72 @@
115 }
116 manifest_destroy(pPost);
117 }
118 db_finalize(&q);
119 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
121 /*
122 ** WEBPAGE: forumthread
123 **
124 ** Show all forum messages associated with a particular message thread.
@@ -146,11 +406,15 @@
146 style_header("Forum");
147 froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
148 if( froot==0 ){
149 webpage_error("Not a forum post: \"%s\"", zName);
150 }
151 forum_thread_chronological(froot);
 
 
 
 
152 style_footer();
153 }
154
155 /*
156 ** Return true if a forum post should be moderated.
157
--- src/forum.c
+++ src/forum.c
@@ -18,10 +18,208 @@
18 ** This file contains code used to generate the user forum.
19 */
20 #include "config.h"
21 #include <assert.h>
22 #include "forum.h"
23
24 #if INTERFACE
25 /*
26 ** Each instance of the following object represents a single message -
27 ** either the initial post, an edit to a post, a reply, or an edit to
28 ** a reply.
29 */
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 */
40 int nIndent; /* Number of levels of indentation for this entry */
41 };
42
43 /*
44 ** A single instance of the following tracks all entries for a thread.
45 */
46 struct ForumThread {
47 ForumEntry *pFirst; /* First entry in chronological order */
48 ForumEntry *pLast; /* Last entry in chronological order */
49 ForumEntry *pDisplay; /* Entries in display order */
50 ForumEntry *pTail; /* Last on the display list */
51 };
52 #endif /* INTERFACE */
53
54 /*
55 ** Delete a complete ForumThread and all its entries.
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
66 /*
67 ** Search a ForumEntry list forwards looking for the entry with fpid
68 */
69 static ForumEntry *forumentry_forward(ForumEntry *p, int fpid){
70 while( p && p->fpid!=fpid ) p = p->pNext;
71 return p;
72 }
73
74 /*
75 ** Search backwards for a ForumEntry
76 */
77 static ForumEntry *forumentry_backward(ForumEntry *p, int fpid){
78 while( p && p->fpid!=fpid ) p = p->pPrev;
79 return p;
80 }
81
82 /*
83 ** Add an entry to the display list
84 */
85 static void forumentry_add_to_display(ForumThread *pThread, ForumEntry *p){
86 if( pThread->pDisplay==0 ){
87 pThread->pDisplay = p;
88 }else{
89 pThread->pTail->pDisplay = p;
90 }
91 pThread->pTail = p;
92 }
93
94 /*
95 ** Extend the display list for pThread by adding all entries that
96 ** reference fpid. The first such entry will be no earlier then
97 ** entry "p".
98 */
99 static void forumthread_display_order(
100 ForumThread *pThread,
101 ForumEntry *p,
102 int fpid,
103 int nIndent
104 ){
105 while( p ){
106 if( p->pEdit==0 && p->mfirt==fpid ){
107 p->nIndent = nIndent;
108 forumentry_add_to_display(pThread, p);
109 forumthread_display_order(pThread, p->pNext, p->fpid, nIndent+1);
110 }
111 p = p->pNext;
112 }
113 }
114
115 /*
116 ** Construct a ForumThread object given the root record id.
117 */
118 static ForumThread *forumthread_create(int froot){
119 ForumThread *pThread;
120 ForumEntry *pEntry;
121 Stmt q;
122 pThread = fossil_malloc( sizeof(*pThread) );
123 memset(pThread, 0, sizeof(*pThread));
124 db_prepare(&q, "SELECT fpid, firt, fprev FROM forumpost"
125 " WHERE froot=%d ORDER BY fmtime", froot);
126 while( db_step(&q)==SQLITE_ROW ){
127 pEntry = fossil_malloc( sizeof(*pEntry) );
128 memset(pEntry, 0, sizeof(*pEntry));
129 pEntry->fpid = db_column_int(&q, 0);
130 pEntry->firt = db_column_int(&q, 1);
131 pEntry->fprev = db_column_int(&q, 2);
132 pEntry->mfirt = pEntry->firt;
133 pEntry->pPrev = pThread->pLast;
134 pEntry->pNext = 0;
135 if( pThread->pLast==0 ){
136 pThread->pFirst = pEntry;
137 }else{
138 pThread->pLast->pNext = pEntry;
139 }
140 pThread->pLast = pEntry;
141 }
142 db_finalize(&q);
143
144 /* Establish which entries are the latest edit. After this loop
145 ** completes, entries that have non-NULL pLeaf should not be
146 ** displayed.
147 */
148 for(pEntry=pThread->pFirst; pEntry; pEntry=pEntry->pNext){
149 if( pEntry->fprev ){
150 ForumEntry *pBase, *p;
151 pBase = p = forumentry_backward(pEntry->pPrev, pEntry->fprev);
152 pEntry->pEdit = p;
153 while( p ){
154 pBase = p;
155 p->pLeaf = pEntry;
156 p = pBase->pEdit;
157 }
158 for(p=pEntry->pNext; p; p=p->pNext){
159 if( p->mfirt==pEntry->fpid ) p->mfirt = pBase->mfirt;
160 }
161 }
162 }
163
164 /* Compute the display order */
165 pEntry = pThread->pFirst;
166 pEntry->nIndent = 1;
167 forumentry_add_to_display(pThread, pEntry);
168 forumthread_display_order(pThread, pEntry, pEntry->fpid, 2);
169
170 /* Return the result */
171 return pThread;
172 }
173
174 /*
175 ** COMMAND: test-forumthread
176 **
177 ** Usage: %fossil test-forumthread THREADID
178 **
179 ** Display a summary of all messages on a thread.
180 */
181 void forumthread_cmd(void){
182 int fpid;
183 int froot;
184 const char *zName;
185 ForumThread *pThread;
186 ForumEntry *p;
187
188 db_find_and_open_repository(0,0);
189 verify_all_options();
190 if( g.argc!=3 ) usage("THREADID");
191 zName = g.argv[2];
192 fpid = symbolic_name_to_rid(zName, "f");
193 if( fpid<=0 ){
194 fossil_fatal("Unknown or ambiguous forum id: \"%s\"", zName);
195 }
196 froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
197 if( froot==0 ){
198 fossil_fatal("Not a forum post: \"%s\"", zName);
199 }
200 fossil_print("fpid = %d\n", fpid);
201 fossil_print("froot = %d\n", froot);
202 pThread = forumthread_create(froot);
203 fossil_print("Chronological:\n");
204 /* 123456789 123456789 123456789 123456789 123456789 */
205 fossil_print(" fpid firt fprev mfirt pLeaf\n");
206 for(p=pThread->pFirst; p; p=p->pNext){
207 fossil_print("%9d %9d %9d %9d %9d\n",
208 p->fpid, p->firt, p->fprev, p->mfirt, p->pLeaf ? p->pLeaf->fpid : 0);
209 }
210 fossil_print("\nDisplay\n");
211 for(p=pThread->pDisplay; p; p=p->pDisplay){
212 fossil_print("%*s", (p->nIndent-1)*3, "");
213 if( p->pLeaf ){
214 fossil_print("%d->%d\n", p->fpid, p->pLeaf->fpid);
215 }else{
216 fossil_print("%d\n", p->fpid);
217 }
218 }
219 forumthread_delete(pThread);
220 }
221
222 /*
223 ** Render a forum post for display
224 */
225 void forum_render(
@@ -50,11 +248,11 @@
248 }
249
250 /*
251 ** Display all posts in a forum thread in chronological order
252 */
253 static void forum_display_chronological(int froot, int target){
254 Stmt q;
255 int i = 0;
256 db_prepare(&q,
257 "SELECT fpid, fprev, firt, uuid, datetime(fmtime,'unixepoch')\n"
258 " FROM forumpost, blob\n"
@@ -115,10 +313,72 @@
313 }
314 manifest_destroy(pPost);
315 }
316 db_finalize(&q);
317 }
318
319 /*
320 ** Display all messages in a forumthread with indentation.
321 */
322 static void forum_display(int froot, int target){
323 ForumThread *pThread;
324 ForumEntry *p;
325 Manifest *pPost;
326 int fpid;
327 char *zDate;
328 char *zUuid;
329
330 pThread = forumthread_create(froot);
331 for(p=pThread->pDisplay; p; p=p->pDisplay){
332 @ <div style='margin-left: %d((p->nIndent-1)*3)ex;'>
333 fpid = p->pLeaf ? p->pLeaf->fpid : p->fpid;
334 pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
335 if( pPost==0 ) continue;
336 if( pPost->zThreadTitle ){
337 @ <h1>%h(pPost->zThreadTitle)</h1>
338 }
339 zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
340 @ <p>By %h(pPost->zUser) on %h(zDate) (%d(fpid))
341 fossil_free(zDate);
342 zUuid = rid_to_uuid(fpid);
343 if( g.perm.Debug ){
344 @ <span class="debug">\
345 @ <a href="%R/artifact/%h(zUuid)">artifact</a></span>
346 }
347 forum_render(0, pPost->zMimetype, pPost->zWiki);
348 if( g.perm.WrForum ){
349 int sameUser = login_is_individual()
350 && fossil_strcmp(pPost->zUser, g.zLogin)==0;
351 int isPrivate = content_is_private(fpid);
352 @ <p><form action="%R/forumedit" method="POST">
353 @ <input type="hidden" name="fpid" value="%s(zUuid)">
354 if( !isPrivate ){
355 /* Reply and Edit are only available if the post has already
356 ** been approved */
357 @ <input type="submit" name="reply" value="Reply">
358 if( g.perm.Admin || sameUser ){
359 @ <input type="submit" name="edit" value="Edit">
360 @ <input type="submit" name="nullout" value="Delete">
361 }
362 }else if( g.perm.ModForum ){
363 /* Provide moderators with moderation buttons for posts that
364 ** are pending moderation */
365 @ <input type="submit" name="approve" value="Approve">
366 @ <input type="submit" name="reject" value="Reject">
367 }else if( sameUser ){
368 /* A post that is pending moderation can be deleted by the
369 ** person who originally submitted the post */
370 @ <input type="submit" name="reject" value="Delete">
371 }
372 @ </form></p>
373 }
374 manifest_destroy(pPost);
375 fossil_free(zUuid);
376 @ </div>
377 }
378 forumthread_delete(pThread);
379 }
380
381 /*
382 ** WEBPAGE: forumthread
383 **
384 ** Show all forum messages associated with a particular message thread.
@@ -146,11 +406,15 @@
406 style_header("Forum");
407 froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
408 if( froot==0 ){
409 webpage_error("Not a forum post: \"%s\"", zName);
410 }
411 if( P("t") ){
412 forum_display_chronological(froot, fpid);
413 }else{
414 forum_display(froot, fpid);
415 }
416 style_footer();
417 }
418
419 /*
420 ** Return true if a forum post should be moderated.
421

Keyboard Shortcuts

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