Fossil SCM

Add default report checkbox and compare report titles instead

kevgrig 2021-07-11 21:55 ticket-search-empty-report-number
Commit eaa7f6bb5cf0342d48e057be3a1aaddf4f8a73fff36de8ce4b8a9b70d0643a00
3 files changed +32 -15 +5 -5 +1 -1
+32 -15
--- src/report.c
+++ src/report.c
@@ -29,15 +29,14 @@
2929
# define SQLITE_RECURSIVE 33
3030
#endif
3131
3232
/* Settings that can be used to control ticket reports */
3333
/*
34
-** SETTING: ticket-search-empty-report-number width=10 default=0
35
-**
36
-** If this setting has an integer value of N, then when the ticket
37
-** search page query is blank, the report with rn=N is shown.
38
-** If N is zero, then no report is shown.
34
+** SETTING: ticket-default-report width=80
35
+** If this setting has a string value, then when the ticket
36
+** search page query is blank, the report with this title is shown.
37
+** If the setting is blank (default), then no report is shown.
3938
*/
4039
4140
/*
4241
** WEBPAGE: reportlist
4342
**
@@ -47,10 +46,11 @@
4746
const char *zScript;
4847
Blob ril; /* Report Item List */
4948
Stmt q;
5049
int rn = 0;
5150
int cnt = 0;
51
+ char *defaultReport = db_get("ticket-default-report", 0);
5252
5353
login_check_credentials();
5454
if( !g.perm.RdTkt && !g.perm.NewTkt ){
5555
login_needed(g.anon.RdTkt || g.anon.NewTkt);
5656
return;
@@ -94,10 +94,13 @@
9494
href("%R/rptedit?rn=%d", rn));
9595
}
9696
if( g.perm.TktFmt ){
9797
blob_appendf(&ril, "[%zsql</a>]",
9898
href("%R/rptsql?rn=%d", rn));
99
+ }
100
+ if( fossil_strcmp(zTitle, defaultReport)==0 ){
101
+ blob_appendf(&ril, "&nbsp;← default");
99102
}
100103
blob_appendf(&ril, "</li>\n");
101104
}
102105
db_finalize(&q);
103106
@@ -393,10 +396,11 @@
393396
const char *z;
394397
const char *zOwner;
395398
const char *zClrKey;
396399
char *zSQL;
397400
char *zErr = 0;
401
+ int dflt = P("dflt") ? 1 : 0;
398402
399403
login_check_credentials();
400404
if( !g.perm.TktFmt ){
401405
login_needed(g.anon.TktFmt);
402406
return;
@@ -461,10 +465,18 @@
461465
}else{
462466
db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime) "
463467
"VALUES(%Q,%Q,%Q,%Q,now())",
464468
zTitle, zSQL, zOwner, zClrKey);
465469
rn = db_last_insert_rowid();
470
+ }
471
+ if( dflt ){
472
+ db_set("ticket-default-report", zTitle, 0);
473
+ }else{
474
+ char *defaultReport = db_get("ticket-default-report", 0);
475
+ if( fossil_strcmp(zTitle, defaultReport)==0 ){
476
+ db_set("ticket-default-report", "", 0);
477
+ }
466478
}
467479
cgi_redirect(mprintf("rptview?rn=%d", rn));
468480
return;
469481
}
470482
}else if( rn==0 ){
@@ -474,14 +486,16 @@
474486
}else{
475487
Stmt q;
476488
db_prepare(&q, "SELECT title, sqlcode, owner, cols "
477489
"FROM reportfmt WHERE rn=%d",rn);
478490
if( db_step(&q)==SQLITE_ROW ){
491
+ char *defaultReport = db_get("ticket-default-report", 0);
479492
zTitle = db_column_malloc(&q, 0);
480493
zSQL = db_column_malloc(&q, 1);
481494
zOwner = db_column_malloc(&q, 2);
482495
zClrKey = db_column_malloc(&q, 3);
496
+ dflt = fossil_strcmp(zTitle, defaultReport)==0;
483497
}
484498
db_finalize(&q);
485499
if( P("copy") ){
486500
rn = 0;
487501
zTitle = mprintf("Copy Of %s", zTitle);
@@ -516,10 +530,12 @@
516530
@ color key is displayed.) Each line contains the text for a single
517531
@ entry in the key. The first token of each line is the background
518532
@ color for that line.<br />
519533
@ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
520534
@ </p>
535
+ @ <p><label><input type="checkbox" name="dflt" %s(dflt?"checked":"")> \
536
+ @ Make this the default report</label></p>
521537
if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){
522538
@ <p>This report format is owned by %h(zOwner). You are not allowed
523539
@ to change it.</p>
524540
@ </form>
525541
report_format_hints();
@@ -998,16 +1014,17 @@
9981014
9991015
/*
10001016
** Render a report.
10011017
*/
10021018
void rptview_page_content(
1003
- int rn, /* Report number. If 0, retrieve from rn query parameter. */
1019
+ const char *defaultTitleSearch, /* If rn and title query parameters are
1020
+ blank, search reports by this title. */
10041021
int pageWrap, /* If true, render full page; otherwise, just the report */
10051022
int redirectMissing /* If true and report not found, go to reportlist */
10061023
){
10071024
int count = 0;
1008
- int rc;
1025
+ int rn, rc;
10091026
char *zSql;
10101027
char *zTitle;
10111028
char *zOwner;
10121029
char *zClrKey;
10131030
int tabs;
@@ -1016,27 +1033,27 @@
10161033
char *zErr2 = 0;
10171034
10181035
login_check_credentials();
10191036
if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
10201037
tabs = P("tablist")!=0;
1021
- if ( rn==0 ) {
1022
- rn = atoi(PD("rn","0"));
1023
- }
10241038
db_prepare(&q,
10251039
"SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE rn=%d",
1026
- rn);
1040
+ atoi(PD("rn","0")));
10271041
rc = db_step(&q);
10281042
if( rc!=SQLITE_ROW ){
1043
+ const char *titleSearch =
1044
+ defaultTitleSearch==0 || trim_string(defaultTitleSearch)[0]==0 ?
1045
+ P("title") : defaultTitleSearch;
10291046
db_finalize(&q);
10301047
db_prepare(&q,
10311048
"SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE title GLOB %Q",
1032
- P("title"));
1049
+ titleSearch);
10331050
rc = db_step(&q);
10341051
}
10351052
if( rc!=SQLITE_ROW ){
10361053
db_finalize(&q);
1037
- if (redirectMissing) {
1054
+ if( redirectMissing ) {
10381055
cgi_redirect("reportlist");
10391056
}
10401057
return;
10411058
}
10421059
zTitle = db_column_malloc(&q, 0);
@@ -1066,11 +1083,11 @@
10661083
struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
10671084
const char *zQS = PD("QUERY_STRING","");
10681085
10691086
db_multi_exec("PRAGMA empty_result_callbacks=ON");
10701087
style_set_current_feature("report");
1071
- if ( pageWrap ) {
1088
+ if( pageWrap ) {
10721089
/* style_finish_page() should provide escaping via %h formatting */
10731090
if( zQS[0] ){
10741091
style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS);
10751092
style_submenu_element("Reports","%R/reportlist?%s",zQS);
10761093
} else {
@@ -1103,11 +1120,11 @@
11031120
@ <p class="reportError">Error: %h(zErr1)</p>
11041121
}else if( zErr2 ){
11051122
@ <p class="reportError">Error: %h(zErr2)</p>
11061123
}
11071124
style_table_sorter();
1108
- if ( pageWrap ) {
1125
+ if( pageWrap ) {
11091126
style_finish_page();
11101127
}
11111128
}else{
11121129
report_restrict_sql(&zErr1);
11131130
db_exec_readonly(g.db, zSql, output_tab_separated, &count, &zErr2);
11141131
--- src/report.c
+++ src/report.c
@@ -29,15 +29,14 @@
29 # define SQLITE_RECURSIVE 33
30 #endif
31
32 /* Settings that can be used to control ticket reports */
33 /*
34 ** SETTING: ticket-search-empty-report-number width=10 default=0
35 **
36 ** If this setting has an integer value of N, then when the ticket
37 ** search page query is blank, the report with rn=N is shown.
38 ** If N is zero, then no report is shown.
39 */
40
41 /*
42 ** WEBPAGE: reportlist
43 **
@@ -47,10 +46,11 @@
47 const char *zScript;
48 Blob ril; /* Report Item List */
49 Stmt q;
50 int rn = 0;
51 int cnt = 0;
 
52
53 login_check_credentials();
54 if( !g.perm.RdTkt && !g.perm.NewTkt ){
55 login_needed(g.anon.RdTkt || g.anon.NewTkt);
56 return;
@@ -94,10 +94,13 @@
94 href("%R/rptedit?rn=%d", rn));
95 }
96 if( g.perm.TktFmt ){
97 blob_appendf(&ril, "[%zsql</a>]",
98 href("%R/rptsql?rn=%d", rn));
 
 
 
99 }
100 blob_appendf(&ril, "</li>\n");
101 }
102 db_finalize(&q);
103
@@ -393,10 +396,11 @@
393 const char *z;
394 const char *zOwner;
395 const char *zClrKey;
396 char *zSQL;
397 char *zErr = 0;
 
398
399 login_check_credentials();
400 if( !g.perm.TktFmt ){
401 login_needed(g.anon.TktFmt);
402 return;
@@ -461,10 +465,18 @@
461 }else{
462 db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime) "
463 "VALUES(%Q,%Q,%Q,%Q,now())",
464 zTitle, zSQL, zOwner, zClrKey);
465 rn = db_last_insert_rowid();
 
 
 
 
 
 
 
 
466 }
467 cgi_redirect(mprintf("rptview?rn=%d", rn));
468 return;
469 }
470 }else if( rn==0 ){
@@ -474,14 +486,16 @@
474 }else{
475 Stmt q;
476 db_prepare(&q, "SELECT title, sqlcode, owner, cols "
477 "FROM reportfmt WHERE rn=%d",rn);
478 if( db_step(&q)==SQLITE_ROW ){
 
479 zTitle = db_column_malloc(&q, 0);
480 zSQL = db_column_malloc(&q, 1);
481 zOwner = db_column_malloc(&q, 2);
482 zClrKey = db_column_malloc(&q, 3);
 
483 }
484 db_finalize(&q);
485 if( P("copy") ){
486 rn = 0;
487 zTitle = mprintf("Copy Of %s", zTitle);
@@ -516,10 +530,12 @@
516 @ color key is displayed.) Each line contains the text for a single
517 @ entry in the key. The first token of each line is the background
518 @ color for that line.<br />
519 @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
520 @ </p>
 
 
521 if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){
522 @ <p>This report format is owned by %h(zOwner). You are not allowed
523 @ to change it.</p>
524 @ </form>
525 report_format_hints();
@@ -998,16 +1014,17 @@
998
999 /*
1000 ** Render a report.
1001 */
1002 void rptview_page_content(
1003 int rn, /* Report number. If 0, retrieve from rn query parameter. */
 
1004 int pageWrap, /* If true, render full page; otherwise, just the report */
1005 int redirectMissing /* If true and report not found, go to reportlist */
1006 ){
1007 int count = 0;
1008 int rc;
1009 char *zSql;
1010 char *zTitle;
1011 char *zOwner;
1012 char *zClrKey;
1013 int tabs;
@@ -1016,27 +1033,27 @@
1016 char *zErr2 = 0;
1017
1018 login_check_credentials();
1019 if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
1020 tabs = P("tablist")!=0;
1021 if ( rn==0 ) {
1022 rn = atoi(PD("rn","0"));
1023 }
1024 db_prepare(&q,
1025 "SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE rn=%d",
1026 rn);
1027 rc = db_step(&q);
1028 if( rc!=SQLITE_ROW ){
 
 
 
1029 db_finalize(&q);
1030 db_prepare(&q,
1031 "SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE title GLOB %Q",
1032 P("title"));
1033 rc = db_step(&q);
1034 }
1035 if( rc!=SQLITE_ROW ){
1036 db_finalize(&q);
1037 if (redirectMissing) {
1038 cgi_redirect("reportlist");
1039 }
1040 return;
1041 }
1042 zTitle = db_column_malloc(&q, 0);
@@ -1066,11 +1083,11 @@
1066 struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1067 const char *zQS = PD("QUERY_STRING","");
1068
1069 db_multi_exec("PRAGMA empty_result_callbacks=ON");
1070 style_set_current_feature("report");
1071 if ( pageWrap ) {
1072 /* style_finish_page() should provide escaping via %h formatting */
1073 if( zQS[0] ){
1074 style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS);
1075 style_submenu_element("Reports","%R/reportlist?%s",zQS);
1076 } else {
@@ -1103,11 +1120,11 @@
1103 @ <p class="reportError">Error: %h(zErr1)</p>
1104 }else if( zErr2 ){
1105 @ <p class="reportError">Error: %h(zErr2)</p>
1106 }
1107 style_table_sorter();
1108 if ( pageWrap ) {
1109 style_finish_page();
1110 }
1111 }else{
1112 report_restrict_sql(&zErr1);
1113 db_exec_readonly(g.db, zSql, output_tab_separated, &count, &zErr2);
1114
--- src/report.c
+++ src/report.c
@@ -29,15 +29,14 @@
29 # define SQLITE_RECURSIVE 33
30 #endif
31
32 /* Settings that can be used to control ticket reports */
33 /*
34 ** SETTING: ticket-default-report width=80
35 ** If this setting has a string value, then when the ticket
36 ** search page query is blank, the report with this title is shown.
37 ** If the setting is blank (default), then no report is shown.
 
38 */
39
40 /*
41 ** WEBPAGE: reportlist
42 **
@@ -47,10 +46,11 @@
46 const char *zScript;
47 Blob ril; /* Report Item List */
48 Stmt q;
49 int rn = 0;
50 int cnt = 0;
51 char *defaultReport = db_get("ticket-default-report", 0);
52
53 login_check_credentials();
54 if( !g.perm.RdTkt && !g.perm.NewTkt ){
55 login_needed(g.anon.RdTkt || g.anon.NewTkt);
56 return;
@@ -94,10 +94,13 @@
94 href("%R/rptedit?rn=%d", rn));
95 }
96 if( g.perm.TktFmt ){
97 blob_appendf(&ril, "[%zsql</a>]",
98 href("%R/rptsql?rn=%d", rn));
99 }
100 if( fossil_strcmp(zTitle, defaultReport)==0 ){
101 blob_appendf(&ril, "&nbsp;← default");
102 }
103 blob_appendf(&ril, "</li>\n");
104 }
105 db_finalize(&q);
106
@@ -393,10 +396,11 @@
396 const char *z;
397 const char *zOwner;
398 const char *zClrKey;
399 char *zSQL;
400 char *zErr = 0;
401 int dflt = P("dflt") ? 1 : 0;
402
403 login_check_credentials();
404 if( !g.perm.TktFmt ){
405 login_needed(g.anon.TktFmt);
406 return;
@@ -461,10 +465,18 @@
465 }else{
466 db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols,mtime) "
467 "VALUES(%Q,%Q,%Q,%Q,now())",
468 zTitle, zSQL, zOwner, zClrKey);
469 rn = db_last_insert_rowid();
470 }
471 if( dflt ){
472 db_set("ticket-default-report", zTitle, 0);
473 }else{
474 char *defaultReport = db_get("ticket-default-report", 0);
475 if( fossil_strcmp(zTitle, defaultReport)==0 ){
476 db_set("ticket-default-report", "", 0);
477 }
478 }
479 cgi_redirect(mprintf("rptview?rn=%d", rn));
480 return;
481 }
482 }else if( rn==0 ){
@@ -474,14 +486,16 @@
486 }else{
487 Stmt q;
488 db_prepare(&q, "SELECT title, sqlcode, owner, cols "
489 "FROM reportfmt WHERE rn=%d",rn);
490 if( db_step(&q)==SQLITE_ROW ){
491 char *defaultReport = db_get("ticket-default-report", 0);
492 zTitle = db_column_malloc(&q, 0);
493 zSQL = db_column_malloc(&q, 1);
494 zOwner = db_column_malloc(&q, 2);
495 zClrKey = db_column_malloc(&q, 3);
496 dflt = fossil_strcmp(zTitle, defaultReport)==0;
497 }
498 db_finalize(&q);
499 if( P("copy") ){
500 rn = 0;
501 zTitle = mprintf("Copy Of %s", zTitle);
@@ -516,10 +530,12 @@
530 @ color key is displayed.) Each line contains the text for a single
531 @ entry in the key. The first token of each line is the background
532 @ color for that line.<br />
533 @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
534 @ </p>
535 @ <p><label><input type="checkbox" name="dflt" %s(dflt?"checked":"")> \
536 @ Make this the default report</label></p>
537 if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){
538 @ <p>This report format is owned by %h(zOwner). You are not allowed
539 @ to change it.</p>
540 @ </form>
541 report_format_hints();
@@ -998,16 +1014,17 @@
1014
1015 /*
1016 ** Render a report.
1017 */
1018 void rptview_page_content(
1019 const char *defaultTitleSearch, /* If rn and title query parameters are
1020 blank, search reports by this title. */
1021 int pageWrap, /* If true, render full page; otherwise, just the report */
1022 int redirectMissing /* If true and report not found, go to reportlist */
1023 ){
1024 int count = 0;
1025 int rn, rc;
1026 char *zSql;
1027 char *zTitle;
1028 char *zOwner;
1029 char *zClrKey;
1030 int tabs;
@@ -1016,27 +1033,27 @@
1033 char *zErr2 = 0;
1034
1035 login_check_credentials();
1036 if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
1037 tabs = P("tablist")!=0;
 
 
 
1038 db_prepare(&q,
1039 "SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE rn=%d",
1040 atoi(PD("rn","0")));
1041 rc = db_step(&q);
1042 if( rc!=SQLITE_ROW ){
1043 const char *titleSearch =
1044 defaultTitleSearch==0 || trim_string(defaultTitleSearch)[0]==0 ?
1045 P("title") : defaultTitleSearch;
1046 db_finalize(&q);
1047 db_prepare(&q,
1048 "SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE title GLOB %Q",
1049 titleSearch);
1050 rc = db_step(&q);
1051 }
1052 if( rc!=SQLITE_ROW ){
1053 db_finalize(&q);
1054 if( redirectMissing ) {
1055 cgi_redirect("reportlist");
1056 }
1057 return;
1058 }
1059 zTitle = db_column_malloc(&q, 0);
@@ -1066,11 +1083,11 @@
1083 struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1084 const char *zQS = PD("QUERY_STRING","");
1085
1086 db_multi_exec("PRAGMA empty_result_callbacks=ON");
1087 style_set_current_feature("report");
1088 if( pageWrap ) {
1089 /* style_finish_page() should provide escaping via %h formatting */
1090 if( zQS[0] ){
1091 style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS);
1092 style_submenu_element("Reports","%R/reportlist?%s",zQS);
1093 } else {
@@ -1103,11 +1120,11 @@
1120 @ <p class="reportError">Error: %h(zErr1)</p>
1121 }else if( zErr2 ){
1122 @ <p class="reportError">Error: %h(zErr2)</p>
1123 }
1124 style_table_sorter();
1125 if( pageWrap ) {
1126 style_finish_page();
1127 }
1128 }else{
1129 report_restrict_sql(&zErr1);
1130 db_exec_readonly(g.db, zSql, output_tab_separated, &count, &zErr2);
1131
+5 -5
--- src/tkt.c
+++ src/tkt.c
@@ -1636,18 +1636,18 @@
16361636
** Usage: /tktsrch?s=PATTERN
16371637
**
16381638
** Full-text search of all current tickets
16391639
*/
16401640
void tkt_srchpage(void){
1641
- int rn;
1641
+ char *defaultReport;
16421642
login_check_credentials();
16431643
style_set_current_feature("tkt");
16441644
style_header("Ticket Search");
16451645
ticket_standard_submenu(T_ALL_BUT(T_SRCH));
1646
- if ( !search_screen(SRCH_TKT, 0) ) {
1647
- rn = db_get_int("ticket-search-empty-report-number",0);
1648
- if ( rn ) {
1649
- rptview_page_content(rn, 0, 0);
1646
+ if( !search_screen(SRCH_TKT, 0) ){
1647
+ defaultReport = db_get("ticket-default-report", 0);
1648
+ if( defaultReport ){
1649
+ rptview_page_content(defaultReport, 0, 0);
16501650
}
16511651
}
16521652
style_finish_page();
16531653
}
16541654
--- src/tkt.c
+++ src/tkt.c
@@ -1636,18 +1636,18 @@
1636 ** Usage: /tktsrch?s=PATTERN
1637 **
1638 ** Full-text search of all current tickets
1639 */
1640 void tkt_srchpage(void){
1641 int rn;
1642 login_check_credentials();
1643 style_set_current_feature("tkt");
1644 style_header("Ticket Search");
1645 ticket_standard_submenu(T_ALL_BUT(T_SRCH));
1646 if ( !search_screen(SRCH_TKT, 0) ) {
1647 rn = db_get_int("ticket-search-empty-report-number",0);
1648 if ( rn ) {
1649 rptview_page_content(rn, 0, 0);
1650 }
1651 }
1652 style_finish_page();
1653 }
1654
--- src/tkt.c
+++ src/tkt.c
@@ -1636,18 +1636,18 @@
1636 ** Usage: /tktsrch?s=PATTERN
1637 **
1638 ** Full-text search of all current tickets
1639 */
1640 void tkt_srchpage(void){
1641 char *defaultReport;
1642 login_check_credentials();
1643 style_set_current_feature("tkt");
1644 style_header("Ticket Search");
1645 ticket_standard_submenu(T_ALL_BUT(T_SRCH));
1646 if( !search_screen(SRCH_TKT, 0) ){
1647 defaultReport = db_get("ticket-default-report", 0);
1648 if( defaultReport ){
1649 rptview_page_content(defaultReport, 0, 0);
1650 }
1651 }
1652 style_finish_page();
1653 }
1654
+1 -1
--- test/tester.tcl
+++ test/tester.tcl
@@ -351,11 +351,11 @@
351351
ssl-ca-location \
352352
ssl-identity \
353353
tclsh \
354354
th1-setup \
355355
th1-uri-regexp \
356
- ticket-search-empty-report-number \
356
+ ticket-default-report \
357357
user-color-map \
358358
uv-sync \
359359
web-browser]
360360
361361
fossil test-th-eval "hasfeature legacyMvRm"
362362
--- test/tester.tcl
+++ test/tester.tcl
@@ -351,11 +351,11 @@
351 ssl-ca-location \
352 ssl-identity \
353 tclsh \
354 th1-setup \
355 th1-uri-regexp \
356 ticket-search-empty-report-number \
357 user-color-map \
358 uv-sync \
359 web-browser]
360
361 fossil test-th-eval "hasfeature legacyMvRm"
362
--- test/tester.tcl
+++ test/tester.tcl
@@ -351,11 +351,11 @@
351 ssl-ca-location \
352 ssl-identity \
353 tclsh \
354 th1-setup \
355 th1-uri-regexp \
356 ticket-default-report \
357 user-color-map \
358 uv-sync \
359 web-browser]
360
361 fossil test-th-eval "hasfeature legacyMvRm"
362

Keyboard Shortcuts

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