Fossil SCM

Enhancements to the infrastructure that generates timeline displays.

drh 2025-10-16 15:03 trunk
Commit 37955439114af325576613512d285ef0d163ff57cad406d1f81cf387db13a608
2 files changed +12 -2 +140 -107
+12 -2
--- src/branch.c
+++ src/branch.c
@@ -1019,11 +1019,17 @@
10191019
/*
10201020
** This routine is called while for each check-in that is rendered by
10211021
** the timeline of a "brlist" page. Add some additional hyperlinks
10221022
** to the end of the line.
10231023
*/
1024
-static void brtimeline_extra(int rid){
1024
+static void brtimeline_extra(
1025
+ Stmt *pQuery, /* Current row of the timeline query */
1026
+ int tmFlags, /* Flags to www_print_timeline() */
1027
+ const char *zThisUser, /* Suppress links to this user */
1028
+ const char *zThisTag /* Suppress links to this tag */
1029
+){
1030
+ int rid = db_column_int(pQuery, 0);
10251031
Stmt q;
10261032
if( !g.perm.Hyperlink ) return;
10271033
db_prepare(&q,
10281034
"SELECT substr(tagname,5) FROM tagxref, tag"
10291035
" WHERE tagxref.rid=%d"
@@ -1032,11 +1038,11 @@
10321038
" AND tag.tagname GLOB 'sym-*'",
10331039
rid
10341040
);
10351041
while( db_step(&q)==SQLITE_ROW ){
10361042
const char *zTagName = db_column_text(&q, 0);
1037
- @ %z(href("%R/timeline?r=%T",zTagName))[timeline]</a>
1043
+ @ %z(href("%R/timeline?r=%T",zTagName))<button>timeline</button></a>
10381044
}
10391045
db_finalize(&q);
10401046
}
10411047
10421048
/*
@@ -1083,13 +1089,17 @@
10831089
db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
10841090
blob_reset(&sql);
10851091
/* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
10861092
** many descenders to (off-screen) parents. */
10871093
tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
1094
+#if 1
10881095
if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
10891096
if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
10901097
if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
1098
+#else
1099
+ tmFlags |= TIMELINE_BRCOLOR | TIMELINE_GRAPH;
1100
+#endif
10911101
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
10921102
db_finalize(&q);
10931103
style_finish_page();
10941104
}
10951105
10961106
--- src/branch.c
+++ src/branch.c
@@ -1019,11 +1019,17 @@
1019 /*
1020 ** This routine is called while for each check-in that is rendered by
1021 ** the timeline of a "brlist" page. Add some additional hyperlinks
1022 ** to the end of the line.
1023 */
1024 static void brtimeline_extra(int rid){
 
 
 
 
 
 
1025 Stmt q;
1026 if( !g.perm.Hyperlink ) return;
1027 db_prepare(&q,
1028 "SELECT substr(tagname,5) FROM tagxref, tag"
1029 " WHERE tagxref.rid=%d"
@@ -1032,11 +1038,11 @@
1032 " AND tag.tagname GLOB 'sym-*'",
1033 rid
1034 );
1035 while( db_step(&q)==SQLITE_ROW ){
1036 const char *zTagName = db_column_text(&q, 0);
1037 @ %z(href("%R/timeline?r=%T",zTagName))[timeline]</a>
1038 }
1039 db_finalize(&q);
1040 }
1041
1042 /*
@@ -1083,13 +1089,17 @@
1083 db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
1084 blob_reset(&sql);
1085 /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
1086 ** many descenders to (off-screen) parents. */
1087 tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
 
1088 if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
1089 if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
1090 if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
 
 
 
