Fossil SCM

Closed forum threads can no longer be edited by non-admins. Fix broken ability of non-builtin users to delete their own pending-moderation post. UI controls for closing/reing-open threads are still TODO.

stephan 2023-02-21 03:49 forumpost-locking
Commit 8f02c1d4a8f984b70cbc473da7305b5ca162082b58c560408a1020d18d4e2a14
2 files changed +90 -22 +6 -8
+90 -22
--- src/forum.c
+++ src/forum.c
@@ -47,11 +47,11 @@
4747
ForumPost *pNext; /* Next in chronological order */
4848
ForumPost *pPrev; /* Previous in chronological order */
4949
ForumPost *pDisplay; /* Next in display order */
5050
int nEdit; /* Number of edits to this post */
5151
int nIndent; /* Number of levels of indentation for this post */
52
- int fClosed; /* tagxref.tagtype if this (sub)thread has a closed tag. */
52
+ int fClosed; /* See forum_rid_is_closed() */
5353
};
5454
5555
/*
5656
** A single instance of the following tracks all entries for a thread.
5757
*/
@@ -83,47 +83,96 @@
8383
8484
/*
8585
** Returns true if p, or any parent of p, has an active "closed" tag.
8686
** Returns 0 if !p. For an edited chain of post, the tag is checked on
8787
** the final edit in the chain, as that permits that a post can be
88
-** locked and later unlocked.
88
+** locked and later unlocked. The return value is the tagxref.rowid
89
+** value of the tagxref entry which applies the "closed" tag, or 0 if
90
+** no active tag is found.
91
+**
92
+** If bCheckParents is true then p's thread parents are checked
93
+** (recursively) for closure, else only p is checked.
8994
*/
90
-int forum_post_is_closed(ForumPost *p){
95
+int forum_post_is_closed(ForumPost *p, int bCheckParents){
9196
if( !p ) return 0;
9297
if( p->pEditTail ) p = p->pEditTail;
93
- if( p->fClosed ) return p->fClosed;
98
+ if( p->fClosed || !bCheckParents ) return p->fClosed;
9499
else if( p->pIrt ){
95100
return forum_post_is_closed(p->pIrt->pEditTail
96
- ? p->pIrt->pEditTail : p->pIrt);
101
+ ? p->pIrt->pEditTail : p->pIrt,
102
+ bCheckParents);
97103
}
98104
return 0;
99105
}
100106
101107
/*
102
-** Given a forum post RID, this function returns true if that post or
103
-** the latest version of any parent post in its hierarchy have an
104
-** active "closed" tag.
108
+** Given a forum post RID, this function returns true if that post has
109
+** an active "closed" tag. If bCheckParents is true, the latest
110
+** version of each parent post is also checked (recursively), else
111
+** they are not. When checking parents, the first parent which is
112
+** closed ends the search.
113
+**
114
+** The return value is one of:
115
+**
116
+** - 0 if no "closed" tag is found.
117
+**
118
+** - The tagxref.rowid of the tagxref entry for the closure if rid is
119
+** the artifact to which the closure applies.
120
+**
121
+** - (-tagxref.rowid) if the given rid inherits a "closed" tag from an
122
+** ancestor forum post.
105123
*/
106
-int forum_rid_is_closed(int rid){
124
+static int forum_rid_is_closed(int rid, int bCheckParents){
107125
static Stmt qIrt = empty_Stmt_m;
108
- int rc;
126
+ int rc = 0;
109127
110128
/* TODO: this can probably be turned into a CTE, rather than a
111129
** recursive call into this function, by someone with superior
112130
** SQL-fu. */
113131
rc = rid_has_active_tag_name(rid, "closed");
114
- if( rc ) return rc;
132
+ if( rc || !bCheckParents ) return rc;
115133
else if( !qIrt.pStmt ) {
116134
db_static_prepare(&qIrt,
117135
"SELECT firt FROM forumpost "
118136
"WHERE fpid=$fpid ORDER BY fmtime DESC"
119137
);
120138
}
121139
db_bind_int(&qIrt, "$fpid", rid);
122
- rc = SQLITE_ROW==db_step(&qIrt) ? db_column_int(&qIrt, 0) : 0;
140
+ rid = SQLITE_ROW==db_step(&qIrt) ? db_column_int(&qIrt, 0) : 0;
123141
db_reset(&qIrt);
124
- return rc>0 ? forum_rid_is_closed(rc) : 0;
142
+ if( rid ){
143
+ rc = forum_rid_is_closed(rid, 1);
144
+ }
145
+ return rc>0 ? -rc : rc;
146
+}
147
+
148
+/*
149
+** If fClosed is true and the current user has admin privileges, this
150
+** renders either a checkbox to unlock forum post fpid (if fClosed>0)
151
+** or a SPAN.warning element that the given post inherits the CLOSED
152
+** status from a parent post (if fClosed<0). If neither of the initial
153
+** conditions is true, this is a no-op.
154
+*/
155
+static void forumpost_emit_unlock_checkbox(int fClosed, int fpid){
156
+ if( fClosed && g.perm.Admin ){
157
+ if( fClosed>0 ){
158
+ /* Only show the "unlock" checkbox on a post which is actually
159
+ ** closed, not on a post which inherits that state. */
160
+ @ <label class='warning'><input type="checkbox" name="reopen" value="1">
161
+ @ Re-open this CLOSED post? (NOT YET IMPLEMENTED)</label>
162
+ }else{
163
+ @ <span class='warning'>This post is CLOSED via a parent post</span>
164
+ }
165
+ }
166
+}
167
+
168
+/*
169
+** Emits a warning that the current forum post is CLOSED and can only
170
+** be edited or responded to by an administrator. */
171
+static void forumpost_error_closed(void){
172
+ @ <div class='error'>This (sub)thread is CLOSED and can only be
173
+ @ edited or replied to by an admin user.</div>
125174
}
126175
127176
/*
128177
** Delete a complete ForumThread and all its entries.
129178
*/
@@ -259,11 +308,11 @@
259308
for(; p; p=p->pEditPrev ){
260309
p->nEdit = pPost->nEdit;
261310
p->pEditTail = pPost;
262311
}
263312
}
264
- pPost->fClosed = rid_has_active_tag_name(pPost->fpid, "closed");
313
+ pPost->fClosed = forum_rid_is_closed(pPost->fpid, 1);
265314
}
266315
db_finalize(&q);
267316
268317
if( computeHierarchy ){
269318
/* Compute the hierarchical display order */
@@ -511,11 +560,11 @@
511560
const char *zMimetype;/* Formatting MIME type */
512561
513562
/* Get the manifest for the post. Abort if not found (e.g. shunned). */
514563
pManifest = manifest_get(p->fpid, CFTYPE_FORUM, 0);
515564
if( !pManifest ) return;
516
- fClosed = forum_post_is_closed(p);
565
+ fClosed = forum_post_is_closed(p, 1);
517566
/* When not in raw mode, create the border around the post. */
518567
if( !bRaw ){
519568
/* Open the <div> enclosing the post. Set the class string to mark the post
520569
** as selected and/or obsolete. */
521570
iIndent = (p->pEditHead ? p->pEditHead->nIndent : p->nIndent)-1;
@@ -1024,10 +1073,15 @@
10241073
Blob x, cksum, formatCheck, errMsg;
10251074
Manifest *pPost;
10261075
int nContent = zContent ? (int)strlen(zContent) : 0;
10271076
10281077
schema_forum();
1078
+ if( !g.perm.Admin && (iEdit || iInReplyTo)
1079
+ && forum_rid_is_closed(iEdit ? iEdit : iInReplyTo, 1) ){
1080
+ forumpost_error_closed();
1081
+ return 0;
1082
+ }
10291083
if( iEdit==0 && whitespace_only(zContent) ){
10301084
return 0;
10311085
}
10321086
if( iInReplyTo==0 && iEdit>0 ){
10331087
iBasis = iEdit;
@@ -1264,10 +1318,13 @@
12641318
char *zDate = 0;
12651319
const char *zFpid = PD("fpid","");
12661320
int isCsrfSafe;
12671321
int isDelete = 0;
12681322
int fClosed = 0;
1323
+ int bSameUser; /* True if author is also the reader */
1324
+ int bPreview; /* True in preview mode. */
1325
+ int bPrivate; /* True if post is private (not yet moderated) */
12691326
12701327
login_check_credentials();
12711328
if( !g.perm.WrForum ){
12721329
login_needed(g.anon.WrForum);
12731330
return;
@@ -1282,14 +1339,18 @@
12821339
}
12831340
if( P("cancel") ){
12841341
cgi_redirectf("%R/forumpost/%S",P("fpid"));
12851342
return;
12861343
}
1287
- fClosed = forum_rid_is_closed(fpid);
1344
+ bPreview = P("preview")!=0;
1345
+ fClosed = forum_rid_is_closed(fpid, froot!=fpid);
12881346
isCsrfSafe = cgi_csrf_safe(1);
1289
- if( g.perm.ModForum && isCsrfSafe ){
1290
- if( P("approve") ){
1347
+ bPrivate = content_is_private(fpid);
1348
+ bSameUser = login_is_individual()
1349
+ && fossil_strcmp(pPost->zUser, g.zLogin)==0;
1350
+ if( isCsrfSafe && (g.perm.ModForum || (bPrivate && bSameUser)) ){
1351
+ if( g.perm.ModForum && P("approve") ){
12911352
const char *zUserToTrust;
12921353
moderation_approve('f', fpid);
12931354
if( g.perm.AdminForum
12941355
&& PB("trust")
12951356
&& (zUserToTrust = P("trustuser"))!=0
@@ -1367,18 +1428,19 @@
13671428
}
13681429
style_header("Edit %s", zTitle ? "Post" : "Reply");
13691430
@ <h2>Original Post:</h2>
13701431
forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki,
13711432
"forumEdit", 1);
1372
- if( P("preview") ){
1433
+ if( bPreview ){
13731434
@ <h2>Preview of Edited Post:</h2>
13741435
forum_render(zTitle, zMimetype, zContent,"forumEdit", 1);
13751436
}
13761437
@ <h2>Revised Message:</h2>
13771438
@ <form action="%R/forume2" method="POST">
13781439
@ <input type="hidden" name="fpid" value="%h(P("fpid"))">
13791440
@ <input type="hidden" name="edit" value="1">
1441
+ if( fClosed ) forumpost_error_closed();
13801442
forum_from_line();
13811443
forum_post_widget(zTitle, zMimetype, zContent);
13821444
}else{
13831445
/* Reply */
13841446
char *zDisplayName;
@@ -1396,27 +1458,33 @@
13961458
zDisplayName = display_name_from_login(pPost->zUser);
13971459
@ <h3 class='forumPostHdr'>By %s(zDisplayName) on %h(zDate)</h3>
13981460
fossil_free(zDisplayName);
13991461
fossil_free(zDate);
14001462
forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1);
1401
- if( P("preview") && !whitespace_only(zContent) ){
1463
+ if( bPreview && !whitespace_only(zContent) ){
14021464
@ <h2>Preview:</h2>
14031465
forum_render(0, zMimetype,zContent, "forumEdit", 1);
14041466
}
14051467
@ <h2>Enter Reply:</h2>
14061468
@ <form action="%R/forume2" method="POST">
14071469
@ <input type="hidden" name="fpid" value="%h(P("fpid"))">
14081470
@ <input type="hidden" name="reply" value="1">
1471
+ if( fClosed ) forumpost_error_closed();
14091472
forum_from_line();
14101473
forum_post_widget(0, zMimetype, zContent);
14111474
}
14121475
if( !isDelete ){
14131476
@ <input type="submit" name="preview" value="Preview">
14141477
}
14151478
@ <input type="submit" name="cancel" value="Cancel">
1416
- if( (P("preview") && !whitespace_only(zContent)) || isDelete ){
1417
- @ <input type="submit" name="submit" value="Submit">
1479
+ if( (bPreview && !whitespace_only(zContent)) || isDelete ){
1480
+ if( !fClosed || g.perm.Admin ) {
1481
+ @ <input type="submit" name="submit" value="Submit">
1482
+ }
1483
+ forumpost_emit_unlock_checkbox(fClosed, fpid);
1484
+ }else if( !bPreview && fClosed ){
1485
+ @ <span class='warning'>This post is CLOSED</span>
14181486
}
14191487
if( g.perm.Debug ){
14201488
/* For the test-forumnew page add these extra debugging controls */
14211489
@ <div class="debug">
14221490
@ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \
14231491
--- src/forum.c
+++ src/forum.c
@@ -47,11 +47,11 @@
47 ForumPost *pNext; /* Next in chronological order */
48 ForumPost *pPrev; /* Previous in chronological order */
49 ForumPost *pDisplay; /* Next in display order */
50 int nEdit; /* Number of edits to this post */
51 int nIndent; /* Number of levels of indentation for this post */
52 int fClosed; /* tagxref.tagtype if this (sub)thread has a closed tag. */
53 };
54
55 /*
56 ** A single instance of the following tracks all entries for a thread.
57 */
@@ -83,47 +83,96 @@
83
84 /*
85 ** Returns true if p, or any parent of p, has an active "closed" tag.
86 ** Returns 0 if !p. For an edited chain of post, the tag is checked on
87 ** the final edit in the chain, as that permits that a post can be
88 ** locked and later unlocked.
 
 
 
 
 
89 */
90 int forum_post_is_closed(ForumPost *p){
91 if( !p ) return 0;
92 if( p->pEditTail ) p = p->pEditTail;
93 if( p->fClosed ) return p->fClosed;
94 else if( p->pIrt ){
95 return forum_post_is_closed(p->pIrt->pEditTail
96 ? p->pIrt->pEditTail : p->pIrt);
 
97 }
98 return 0;
99 }
100
101 /*
102 ** Given a forum post RID, this function returns true if that post or
103 ** the latest version of any parent post in its hierarchy have an
104 ** active "closed" tag.
 
 
 
 
 
 
 
 
 
 
 
 
105 */
106 int forum_rid_is_closed(int rid){
107 static Stmt qIrt = empty_Stmt_m;
108 int rc;
109
110 /* TODO: this can probably be turned into a CTE, rather than a
111 ** recursive call into this function, by someone with superior
112 ** SQL-fu. */
113 rc = rid_has_active_tag_name(rid, "closed");
114 if( rc ) return rc;
115 else if( !qIrt.pStmt ) {
116 db_static_prepare(&qIrt,
117 "SELECT firt FROM forumpost "
118 "WHERE fpid=$fpid ORDER BY fmtime DESC"
119 );
120 }
121 db_bind_int(&qIrt, "$fpid", rid);
122 rc = SQLITE_ROW==db_step(&qIrt) ? db_column_int(&qIrt, 0) : 0;
123 db_reset(&qIrt);
124 return rc>0 ? forum_rid_is_closed(rc) : 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125 }
126
127 /*
128 ** Delete a complete ForumThread and all its entries.
129 */
@@ -259,11 +308,11 @@
259 for(; p; p=p->pEditPrev ){
260 p->nEdit = pPost->nEdit;
261 p->pEditTail = pPost;
262 }
263 }
264 pPost->fClosed = rid_has_active_tag_name(pPost->fpid, "closed");
265 }
266 db_finalize(&q);
267
268 if( computeHierarchy ){
269 /* Compute the hierarchical display order */
@@ -511,11 +560,11 @@
511 const char *zMimetype;/* Formatting MIME type */
512
513 /* Get the manifest for the post. Abort if not found (e.g. shunned). */
514 pManifest = manifest_get(p->fpid, CFTYPE_FORUM, 0);
515 if( !pManifest ) return;
516 fClosed = forum_post_is_closed(p);
517 /* When not in raw mode, create the border around the post. */
518 if( !bRaw ){
519 /* Open the <div> enclosing the post. Set the class string to mark the post
520 ** as selected and/or obsolete. */
521 iIndent = (p->pEditHead ? p->pEditHead->nIndent : p->nIndent)-1;
@@ -1024,10 +1073,15 @@
1024 Blob x, cksum, formatCheck, errMsg;
1025 Manifest *pPost;
1026 int nContent = zContent ? (int)strlen(zContent) : 0;
1027
1028 schema_forum();
 
 
 
 
 
1029 if( iEdit==0 && whitespace_only(zContent) ){
1030 return 0;
1031 }
1032 if( iInReplyTo==0 && iEdit>0 ){
1033 iBasis = iEdit;
@@ -1264,10 +1318,13 @@
1264 char *zDate = 0;
1265 const char *zFpid = PD("fpid","");
1266 int isCsrfSafe;
1267 int isDelete = 0;
1268 int fClosed = 0;
 
 
 
1269
1270 login_check_credentials();
1271 if( !g.perm.WrForum ){
1272 login_needed(g.anon.WrForum);
1273 return;
@@ -1282,14 +1339,18 @@
1282 }
1283 if( P("cancel") ){
1284 cgi_redirectf("%R/forumpost/%S",P("fpid"));
1285 return;
1286 }
1287 fClosed = forum_rid_is_closed(fpid);
 
1288 isCsrfSafe = cgi_csrf_safe(1);
1289 if( g.perm.ModForum && isCsrfSafe ){
1290 if( P("approve") ){
 
 
 
1291 const char *zUserToTrust;
1292 moderation_approve('f', fpid);
1293 if( g.perm.AdminForum
1294 && PB("trust")
1295 && (zUserToTrust = P("trustuser"))!=0
@@ -1367,18 +1428,19 @@
1367 }
1368 style_header("Edit %s", zTitle ? "Post" : "Reply");
1369 @ <h2>Original Post:</h2>
1370 forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki,
1371 "forumEdit", 1);
1372 if( P("preview") ){
1373 @ <h2>Preview of Edited Post:</h2>
1374 forum_render(zTitle, zMimetype, zContent,"forumEdit", 1);
1375 }
1376 @ <h2>Revised Message:</h2>
1377 @ <form action="%R/forume2" method="POST">
1378 @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
1379 @ <input type="hidden" name="edit" value="1">
 
1380 forum_from_line();
1381 forum_post_widget(zTitle, zMimetype, zContent);
1382 }else{
1383 /* Reply */
1384 char *zDisplayName;
@@ -1396,27 +1458,33 @@
1396 zDisplayName = display_name_from_login(pPost->zUser);
1397 @ <h3 class='forumPostHdr'>By %s(zDisplayName) on %h(zDate)</h3>
1398 fossil_free(zDisplayName);
1399 fossil_free(zDate);
1400 forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1);
1401 if( P("preview") && !whitespace_only(zContent) ){
1402 @ <h2>Preview:</h2>
1403 forum_render(0, zMimetype,zContent, "forumEdit", 1);
1404 }
1405 @ <h2>Enter Reply:</h2>
1406 @ <form action="%R/forume2" method="POST">
1407 @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
1408 @ <input type="hidden" name="reply" value="1">
 
1409 forum_from_line();
1410 forum_post_widget(0, zMimetype, zContent);
1411 }
1412 if( !isDelete ){
1413 @ <input type="submit" name="preview" value="Preview">
1414 }
1415 @ <input type="submit" name="cancel" value="Cancel">
1416 if( (P("preview") && !whitespace_only(zContent)) || isDelete ){
1417 @ <input type="submit" name="submit" value="Submit">
 
 
 
 
 
1418 }
1419 if( g.perm.Debug ){
1420 /* For the test-forumnew page add these extra debugging controls */
1421 @ <div class="debug">
1422 @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \
1423
--- src/forum.c
+++ src/forum.c
@@ -47,11 +47,11 @@
47 ForumPost *pNext; /* Next in chronological order */
48 ForumPost *pPrev; /* Previous in chronological order */
49 ForumPost *pDisplay; /* Next in display order */
50 int nEdit; /* Number of edits to this post */
51 int nIndent; /* Number of levels of indentation for this post */
52 int fClosed; /* See forum_rid_is_closed() */
53 };
54
55 /*
56 ** A single instance of the following tracks all entries for a thread.
57 */
@@ -83,47 +83,96 @@
83
84 /*
85 ** Returns true if p, or any parent of p, has an active "closed" tag.
86 ** Returns 0 if !p. For an edited chain of post, the tag is checked on
87 ** the final edit in the chain, as that permits that a post can be
88 ** locked and later unlocked. The return value is the tagxref.rowid
89 ** value of the tagxref entry which applies the "closed" tag, or 0 if
90 ** no active tag is found.
91 **
92 ** If bCheckParents is true then p's thread parents are checked
93 ** (recursively) for closure, else only p is checked.
94 */
95 int forum_post_is_closed(ForumPost *p, int bCheckParents){
96 if( !p ) return 0;
97 if( p->pEditTail ) p = p->pEditTail;
98 if( p->fClosed || !bCheckParents ) return p->fClosed;
99 else if( p->pIrt ){
100 return forum_post_is_closed(p->pIrt->pEditTail
101 ? p->pIrt->pEditTail : p->pIrt,
102 bCheckParents);
103 }
104 return 0;
105 }
106
107 /*
108 ** Given a forum post RID, this function returns true if that post has
109 ** an active "closed" tag. If bCheckParents is true, the latest
110 ** version of each parent post is also checked (recursively), else
111 ** they are not. When checking parents, the first parent which is
112 ** closed ends the search.
113 **
114 ** The return value is one of:
115 **
116 ** - 0 if no "closed" tag is found.
117 **
118 ** - The tagxref.rowid of the tagxref entry for the closure if rid is
119 ** the artifact to which the closure applies.
120 **
121 ** - (-tagxref.rowid) if the given rid inherits a "closed" tag from an
122 ** ancestor forum post.
123 */
124 static int forum_rid_is_closed(int rid, int bCheckParents){
125 static Stmt qIrt = empty_Stmt_m;
126 int rc = 0;
127
128 /* TODO: this can probably be turned into a CTE, rather than a
129 ** recursive call into this function, by someone with superior
130 ** SQL-fu. */
131 rc = rid_has_active_tag_name(rid, "closed");
132 if( rc || !bCheckParents ) return rc;
133 else if( !qIrt.pStmt ) {
134 db_static_prepare(&qIrt,
135 "SELECT firt FROM forumpost "
136 "WHERE fpid=$fpid ORDER BY fmtime DESC"
137 );
138 }
139 db_bind_int(&qIrt, "$fpid", rid);
140 rid = SQLITE_ROW==db_step(&qIrt) ? db_column_int(&qIrt, 0) : 0;
141 db_reset(&qIrt);
142 if( rid ){
143 rc = forum_rid_is_closed(rid, 1);
144 }
145 return rc>0 ? -rc : rc;
146 }
147
148 /*
149 ** If fClosed is true and the current user has admin privileges, this
150 ** renders either a checkbox to unlock forum post fpid (if fClosed>0)
151 ** or a SPAN.warning element that the given post inherits the CLOSED
152 ** status from a parent post (if fClosed<0). If neither of the initial
153 ** conditions is true, this is a no-op.
154 */
155 static void forumpost_emit_unlock_checkbox(int fClosed, int fpid){
156 if( fClosed && g.perm.Admin ){
157 if( fClosed>0 ){
158 /* Only show the "unlock" checkbox on a post which is actually
159 ** closed, not on a post which inherits that state. */
160 @ <label class='warning'><input type="checkbox" name="reopen" value="1">
161 @ Re-open this CLOSED post? (NOT YET IMPLEMENTED)</label>
162 }else{
163 @ <span class='warning'>This post is CLOSED via a parent post</span>
164 }
165 }
166 }
167
168 /*
169 ** Emits a warning that the current forum post is CLOSED and can only
170 ** be edited or responded to by an administrator. */
171 static void forumpost_error_closed(void){
172 @ <div class='error'>This (sub)thread is CLOSED and can only be
173 @ edited or replied to by an admin user.</div>
174 }
175
176 /*
177 ** Delete a complete ForumThread and all its entries.
178 */
@@ -259,11 +308,11 @@
308 for(; p; p=p->pEditPrev ){
309 p->nEdit = pPost->nEdit;
310 p->pEditTail = pPost;
311 }
312 }
313 pPost->fClosed = forum_rid_is_closed(pPost->fpid, 1);
314 }
315 db_finalize(&q);
316
317 if( computeHierarchy ){
318 /* Compute the hierarchical display order */
@@ -511,11 +560,11 @@
560 const char *zMimetype;/* Formatting MIME type */
561
562 /* Get the manifest for the post. Abort if not found (e.g. shunned). */
563 pManifest = manifest_get(p->fpid, CFTYPE_FORUM, 0);
564 if( !pManifest ) return;
565 fClosed = forum_post_is_closed(p, 1);
566 /* When not in raw mode, create the border around the post. */
567 if( !bRaw ){
568 /* Open the <div> enclosing the post. Set the class string to mark the post
569 ** as selected and/or obsolete. */
570 iIndent = (p->pEditHead ? p->pEditHead->nIndent : p->nIndent)-1;
@@ -1024,10 +1073,15 @@
1073 Blob x, cksum, formatCheck, errMsg;
1074 Manifest *pPost;
1075 int nContent = zContent ? (int)strlen(zContent) : 0;
1076
1077 schema_forum();
1078 if( !g.perm.Admin && (iEdit || iInReplyTo)
1079 && forum_rid_is_closed(iEdit ? iEdit : iInReplyTo, 1) ){
1080 forumpost_error_closed();
1081 return 0;
1082 }
1083 if( iEdit==0 && whitespace_only(zContent) ){
1084 return 0;
1085 }
1086 if( iInReplyTo==0 && iEdit>0 ){
1087 iBasis = iEdit;
@@ -1264,10 +1318,13 @@
1318 char *zDate = 0;
1319 const char *zFpid = PD("fpid","");
1320 int isCsrfSafe;
1321 int isDelete = 0;
1322 int fClosed = 0;
1323 int bSameUser; /* True if author is also the reader */
1324 int bPreview; /* True in preview mode. */
1325 int bPrivate; /* True if post is private (not yet moderated) */
1326
1327 login_check_credentials();
1328 if( !g.perm.WrForum ){
1329 login_needed(g.anon.WrForum);
1330 return;
@@ -1282,14 +1339,18 @@
1339 }
1340 if( P("cancel") ){
1341 cgi_redirectf("%R/forumpost/%S",P("fpid"));
1342 return;
1343 }
1344 bPreview = P("preview")!=0;
1345 fClosed = forum_rid_is_closed(fpid, froot!=fpid);
1346 isCsrfSafe = cgi_csrf_safe(1);
1347 bPrivate = content_is_private(fpid);
1348 bSameUser = login_is_individual()
1349 && fossil_strcmp(pPost->zUser, g.zLogin)==0;
1350 if( isCsrfSafe && (g.perm.ModForum || (bPrivate && bSameUser)) ){
1351 if( g.perm.ModForum && P("approve") ){
1352 const char *zUserToTrust;
1353 moderation_approve('f', fpid);
1354 if( g.perm.AdminForum
1355 && PB("trust")
1356 && (zUserToTrust = P("trustuser"))!=0
@@ -1367,18 +1428,19 @@
1428 }
1429 style_header("Edit %s", zTitle ? "Post" : "Reply");
1430 @ <h2>Original Post:</h2>
1431 forum_render(pPost->zThreadTitle, pPost->zMimetype, pPost->zWiki,
1432 "forumEdit", 1);
1433 if( bPreview ){
1434 @ <h2>Preview of Edited Post:</h2>
1435 forum_render(zTitle, zMimetype, zContent,"forumEdit", 1);
1436 }
1437 @ <h2>Revised Message:</h2>
1438 @ <form action="%R/forume2" method="POST">
1439 @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
1440 @ <input type="hidden" name="edit" value="1">
1441 if( fClosed ) forumpost_error_closed();
1442 forum_from_line();
1443 forum_post_widget(zTitle, zMimetype, zContent);
1444 }else{
1445 /* Reply */
1446 char *zDisplayName;
@@ -1396,27 +1458,33 @@
1458 zDisplayName = display_name_from_login(pPost->zUser);
1459 @ <h3 class='forumPostHdr'>By %s(zDisplayName) on %h(zDate)</h3>
1460 fossil_free(zDisplayName);
1461 fossil_free(zDate);
1462 forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1);
1463 if( bPreview && !whitespace_only(zContent) ){
1464 @ <h2>Preview:</h2>
1465 forum_render(0, zMimetype,zContent, "forumEdit", 1);
1466 }
1467 @ <h2>Enter Reply:</h2>
1468 @ <form action="%R/forume2" method="POST">
1469 @ <input type="hidden" name="fpid" value="%h(P("fpid"))">
1470 @ <input type="hidden" name="reply" value="1">
1471 if( fClosed ) forumpost_error_closed();
1472 forum_from_line();
1473 forum_post_widget(0, zMimetype, zContent);
1474 }
1475 if( !isDelete ){
1476 @ <input type="submit" name="preview" value="Preview">
1477 }
1478 @ <input type="submit" name="cancel" value="Cancel">
1479 if( (bPreview && !whitespace_only(zContent)) || isDelete ){
1480 if( !fClosed || g.perm.Admin ) {
1481 @ <input type="submit" name="submit" value="Submit">
1482 }
1483 forumpost_emit_unlock_checkbox(fClosed, fpid);
1484 }else if( !bPreview && fClosed ){
1485 @ <span class='warning'>This post is CLOSED</span>
1486 }
1487 if( g.perm.Debug ){
1488 /* For the test-forumnew page add these extra debugging controls */
1489 @ <div class="debug">
1490 @ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \
1491
+6 -8
--- src/tag.c
+++ src/tag.c
@@ -913,24 +913,22 @@
913913
** string, else returns 0. Note that this function does not
914914
** distinguish between a non-existent tag and a cancelled tag.
915915
*/
916916
int rid_has_active_tag_name(int rid, const char *zTagName){
917917
static Stmt q = empty_Stmt_m;
918
- int rc = 0;
918
+ int rc;
919919
920920
assert( 0 != zTagName );
921921
if( !q.pStmt ){
922922
db_static_prepare(&q,
923
- "SELECT tagxref.rowid FROM tagxref, tag"
924
- " WHERE tagxref.rid=$rid AND tagtype>0 "
925
- " AND tag.tagname=$tagname"
926
- " AND tagxref.tagid=tag.tagid"
923
+ "SELECT x.rowid FROM tagxref x, tag t"
924
+ " WHERE x.rid=$rid AND x.tagtype>0 "
925
+ " AND x.tagid=t.tagid"
926
+ " AND t.tagname=$tagname"
927927
);
928928
}
929929
db_bind_int(&q, "$rid", rid);
930930
db_bind_text(&q, "$tagname", zTagName);
931
- if( SQLITE_ROW==db_step(&q) ){
932
- rc = db_column_int(&q, 0);
933
- }
931
+ rc = (SQLITE_ROW==db_step(&q)) ? db_column_int(&q, 0) : 0;
934932
db_reset(&q);
935933
return rc;
936934
}
937935
--- src/tag.c
+++ src/tag.c
@@ -913,24 +913,22 @@
913 ** string, else returns 0. Note that this function does not
914 ** distinguish between a non-existent tag and a cancelled tag.
915 */
916 int rid_has_active_tag_name(int rid, const char *zTagName){
917 static Stmt q = empty_Stmt_m;
918 int rc = 0;
919
920 assert( 0 != zTagName );
921 if( !q.pStmt ){
922 db_static_prepare(&q,
923 "SELECT tagxref.rowid FROM tagxref, tag"
924 " WHERE tagxref.rid=$rid AND tagtype>0 "
925 " AND tag.tagname=$tagname"
926 " AND tagxref.tagid=tag.tagid"
927 );
928 }
929 db_bind_int(&q, "$rid", rid);
930 db_bind_text(&q, "$tagname", zTagName);
931 if( SQLITE_ROW==db_step(&q) ){
932 rc = db_column_int(&q, 0);
933 }
934 db_reset(&q);
935 return rc;
936 }
937
--- src/tag.c
+++ src/tag.c
@@ -913,24 +913,22 @@
913 ** string, else returns 0. Note that this function does not
914 ** distinguish between a non-existent tag and a cancelled tag.
915 */
916 int rid_has_active_tag_name(int rid, const char *zTagName){
917 static Stmt q = empty_Stmt_m;
918 int rc;
919
920 assert( 0 != zTagName );
921 if( !q.pStmt ){
922 db_static_prepare(&q,
923 "SELECT x.rowid FROM tagxref x, tag t"
924 " WHERE x.rid=$rid AND x.tagtype>0 "
925 " AND x.tagid=t.tagid"
926 " AND t.tagname=$tagname"
927 );
928 }
929 db_bind_int(&q, "$rid", rid);
930 db_bind_text(&q, "$tagname", zTagName);
931 rc = (SQLITE_ROW==db_step(&q)) ? db_column_int(&q, 0) : 0;
 
 
932 db_reset(&q);
933 return rc;
934 }
935

Keyboard Shortcuts

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