| | @@ -76,27 +76,32 @@ |
| 76 | 76 | if( !g.perm.RdForum ){ login_needed(g.anon.RdForum); return; } |
| 77 | 77 | forum_verify_schema(); |
| 78 | 78 | style_header("Forum"); |
| 79 | 79 | itemId = atoi(PD("item","0")); |
| 80 | 80 | if( itemId>0 ){ |
| 81 | + int iUp; |
| 82 | + style_submenu_element("Topics", "%R/forum"); |
| 83 | + iUp = db_int(0, "SELECT inreplyto FROM forumpost WHERE mpostid=%d", itemId); |
| 84 | + if( iUp ){ |
| 85 | + style_submenu_element("Parent", "%R/forum?item=%d", iUp); |
| 86 | + } |
| 81 | 87 | double rNow = db_double(0.0, "SELECT julianday('now')"); |
| 82 | 88 | /* Show the post given by itemId and all its descendents */ |
| 83 | 89 | db_prepare(&q, |
| 84 | 90 | "WITH RECURSIVE" |
| 85 | 91 | " post(id,uname,mstat,mime,ipaddr,parent,mbody,depth,mtime) AS (" |
| 86 | 92 | " SELECT mpostid, uname, mstatus, mimetype, ipaddr, inreplyto, mbody," |
| 87 | | - " 0, 1 FROM forumpost WHERE mpostid=%d" |
| 93 | + " 0, mtime FROM forumpost WHERE mpostid=%d" |
| 88 | 94 | " UNION" |
| 89 | 95 | " SELECT f.mpostid, f.uname, f.mstatus, f.mimetype, f.ipaddr," |
| 90 | 96 | " f.inreplyto, f.mbody, p.depth+1 AS xdepth, f.mtime AS xtime" |
| 91 | 97 | " FROM forumpost AS f, post AS p" |
| 92 | | - " WHERE forumpost.inreplyto=post.id" |
| 98 | + " WHERE f.inreplyto=p.id" |
| 93 | 99 | " ORDER BY xdepth DESC, xtime ASC" |
| 94 | 100 | ") SELECT * FROM post;", |
| 95 | 101 | itemId |
| 96 | 102 | ); |
| 97 | | - @ <table border=0 class="forumtable"> |
| 98 | 103 | while( db_step(&q)==SQLITE_ROW ){ |
| 99 | 104 | int id = db_column_int(&q, 0); |
| 100 | 105 | const char *zUser = db_column_text(&q, 1); |
| 101 | 106 | const char *zStat = db_column_text(&q, 2); |
| 102 | 107 | const char *zMime = db_column_text(&q, 3); |
| | @@ -104,29 +109,32 @@ |
| 104 | 109 | int iDepth = db_column_int(&q, 7); |
| 105 | 110 | double rMTime = db_column_double(&q, 8); |
| 106 | 111 | char *zAge = db_timespan_name(rNow - rMTime); |
| 107 | 112 | Blob body; |
| 108 | 113 | @ <!-- Forum post %d(id) --> |
| 114 | + @ <table class="forum_post"> |
| 109 | 115 | @ <tr> |
| 110 | | - @ <td class="forum_margin" width="%d((iDepth-1)*10)" rowspan="3"></td> |
| 111 | | - @ <td>%h(zUser) %z(zAge) ago</td> |
| 112 | | - @ </tr> |
| 113 | | - @ <tr><td class="forum_body"> |
| 114 | | - blob_init(&body, db_column_text(&q,6), db_column_bytes(&q,6)); |
| 115 | | - wiki_render_by_mimetype(&body, zMime); |
| 116 | | - blob_reset(&body); |
| 117 | | - @ </td></tr> |
| 118 | | - @ <tr><td class="forum_buttons"> |
| 116 | + @ <td class="forum_margin" width="%d(iDepth*25)" rowspan="2"> |
| 117 | + @ <td><span class="forum_author">%h(zUser)</span> |
| 118 | + @ <span class="forum_age">%s(zAge) ago</span> |
| 119 | + sqlite3_free(zAge); |
| 119 | 120 | if( g.perm.WrForum ){ |
| 121 | + @ <span class="forum_buttons"> |
| 120 | 122 | if( g.perm.AdminForum || fossil_strcmp(g.zLogin, zUser)==0 ){ |
| 121 | 123 | @ <a href='%R/forumedit?item=%d(id)'>Edit</a> |
| 122 | 124 | } |
| 123 | 125 | @ <a href='%R/forumedit?replyto=%d(id)'>Reply</a> |
| 126 | + @ </span> |
| 124 | 127 | } |
| 125 | | - @ </td></tr> |
| 128 | + @ </tr> |
| 129 | + @ <tr><td><div class="forum_body"> |
| 130 | + blob_init(&body, db_column_text(&q,6), db_column_bytes(&q,6)); |
| 131 | + wiki_render_by_mimetype(&body, zMime); |
| 132 | + blob_reset(&body); |
| 133 | + @ </div></td></tr> |
| 134 | + @ </table> |
| 126 | 135 | } |
| 127 | | - @ </table> |
| 128 | 136 | }else{ |
| 129 | 137 | /* If we reach this point, that means the users wants a list of |
| 130 | 138 | ** recent threads. |
| 131 | 139 | */ |
| 132 | 140 | i = 0; |
| | @@ -138,21 +146,21 @@ |
| 138 | 146 | " ORDER BY a.mtime DESC LIMIT 40" |
| 139 | 147 | ); |
| 140 | 148 | if( g.perm.WrForum ){ |
| 141 | 149 | style_submenu_element("New", "%R/forumedit"); |
| 142 | 150 | } |
| 143 | | - @ <h1>Recent Forum Threads</h> |
| 144 | | - while( db_step(&q)==SQLITE_OK ){ |
| 151 | + @ <h1>Recent Forum Threads</h1> |
| 152 | + while( db_step(&q)==SQLITE_ROW ){ |
| 145 | 153 | int n = db_column_int(&q,1); |
| 146 | 154 | int itemid = db_column_int(&q,2); |
| 147 | 155 | const char *zTitle = db_column_text(&q,0); |
| 148 | | - if( i==0 ){ |
| 156 | + if( (i++)==0 ){ |
| 149 | 157 | @ <ol> |
| 150 | 158 | } |
| 151 | | - @ <li> |
| 152 | | - @ %z(href("%R/forum?item=%d",itemid))%h(zTitle)</a><br> |
| 153 | | - @ %d(n) post%s(n==1?"":"s")</li> |
| 159 | + @ <li><span class="forum_title"> |
| 160 | + @ %z(href("%R/forum?item=%d",itemid))%h(zTitle)</a></span> |
| 161 | + @ <span class="forum_npost">%d(n) post%s(n==1?"":"s")</span></li> |
| 154 | 162 | } |
| 155 | 163 | if( i ){ |
| 156 | 164 | @ </ol> |
| 157 | 165 | } |
| 158 | 166 | } |
| | @@ -175,10 +183,11 @@ |
| 175 | 183 | */ |
| 176 | 184 | static int forum_post(int itemId, int parentId, char **pzErr){ |
| 177 | 185 | const char *zSubject = 0; |
| 178 | 186 | int threadId; |
| 179 | 187 | double rNow = db_double(0.0, "SELECT julianday('now')"); |
| 188 | + const char *zMime = wiki_filter_mimetypes(P("m")); |
| 180 | 189 | if( itemId==0 && parentId==0 ){ |
| 181 | 190 | /* Start a new thread. Subject required. */ |
| 182 | 191 | sqlite3_uint64 r1, r2; |
| 183 | 192 | zSubject = PT("s"); |
| 184 | 193 | if( zSubject==0 || zSubject[0]==0 ){ |
| | @@ -191,12 +200,22 @@ |
| 191 | 200 | "INSERT INTO forumthread(mthreadhash, mtitle, mtime, npost)" |
| 192 | 201 | "VALUES(lower(hex(randomblob(32))),%Q,%!.17g,1)", |
| 193 | 202 | zSubject, rNow |
| 194 | 203 | ); |
| 195 | 204 | threadId = db_last_insert_rowid(); |
| 205 | + }else{ |
| 206 | + threadId = db_int(0, "SELECT mthreadid FROM forumpost" |
| 207 | + " WHERE mpostid=%d", itemId ? itemId : parentId); |
| 196 | 208 | } |
| 197 | 209 | if( itemId ){ |
| 210 | + if( db_int(0, "SELECT inreplyto IS NULL FROM forumpost" |
| 211 | + " WHERE mpostid=%d", itemId) ){ |
| 212 | + db_multi_exec( |
| 213 | + "UPDATE forumthread SET mtitle=%Q WHERE mthreadid=%d", |
| 214 | + PT("s"), threadId |
| 215 | + ); |
| 216 | + } |
| 198 | 217 | db_multi_exec( |
| 199 | 218 | "UPDATE forumpost SET" |
| 200 | 219 | " mtime=%!.17g," |
| 201 | 220 | " mimetype=%Q," |
| 202 | 221 | " ipaddr=%Q," |
| | @@ -206,17 +225,17 @@ |
| 206 | 225 | ); |
| 207 | 226 | }else{ |
| 208 | 227 | db_multi_exec( |
| 209 | 228 | "INSERT INTO forumpost(mposthash,mthreadid,uname,mtime," |
| 210 | 229 | " mstatus,mimetype,ipaddr,inreplyto,mbody) VALUES" |
| 211 | | - " (lower(hex(randomblob(32))),%d,%Q,%!.17g,%Q,%Q,%Q,NULL,%Q)", |
| 212 | | - threadId,g.zLogin,rNow,NULL,P("m"),P("REMOTE_ADDR"),P("b")); |
| 230 | + " (lower(hex(randomblob(32))),%d,%Q,%!.17g,%Q,%Q,%Q,nullif(%d,0),%Q)", |
| 231 | + threadId,g.zLogin,rNow,NULL,zMime,P("REMOTE_ADDR"),parentId,P("b")); |
| 213 | 232 | itemId = db_last_insert_rowid(); |
| 214 | 233 | } |
| 215 | 234 | if( zSubject==0 ){ |
| 216 | 235 | db_multi_exec( |
| 217 | | - "UPDATE forumthread SET mtime=%!.17g" |
| 236 | + "UPDATE forumthread SET mtime=%!.17g, npost=npost+1" |
| 218 | 237 | " WHERE mthreadid=(SELECT mthreadid FROM forumpost WHERE mpostid=%d)", |
| 219 | 238 | rNow, itemId |
| 220 | 239 | ); |
| 221 | 240 | } |
| 222 | 241 | return itemId; |
| | @@ -233,31 +252,42 @@ |
| 233 | 252 | ** b=BODY Body of the post |
| 234 | 253 | ** m=MIMETYPE Mimetype for the body of the post |
| 235 | 254 | ** x Submit changes |
| 236 | 255 | ** p Preview changes |
| 237 | 256 | */ |
| 238 | | -static void forum_reply_page(void){ |
| 257 | +void forum_edit_page(void){ |
| 239 | 258 | int itemId; |
| 240 | 259 | int parentId; |
| 241 | | - const char *zErr = 0; |
| 260 | + char *zErr = 0; |
| 242 | 261 | login_check_credentials(); |
| 243 | 262 | const char *zBody; |
| 244 | 263 | const char *zMime; |
| 245 | 264 | const char *zSub; |
| 246 | 265 | if( !g.perm.WrForum ){ login_needed(g.anon.WrForum); return; } |
| 247 | 266 | forum_verify_schema(); |
| 248 | 267 | itemId = atoi(PD("item","0")); |
| 249 | 268 | parentId = atoi(PD("replyto","0")); |
| 269 | + if( P("cancel")!=0 ){ |
| 270 | + cgi_redirectf("%R/forum?item=%d", itemId ? itemId : parentId); |
| 271 | + return; |
| 272 | + } |
| 250 | 273 | if( P("x")!=0 && cgi_csrf_safe(1) ){ |
| 251 | 274 | itemId = forum_post(itemId,parentId,&zErr); |
| 252 | 275 | if( itemId ){ |
| 253 | 276 | cgi_redirectf("%R/forum?item=%d",itemId); |
| 254 | 277 | return; |
| 255 | 278 | } |
| 256 | 279 | } |
| 257 | | - style_header("Edit Forum Post"); |
| 258 | | - @ <form method="POST"> |
| 280 | + zMime = wiki_filter_mimetypes(P("m")); |
| 281 | + if( itemId>0 ){ |
| 282 | + style_header("Edit Forum Post"); |
| 283 | + }else if( parentId>0 ){ |
| 284 | + style_header("Comment On Forum Post"); |
| 285 | + }else{ |
| 286 | + style_header("New Forum Thread"); |
| 287 | + } |
| 288 | + @ <form action="%R/forumedit" method="POST"> |
| 259 | 289 | if( itemId ){ |
| 260 | 290 | @ <input type="hidden" name="item" value="%d(itemId)"> |
| 261 | 291 | } |
| 262 | 292 | if( parentId ){ |
| 263 | 293 | @ <input type="hidden" name="replyto" value="%d(parentId)"> |
| | @@ -272,14 +302,41 @@ |
| 272 | 302 | blob_init(&x, PT("b"), -1); |
| 273 | 303 | wiki_render_by_mimetype(&x, PT("m")); |
| 274 | 304 | blob_reset(&x); |
| 275 | 305 | @ </div> |
| 276 | 306 | @ </div> |
| 307 | + @ <hr> |
| 277 | 308 | } |
| 278 | 309 | @ <table border="0" class="forumeditform"> |
| 279 | | - if( itemId==0 && parentId==0 ){ |
| 310 | + if( zErr ){ |
| 311 | + @ <tr><td colspan="2"> |
| 312 | + @ <span class='forumFormErr'>%h(zErr)</span> |
| 313 | + } |
| 314 | + if( (itemId==0 && parentId==0) |
| 315 | + || (itemId && db_int(0, "SELECT inreplyto IS NULL FROM forumpost" |
| 316 | + " WHERE mpostid=%d", itemId)) |
| 317 | + ){ |
| 280 | 318 | zSub = PT("s"); |
| 319 | + if( zSub==0 && itemId ){ |
| 320 | + zSub = db_text("", |
| 321 | + "SELECT mtitle FROM forumthread" |
| 322 | + " WHERE mthreadid=(SELECT mthreadid FROM forumpost" |
| 323 | + " WHERE mpostid=%d)", itemId); |
| 324 | + } |
| 325 | + @ <tr><td>Subject:</td> |
| 326 | + @ <td><input type='text' class='forumFormSubject' name='s' value='%h(zSub)'> |
| 327 | + } |
| 328 | + @ <tr><td>Markup:</td><td> |
| 329 | + mimetype_option_menu(zMime); |
| 330 | + @ <tr><td>Comment:</td><td> |
| 331 | + @ <textarea name="b" class="wikiedit" cols="80"\ |
| 332 | + @ rows="20" wrap="virtual">%h(PD("b",""))</textarea></td> |
| 333 | + @ <tr><td></td><td> |
| 334 | + @ <input type="submit" name="p" value="Preview"> |
| 335 | + if( P("p")!=0 ){ |
| 336 | + @ <input type="submit" name="x" value="Submit"> |
| 281 | 337 | } |
| 338 | + @ <input type="submit" name="cancel" value="Cancel"> |
| 282 | 339 | @ </table> |
| 283 | 340 | @ </form> |
| 284 | 341 | style_footer(); |
| 285 | 342 | } |
| 286 | 343 | |