1091 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
1092 db_finalize(&q);
1093 style_finish_page();
1094 }
1095
1096
--- src/branch.c
+++ src/branch.c
@@ -1019,11 +1019,17 @@
1019 /*
1020 ** This routine is called while for each check-in that is rendered by
1021 ** the timeline of a "brlist" page. Add some additional hyperlinks
1022 ** to the end of the line.
1023 */
1024 static void brtimeline_extra(
1025 Stmt *pQuery, /* Current row of the timeline query */
1026 int tmFlags, /* Flags to www_print_timeline() */
1027 const char *zThisUser, /* Suppress links to this user */
1028 const char *zThisTag /* Suppress links to this tag */
1029 ){
1030 int rid = db_column_int(pQuery, 0);
1031 Stmt q;
1032 if( !g.perm.Hyperlink ) return;
1033 db_prepare(&q,
1034 "SELECT substr(tagname,5) FROM tagxref, tag"
1035 " WHERE tagxref.rid=%d"
@@ -1032,11 +1038,11 @@
1038 " AND tag.tagname GLOB 'sym-*'",
1039 rid
1040 );
1041 while( db_step(&q)==SQLITE_ROW ){
1042 const char *zTagName = db_column_text(&q, 0);
1043 @ %z(href("%R/timeline?r=%T",zTagName))<button>timeline</button></a>
1044 }
1045 db_finalize(&q);
1046 }
1047
1048 /*
@@ -1083,13 +1089,17 @@
1089 db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
1090 blob_reset(&sql);
1091 /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
1092 ** many descenders to (off-screen) parents. */
1093 tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
1094 #if 1
1095 if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
1096 if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
1097 if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
1098 #else
1099 tmFlags |= TIMELINE_BRCOLOR | TIMELINE_GRAPH;
1100 #endif
1101 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
1102 db_finalize(&q);
1103 style_finish_page();
1104 }
1105
1106
+140 -107
--- src/timeline.c
+++ src/timeline.c
@@ -169,10 +169,145 @@
169169
sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT);
170170
manifest_destroy(pPost);
171171
}
172172
}
173173
174
+/*
175
+** This routine generates the default "extra" text after the description
176
+** in a timeline.
177
+**
178
+** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
179
+**
180
+** This routine is used if no xExtra argument is supplied to
181
+** www_print_timeline().
182
+*/
183
+static void defaultExtra(
184
+ Stmt *pQuery, /* Current row of the timeline query */
185
+ int tmFlags, /* Flags to www_print_timeline() */
186
+ const char *zThisUser, /* Suppress links to this user */
187
+ const char *zThisTag /* Suppress links to this tag */
188
+){
189
+ int rid = db_column_int(pQuery, 0);
190
+ const char *zUuid = db_column_text(pQuery, 1);
191
+ int isLeaf = db_column_int(pQuery, 5);
192
+ const char *zDate = db_column_text(pQuery, 2);
193
+ const char *zType = db_column_text(pQuery, 7);
194
+ const char *zUser = db_column_text(pQuery, 4);
195
+ const char *zTagList = db_column_text(pQuery, 8);
196
+ int tagid = db_column_int(pQuery, 9);
197
+ const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
198
+
199
+ if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
200
+ cgi_printf("(");
201
+ }
202
+
203
+ if( (tmFlags & TIMELINE_CLASSIC)==0 ){
204
+ if( zType[0]=='c' ){
205
+ if( isLeaf ){
206
+ if( has_closed_tag(rid) ){
207
+ @ <span class='timelineLeaf'>Closed-Leaf</span>
208
+ }else{
209
+ @ <span class='timelineLeaf'>Leaf</span>
210
+ }
211
+ }
212
+ cgi_printf("check-in:&nbsp;%z%S</a> ",
213
+ href("%R/info/%!S",zUuid),zUuid);
214
+ }else if( zType[0]=='e' && tagid ){
215
+ cgi_printf("technote:&nbsp;");
216
+ hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
217
+ }else{
218
+ cgi_printf("artifact:&nbsp;%z%S</a> ",
219
+ href("%R/info/%!S",zUuid),zUuid);
220
+ }
221
+ }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
222
+ || zType[0]=='n' || zType[0]=='f'){
223
+ cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
224
+ }
225
+
226
+ if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
227
+ char *zLink;
228
+ if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
229
+ zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
230
+ }else{
231
+ zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
232
+ }
233
+ cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
234
+ }else{
235
+ cgi_printf("user:&nbsp;%h", zDispUser);
236
+ }
237
+
238
+ /* Generate the "tags: TAGLIST" at the end of the comment, together
239
+ ** with hyperlinks to the tag list.
240
+ */
241
+ if( zTagList && zTagList[0]==0 ) zTagList = 0;
242
+ if( zTagList ){
243
+ if( g.perm.Hyperlink ){
244
+ int i;
245
+ const char *z = zTagList;
246
+ Blob links;
247
+ blob_zero(&links);
248
+ while( z && z[0] ){
249
+ for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
250
+ if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
251
+ blob_appendf(&links,
252
+ "%z%#h</a>%.2s",
253
+ href("%R/timeline?r=%#t&c=%t",i,z,zDate), i,z, &z[i]
254
+ );
255
+ }else{
256
+ blob_appendf(&links, "%#h", i+2, z);
257
+ }
258
+ if( z[i]==0 ) break;
259
+ z += i+2;
260
+ }
261
+ cgi_printf(" tags:&nbsp;%s", blob_str(&links));
262
+ blob_reset(&links);
263
+ }else{
264
+ cgi_printf(" tags:&nbsp;%h", zTagList);
265
+ }
266
+ }
267
+
268
+ if( tmFlags & TIMELINE_SHOWRID ){
269
+ int srcId = delta_source_rid(rid);
270
+ if( srcId ){
271
+ cgi_printf(" id:&nbsp;%z%d&larr;%d</a>",
272
+ href("%R/deltachain/%d",rid), rid, srcId);
273
+ }else{
274
+ cgi_printf(" id:&nbsp;%z%d</a>",
275
+ href("%R/deltachain/%d",rid), rid);
276
+ }
277
+ }
278
+ tag_private_status(rid);
279
+ /* End timelineDetail */
280
+ if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
281
+ cgi_printf(")");
282
+ }
283
+ if( tmFlags & TIMELINE_COMPACT ){
284
+ @ </span></span>
285
+ }else{
286
+ @ </span>
287
+ }
288
+}
289
+
290
+
291
+/*
292
+** SETTING: timeline-truncate-at-blank boolean default=off
293
+**
294
+** If enabled, check-in comments displayed on the timeline are truncated
295
+** at the first blank line of the comment text. The comment text after
296
+** the first blank line is only seen in the /info or similar pages that
297
+** show details about the check-in.
298
+*/
299
+/*
300
+** SETTING: timeline-tslink-info boolean default=off
301
+**
302
+** The hyperlink on the timestamp associated with each timeline entry,
303
+** on the far left-hand side of the screen, normally targets another
304
+** /timeline page that shows the entry in context. However, if this
305
+** option is turned on, that hyperlink targets the /info page showing
306
+** the details of the entry.
307
+*/
308
+
174309
175310
/*
176311
** Output a timeline in the web format given a query. The query
177312
** should return these columns:
178313
**
@@ -194,11 +329,11 @@
194329
const char *zThisUser, /* Suppress links to this user */
195330
const char *zThisTag, /* Suppress links to this tag */
196331
Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
197332
int selectedRid, /* Highlight the line with this RID value or zero */
198333
int secondRid, /* Secondary highlight (or zero) */
199
- void (*xExtra)(int) /* Routine to call on each line of display */
334
+ void (*xExtra)(Stmt*,int,const char*,const char*) /* generate "extra" text */
200335
){
201336
int mxWikiLen;
202337
Blob comment;
203338
int prevTagid = 0;
204339
int suppressCnt = 0;
@@ -218,31 +353,15 @@
218353
** page rather than the /timeline page */
219354
220355
if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
221356
vid = db_lget_int("checkout", 0);
222357
}
358
+ if( xExtra==0 ) xExtra = defaultExtra;
223359
zPrevDate[0] = 0;
224360
mxWikiLen = db_get_int("timeline-max-comment", 0);
225361
dateFormat = db_get_int("timeline-date-format", 0);
226
-/*
227
-** SETTING: timeline-truncate-at-blank boolean default=off
228
-**
229
-** If enabled, check-in comments displayed on the timeline are truncated
230
-** at the first blank line of the comment text. The comment text after
231
-** the first blank line is only seen in the /info or similar pages that
232
-** show details about the check-in.
233
-*/
234362
bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
235
-/*
236
-** SETTING: timeline-tslink-info boolean default=off
237
-**
238
-** The hyperlink on the timestamp associated with each timeline entry,
239
-** on the far left-hand side of the screen, normally targets another
240
-** /timeline page that shows the entry in context. However, if this
241
-** option is turned on, that hyperlink targets the /info page showing
242
-** the details of the entry.
243
-*/
244363
bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
245364
if( (tmFlags & TIMELINE_VIEWS)==0 ){
246365
tmFlags |= timeline_ss_cookie();
247366
}
248367
if( tmFlags & TIMELINE_COLUMNAR ){
@@ -275,13 +394,11 @@
275394
int isLeaf = db_column_int(pQuery, 5);
276395
const char *zBgClr = db_column_text(pQuery, 6);
277396
const char *zDate = db_column_text(pQuery, 2);
278397
const char *zType = db_column_text(pQuery, 7);
279398
const char *zUser = db_column_text(pQuery, 4);
280
- const char *zTagList = db_column_text(pQuery, 8);
281399
int tagid = db_column_int(pQuery, 9);
282
- const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
283400
char *zBr = 0; /* Branch */
284401
int commentColumn = 3; /* Column containing comment text */
285402
int modPending; /* Pending moderation */
286403
char *zDateLink; /* URL for the link on the timestamp */
287404
int drawDetailEllipsis; /* True to show ellipsis in place of detail */
@@ -611,11 +728,11 @@
611728
}
612729
613730
@ </span>
614731
blob_reset(&comment);
615732
616
- /* Generate extra information and hyperlinks to follow the comment.
733
+ /* Generate extra information and hyperlinks that follow the comment.
617734
** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
618735
*/
619736
if( drawDetailEllipsis ){
620737
@ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
621738
@ data-id='%d(rid)'>...</span>
@@ -629,101 +746,17 @@
629746
}
630747
if( tmFlags & TIMELINE_COMPACT ){
631748
cgi_printf("<span class='clutter' id='detail-%d'>",rid);
632749
}
633750
cgi_printf("<span class='timeline%sDetail'>", zStyle);
634
- if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
635
- cgi_printf("(");
636
- }
637
-
638
- if( (tmFlags & TIMELINE_CLASSIC)==0 ){
639
- if( zType[0]=='c' ){
640
- if( isLeaf ){
641
- if( has_closed_tag(rid) ){
642
- @ <span class='timelineLeaf'>Closed-Leaf</span>
643
- }else{
644
- @ <span class='timelineLeaf'>Leaf</span>
645
- }
646
- }
647
- cgi_printf("check-in:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
648
- }else if( zType[0]=='e' && tagid ){
649
- cgi_printf("technote:&nbsp;");
650
- hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
651
- }else{
652
- cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
653
- }
654
- }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
655
- || zType[0]=='n' || zType[0]=='f'){
656
- cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
657
- }
658
-
659
- if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
660
- char *zLink;
661
- if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
662
- zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
663
- }else{
664
- zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
665
- }
666
- cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
667
- }else{
668
- cgi_printf("user:&nbsp;%h", zDispUser);
669
- }
670
-
671
- /* Generate the "tags: TAGLIST" at the end of the comment, together
672
- ** with hyperlinks to the tag list.
673
- */
674
- if( zTagList && zTagList[0]==0 ) zTagList = 0;
675
- if( zTagList ){
676
- if( g.perm.Hyperlink ){
677
- int i;
678
- const char *z = zTagList;
679
- Blob links;
680
- blob_zero(&links);
681
- while( z && z[0] ){
682
- for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
683
- if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
684
- blob_appendf(&links,
685
- "%z%#h</a>%.2s",
686
- href("%R/timeline?r=%#t&c=%t",i,z,zDate), i,z, &z[i]
687
- );
688
- }else{
689
- blob_appendf(&links, "%#h", i+2, z);
690
- }
691
- if( z[i]==0 ) break;
692
- z += i+2;
693
- }
694
- cgi_printf(" tags:&nbsp;%s", blob_str(&links));
695
- blob_reset(&links);
696
- }else{
697
- cgi_printf(" tags:&nbsp;%h", zTagList);
698
- }
699
- }
700
-
701
- if( tmFlags & TIMELINE_SHOWRID ){
702
- int srcId = delta_source_rid(rid);
703
- if( srcId ){
704
- cgi_printf(" id:&nbsp;%z%d&larr;%d</a>",
705
- href("%R/deltachain/%d",rid), rid, srcId);
706
- }else{
707
- cgi_printf(" id:&nbsp;%z%d</a>",
708
- href("%R/deltachain/%d",rid), rid);
709
- }
710
- }
711
- tag_private_status(rid);
712
- if( xExtra ){
713
- xExtra(rid);
714
- }
715
- /* End timelineDetail */
716
- if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
717
- cgi_printf(")");
718
- }
751
+ xExtra(pQuery, tmFlags, zThisUser, zThisTag);
719752
if( tmFlags & TIMELINE_COMPACT ){
720753
@ </span></span>
721754
}else{
722755
@ </span>
723756
}
724
-
757
+
725758
/* Generate the file-change list if requested */
726759
if( (tmFlags & (TIMELINE_FCHANGES|TIMELINE_FRENAMES))!=0
727760
&& zType[0]=='c' && g.perm.Hyperlink
728761
){
729762
int inUl = 0;
730763
--- src/timeline.c
+++ src/timeline.c
@@ -169,10 +169,145 @@
169 sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT);
170 manifest_destroy(pPost);
171 }
172 }
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
175 /*
176 ** Output a timeline in the web format given a query. The query
177 ** should return these columns:
178 **
@@ -194,11 +329,11 @@
194 const char *zThisUser, /* Suppress links to this user */
195 const char *zThisTag, /* Suppress links to this tag */
196 Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
197 int selectedRid, /* Highlight the line with this RID value or zero */
198 int secondRid, /* Secondary highlight (or zero) */
199 void (*xExtra)(int) /* Routine to call on each line of display */
200 ){
201 int mxWikiLen;
202 Blob comment;
203 int prevTagid = 0;
204 int suppressCnt = 0;
@@ -218,31 +353,15 @@
218 ** page rather than the /timeline page */
219
220 if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
221 vid = db_lget_int("checkout", 0);
222 }
 
