Fossil SCM
Render a column for each post's status in the /forum list if the repo has a valid forum-statuses setting. These are exploratory, not definitive, changes. It's not clear how to get both a status's label and its value into this list without duplicating one of the inner queries.
Commit
696c85418d037a84f41d62ea8ce1c25b1a642eaab9e1c715ffa6e8c21bc0737a
Parent
555a0556e670d05…
1 file changed
+49
-24
+49
-24
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -89,11 +89,10 @@ | ||
| 89 | 89 | static const ForumStatusList * forum_statuses(void){ |
| 90 | 90 | static ForumStatusList fses = {0,0}; |
| 91 | 91 | static int once = 0; |
| 92 | 92 | while( !once ){ |
| 93 | 93 | Stmt q; |
| 94 | - char *zSetting; | |
| 95 | 94 | ++once; |
| 96 | 95 | /* Read `forum-statuses` setting and transform it into the |
| 97 | 96 | ** fses object. |
| 98 | 97 | ** |
| 99 | 98 | ** Maybe: if it's empty, synthesize a length-1 list from |
| @@ -100,32 +99,40 @@ | ||
| 100 | 99 | ** {value:"default",label:"Default",...}. It's expected that |
| 101 | 100 | ** usage may be slightly simplified if we always have a non-empty |
| 102 | 101 | ** list. A length-1 list is, for purposes of the UI, identical to |
| 103 | 102 | ** an empty one - status selection/filtering makes no sense if |
| 104 | 103 | ** there's only one choice. */ |
| 105 | - zSetting = db_get("forum-statuses", 0); | |
| 104 | + db_get("forum-statuses", 0); | |
| 106 | 105 | db_multi_exec( |
| 107 | 106 | "CREATE TEMP TABLE forumstatus(" |
| 108 | 107 | " ord, label, value, descr" |
| 109 | 108 | ");" |
| 110 | 109 | ); |
| 111 | - if( !zSetting ) break; | |
| 112 | 110 | db_prepare(&q, |
| 113 | - " WITH setting(v) AS (SELECT %Q)," | |
| 114 | - " room(r) AS (" | |
| 115 | - " SELECT e.value FROM setting s, jsonb_each(s.v) e" | |
| 116 | - " WHERE json_valid(s.v, 0x02)" | |
| 117 | - " )" | |
| 111 | + "WITH setting(v) AS (" | |
| 112 | + " SELECT value v FROM config WHERE name='forum-statuses'" | |
| 113 | + ")," | |
| 114 | + "room(r) AS (" | |
| 115 | + " SELECT e.value FROM setting s, jsonb_each(s.v) e" | |
| 116 | + " WHERE json_valid(s.v, 0x02)" | |
| 117 | + ")" | |
| 118 | 118 | " SELECT r->>'label', r->>'value', r->>'description'" |
| 119 | - " FROM room", | |
| 120 | - zSetting | |
| 119 | + " FROM room" | |
| 121 | 120 | ); |
| 121 | + /* | |
| 122 | + ** Quirk: we run the query twice: once to count and once to insert | |
| 123 | + ** into the forumstatus table. We could do the query and populate | |
| 124 | + ** in the same step but we need an ordinal value to go with each | |
| 125 | + ** so that we can retain their order. If the above CTE can be | |
| 126 | + ** altered to provide a sequential ordinal value then we can | |
| 127 | + ** eliminate this double-step. | |
| 128 | + */ | |
| 122 | 129 | while( SQLITE_ROW==db_step(&q) ){ |
| 123 | 130 | ++fses.n; |
| 124 | 131 | } |
| 125 | 132 | if( fses.n ){ |
| 126 | - unsigned int i = 0; | |
| 133 | + int i = 0; | |
| 127 | 134 | Stmt qIns; |
| 128 | 135 | fses.aStatus = fossil_malloc(sizeof(fses.aStatus[0]) * fses.n); |
| 129 | 136 | db_reset(&q); |
| 130 | 137 | db_prepare(&qIns, |
| 131 | 138 | "INSERT INTO forumstatus(ord,label,value,descr)" |
| @@ -134,20 +141,19 @@ | ||
| 134 | 141 | ForumStatus * fs = &fses.aStatus[i++]; |
| 135 | 142 | fs->zLabel = fossil_strdup(db_column_text(&q, 0)); |
| 136 | 143 | fs->zValue = fossil_strdup(db_column_text(&q, 1)); |
| 137 | 144 | fs->zDescr = fossil_strdup(db_column_text(&q, 2)); |
| 138 | 145 | db_reset(&qIns); |
| 139 | - db_bind_int(&qIns, ":ord", (int)i); | |
| 146 | + db_bind_int(&qIns, ":ord", i); | |
| 140 | 147 | db_bind_text(&qIns, ":label", fs->zLabel); |
| 141 | 148 | db_bind_text(&qIns, ":value", fs->zValue); |
| 142 | 149 | db_bind_text(&qIns, ":descr", fs->zDescr); |
| 143 | 150 | db_step(&qIns); |
| 144 | 151 | } |
| 145 | 152 | db_finalize(&qIns); |
| 146 | 153 | } |
| 147 | 154 | db_finalize(&q); |
| 148 | - fossil_free(zSetting); | |
| 149 | 155 | } |
| 150 | 156 | return &fses; |
| 151 | 157 | } |
| 152 | 158 | |
| 153 | 159 | /* |
| @@ -2358,29 +2364,42 @@ | ||
| 2358 | 2364 | } |
| 2359 | 2365 | style_submenu_entry("n","Max:",4,0); |
| 2360 | 2366 | iOfst = atoi(PD("x","0")); |
| 2361 | 2367 | iCnt = 0; |
| 2362 | 2368 | if( db_table_exists("repository","forumpost") ){ |
| 2369 | + const int bHasStatus = forum_statuses()->n>1; | |
| 2363 | 2370 | db_prepare(&q, |
| 2364 | - "WITH thread(age,duration,cnt,root,last,pinned) AS (" | |
| 2371 | + "WITH thread(age,duration,cnt,root,last,pinned,status) AS (" | |
| 2365 | 2372 | " SELECT" |
| 2366 | 2373 | " julianday('now') - max(fmtime)," |
| 2367 | 2374 | " max(fmtime) - min(fmtime)," |
| 2368 | 2375 | " sum(fprev IS NULL)," |
| 2369 | 2376 | " froot," |
| 2370 | 2377 | " (SELECT fpid FROM forumpost AS y" |
| 2371 | 2378 | " WHERE y.froot=x.froot %s" |
| 2372 | 2379 | " ORDER BY y.fmtime DESC LIMIT 1)," |
| 2373 | - " CASE WHEN" | |
| 2374 | - " firt IS NULL AND" | |
| 2375 | - " (SELECT 1 FROM tagxref ref, tag t" | |
| 2376 | - " WHERE ref.rid=x.fpid AND ref.tagtype>0" | |
| 2380 | + " (firt IS NULL AND" | |
| 2381 | + " (SELECT 1 FROM tagxref ref, tag t" | |
| 2382 | + " WHERE ref.rid=x.fpid AND ref.tagtype>0" | |
| 2383 | + " AND ref.tagid=t.tagid" | |
| 2384 | + " AND t.tagname='pinned'))," | |
| 2385 | +#if 0 | |
| 2386 | + " (SELECT ref.value FROM tagxref ref, tag t" | |
| 2387 | + " WHERE ref.rid=x.froot AND ref.tagtype>0" | |
| 2388 | + " AND ref.tagid=t.tagid" | |
| 2389 | + " AND t.tagname='status'" | |
| 2390 | + " UNION ALL" | |
| 2391 | + " SELECT value FROM forumstatus WHERE ord=1)" | |
| 2392 | +#else | |
| 2393 | + " (SELECT fs.label FROM tagxref ref, tag t, forumstatus fs" | |
| 2394 | + " WHERE ref.rid=x.froot AND ref.tagtype>0" | |
| 2377 | 2395 | " AND ref.tagid=t.tagid" |
| 2378 | - " AND t.tagname='pinned')" | |
| 2379 | - " THEN 1" | |
| 2380 | - " ELSE 0" | |
| 2381 | - " END" | |
| 2396 | + " AND t.tagname='status'" | |
| 2397 | + " AND fs.value=ref.value" | |
| 2398 | + " UNION ALL" | |
| 2399 | + " SELECT label FROM forumstatus WHERE ord=1)" | |
| 2400 | +#endif | |
| 2382 | 2401 | " FROM forumpost AS x" |
| 2383 | 2402 | " WHERE %s" |
| 2384 | 2403 | " GROUP BY froot" |
| 2385 | 2404 | " ORDER BY 6 DESC, 1 LIMIT %d OFFSET %d" |
| 2386 | 2405 | ")" |
| @@ -2389,11 +2408,12 @@ | ||
| 2389 | 2408 | " thread.duration," /* 1 */ |
| 2390 | 2409 | " thread.cnt," /* 2 */ |
| 2391 | 2410 | " blob.uuid," /* 3 */ |
| 2392 | 2411 | " substr(event.comment,instr(event.comment,':')+1)," /* 4 */ |
| 2393 | 2412 | " thread.last," /* 5 */ |
| 2394 | - " thread.pinned" /* 6 */ | |
| 2413 | + " thread.pinned," /* 6 */ | |
| 2414 | + " thread.status" /* 7 */ | |
| 2395 | 2415 | " FROM thread, blob, event" |
| 2396 | 2416 | " WHERE blob.rid=thread.last" |
| 2397 | 2417 | " AND event.objid=thread.last" |
| 2398 | 2418 | " ORDER BY 7 DESC, 1;", |
| 2399 | 2419 | g.perm.ModForum ? "" : "AND y.fpid NOT IN private" /*safe-for-%s*/, |
| @@ -2404,10 +2424,11 @@ | ||
| 2404 | 2424 | char *zAge = human_readable_age(db_column_double(&q,0)); |
| 2405 | 2425 | int nMsg = db_column_int(&q, 2); |
| 2406 | 2426 | int bPinned = db_column_int(&q, 6); |
| 2407 | 2427 | const char *zUuid = db_column_text(&q, 3); |
| 2408 | 2428 | const char *zTitle = db_column_text(&q, 4); |
| 2429 | + const char *zStatus = bHasStatus ? db_column_text(&q, 7) : NULL; | |
| 2409 | 2430 | if( iCnt==0 ){ |
| 2410 | 2431 | if( iOfst>0 ){ |
| 2411 | 2432 | @ <h1>Threads at least %s(zAge) old</h1> |
| 2412 | 2433 | }else{ |
| 2413 | 2434 | @ <h1>Most recent threads</h1> |
| @@ -2444,11 +2465,15 @@ | ||
| 2444 | 2465 | }else{ |
| 2445 | 2466 | char *zDuration = human_readable_age(db_column_double(&q,1)); |
| 2446 | 2467 | @ %d(nMsg) posts spanning %h(zDuration)\ |
| 2447 | 2468 | fossil_free(zDuration); |
| 2448 | 2469 | } |
| 2449 | - @ </td></tr> | |
| 2470 | + @ </td>\ | |
| 2471 | + if( bHasStatus ){ | |
| 2472 | + @ <td>%h(zStatus)</td>\ | |
| 2473 | + } | |
| 2474 | + @</tr> | |
| 2450 | 2475 | fossil_free(zAge); |
| 2451 | 2476 | } |
| 2452 | 2477 | db_finalize(&q); |
| 2453 | 2478 | } |
| 2454 | 2479 | if( iCnt>0 ){ |
| 2455 | 2480 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -89,11 +89,10 @@ | |
| 89 | static const ForumStatusList * forum_statuses(void){ |
| 90 | static ForumStatusList fses = {0,0}; |
| 91 | static int once = 0; |
| 92 | while( !once ){ |
| 93 | Stmt q; |
| 94 | char *zSetting; |
| 95 | ++once; |
| 96 | /* Read `forum-statuses` setting and transform it into the |
| 97 | ** fses object. |
| 98 | ** |
| 99 | ** Maybe: if it's empty, synthesize a length-1 list from |
| @@ -100,32 +99,40 @@ | |
| 100 | ** {value:"default",label:"Default",...}. It's expected that |
| 101 | ** usage may be slightly simplified if we always have a non-empty |
| 102 | ** list. A length-1 list is, for purposes of the UI, identical to |
| 103 | ** an empty one - status selection/filtering makes no sense if |
| 104 | ** there's only one choice. */ |
| 105 | zSetting = db_get("forum-statuses", 0); |
| 106 | db_multi_exec( |
| 107 | "CREATE TEMP TABLE forumstatus(" |
| 108 | " ord, label, value, descr" |
| 109 | ");" |
| 110 | ); |
| 111 | if( !zSetting ) break; |
| 112 | db_prepare(&q, |
| 113 | " WITH setting(v) AS (SELECT %Q)," |
| 114 | " room(r) AS (" |
| 115 | " SELECT e.value FROM setting s, jsonb_each(s.v) e" |
| 116 | " WHERE json_valid(s.v, 0x02)" |
| 117 | " )" |
| 118 | " SELECT r->>'label', r->>'value', r->>'description'" |
| 119 | " FROM room", |
| 120 | zSetting |
| 121 | ); |
| 122 | while( SQLITE_ROW==db_step(&q) ){ |
| 123 | ++fses.n; |
| 124 | } |
| 125 | if( fses.n ){ |
| 126 | unsigned int i = 0; |
| 127 | Stmt qIns; |
| 128 | fses.aStatus = fossil_malloc(sizeof(fses.aStatus[0]) * fses.n); |
| 129 | db_reset(&q); |
| 130 | db_prepare(&qIns, |
| 131 | "INSERT INTO forumstatus(ord,label,value,descr)" |
| @@ -134,20 +141,19 @@ | |
| 134 | ForumStatus * fs = &fses.aStatus[i++]; |
| 135 | fs->zLabel = fossil_strdup(db_column_text(&q, 0)); |
| 136 | fs->zValue = fossil_strdup(db_column_text(&q, 1)); |
| 137 | fs->zDescr = fossil_strdup(db_column_text(&q, 2)); |
| 138 | db_reset(&qIns); |
| 139 | db_bind_int(&qIns, ":ord", (int)i); |
| 140 | db_bind_text(&qIns, ":label", fs->zLabel); |
| 141 | db_bind_text(&qIns, ":value", fs->zValue); |
| 142 | db_bind_text(&qIns, ":descr", fs->zDescr); |
| 143 | db_step(&qIns); |
| 144 | } |
| 145 | db_finalize(&qIns); |
| 146 | } |
| 147 | db_finalize(&q); |
| 148 | fossil_free(zSetting); |
| 149 | } |
| 150 | return &fses; |
| 151 | } |
| 152 | |
| 153 | /* |
| @@ -2358,29 +2364,42 @@ | |
| 2358 | } |
| 2359 | style_submenu_entry("n","Max:",4,0); |
| 2360 | iOfst = atoi(PD("x","0")); |
| 2361 | iCnt = 0; |
| 2362 | if( db_table_exists("repository","forumpost") ){ |
| 2363 | db_prepare(&q, |
| 2364 | "WITH thread(age,duration,cnt,root,last,pinned) AS (" |
| 2365 | " SELECT" |
| 2366 | " julianday('now') - max(fmtime)," |
| 2367 | " max(fmtime) - min(fmtime)," |
| 2368 | " sum(fprev IS NULL)," |
| 2369 | " froot," |
| 2370 | " (SELECT fpid FROM forumpost AS y" |
| 2371 | " WHERE y.froot=x.froot %s" |
| 2372 | " ORDER BY y.fmtime DESC LIMIT 1)," |
| 2373 | " CASE WHEN" |
| 2374 | " firt IS NULL AND" |
| 2375 | " (SELECT 1 FROM tagxref ref, tag t" |
| 2376 | " WHERE ref.rid=x.fpid AND ref.tagtype>0" |
| 2377 | " AND ref.tagid=t.tagid" |
| 2378 | " AND t.tagname='pinned')" |
| 2379 | " THEN 1" |
| 2380 | " ELSE 0" |
| 2381 | " END" |
| 2382 | " FROM forumpost AS x" |
| 2383 | " WHERE %s" |
| 2384 | " GROUP BY froot" |
| 2385 | " ORDER BY 6 DESC, 1 LIMIT %d OFFSET %d" |
| 2386 | ")" |
| @@ -2389,11 +2408,12 @@ | |
| 2389 | " thread.duration," /* 1 */ |
| 2390 | " thread.cnt," /* 2 */ |
| 2391 | " blob.uuid," /* 3 */ |
| 2392 | " substr(event.comment,instr(event.comment,':')+1)," /* 4 */ |
| 2393 | " thread.last," /* 5 */ |
| 2394 | " thread.pinned" /* 6 */ |
| 2395 | " FROM thread, blob, event" |
| 2396 | " WHERE blob.rid=thread.last" |
| 2397 | " AND event.objid=thread.last" |
| 2398 | " ORDER BY 7 DESC, 1;", |
| 2399 | g.perm.ModForum ? "" : "AND y.fpid NOT IN private" /*safe-for-%s*/, |
| @@ -2404,10 +2424,11 @@ | |
| 2404 | char *zAge = human_readable_age(db_column_double(&q,0)); |
| 2405 | int nMsg = db_column_int(&q, 2); |
| 2406 | int bPinned = db_column_int(&q, 6); |
| 2407 | const char *zUuid = db_column_text(&q, 3); |
| 2408 | const char *zTitle = db_column_text(&q, 4); |
| 2409 | if( iCnt==0 ){ |
| 2410 | if( iOfst>0 ){ |
| 2411 | @ <h1>Threads at least %s(zAge) old</h1> |
| 2412 | }else{ |
| 2413 | @ <h1>Most recent threads</h1> |
| @@ -2444,11 +2465,15 @@ | |
| 2444 | }else{ |
| 2445 | char *zDuration = human_readable_age(db_column_double(&q,1)); |
| 2446 | @ %d(nMsg) posts spanning %h(zDuration)\ |
| 2447 | fossil_free(zDuration); |
| 2448 | } |
| 2449 | @ </td></tr> |
| 2450 | fossil_free(zAge); |
| 2451 | } |
| 2452 | db_finalize(&q); |
| 2453 | } |
| 2454 | if( iCnt>0 ){ |
| 2455 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -89,11 +89,10 @@ | |
| 89 | static const ForumStatusList * forum_statuses(void){ |
| 90 | static ForumStatusList fses = {0,0}; |
| 91 | static int once = 0; |
| 92 | while( !once ){ |
| 93 | Stmt q; |
| 94 | ++once; |
| 95 | /* Read `forum-statuses` setting and transform it into the |
| 96 | ** fses object. |
| 97 | ** |
| 98 | ** Maybe: if it's empty, synthesize a length-1 list from |
| @@ -100,32 +99,40 @@ | |
| 99 | ** {value:"default",label:"Default",...}. It's expected that |
| 100 | ** usage may be slightly simplified if we always have a non-empty |
| 101 | ** list. A length-1 list is, for purposes of the UI, identical to |
| 102 | ** an empty one - status selection/filtering makes no sense if |
| 103 | ** there's only one choice. */ |
| 104 | db_get("forum-statuses", 0); |
| 105 | db_multi_exec( |
| 106 | "CREATE TEMP TABLE forumstatus(" |
| 107 | " ord, label, value, descr" |
| 108 | ");" |
| 109 | ); |
| 110 | db_prepare(&q, |
| 111 | "WITH setting(v) AS (" |
| 112 | " SELECT value v FROM config WHERE name='forum-statuses'" |
| 113 | ")," |
| 114 | "room(r) AS (" |
| 115 | " SELECT e.value FROM setting s, jsonb_each(s.v) e" |
| 116 | " WHERE json_valid(s.v, 0x02)" |
| 117 | ")" |
| 118 | " SELECT r->>'label', r->>'value', r->>'description'" |
| 119 | " FROM room" |
| 120 | ); |
| 121 | /* |
| 122 | ** Quirk: we run the query twice: once to count and once to insert |
| 123 | ** into the forumstatus table. We could do the query and populate |
| 124 | ** in the same step but we need an ordinal value to go with each |
| 125 | ** so that we can retain their order. If the above CTE can be |
| 126 | ** altered to provide a sequential ordinal value then we can |
| 127 | ** eliminate this double-step. |
| 128 | */ |
| 129 | while( SQLITE_ROW==db_step(&q) ){ |
| 130 | ++fses.n; |
| 131 | } |
| 132 | if( fses.n ){ |
| 133 | int i = 0; |
| 134 | Stmt qIns; |
| 135 | fses.aStatus = fossil_malloc(sizeof(fses.aStatus[0]) * fses.n); |
| 136 | db_reset(&q); |
| 137 | db_prepare(&qIns, |
| 138 | "INSERT INTO forumstatus(ord,label,value,descr)" |
| @@ -134,20 +141,19 @@ | |
| 141 | ForumStatus * fs = &fses.aStatus[i++]; |
| 142 | fs->zLabel = fossil_strdup(db_column_text(&q, 0)); |
| 143 | fs->zValue = fossil_strdup(db_column_text(&q, 1)); |
| 144 | fs->zDescr = fossil_strdup(db_column_text(&q, 2)); |
| 145 | db_reset(&qIns); |
| 146 | db_bind_int(&qIns, ":ord", i); |
| 147 | db_bind_text(&qIns, ":label", fs->zLabel); |
| 148 | db_bind_text(&qIns, ":value", fs->zValue); |
| 149 | db_bind_text(&qIns, ":descr", fs->zDescr); |
| 150 | db_step(&qIns); |
| 151 | } |
| 152 | db_finalize(&qIns); |
| 153 | } |
| 154 | db_finalize(&q); |
| 155 | } |
| 156 | return &fses; |
| 157 | } |
| 158 | |
| 159 | /* |
| @@ -2358,29 +2364,42 @@ | |
| 2364 | } |
| 2365 | style_submenu_entry("n","Max:",4,0); |
| 2366 | iOfst = atoi(PD("x","0")); |
| 2367 | iCnt = 0; |
| 2368 | if( db_table_exists("repository","forumpost") ){ |
| 2369 | const int bHasStatus = forum_statuses()->n>1; |
| 2370 | db_prepare(&q, |
| 2371 | "WITH thread(age,duration,cnt,root,last,pinned,status) AS (" |
| 2372 | " SELECT" |
| 2373 | " julianday('now') - max(fmtime)," |
| 2374 | " max(fmtime) - min(fmtime)," |
| 2375 | " sum(fprev IS NULL)," |
| 2376 | " froot," |
| 2377 | " (SELECT fpid FROM forumpost AS y" |
| 2378 | " WHERE y.froot=x.froot %s" |
| 2379 | " ORDER BY y.fmtime DESC LIMIT 1)," |
| 2380 | " (firt IS NULL AND" |
| 2381 | " (SELECT 1 FROM tagxref ref, tag t" |
| 2382 | " WHERE ref.rid=x.fpid AND ref.tagtype>0" |
| 2383 | " AND ref.tagid=t.tagid" |
| 2384 | " AND t.tagname='pinned'))," |
| 2385 | #if 0 |
| 2386 | " (SELECT ref.value FROM tagxref ref, tag t" |
| 2387 | " WHERE ref.rid=x.froot AND ref.tagtype>0" |
| 2388 | " AND ref.tagid=t.tagid" |
| 2389 | " AND t.tagname='status'" |
| 2390 | " UNION ALL" |
| 2391 | " SELECT value FROM forumstatus WHERE ord=1)" |
| 2392 | #else |
| 2393 | " (SELECT fs.label FROM tagxref ref, tag t, forumstatus fs" |
| 2394 | " WHERE ref.rid=x.froot AND ref.tagtype>0" |
| 2395 | " AND ref.tagid=t.tagid" |
| 2396 | " AND t.tagname='status'" |
| 2397 | " AND fs.value=ref.value" |
| 2398 | " UNION ALL" |
| 2399 | " SELECT label FROM forumstatus WHERE ord=1)" |
| 2400 | #endif |
| 2401 | " FROM forumpost AS x" |
| 2402 | " WHERE %s" |
| 2403 | " GROUP BY froot" |
| 2404 | " ORDER BY 6 DESC, 1 LIMIT %d OFFSET %d" |
| 2405 | ")" |
| @@ -2389,11 +2408,12 @@ | |
| 2408 | " thread.duration," /* 1 */ |
| 2409 | " thread.cnt," /* 2 */ |
| 2410 | " blob.uuid," /* 3 */ |
| 2411 | " substr(event.comment,instr(event.comment,':')+1)," /* 4 */ |
| 2412 | " thread.last," /* 5 */ |
| 2413 | " thread.pinned," /* 6 */ |
| 2414 | " thread.status" /* 7 */ |
| 2415 | " FROM thread, blob, event" |
| 2416 | " WHERE blob.rid=thread.last" |
| 2417 | " AND event.objid=thread.last" |
| 2418 | " ORDER BY 7 DESC, 1;", |
| 2419 | g.perm.ModForum ? "" : "AND y.fpid NOT IN private" /*safe-for-%s*/, |
| @@ -2404,10 +2424,11 @@ | |
| 2424 | char *zAge = human_readable_age(db_column_double(&q,0)); |
| 2425 | int nMsg = db_column_int(&q, 2); |
| 2426 | int bPinned = db_column_int(&q, 6); |
| 2427 | const char *zUuid = db_column_text(&q, 3); |
| 2428 | const char *zTitle = db_column_text(&q, 4); |
| 2429 | const char *zStatus = bHasStatus ? db_column_text(&q, 7) : NULL; |
| 2430 | if( iCnt==0 ){ |
| 2431 | if( iOfst>0 ){ |
| 2432 | @ <h1>Threads at least %s(zAge) old</h1> |
| 2433 | }else{ |
| 2434 | @ <h1>Most recent threads</h1> |
| @@ -2444,11 +2465,15 @@ | |
| 2465 | }else{ |
| 2466 | char *zDuration = human_readable_age(db_column_double(&q,1)); |
| 2467 | @ %d(nMsg) posts spanning %h(zDuration)\ |
| 2468 | fossil_free(zDuration); |
| 2469 | } |
| 2470 | @ </td>\ |
| 2471 | if( bHasStatus ){ |
| 2472 | @ <td>%h(zStatus)</td>\ |
| 2473 | } |
| 2474 | @</tr> |
| 2475 | fossil_free(zAge); |
| 2476 | } |
| 2477 | db_finalize(&q); |
| 2478 | } |
| 2479 | if( iCnt>0 ){ |
| 2480 |