Fossil SCM

Add the (experimental) /tarlist page to offer suggested tarballs and ZIPs for download.

drh 2025-10-16 23:18 timeline-enhance-2025
Commit 2c6d015c008a268d77e42228fd267fd53fa47f05f7fb03a1fc04f8fbc15a3c1a
+2 -2
--- src/db.c
+++ src/db.c
@@ -3599,16 +3599,16 @@
35993599
36003600
/*
36013601
** Return true if the string zVal represents "true" (or "false").
36023602
*/
36033603
int is_truth(const char *zVal){
3604
- static const char *const azOn[] = { "on", "yes", "true", "1" };
3604
+ static const char *const azOn[] = { "on", "yes", "true" };
36053605
int i;
36063606
for(i=0; i<count(azOn); i++){
36073607
if( fossil_stricmp(zVal,azOn[i])==0 ) return 1;
36083608
}
3609
- return 0;
3609
+ return atoi(zVal);
36103610
}
36113611
int is_false(const char *zVal){
36123612
static const char *const azOff[] = { "off", "no", "false", "0" };
36133613
int i;
36143614
for(i=0; i<count(azOff); i++){
36153615
--- src/db.c
+++ src/db.c
@@ -3599,16 +3599,16 @@
3599
3600 /*
3601 ** Return true if the string zVal represents "true" (or "false").
3602 */
3603 int is_truth(const char *zVal){
3604 static const char *const azOn[] = { "on", "yes", "true", "1" };
3605 int i;
3606 for(i=0; i<count(azOn); i++){
3607 if( fossil_stricmp(zVal,azOn[i])==0 ) return 1;
3608 }
3609 return 0;
3610 }
3611 int is_false(const char *zVal){
3612 static const char *const azOff[] = { "off", "no", "false", "0" };
3613 int i;
3614 for(i=0; i<count(azOff); i++){
3615
--- src/db.c
+++ src/db.c
@@ -3599,16 +3599,16 @@
3599
3600 /*
3601 ** Return true if the string zVal represents "true" (or "false").
3602 */
3603 int is_truth(const char *zVal){
3604 static const char *const azOn[] = { "on", "yes", "true" };
3605 int i;
3606 for(i=0; i<count(azOn); i++){
3607 if( fossil_stricmp(zVal,azOn[i])==0 ) return 1;
3608 }
3609 return atoi(zVal);
3610 }
3611 int is_false(const char *zVal){
3612 static const char *const azOff[] = { "off", "no", "false", "0" };
3613 int i;
3614 for(i=0; i<count(azOff); i++){
3615
+21
--- src/setup.c
+++ src/setup.c
@@ -1425,10 +1425,31 @@
14251425
@ <p>The default value is blank, meaning no added entries.
14261426
@ (Property: sitemap-extra)
14271427
@ <p>
14281428
textarea_attribute("Custom Sitemap Entries", 8, 80,
14291429
"sitemap-extra", "smextra", "", 0);
1430
+ @ <hr>
1431
+ @ <p>Configuration for the <a href="%R/tarlist">/tarlist</a> page.
1432
+ @ The value is a TCL list divided into pairs.
1433
+ @ <ol>
1434
+ @ <li> The first term of each pair is an integer (N).
1435
+ @ <li> The second term of each pair is a glob pattern (PATTERN).
1436
+ @ </ol>
1437
+ @ For each pair, the most recent N check-ins that have a tag that
1438
+ @ matches PATTERN are included in on the /tarlist page. The special
1439
+ @ pattern of "OPEN-LEAF" matches all open leaf check-ins. Example:
1440
+ @ <blockquote><tt>1 trunk 3 release 5 OPEN-LEAF</tt></blockquote>
1441
+ @ The example pattern above shows the union of the most recent trunk
1442
+ @ check-in, the 5 most recent open leaf check-ins, and the 3 most
1443
+ @ recent check-ins tagged with "release".
1444
+ @ <p>
1445
+ @ The /tarlist page is omitted from the <a href="%R/sitemap">/sitemap</a>
1446
+ @ if the first token is "0". The default value is "5 OPEN-LEAF".
1447
+ @ (Property: suggested-tarlist)
1448
+ @ <p>
1449
+ textarea_attribute("Check-ins To Show On /tarlist", 2, 80,
1450
+ "suggested-tarlist", "sgtrlst", "", 0);
14301451
@ <hr>
14311452
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
14321453
@ </div></form>
14331454
db_end_transaction(0);
14341455
style_finish_page();
14351456
--- src/setup.c
+++ src/setup.c
@@ -1425,10 +1425,31 @@
1425 @ <p>The default value is blank, meaning no added entries.
1426 @ (Property: sitemap-extra)
1427 @ <p>
1428 textarea_attribute("Custom Sitemap Entries", 8, 80,
1429 "sitemap-extra", "smextra", "", 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1430 @ <hr>
1431 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
1432 @ </div></form>
1433 db_end_transaction(0);
1434 style_finish_page();
1435
--- src/setup.c
+++ src/setup.c
@@ -1425,10 +1425,31 @@
1425 @ <p>The default value is blank, meaning no added entries.
1426 @ (Property: sitemap-extra)
1427 @ <p>
1428 textarea_attribute("Custom Sitemap Entries", 8, 80,
1429 "sitemap-extra", "smextra", "", 0);
1430 @ <hr>
1431 @ <p>Configuration for the <a href="%R/tarlist">/tarlist</a> page.
1432 @ The value is a TCL list divided into pairs.
1433 @ <ol>
1434 @ <li> The first term of each pair is an integer (N).
1435 @ <li> The second term of each pair is a glob pattern (PATTERN).
1436 @ </ol>
1437 @ For each pair, the most recent N check-ins that have a tag that
1438 @ matches PATTERN are included in on the /tarlist page. The special
1439 @ pattern of "OPEN-LEAF" matches all open leaf check-ins. Example:
1440 @ <blockquote><tt>1 trunk 3 release 5 OPEN-LEAF</tt></blockquote>
1441 @ The example pattern above shows the union of the most recent trunk
1442 @ check-in, the 5 most recent open leaf check-ins, and the 3 most
1443 @ recent check-ins tagged with "release".
1444 @ <p>
1445 @ The /tarlist page is omitted from the <a href="%R/sitemap">/sitemap</a>
1446 @ if the first token is "0". The default value is "5 OPEN-LEAF".
1447 @ (Property: suggested-tarlist)
1448 @ <p>
1449 textarea_attribute("Check-ins To Show On /tarlist", 2, 80,
1450 "suggested-tarlist", "sgtrlst", "", 0);
1451 @ <hr>
1452 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
1453 @ </div></form>
1454 db_end_transaction(0);
1455 style_finish_page();
1456
--- src/sitemap.c
+++ src/sitemap.c
@@ -132,10 +132,13 @@
132132
@ <li>%z(href("%R/uvlist"))Unversioned Files</a>
133133
if( g.perm.Write && zEditGlob[0]!=0 ){
134134
@ <li>%z(href("%R/fileedit"))On-line File Editor</li>
135135
}
136136
@ </ul>
137
+ }
138
+ if( g.perm.Zip && db_get_boolean("suggested-tarlist",1)!=0 ){
139
+ @ <li>%z(href("%R/tarlist"))Tarballs and ZIPs</a>
137140
}
138141
if( g.perm.Read ){
139142
@ <li>%z(href("%R/timeline"))Project Timeline</a>
140143
@ <ul>
141144
@ <li>%z(href("%R/reports"))Activity Reports</a></li>
142145
--- src/sitemap.c
+++ src/sitemap.c
@@ -132,10 +132,13 @@
132 @ <li>%z(href("%R/uvlist"))Unversioned Files</a>
133 if( g.perm.Write && zEditGlob[0]!=0 ){
134 @ <li>%z(href("%R/fileedit"))On-line File Editor</li>
135 }
136 @ </ul>
 
 
 
137 }
138 if( g.perm.Read ){
139 @ <li>%z(href("%R/timeline"))Project Timeline</a>
140 @ <ul>
141 @ <li>%z(href("%R/reports"))Activity Reports</a></li>
142
--- src/sitemap.c
+++ src/sitemap.c
@@ -132,10 +132,13 @@
132 @ <li>%z(href("%R/uvlist"))Unversioned Files</a>
133 if( g.perm.Write && zEditGlob[0]!=0 ){
134 @ <li>%z(href("%R/fileedit"))On-line File Editor</li>
135 }
136 @ </ul>
137 }
138 if( g.perm.Zip && db_get_boolean("suggested-tarlist",1)!=0 ){
139 @ <li>%z(href("%R/tarlist"))Tarballs and ZIPs</a>
140 }
141 if( g.perm.Read ){
142 @ <li>%z(href("%R/timeline"))Project Timeline</a>
143 @ <ul>
144 @ <li>%z(href("%R/reports"))Activity Reports</a></li>
145
+139
--- src/tar.c
+++ src/tar.c
@@ -931,5 +931,144 @@
931931
g.zOpenRevision = 0;
932932
blob_reset(&cacheKey);
933933
cgi_set_content(&tarball);
934934
cgi_set_content_type("application/x-compressed");
935935
}
936
+
937
+/*
938
+** This routine is called for each check-in on the /tarlist page to
939
+** construct the "extra" information after the description.
940
+*/
941
+static void tarlist_extra(
942
+ Stmt *pQuery, /* Current row of the timeline query */
943
+ int tmFlags, /* Flags to www_print_timeline() */
944
+ const char *zThisUser, /* Suppress links to this user */
945
+ const char *zThisTag /* Suppress links to this tag */
946
+){
947
+ int rid = db_column_int(pQuery, 0);
948
+ const char *zUuid = db_column_text(pQuery, 1);
949
+ const char *zDate = db_column_text(pQuery, 2);
950
+ char *zBrName = branch_of_rid(rid);
951
+ static const char *zProject = 0;
952
+ int nProject;
953
+ char *zNm;
954
+
955
+ if( zProject==0 ) zProject = db_get("project-name","unnamed");
956
+ zNm = mprintf("%s-%sZ-%.8s", zProject, zDate, zUuid);
957
+ nProject = (int)strlen(zProject);
958
+ zNm[nProject+11] = 'T';
959
+ @ <strong>%h(zBrName)</strong><br>\
960
+ @ %z(href("%R/timeline?c=%!S&y=ci&n=11",zUuid))<button>Context</button></a>
961
+ @ %z(href("%R/tarball/%!S/%t.tar.gz",zUuid,zNm))\
962
+ @ <button>Tarball</button></a>
963
+ @ %z(href("%R/zip/%!S/%t.zip",zUuid,zNm))\
964
+ @ <button>ZIP&nbsp;Archive</button></a>
965
+ fossil_free(zBrName);
966
+ fossil_free(zNm);
967
+}
968
+
969
+/*
970
+** SETTING: suggested-tarlist width=70 block-text
971
+**
972
+** This setting controls the suggested tarball/ZIP downloads on the
973
+** [[/tarlist]] page. The value is a TCL list. Each pair of items
974
+** defines a set of check-ins to be added to the suggestion list.
975
+** The first item of each pair is an integer count (N) and second
976
+** item is a tag GLOB pattern (PATTERN). For each pair, the most
977
+** recent N check-ins that have a tag matching PATTERN are added
978
+** to the list. The special pattern "OPEN-LEAF" matches any open
979
+** leaf check-in.
980
+**
981
+** Example:
982
+**
983
+** 3 OPEN-LEAF 3 release 1 trunk
984
+**
985
+** The value causes the /tarlist page to show the union of the 3
986
+** most recent open leaves, the three most recent check-ins marked
987
+** "release", and the single most recent trunk check-in.
988
+*/
989
+
990
+/*
991
+** WEBPAGE: /tarlist
992
+**
993
+** Show a special no-graph timeline of recent important check-ins with
994
+** an opportunity to pull tarballs and ZIPs.
995
+*/
996
+void tarlist_page(void){
997
+ Stmt q; /* The actual timeline query */
998
+ const char *zTarlistCfg; /* Configuration string */
999
+ char **azItem; /* Decomposed elements of zTarlistCfg */
1000
+ int *anItem; /* Bytes in each term of azItem[] */
1001
+ int nItem; /* Number of terms in azItem[] */
1002
+ int i; /* Loop counter */
1003
+ int tmFlags; /* Timeline display flags */
1004
+ int n; /* Number of suggested downloads */
1005
+
1006
+ login_check_credentials();
1007
+ if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1008
+
1009
+ style_set_current_feature("timeline");
1010
+ style_header("Suggested Tarballs And ZIP Archives");
1011
+
1012
+ zTarlistCfg = db_get("suggested-tarlist","5 OPEN-LEAF");
1013
+ db_multi_exec(
1014
+ "CREATE TEMP TABLE tarlist(rid INTEGER PRIMARY KEY);"
1015
+ );
1016
+ if( !g.interp ) Th_FossilInit(0);
1017
+ Th_SplitList(g.interp, zTarlistCfg, (int)strlen(zTarlistCfg),
1018
+ &azItem, &anItem, &nItem);
1019
+ for(i=0; i<nItem-1; i+=2){
1020
+ int cnt;
1021
+ char *zLabel;
1022
+ if( anItem[i]==1 && azItem[i][0]=='*' ){
1023
+ cnt = -1;
1024
+ }else if( anItem[i]<1 ){
1025
+ cnt = 0;
1026
+ }else{
1027
+ cnt = atoi(azItem[i]);
1028
+ }
1029
+ if( cnt==0 ) continue;
1030
+ zLabel = fossil_strndup(azItem[i+1],anItem[i+1]);
1031
+ if( fossil_strcmp("OPEN-LEAF",zLabel)==0 ){
1032
+ db_multi_exec(
1033
+ "INSERT OR IGNORE INTO tarlist(rid)"
1034
+ " SELECT leaf.rid FROM leaf, event"
1035
+ " WHERE event.objid=leaf.rid"
1036
+ " AND NOT EXISTS(SELECT 1 FROM tagxref"
1037
+ " WHERE tagxref.rid=leaf.rid"
1038
+ " AND tagid=%d AND tagtype>0)"
1039
+ " ORDER BY event.mtime DESC LIMIT %d", TAG_CLOSED, cnt
1040
+ );
1041
+ }else{
1042
+ db_multi_exec(
1043
+ "WITH taglist(tid) AS"
1044
+ " (SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q')"
1045
+ "INSERT OR IGNORE INTO tarlist(rid)"
1046
+ " SELECT event.objid FROM event CROSS JOIN tagxref"
1047
+ " WHERE event.type='ci'"
1048
+ " AND tagxref.tagid IN taglist"
1049
+ " AND tagtype>0"
1050
+ " AND tagxref.rid=event.objid"
1051
+ " ORDER BY event.mtime DESC LIMIT %d", zLabel, cnt
1052
+ );
1053
+ }
1054
+ fossil_free(zLabel);
1055
+ }
1056
+ Th_Free(g.interp, azItem);
1057
+
1058
+ n = db_int(0, "SELECT count(*) FROM tarlist");
1059
+ if( n==0 ){
1060
+ @ <h2>No tarball/ZIP suggestions are available at this time</h2>
1061
+ }else{
1062
+ @ <h2>%d(n) Tarball/ZIP Download Suggestions:</h2>
1063
+ db_prepare(&q,
1064
+ "%s AND blob.rid IN tarlist ORDER BY event.mtime DESC",
1065
+ timeline_query_for_www()
1066
+ );
1067
+
1068
+ tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL | TIMELINE_COLUMNAR
1069
+ | TIMELINE_BRCOLOR;
1070
+ www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, tarlist_extra);
1071
+ db_finalize(&q);
1072
+ }
1073
+ style_finish_page();
1074
+}
9361075
--- src/tar.c
+++ src/tar.c
@@ -931,5 +931,144 @@
931 g.zOpenRevision = 0;
932 blob_reset(&cacheKey);
933 cgi_set_content(&tarball);
934 cgi_set_content_type("application/x-compressed");
935 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
936
--- src/tar.c
+++ src/tar.c
@@ -931,5 +931,144 @@
931 g.zOpenRevision = 0;
932 blob_reset(&cacheKey);
933 cgi_set_content(&tarball);
934 cgi_set_content_type("application/x-compressed");
935 }
936
937 /*
938 ** This routine is called for each check-in on the /tarlist page to
939 ** construct the "extra" information after the description.
940 */
941 static void tarlist_extra(
942 Stmt *pQuery, /* Current row of the timeline query */
943 int tmFlags, /* Flags to www_print_timeline() */
944 const char *zThisUser, /* Suppress links to this user */
945 const char *zThisTag /* Suppress links to this tag */
946 ){
947 int rid = db_column_int(pQuery, 0);
948 const char *zUuid = db_column_text(pQuery, 1);
949 const char *zDate = db_column_text(pQuery, 2);
950 char *zBrName = branch_of_rid(rid);
951 static const char *zProject = 0;
952 int nProject;
953 char *zNm;
954
955 if( zProject==0 ) zProject = db_get("project-name","unnamed");
956 zNm = mprintf("%s-%sZ-%.8s", zProject, zDate, zUuid);
957 nProject = (int)strlen(zProject);
958 zNm[nProject+11] = 'T';
959 @ <strong>%h(zBrName)</strong><br>\
960 @ %z(href("%R/timeline?c=%!S&y=ci&n=11",zUuid))<button>Context</button></a>
961 @ %z(href("%R/tarball/%!S/%t.tar.gz",zUuid,zNm))\
962 @ <button>Tarball</button></a>
963 @ %z(href("%R/zip/%!S/%t.zip",zUuid,zNm))\
964 @ <button>ZIP&nbsp;Archive</button></a>
965 fossil_free(zBrName);
966 fossil_free(zNm);
967 }
968
969 /*
970 ** SETTING: suggested-tarlist width=70 block-text
971 **
972 ** This setting controls the suggested tarball/ZIP downloads on the
973 ** [[/tarlist]] page. The value is a TCL list. Each pair of items
974 ** defines a set of check-ins to be added to the suggestion list.
975 ** The first item of each pair is an integer count (N) and second
976 ** item is a tag GLOB pattern (PATTERN). For each pair, the most
977 ** recent N check-ins that have a tag matching PATTERN are added
978 ** to the list. The special pattern "OPEN-LEAF" matches any open
979 ** leaf check-in.
980 **
981 ** Example:
982 **
983 ** 3 OPEN-LEAF 3 release 1 trunk
984 **
985 ** The value causes the /tarlist page to show the union of the 3
986 ** most recent open leaves, the three most recent check-ins marked
987 ** "release", and the single most recent trunk check-in.
988 */
989
990 /*
991 ** WEBPAGE: /tarlist
992 **
993 ** Show a special no-graph timeline of recent important check-ins with
994 ** an opportunity to pull tarballs and ZIPs.
995 */
996 void tarlist_page(void){
997 Stmt q; /* The actual timeline query */
998 const char *zTarlistCfg; /* Configuration string */
999 char **azItem; /* Decomposed elements of zTarlistCfg */
1000 int *anItem; /* Bytes in each term of azItem[] */
1001 int nItem; /* Number of terms in azItem[] */
1002 int i; /* Loop counter */
1003 int tmFlags; /* Timeline display flags */
1004 int n; /* Number of suggested downloads */
1005
1006 login_check_credentials();
1007 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
1008
1009 style_set_current_feature("timeline");
1010 style_header("Suggested Tarballs And ZIP Archives");
1011
1012 zTarlistCfg = db_get("suggested-tarlist","5 OPEN-LEAF");
1013 db_multi_exec(
1014 "CREATE TEMP TABLE tarlist(rid INTEGER PRIMARY KEY);"
1015 );
1016 if( !g.interp ) Th_FossilInit(0);
1017 Th_SplitList(g.interp, zTarlistCfg, (int)strlen(zTarlistCfg),
1018 &azItem, &anItem, &nItem);
1019 for(i=0; i<nItem-1; i+=2){
1020 int cnt;
1021 char *zLabel;
1022 if( anItem[i]==1 && azItem[i][0]=='*' ){
1023 cnt = -1;
1024 }else if( anItem[i]<1 ){
1025 cnt = 0;
1026 }else{
1027 cnt = atoi(azItem[i]);
1028 }
1029 if( cnt==0 ) continue;
1030 zLabel = fossil_strndup(azItem[i+1],anItem[i+1]);
1031 if( fossil_strcmp("OPEN-LEAF",zLabel)==0 ){
1032 db_multi_exec(
1033 "INSERT OR IGNORE INTO tarlist(rid)"
1034 " SELECT leaf.rid FROM leaf, event"
1035 " WHERE event.objid=leaf.rid"
1036 " AND NOT EXISTS(SELECT 1 FROM tagxref"
1037 " WHERE tagxref.rid=leaf.rid"
1038 " AND tagid=%d AND tagtype>0)"
1039 " ORDER BY event.mtime DESC LIMIT %d", TAG_CLOSED, cnt
1040 );
1041 }else{
1042 db_multi_exec(
1043 "WITH taglist(tid) AS"
1044 " (SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q')"
1045 "INSERT OR IGNORE INTO tarlist(rid)"
1046 " SELECT event.objid FROM event CROSS JOIN tagxref"
1047 " WHERE event.type='ci'"
1048 " AND tagxref.tagid IN taglist"
1049 " AND tagtype>0"
1050 " AND tagxref.rid=event.objid"
1051 " ORDER BY event.mtime DESC LIMIT %d", zLabel, cnt
1052 );
1053 }
1054 fossil_free(zLabel);
1055 }
1056 Th_Free(g.interp, azItem);
1057
1058 n = db_int(0, "SELECT count(*) FROM tarlist");
1059 if( n==0 ){
1060 @ <h2>No tarball/ZIP suggestions are available at this time</h2>
1061 }else{
1062 @ <h2>%d(n) Tarball/ZIP Download Suggestions:</h2>
1063 db_prepare(&q,
1064 "%s AND blob.rid IN tarlist ORDER BY event.mtime DESC",
1065 timeline_query_for_www()
1066 );
1067
1068 tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL | TIMELINE_COLUMNAR
1069 | TIMELINE_BRCOLOR;
1070 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, tarlist_extra);
1071 db_finalize(&q);
1072 }
1073 style_finish_page();
1074 }
1075

Keyboard Shortcuts

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