223 zPrevDate[0] = 0;
224 mxWikiLen = db_get_int("timeline-max-comment", 0);
225 dateFormat = db_get_int("timeline-date-format", 0);
226 /*
227 ** SETTING: timeline-truncate-at-blank boolean default=off
228 **
229 ** If enabled, check-in comments displayed on the timeline are truncated
230 ** at the first blank line of the comment text. The comment text after
231 ** the first blank line is only seen in the /info or similar pages that
232 ** show details about the check-in.
233 */
234 bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
235 /*
236 ** SETTING: timeline-tslink-info boolean default=off
237 **
238 ** The hyperlink on the timestamp associated with each timeline entry,
239 ** on the far left-hand side of the screen, normally targets another
240 ** /timeline page that shows the entry in context. However, if this
241 ** option is turned on, that hyperlink targets the /info page showing
242 ** the details of the entry.
243 */
244 bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
245 if( (tmFlags & TIMELINE_VIEWS)==0 ){
246 tmFlags |= timeline_ss_cookie();
247 }
248 if( tmFlags & TIMELINE_COLUMNAR ){
@@ -275,13 +394,11 @@
275 int isLeaf = db_column_int(pQuery, 5);
276 const char *zBgClr = db_column_text(pQuery, 6);
277 const char *zDate = db_column_text(pQuery, 2);
278 const char *zType = db_column_text(pQuery, 7);
279 const char *zUser = db_column_text(pQuery, 4);
280 const char *zTagList = db_column_text(pQuery, 8);
281 int tagid = db_column_int(pQuery, 9);
282 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
283 char *zBr = 0; /* Branch */
284 int commentColumn = 3; /* Column containing comment text */
285 int modPending; /* Pending moderation */
286 char *zDateLink; /* URL for the link on the timestamp */
287 int drawDetailEllipsis; /* True to show ellipsis in place of detail */
@@ -611,11 +728,11 @@
611 }
612
613 @ </span>
614 blob_reset(&comment);
615
616 /* Generate extra information and hyperlinks to follow the comment.
617 ** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
618 */
619 if( drawDetailEllipsis ){
620 @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
621 @ data-id='%d(rid)'>...</span>
@@ -629,101 +746,17 @@
629 }
630 if( tmFlags & TIMELINE_COMPACT ){
631 cgi_printf("<span class='clutter' id='detail-%d'>",rid);
632 }
633 cgi_printf("<span class='timeline%sDetail'>", zStyle);
634 if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
635 cgi_printf("(");
636 }
637
638 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
639 if( zType[0]=='c' ){
640 if( isLeaf ){
641 if( has_closed_tag(rid) ){
642 @ <span class='timelineLeaf'>Closed-Leaf</span>
643 }else{
644 @ <span class='timelineLeaf'>Leaf</span>
645 }
646 }
647 cgi_printf("check-in:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
648 }else if( zType[0]=='e' && tagid ){
649 cgi_printf("technote:&nbsp;");
650 hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
651 }else{
652 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
653 }
654 }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
655 || zType[0]=='n' || zType[0]=='f'){
656 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
657 }
658
659 if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
660 char *zLink;
661 if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
662 zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
663 }else{
664 zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
665 }
666 cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
667 }else{
668 cgi_printf("user:&nbsp;%h", zDispUser);
669 }
670
671 /* Generate the "tags: TAGLIST" at the end of the comment, together
672 ** with hyperlinks to the tag list.
673 */
674 if( zTagList && zTagList[0]==0 ) zTagList = 0;
675 if( zTagList ){
676 if( g.perm.Hyperlink ){
677 int i;
678 const char *z = zTagList;
679 Blob links;
680 blob_zero(&links);
681 while( z && z[0] ){
682 for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
683 if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
684 blob_appendf(&links,
685 "%z%#h</a>%.2s",
686 href("%R/timeline?r=%#t&c=%t",i,z,zDate), i,z, &z[i]
687 );
688 }else{
689 blob_appendf(&links, "%#h", i+2, z);
690 }
691 if( z[i]==0 ) break;
692 z += i+2;
693 }
694 cgi_printf(" tags:&nbsp;%s", blob_str(&links));
695 blob_reset(&links);
696 }else{
697 cgi_printf(" tags:&nbsp;%h", zTagList);
698 }
699 }
700
701 if( tmFlags & TIMELINE_SHOWRID ){
702 int srcId = delta_source_rid(rid);
703 if( srcId ){
704 cgi_printf(" id:&nbsp;%z%d&larr;%d</a>",
705 href("%R/deltachain/%d",rid), rid, srcId);
706 }else{
707 cgi_printf(" id:&nbsp;%z%d</a>",
708 href("%R/deltachain/%d",rid), rid);
709 }
710 }
711 tag_private_status(rid);
712 if( xExtra ){
713 xExtra(rid);
714 }
715 /* End timelineDetail */
716 if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
717 cgi_printf(")");
718 }
719 if( tmFlags & TIMELINE_COMPACT ){
720 @ </span></span>
721 }else{
722 @ </span>
723 }
724
725 /* Generate the file-change list if requested */
726 if( (tmFlags & (TIMELINE_FCHANGES|TIMELINE_FRENAMES))!=0
727 && zType[0]=='c' && g.perm.Hyperlink
728 ){
729 int inUl = 0;
730
--- src/timeline.c
+++ src/timeline.c
@@ -169,10 +169,145 @@
169 sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT);
170 manifest_destroy(pPost);
171 }
172 }
173
174 /*
175 ** This routine generates the default "extra" text after the description
176 ** in a timeline.
177 **
178 ** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
179 **
180 ** This routine is used if no xExtra argument is supplied to
181 ** www_print_timeline().
182 */
183 static void defaultExtra(
184 Stmt *pQuery, /* Current row of the timeline query */
185 int tmFlags, /* Flags to www_print_timeline() */
186 const char *zThisUser, /* Suppress links to this user */
187 const char *zThisTag /* Suppress links to this tag */
188 ){
189 int rid = db_column_int(pQuery, 0);
190 const char *zUuid = db_column_text(pQuery, 1);
191 int isLeaf = db_column_int(pQuery, 5);
192 const char *zDate = db_column_text(pQuery, 2);
193 const char *zType = db_column_text(pQuery, 7);
194 const char *zUser = db_column_text(pQuery, 4);
195 const char *zTagList = db_column_text(pQuery, 8);
196 int tagid = db_column_int(pQuery, 9);
197 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
198
199 if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
200 cgi_printf("(");
201 }
202
203 if( (tmFlags & TIMELINE_CLASSIC)==0 ){
204 if( zType[0]=='c' ){
205 if( isLeaf ){
206 if( has_closed_tag(rid) ){
207 @ <span class='timelineLeaf'>Closed-Leaf</span>
208 }else{
209 @ <span class='timelineLeaf'>Leaf</span>
210 }
211 }
212 cgi_printf("check-in:&nbsp;%z%S</a> ",
213 href("%R/info/%!S",zUuid),zUuid);
214 }else if( zType[0]=='e' && tagid ){
215 cgi_printf("technote:&nbsp;");
216 hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
217 }else{
218 cgi_printf("artifact:&nbsp;%z%S</a> ",
219 href("%R/info/%!S",zUuid),zUuid);
220 }
221 }else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
222 || zType[0]=='n' || zType[0]=='f'){
223 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
224 }
225
226 if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
227 char *zLink;
228 if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
229 zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
230 }else{
231 zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
232 }
233 cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
234 }else{
235 cgi_printf("user:&nbsp;%h", zDispUser);
236 }
237
238 /* Generate the "tags: TAGLIST" at the end of the comment, together
239 ** with hyperlinks to the tag list.
240 */
241 if( zTagList && zTagList[0]==0 ) zTagList = 0;
242 if( zTagList ){
243 if( g.perm.Hyperlink ){
244 int i;
245 const char *z = zTagList;
246 Blob links;
247 blob_zero(&links);
248 while( z && z[0] ){
249 for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
250 if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
251 blob_appendf(&links,
252 "%z%#h</a>%.2s",
253 href("%R/timeline?r=%#t&c=%t",i,z,zDate), i,z, &z[i]
254 );
255 }else{
256 blob_appendf(&links, "%#h", i+2, z);
257 }
258 if( z[i]==0 ) break;
259 z += i+2;
260 }
261 cgi_printf(" tags:&nbsp;%s", blob_str(&links));
262 blob_reset(&links);
263 }else{
264 cgi_printf(" tags:&nbsp;%h", zTagList);
265 }
266 }
267
268 if( tmFlags & TIMELINE_SHOWRID ){
269 int srcId = delta_source_rid(rid);
270 if( srcId ){
271 cgi_printf(" id:&nbsp;%z%d&larr;%d</a>",
272 href("%R/deltachain/%d",rid), rid, srcId);
273 }else{
274 cgi_printf(" id:&nbsp;%z%d</a>",
275 href("%R/deltachain/%d",rid), rid);
276 }
277 }
278 tag_private_status(rid);
279 /* End timelineDetail */
280 if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
281 cgi_printf(")");
282 }
283 if( tmFlags & TIMELINE_COMPACT ){
284 @ </span></span>
285 }else{
286 @ </span>
287 }
288 }
289
290
291 /*
292 ** SETTING: timeline-truncate-at-blank boolean default=off
293 **
294 ** If enabled, check-in comments displayed on the timeline are truncated
295 ** at the first blank line of the comment text. The comment text after
296 ** the first blank line is only seen in the /info or similar pages that
297 ** show details about the check-in.
298 */
299 /*
300 ** SETTING: timeline-tslink-info boolean default=off
301 **
302 ** The hyperlink on the timestamp associated with each timeline entry,
303 ** on the far left-hand side of the screen, normally targets another
304 ** /timeline page that shows the entry in context. However, if this
305 ** option is turned on, that hyperlink targets the /info page showing
306 ** the details of the entry.
307 */
308
309
310 /*
311 ** Output a timeline in the web format given a query. The query
312 ** should return these columns:
313 **
@@ -194,11 +329,11 @@
329 const char *zThisUser, /* Suppress links to this user */
330 const char *zThisTag, /* Suppress links to this tag */
331 Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
332 int selectedRid, /* Highlight the line with this RID value or zero */
333 int secondRid, /* Secondary highlight (or zero) */
334 void (*xExtra)(Stmt*,int,const char*,const char*) /* generate "extra" text */
335 ){
336 int mxWikiLen;
337 Blob comment;
338 int prevTagid = 0;
339 int suppressCnt = 0;
@@ -218,31 +353,15 @@
353 ** page rather than the /timeline page */
354
355 if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
356 vid = db_lget_int("checkout", 0);
357 }
358 if( xExtra==0 ) xExtra = defaultExtra;
359 zPrevDate[0] = 0;
360 mxWikiLen = db_get_int("timeline-max-comment", 0);
361 dateFormat = db_get_int("timeline-date-format", 0);
 
 
 
 
 
 
 
 
362 bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
 
 
 
 
 
 
 
 
 
