Fossil SCM
Add the status= query parameter to /timeline. For y=f or y=h (display of only forum posts and forum threads) the status= query parameter restricts the display to only those posts or threads that have the specified status. This is a no-op if forum-statuses are disabled (the default).
Commit
556f21daf7c73e431ee75d15f2ec7fcd2a264f28667b8aae30937a07a1e0885e
Parent
bcf094c3d7b1200…
2 files changed
+31
+18
+31
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -2559,10 +2559,41 @@ | ||
| 2559 | 2559 | i = 0; |
| 2560 | 2560 | } |
| 2561 | 2561 | db_reset(&q); |
| 2562 | 2562 | sqlite3_result_int(context, i==pMData->iMatch); |
| 2563 | 2563 | } |
| 2564 | + | |
| 2565 | +/* | |
| 2566 | +** Register the status_match() SQL function so that it matches forum | |
| 2567 | +** threads having status zStatus. Used to implement the status=NAME | |
| 2568 | +** query parameter on /timeline. | |
| 2569 | +** | |
| 2570 | +** Return true if the status_match() function was registered and | |
| 2571 | +** filtering should be applied. Return false (and register nothing) | |
| 2572 | +** if zStatus is NULL, empty, or "*", or if the forum-statuses setting | |
| 2573 | +** has fewer than two entries, or if there is no forum. | |
| 2574 | +*/ | |
| 2575 | +int forum_status_match_setup(const char *zStatus){ | |
| 2576 | + static ForumStatusMatch sFSM; | |
| 2577 | + const ForumStatusList *pFstat; | |
| 2578 | + if( zStatus==0 || zStatus[0]==0 || fossil_strcmp(zStatus,"*")==0 ){ | |
| 2579 | + return 0; | |
| 2580 | + } | |
| 2581 | + if( !db_table_exists("repository","forumpost") ) return 0; | |
| 2582 | + pFstat = forum_statuses(); | |
| 2583 | + if( pFstat->n<2 ) return 0; | |
| 2584 | + sFSM.pFses = pFstat; | |
| 2585 | + sFSM.eStatusTag = db_int(0, "SELECT tagid FROM tag WHERE tagname='status'"); | |
| 2586 | + for(sFSM.iMatch=0; sFSM.iMatch<pFstat->n; sFSM.iMatch++){ | |
| 2587 | + if( 0==fossil_strcmp(zStatus, pFstat->aStatus[sFSM.iMatch].zValue) ){ | |
| 2588 | + break; | |
| 2589 | + } | |
| 2590 | + } | |
| 2591 | + sqlite3_create_function(g.db,"status_match",1,SQLITE_UTF8,(void*)&sFSM, | |
| 2592 | + forum_status_match, 0, 0); | |
| 2593 | + return 1; | |
| 2594 | +} | |
| 2564 | 2595 | |
| 2565 | 2596 | /* |
| 2566 | 2597 | ** WEBPAGE: forummain |
| 2567 | 2598 | ** WEBPAGE: forum |
| 2568 | 2599 | ** |
| 2569 | 2600 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -2559,10 +2559,41 @@ | |
| 2559 | i = 0; |
| 2560 | } |
| 2561 | db_reset(&q); |
| 2562 | sqlite3_result_int(context, i==pMData->iMatch); |
| 2563 | } |
| 2564 | |
| 2565 | /* |
| 2566 | ** WEBPAGE: forummain |
| 2567 | ** WEBPAGE: forum |
| 2568 | ** |
| 2569 |
| --- src/forum.c | |
| +++ src/forum.c | |
| @@ -2559,10 +2559,41 @@ | |
| 2559 | i = 0; |
| 2560 | } |
| 2561 | db_reset(&q); |
| 2562 | sqlite3_result_int(context, i==pMData->iMatch); |
| 2563 | } |
| 2564 | |
| 2565 | /* |
| 2566 | ** Register the status_match() SQL function so that it matches forum |
| 2567 | ** threads having status zStatus. Used to implement the status=NAME |
| 2568 | ** query parameter on /timeline. |
| 2569 | ** |
| 2570 | ** Return true if the status_match() function was registered and |
| 2571 | ** filtering should be applied. Return false (and register nothing) |
| 2572 | ** if zStatus is NULL, empty, or "*", or if the forum-statuses setting |
| 2573 | ** has fewer than two entries, or if there is no forum. |
| 2574 | */ |
| 2575 | int forum_status_match_setup(const char *zStatus){ |
| 2576 | static ForumStatusMatch sFSM; |
| 2577 | const ForumStatusList *pFstat; |
| 2578 | if( zStatus==0 || zStatus[0]==0 || fossil_strcmp(zStatus,"*")==0 ){ |
| 2579 | return 0; |
| 2580 | } |
| 2581 | if( !db_table_exists("repository","forumpost") ) return 0; |
| 2582 | pFstat = forum_statuses(); |
| 2583 | if( pFstat->n<2 ) return 0; |
| 2584 | sFSM.pFses = pFstat; |
| 2585 | sFSM.eStatusTag = db_int(0, "SELECT tagid FROM tag WHERE tagname='status'"); |
| 2586 | for(sFSM.iMatch=0; sFSM.iMatch<pFstat->n; sFSM.iMatch++){ |
| 2587 | if( 0==fossil_strcmp(zStatus, pFstat->aStatus[sFSM.iMatch].zValue) ){ |
| 2588 | break; |
| 2589 | } |
| 2590 | } |
| 2591 | sqlite3_create_function(g.db,"status_match",1,SQLITE_UTF8,(void*)&sFSM, |
| 2592 | forum_status_match, 0, 0); |
| 2593 | return 1; |
| 2594 | } |
| 2595 | |
| 2596 | /* |
| 2597 | ** WEBPAGE: forummain |
| 2598 | ** WEBPAGE: forum |
| 2599 | ** |
| 2600 |
+18
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -1747,10 +1747,13 @@ | ||
| 1747 | 1747 | ** nowiki Do not show wiki associated with branch or tag |
| 1748 | 1748 | ** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob", |
| 1749 | 1749 | ** "like", or "regexp". |
| 1750 | 1750 | ** u=USER Only show items associated with USER |
| 1751 | 1751 | ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', 'h', or 'all'. |
| 1752 | +** status=NAME With y=f or y=h, show only forum posts or threads | |
| 1753 | +** whose thread has status NAME. See the | |
| 1754 | +** forum-statuses setting. | |
| 1752 | 1755 | ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar", |
| 1753 | 1756 | ** x: "Classic". |
| 1754 | 1757 | ** advm Use the "Advanced" or "Busy" menu design. |
| 1755 | 1758 | ** ng No Graph. |
| 1756 | 1759 | ** ncp Omit cherrypick merges |
| @@ -2626,10 +2629,11 @@ | ||
| 2626 | 2629 | } |
| 2627 | 2630 | }else{ |
| 2628 | 2631 | /* Otherwise, a timeline based on a span of time */ |
| 2629 | 2632 | int n; |
| 2630 | 2633 | const char *zEType = "event"; |
| 2634 | + const char *zStatus = 0; /* status=NAME filter for forum threads */ | |
| 2631 | 2635 | char *zDate; |
| 2632 | 2636 | Blob cond; |
| 2633 | 2637 | blob_zero(&cond); |
| 2634 | 2638 | tmFlags |= TIMELINE_FILLGAPS; |
| 2635 | 2639 | if( zChng && *zChng ){ |
| @@ -3039,10 +3043,18 @@ | ||
| 3039 | 3043 | }else if( zType[0]=='f' ){ |
| 3040 | 3044 | zEType = "forum post"; |
| 3041 | 3045 | }else if( zType[0]=='h' ){ |
| 3042 | 3046 | zEType = "new forum thread"; |
| 3043 | 3047 | } |
| 3048 | + if( (zType[0]=='f' || zType[0]=='h') | |
| 3049 | + && forum_status_match_setup(P("status")) | |
| 3050 | + ){ | |
| 3051 | + zStatus = P("status"); | |
| 3052 | + blob_append_sql(&cond, | |
| 3053 | + " AND status_match((SELECT froot FROM forumpost" | |
| 3054 | + " WHERE fpid=event.objid))"); | |
| 3055 | + } | |
| 3044 | 3056 | } |
| 3045 | 3057 | if( zUser ){ |
| 3046 | 3058 | int n = db_int(0,"SELECT count(*) FROM event" |
| 3047 | 3059 | " WHERE user=%Q OR euser=%Q", zUser, zUser); |
| 3048 | 3060 | if( n<=nEntry ){ |
| @@ -3134,10 +3146,13 @@ | ||
| 3134 | 3146 | }else if( zBefore==0 && zCirca==0 && n>=nEntry && nEntry>0 ){ |
| 3135 | 3147 | blob_appendf(&desc, "%d most recent %s%s", n, zEType, zPlural); |
| 3136 | 3148 | }else{ |
| 3137 | 3149 | blob_appendf(&desc, "%d %s%s", n, zEType, zPlural); |
| 3138 | 3150 | } |
| 3151 | + if( zStatus ){ | |
| 3152 | + blob_appendf(&desc, " with status %h", zStatus); | |
| 3153 | + } | |
| 3139 | 3154 | if( zUses ){ |
| 3140 | 3155 | char *zFilenames = names_of_file(zUses); |
| 3141 | 3156 | blob_appendf(&desc, " using file %s version %z%S</a>", zFilenames, |
| 3142 | 3157 | href("%R/artifact/%!S",zUses), zUses); |
| 3143 | 3158 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| @@ -3252,10 +3267,13 @@ | ||
| 3252 | 3267 | style_submenu_entry("t", "Tag Filter:", -8, 0); |
| 3253 | 3268 | style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0); |
| 3254 | 3269 | } |
| 3255 | 3270 | } |
| 3256 | 3271 | blob_zero(&cond); |
| 3272 | + if( zStatus ){ | |
| 3273 | + sqlite3_create_function(g.db,"status_match",1,SQLITE_UTF8,0,0,0,0); | |
| 3274 | + } | |
| 3257 | 3275 | } |
| 3258 | 3276 | if( showSql ){ |
| 3259 | 3277 | db_append_dml_to_blob(0); |
| 3260 | 3278 | @ <pre>%h(blob_str(&allSql))</pre> |
| 3261 | 3279 | blob_reset(&allSql); |
| 3262 | 3280 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1747,10 +1747,13 @@ | |
| 1747 | ** nowiki Do not show wiki associated with branch or tag |
| 1748 | ** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob", |
| 1749 | ** "like", or "regexp". |
| 1750 | ** u=USER Only show items associated with USER |
| 1751 | ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', 'h', or 'all'. |
| 1752 | ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar", |
| 1753 | ** x: "Classic". |
| 1754 | ** advm Use the "Advanced" or "Busy" menu design. |
| 1755 | ** ng No Graph. |
| 1756 | ** ncp Omit cherrypick merges |
| @@ -2626,10 +2629,11 @@ | |
| 2626 | } |
| 2627 | }else{ |
| 2628 | /* Otherwise, a timeline based on a span of time */ |
| 2629 | int n; |
| 2630 | const char *zEType = "event"; |
| 2631 | char *zDate; |
| 2632 | Blob cond; |
| 2633 | blob_zero(&cond); |
| 2634 | tmFlags |= TIMELINE_FILLGAPS; |
| 2635 | if( zChng && *zChng ){ |
| @@ -3039,10 +3043,18 @@ | |
| 3039 | }else if( zType[0]=='f' ){ |
| 3040 | zEType = "forum post"; |
| 3041 | }else if( zType[0]=='h' ){ |
| 3042 | zEType = "new forum thread"; |
| 3043 | } |
| 3044 | } |
| 3045 | if( zUser ){ |
| 3046 | int n = db_int(0,"SELECT count(*) FROM event" |
| 3047 | " WHERE user=%Q OR euser=%Q", zUser, zUser); |
| 3048 | if( n<=nEntry ){ |
| @@ -3134,10 +3146,13 @@ | |
| 3134 | }else if( zBefore==0 && zCirca==0 && n>=nEntry && nEntry>0 ){ |
| 3135 | blob_appendf(&desc, "%d most recent %s%s", n, zEType, zPlural); |
| 3136 | }else{ |
| 3137 | blob_appendf(&desc, "%d %s%s", n, zEType, zPlural); |
| 3138 | } |
| 3139 | if( zUses ){ |
| 3140 | char *zFilenames = names_of_file(zUses); |
| 3141 | blob_appendf(&desc, " using file %s version %z%S</a>", zFilenames, |
| 3142 | href("%R/artifact/%!S",zUses), zUses); |
| 3143 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| @@ -3252,10 +3267,13 @@ | |
| 3252 | style_submenu_entry("t", "Tag Filter:", -8, 0); |
| 3253 | style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0); |
| 3254 | } |
| 3255 | } |
| 3256 | blob_zero(&cond); |
| 3257 | } |
| 3258 | if( showSql ){ |
| 3259 | db_append_dml_to_blob(0); |
| 3260 | @ <pre>%h(blob_str(&allSql))</pre> |
| 3261 | blob_reset(&allSql); |
| 3262 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -1747,10 +1747,13 @@ | |
| 1747 | ** nowiki Do not show wiki associated with branch or tag |
| 1748 | ** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob", |
| 1749 | ** "like", or "regexp". |
| 1750 | ** u=USER Only show items associated with USER |
| 1751 | ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', 'h', or 'all'. |
| 1752 | ** status=NAME With y=f or y=h, show only forum posts or threads |
| 1753 | ** whose thread has status NAME. See the |
| 1754 | ** forum-statuses setting. |
| 1755 | ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar", |
| 1756 | ** x: "Classic". |
| 1757 | ** advm Use the "Advanced" or "Busy" menu design. |
| 1758 | ** ng No Graph. |
| 1759 | ** ncp Omit cherrypick merges |
| @@ -2626,10 +2629,11 @@ | |
| 2629 | } |
| 2630 | }else{ |
| 2631 | /* Otherwise, a timeline based on a span of time */ |
| 2632 | int n; |
| 2633 | const char *zEType = "event"; |
| 2634 | const char *zStatus = 0; /* status=NAME filter for forum threads */ |
| 2635 | char *zDate; |
| 2636 | Blob cond; |
| 2637 | blob_zero(&cond); |
| 2638 | tmFlags |= TIMELINE_FILLGAPS; |
| 2639 | if( zChng && *zChng ){ |
| @@ -3039,10 +3043,18 @@ | |
| 3043 | }else if( zType[0]=='f' ){ |
| 3044 | zEType = "forum post"; |
| 3045 | }else if( zType[0]=='h' ){ |
| 3046 | zEType = "new forum thread"; |
| 3047 | } |
| 3048 | if( (zType[0]=='f' || zType[0]=='h') |
| 3049 | && forum_status_match_setup(P("status")) |
| 3050 | ){ |
| 3051 | zStatus = P("status"); |
| 3052 | blob_append_sql(&cond, |
| 3053 | " AND status_match((SELECT froot FROM forumpost" |
| 3054 | " WHERE fpid=event.objid))"); |
| 3055 | } |
| 3056 | } |
| 3057 | if( zUser ){ |
| 3058 | int n = db_int(0,"SELECT count(*) FROM event" |
| 3059 | " WHERE user=%Q OR euser=%Q", zUser, zUser); |
| 3060 | if( n<=nEntry ){ |
| @@ -3134,10 +3146,13 @@ | |
| 3146 | }else if( zBefore==0 && zCirca==0 && n>=nEntry && nEntry>0 ){ |
| 3147 | blob_appendf(&desc, "%d most recent %s%s", n, zEType, zPlural); |
| 3148 | }else{ |
| 3149 | blob_appendf(&desc, "%d %s%s", n, zEType, zPlural); |
| 3150 | } |
| 3151 | if( zStatus ){ |
| 3152 | blob_appendf(&desc, " with status %h", zStatus); |
| 3153 | } |
| 3154 | if( zUses ){ |
| 3155 | char *zFilenames = names_of_file(zUses); |
| 3156 | blob_appendf(&desc, " using file %s version %z%S</a>", zFilenames, |
| 3157 | href("%R/artifact/%!S",zUses), zUses); |
| 3158 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| @@ -3252,10 +3267,13 @@ | |
| 3267 | style_submenu_entry("t", "Tag Filter:", -8, 0); |
| 3268 | style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0); |
| 3269 | } |
| 3270 | } |
| 3271 | blob_zero(&cond); |
| 3272 | if( zStatus ){ |
| 3273 | sqlite3_create_function(g.db,"status_match",1,SQLITE_UTF8,0,0,0,0); |
| 3274 | } |
| 3275 | } |
| 3276 | if( showSql ){ |
| 3277 | db_append_dml_to_blob(0); |
| 3278 | @ <pre>%h(blob_str(&allSql))</pre> |
| 3279 | blob_reset(&allSql); |
| 3280 |