Fossil SCM

PoC for sticky forum posts, where a 'sticky' tag set on the first version of the root post of a thread will cause it to sort to the top of the list. Pending is a way for Setup users (only) to set/unset the sticky tag (it currently requires tagging from the CLI).

stephan 2026-05-25 13:44 UTC trunk
Commit 70ac3f6e836d3c8820c56deea5f05e1785718711e84b48b23d064d8296af4286
2 files changed +5 +17 -6
--- src/default.css
+++ src/default.css
@@ -1113,10 +1113,15 @@
11131113
11141114
div.setup_forum-column {
11151115
display: flex;
11161116
flex-direction: column;
11171117
}
1118
+
1119
+body.forum div.forumPosts table tr.sticky > td:nth-child(1):before {
1120
+ padding-right: 0.25em;
1121
+ content: "📌";
1122
+}
11181123
11191124
body.cpage-setup_forum > .content table {
11201125
margin-bottom: 1em;
11211126
}
11221127
body.cpage-setup_forum > .content table.bordered {
11231128
--- src/default.css
+++ src/default.css
@@ -1113,10 +1113,15 @@
1113
1114 div.setup_forum-column {
1115 display: flex;
1116 flex-direction: column;
1117 }
 
 
 
 
 
1118
1119 body.cpage-setup_forum > .content table {
1120 margin-bottom: 1em;
1121 }
1122 body.cpage-setup_forum > .content table.bordered {
1123
--- src/default.css
+++ src/default.css
@@ -1113,10 +1113,15 @@
1113
1114 div.setup_forum-column {
1115 display: flex;
1116 flex-direction: column;
1117 }
1118
1119 body.forum div.forumPosts table tr.sticky > td:nth-child(1):before {
1120 padding-right: 0.25em;
1121 content: "📌";
1122 }
1123
1124 body.cpage-setup_forum > .content table {
1125 margin-bottom: 1em;
1126 }
1127 body.cpage-setup_forum > .content table.bordered {
1128
+17 -6
--- src/forum.c
+++ src/forum.c
@@ -2168,42 +2168,53 @@
21682168
style_submenu_entry("n","Max:",4,0);
21692169
iOfst = atoi(PD("x","0"));
21702170
iCnt = 0;
21712171
if( db_table_exists("repository","forumpost") ){
21722172
db_prepare(&q,
2173
- "WITH thread(age,duration,cnt,root,last) AS ("
2173
+ "WITH thread(age,duration,cnt,root,last,sticky) AS ("
21742174
" SELECT"
21752175
" julianday('now') - max(fmtime),"
21762176
" max(fmtime) - min(fmtime),"
21772177
" sum(fprev IS NULL),"
21782178
" froot,"
21792179
" (SELECT fpid FROM forumpost AS y"
21802180
" WHERE y.froot=x.froot %s"
2181
- " ORDER BY y.fmtime DESC LIMIT 1)"
2181
+ " ORDER BY y.fmtime DESC LIMIT 1),"
2182
+ " CASE WHEN"
2183
+ " firt IS NULL AND"
2184
+ " (SELECT 1 FROM tagxref ref, tag t"
2185
+ " WHERE ref.rid=x.fpid AND ref.tagtype>0"
2186
+ " AND ref.tagid=t.tagid"
2187
+ " AND t.tagname='sticky')"
2188
+ " THEN 1"
2189
+ " ELSE 0"
2190
+ " END"
21822191
" FROM forumpost AS x"
21832192
" WHERE %s"
21842193
" GROUP BY froot"
2185
- " ORDER BY 1 LIMIT %d OFFSET %d"
2194
+ " ORDER BY 6 DESC, 1 LIMIT %d OFFSET %d"
21862195
")"
21872196
"SELECT"
21882197
" thread.age," /* 0 */
21892198
" thread.duration," /* 1 */
21902199
" thread.cnt," /* 2 */
21912200
" blob.uuid," /* 3 */
21922201
" substr(event.comment,instr(event.comment,':')+1)," /* 4 */
2193
- " thread.last" /* 5 */
2202
+ " thread.last," /* 5 */
2203
+ " thread.sticky" /* 6 */
21942204
" FROM thread, blob, event"
21952205
" WHERE blob.rid=thread.last"
21962206
" AND event.objid=thread.last"
2197
- " ORDER BY 1;",
2207
+ " ORDER BY 7 DESC, 1;",
21982208
g.perm.ModForum ? "" : "AND y.fpid NOT IN private" /*safe-for-%s*/,
21992209
g.perm.ModForum ? "true" : "fpid NOT IN private" /*safe-for-%s*/,
22002210
iLimit+1, iOfst
22012211
);
22022212
while( db_step(&q)==SQLITE_ROW ){
22032213
char *zAge = human_readable_age(db_column_double(&q,0));
22042214
int nMsg = db_column_int(&q, 2);
2215
+ int bSticky = db_column_int(&q, 6);
22052216
const char *zUuid = db_column_text(&q, 3);
22062217
const char *zTitle = db_column_text(&q, 4);
22072218
if( iCnt==0 ){
22082219
if( iOfst>0 ){
22092220
@ <h1>Threads at least %s(zAge) old</h1>
@@ -2228,11 +2239,11 @@
22282239
@ %z(href("%R/forum?x=%d&n=%d",iOfst+iLimit,iLimit))\
22292240
@ &darr; Older...</a></td></tr>
22302241
fossil_free(zAge);
22312242
break;
22322243
}
2233
- @ <tr><td>%h(zAge) ago</td>
2244
+ @ <tr%s(bSticky ? " class='sticky'" : "")><td>%h(zAge) ago</td>
22342245
@ <td>%z(href("%R/forumpost/%S",zUuid))%h(zTitle)</a></td>
22352246
@ <td>\
22362247
if( g.perm.ModForum && moderation_pending(db_column_int(&q,5)) ){
22372248
@ <span class="modpending">\
22382249
@ Awaiting Moderator Approval</span><br>
22392250
--- src/forum.c
+++ src/forum.c
@@ -2168,42 +2168,53 @@
2168 style_submenu_entry("n","Max:",4,0);
2169 iOfst = atoi(PD("x","0"));
2170 iCnt = 0;
2171 if( db_table_exists("repository","forumpost") ){
2172 db_prepare(&q,
2173 "WITH thread(age,duration,cnt,root,last) AS ("
2174 " SELECT"
2175 " julianday('now') - max(fmtime),"
2176 " max(fmtime) - min(fmtime),"
2177 " sum(fprev IS NULL),"
2178 " froot,"
2179 " (SELECT fpid FROM forumpost AS y"
2180 " WHERE y.froot=x.froot %s"
2181 " ORDER BY y.fmtime DESC LIMIT 1)"
 
 
 
 
 
 
 
 
 
2182 " FROM forumpost AS x"
2183 " WHERE %s"
2184 " GROUP BY froot"
2185 " ORDER BY 1 LIMIT %d OFFSET %d"
2186 ")"
2187 "SELECT"
2188 " thread.age," /* 0 */
2189 " thread.duration," /* 1 */
2190 " thread.cnt," /* 2 */
2191 " blob.uuid," /* 3 */
2192 " substr(event.comment,instr(event.comment,':')+1)," /* 4 */
2193 " thread.last" /* 5 */
 
2194 " FROM thread, blob, event"
2195 " WHERE blob.rid=thread.last"
2196 " AND event.objid=thread.last"
2197 " ORDER BY 1;",
2198 g.perm.ModForum ? "" : "AND y.fpid NOT IN private" /*safe-for-%s*/,
2199 g.perm.ModForum ? "true" : "fpid NOT IN private" /*safe-for-%s*/,
2200 iLimit+1, iOfst
2201 );
2202 while( db_step(&q)==SQLITE_ROW ){
2203 char *zAge = human_readable_age(db_column_double(&q,0));
2204 int nMsg = db_column_int(&q, 2);
 
2205 const char *zUuid = db_column_text(&q, 3);
2206 const char *zTitle = db_column_text(&q, 4);
2207 if( iCnt==0 ){
2208 if( iOfst>0 ){
2209 @ <h1>Threads at least %s(zAge) old</h1>
@@ -2228,11 +2239,11 @@
2228 @ %z(href("%R/forum?x=%d&n=%d",iOfst+iLimit,iLimit))\
2229 @ &darr; Older...</a></td></tr>
2230 fossil_free(zAge);
2231 break;
2232 }
2233 @ <tr><td>%h(zAge) ago</td>
2234 @ <td>%z(href("%R/forumpost/%S",zUuid))%h(zTitle)</a></td>
2235 @ <td>\
2236 if( g.perm.ModForum && moderation_pending(db_column_int(&q,5)) ){
2237 @ <span class="modpending">\
2238 @ Awaiting Moderator Approval</span><br>
2239
--- src/forum.c
+++ src/forum.c
@@ -2168,42 +2168,53 @@
2168 style_submenu_entry("n","Max:",4,0);
2169 iOfst = atoi(PD("x","0"));
2170 iCnt = 0;
2171 if( db_table_exists("repository","forumpost") ){
2172 db_prepare(&q,
2173 "WITH thread(age,duration,cnt,root,last,sticky) AS ("
2174 " SELECT"
2175 " julianday('now') - max(fmtime),"
2176 " max(fmtime) - min(fmtime),"
2177 " sum(fprev IS NULL),"
2178 " froot,"
2179 " (SELECT fpid FROM forumpost AS y"
2180 " WHERE y.froot=x.froot %s"
2181 " ORDER BY y.fmtime DESC LIMIT 1),"
2182 " CASE WHEN"
2183 " firt IS NULL AND"
2184 " (SELECT 1 FROM tagxref ref, tag t"
2185 " WHERE ref.rid=x.fpid AND ref.tagtype>0"
2186 " AND ref.tagid=t.tagid"
2187 " AND t.tagname='sticky')"
2188 " THEN 1"
2189 " ELSE 0"
2190 " END"
2191 " FROM forumpost AS x"
2192 " WHERE %s"
2193 " GROUP BY froot"
2194 " ORDER BY 6 DESC, 1 LIMIT %d OFFSET %d"
2195 ")"
2196 "SELECT"
2197 " thread.age," /* 0 */
2198 " thread.duration," /* 1 */
2199 " thread.cnt," /* 2 */
2200 " blob.uuid," /* 3 */
2201 " substr(event.comment,instr(event.comment,':')+1)," /* 4 */
2202 " thread.last," /* 5 */
2203 " thread.sticky" /* 6 */
2204 " FROM thread, blob, event"
2205 " WHERE blob.rid=thread.last"
2206 " AND event.objid=thread.last"
2207 " ORDER BY 7 DESC, 1;",
2208 g.perm.ModForum ? "" : "AND y.fpid NOT IN private" /*safe-for-%s*/,
2209 g.perm.ModForum ? "true" : "fpid NOT IN private" /*safe-for-%s*/,
2210 iLimit+1, iOfst
2211 );
2212 while( db_step(&q)==SQLITE_ROW ){
2213 char *zAge = human_readable_age(db_column_double(&q,0));
2214 int nMsg = db_column_int(&q, 2);
2215 int bSticky = db_column_int(&q, 6);
2216 const char *zUuid = db_column_text(&q, 3);
2217 const char *zTitle = db_column_text(&q, 4);
2218 if( iCnt==0 ){
2219 if( iOfst>0 ){
2220 @ <h1>Threads at least %s(zAge) old</h1>
@@ -2228,11 +2239,11 @@
2239 @ %z(href("%R/forum?x=%d&n=%d",iOfst+iLimit,iLimit))\
2240 @ &darr; Older...</a></td></tr>
2241 fossil_free(zAge);
2242 break;
2243 }
2244 @ <tr%s(bSticky ? " class='sticky'" : "")><td>%h(zAge) ago</td>
2245 @ <td>%z(href("%R/forumpost/%S",zUuid))%h(zTitle)</a></td>
2246 @ <td>\
2247 if( g.perm.ModForum && moderation_pending(db_column_int(&q,5)) ){
2248 @ <span class="modpending">\
2249 @ Awaiting Moderator Approval</span><br>
2250

Keyboard Shortcuts

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