363 bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
364 if( (tmFlags & TIMELINE_VIEWS)==0 ){
365 tmFlags |= timeline_ss_cookie();
366 }
367 if( tmFlags & TIMELINE_COLUMNAR ){
@@ -275,13 +394,11 @@
394 int isLeaf = db_column_int(pQuery, 5);
395 const char *zBgClr = db_column_text(pQuery, 6);
396 const char *zDate = db_column_text(pQuery, 2);
397 const char *zType = db_column_text(pQuery, 7);
398 const char *zUser = db_column_text(pQuery, 4);
 
399 int tagid = db_column_int(pQuery, 9);
 
400 char *zBr = 0; /* Branch */
401 int commentColumn = 3; /* Column containing comment text */
402 int modPending; /* Pending moderation */
403 char *zDateLink; /* URL for the link on the timestamp */
404 int drawDetailEllipsis; /* True to show ellipsis in place of detail */
@@ -611,11 +728,11 @@
728 }
729
730 @ </span>
731 blob_reset(&comment);
732
733 /* Generate extra information and hyperlinks that follow the comment.
734 ** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
735 */
736 if( drawDetailEllipsis ){
737 @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
738 @ data-id='%d(rid)'>...</span>
@@ -629,101 +746,17 @@
746 }
747 if( tmFlags & TIMELINE_COMPACT ){
748 cgi_printf("<span class='clutter' id='detail-%d'>",rid);
749 }
750 cgi_printf("<span class='timeline%sDetail'>", zStyle);
751 xExtra(pQuery, tmFlags, zThisUser, zThisTag);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
752 if( tmFlags & TIMELINE_COMPACT ){
753 @ </span></span>
754 }else{
755 @ </span>
756 }
757
758 /* Generate the file-change list if requested */
759 if( (tmFlags & (TIMELINE_FCHANGES|TIMELINE_FRENAMES))!=0
760 && zType[0]=='c' && g.perm.Hyperlink
761 ){
762 int inUl = 0;
763

Keyboard Shortcuts

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