Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Enhancements to the infrastructure that generates timeline displays. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | timeline-enhance-2025 |
| Files: | files | file ages | folders |
| SHA3-256: |
37955439114af325576613512d285ef0 |
| User & Date: | drh 2025-10-16 15:03:05.940 |
Context
|
2025-10-16
| ||
| 15:10 | Timeline computes the main branch name once, instead of once per row. ... (check-in: fa3e6ed20a user: drh tags: timeline-enhance-2025) | |
| 15:03 | Enhancements to the infrastructure that generates timeline displays. ... (check-in: 3795543911 user: drh tags: timeline-enhance-2025) | |
| 07:24 | Spelling correction to error message for "fossil get" command. ... (check-in: 196cb77223 user: andybradford tags: trunk) | |
Changes
Changes to src/branch.c.
| ︙ | ︙ | |||
1017 1018 1019 1020 1021 1022 1023 | } /* ** This routine is called while for each check-in that is rendered by ** the timeline of a "brlist" page. Add some additional hyperlinks ** to the end of the line. */ | | > > > > > > | | 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 |
}
/*
** This routine is called while for each check-in that is rendered by
** the timeline of a "brlist" page. Add some additional hyperlinks
** to the end of the line.
*/
static void brtimeline_extra(
Stmt *pQuery, /* Current row of the timeline query */
int tmFlags, /* Flags to www_print_timeline() */
const char *zThisUser, /* Suppress links to this user */
const char *zThisTag /* Suppress links to this tag */
){
int rid = db_column_int(pQuery, 0);
Stmt q;
if( !g.perm.Hyperlink ) return;
db_prepare(&q,
"SELECT substr(tagname,5) FROM tagxref, tag"
" WHERE tagxref.rid=%d"
" AND tagxref.tagid=tag.tagid"
" AND tagxref.tagtype>0"
" AND tag.tagname GLOB 'sym-*'",
rid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagName = db_column_text(&q, 0);
@ %z(href("%R/timeline?r=%T",zTagName))<button>timeline</button></a>
}
db_finalize(&q);
}
/*
** WEBPAGE: brtimeline
**
|
| ︙ | ︙ | |||
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 |
zUnaryOp/*safe-for-%s*/, TAG_HIDDEN);
}
db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
blob_reset(&sql);
/* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
** many descenders to (off-screen) parents. */
tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
db_finalize(&q);
style_finish_page();
}
/*
** Generate a multichoice submenu for the few recent active branches. zName is
| > > > > | 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 |
zUnaryOp/*safe-for-%s*/, TAG_HIDDEN);
}
db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
blob_reset(&sql);
/* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
** many descenders to (off-screen) parents. */
tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
#if 1
if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
#else
tmFlags |= TIMELINE_BRCOLOR | TIMELINE_GRAPH;
#endif
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
db_finalize(&q);
style_finish_page();
}
/*
** Generate a multichoice submenu for the few recent active branches. zName is
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
Manifest *pPost = manifest_get(rid, CFTYPE_FORUM, 0);
if( pPost ){
sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT);
manifest_destroy(pPost);
}
}
/*
** Output a timeline in the web format given a query. The query
** should return these columns:
**
** 0. rid
** 1. artifact hash
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
Manifest *pPost = manifest_get(rid, CFTYPE_FORUM, 0);
if( pPost ){
sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT);
manifest_destroy(pPost);
}
}
/*
** This routine generates the default "extra" text after the description
** in a timeline.
**
** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
**
** This routine is used if no xExtra argument is supplied to
** www_print_timeline().
*/
static void defaultExtra(
Stmt *pQuery, /* Current row of the timeline query */
int tmFlags, /* Flags to www_print_timeline() */
const char *zThisUser, /* Suppress links to this user */
const char *zThisTag /* Suppress links to this tag */
){
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
int isLeaf = db_column_int(pQuery, 5);
const char *zDate = db_column_text(pQuery, 2);
const char *zType = db_column_text(pQuery, 7);
const char *zUser = db_column_text(pQuery, 4);
const char *zTagList = db_column_text(pQuery, 8);
int tagid = db_column_int(pQuery, 9);
const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
cgi_printf("(");
}
if( (tmFlags & TIMELINE_CLASSIC)==0 ){
if( zType[0]=='c' ){
if( isLeaf ){
if( has_closed_tag(rid) ){
@ <span class='timelineLeaf'>Closed-Leaf</span>
}else{
@ <span class='timelineLeaf'>Leaf</span>
}
}
cgi_printf("check-in: %z%S</a> ",
href("%R/info/%!S",zUuid),zUuid);
}else if( zType[0]=='e' && tagid ){
cgi_printf("technote: ");
hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
}else{
cgi_printf("artifact: %z%S</a> ",
href("%R/info/%!S",zUuid),zUuid);
}
}else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
|| zType[0]=='n' || zType[0]=='f'){
cgi_printf("artifact: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
}
if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
char *zLink;
if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
}else{
zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
}
cgi_printf("user: %z%h</a>", href("%z",zLink), zDispUser);
}else{
cgi_printf("user: %h", zDispUser);
}
/* Generate the "tags: TAGLIST" at the end of the comment, together
** with hyperlinks to the tag list.
*/
if( zTagList && zTagList[0]==0 ) zTagList = 0;
if( zTagList ){
if( g.perm.Hyperlink ){
int i;
const char *z = zTagList;
Blob links;
blob_zero(&links);
while( z && z[0] ){
for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
blob_appendf(&links,
"%z%#h</a>%.2s",
href("%R/timeline?r=%#t&c=%t",i,z,zDate), i,z, &z[i]
);
}else{
blob_appendf(&links, "%#h", i+2, z);
}
if( z[i]==0 ) break;
z += i+2;
}
cgi_printf(" tags: %s", blob_str(&links));
blob_reset(&links);
}else{
cgi_printf(" tags: %h", zTagList);
}
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(rid);
if( srcId ){
cgi_printf(" id: %z%d←%d</a>",
href("%R/deltachain/%d",rid), rid, srcId);
}else{
cgi_printf(" id: %z%d</a>",
href("%R/deltachain/%d",rid), rid);
}
}
tag_private_status(rid);
/* End timelineDetail */
if( (tmFlags & (TIMELINE_CLASSIC|TIMELINE_VERBOSE|TIMELINE_COMPACT))!=0 ){
cgi_printf(")");
}
if( tmFlags & TIMELINE_COMPACT ){
@ </span></span>
}else{
@ </span>
}
}
/*
** SETTING: timeline-truncate-at-blank boolean default=off
**
** If enabled, check-in comments displayed on the timeline are truncated
** at the first blank line of the comment text. The comment text after
** the first blank line is only seen in the /info or similar pages that
** show details about the check-in.
*/
/*
** SETTING: timeline-tslink-info boolean default=off
**
** The hyperlink on the timestamp associated with each timeline entry,
** on the far left-hand side of the screen, normally targets another
** /timeline page that shows the entry in context. However, if this
** option is turned on, that hyperlink targets the /info page showing
** the details of the entry.
*/
/*
** Output a timeline in the web format given a query. The query
** should return these columns:
**
** 0. rid
** 1. artifact hash
|
| ︙ | ︙ | |||
192 193 194 195 196 197 198 | Stmt *pQuery, /* Query to implement the timeline */ int tmFlags, /* Flags controlling display behavior */ const char *zThisUser, /* Suppress links to this user */ const char *zThisTag, /* Suppress links to this tag */ Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */ int selectedRid, /* Highlight the line with this RID value or zero */ int secondRid, /* Secondary highlight (or zero) */ | | | 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
Stmt *pQuery, /* Query to implement the timeline */
int tmFlags, /* Flags controlling display behavior */
const char *zThisUser, /* Suppress links to this user */
const char *zThisTag, /* Suppress links to this tag */
Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
int selectedRid, /* Highlight the line with this RID value or zero */
int secondRid, /* Secondary highlight (or zero) */
void (*xExtra)(Stmt*,int,const char*,const char*) /* generate "extra" text */
){
int mxWikiLen;
Blob comment;
int prevTagid = 0;
int suppressCnt = 0;
char zPrevDate[20];
GraphContext *pGraph = 0;
|
| ︙ | ︙ | |||
216 217 218 219 220 221 222 223 224 225 |
int iTableId = timeline_tableid();
int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info
** page rather than the /timeline page */
if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
vid = db_lget_int("checkout", 0);
}
zPrevDate[0] = 0;
mxWikiLen = db_get_int("timeline-max-comment", 0);
dateFormat = db_get_int("timeline-date-format", 0);
| > < < < < < < < < < < < < < < < < < | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
int iTableId = timeline_tableid();
int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info
** page rather than the /timeline page */
if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
vid = db_lget_int("checkout", 0);
}
if( xExtra==0 ) xExtra = defaultExtra;
zPrevDate[0] = 0;
mxWikiLen = db_get_int("timeline-max-comment", 0);
dateFormat = db_get_int("timeline-date-format", 0);
bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
if( (tmFlags & TIMELINE_VIEWS)==0 ){
tmFlags |= timeline_ss_cookie();
}
if( tmFlags & TIMELINE_COLUMNAR ){
zStyle = "Columnar";
}else if( tmFlags & TIMELINE_COMPACT ){
|
| ︙ | ︙ | |||
273 274 275 276 277 278 279 |
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
int isLeaf = db_column_int(pQuery, 5);
const char *zBgClr = db_column_text(pQuery, 6);
const char *zDate = db_column_text(pQuery, 2);
const char *zType = db_column_text(pQuery, 7);
const char *zUser = db_column_text(pQuery, 4);
| < < | 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
int isLeaf = db_column_int(pQuery, 5);
const char *zBgClr = db_column_text(pQuery, 6);
const char *zDate = db_column_text(pQuery, 2);
const char *zType = db_column_text(pQuery, 7);
const char *zUser = db_column_text(pQuery, 4);
int tagid = db_column_int(pQuery, 9);
char *zBr = 0; /* Branch */
int commentColumn = 3; /* Column containing comment text */
int modPending; /* Pending moderation */
char *zDateLink; /* URL for the link on the timestamp */
int drawDetailEllipsis; /* True to show ellipsis in place of detail */
int gidx = 0; /* Graph row identifier */
int isSelectedOrCurrent = 0; /* True if current row is selected */
|
| ︙ | ︙ | |||
609 610 611 612 613 614 615 |
** check-in with "This is me!". */
@ <b>← This is me!</b>
}
@ </span>
blob_reset(&comment);
| | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < | | 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 |
** check-in with "This is me!". */
@ <b>← This is me!</b>
}
@ </span>
blob_reset(&comment);
/* Generate extra information and hyperlinks that follow the comment.
** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
*/
if( drawDetailEllipsis ){
@ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
@ data-id='%d(rid)'>...</span>
}
if( tmFlags & TIMELINE_COLUMNAR ){
if( !isSelectedOrCurrent ){
@ <td class="timelineDetailCell%s(zExtraClass)" id='md%d(gidx)'>
}else{
@ <td class="timelineDetailCell%s(zExtraClass)">
}
}
if( tmFlags & TIMELINE_COMPACT ){
cgi_printf("<span class='clutter' id='detail-%d'>",rid);
}
cgi_printf("<span class='timeline%sDetail'>", zStyle);
xExtra(pQuery, tmFlags, zThisUser, zThisTag);
if( tmFlags & TIMELINE_COMPACT ){
@ </span></span>
}else{
@ </span>
}
/* Generate the file-change list if requested */
if( (tmFlags & (TIMELINE_FCHANGES|TIMELINE_FRENAMES))!=0
&& zType[0]=='c' && g.perm.Hyperlink
){
int inUl = 0;
if( !fchngQueryInit ){
db_prepare(&fchngQuery,
|
| ︙ | ︙ |