Fossil SCM

Improvements to the /ci_edit page so that it shows the context of the checkin being editted, before and after the proposed change. Clean up various internal interfaces in the process.

drh 2026-05-06 18:03 UTC trunk merge
Commit e454028a292625dae3e7ee1c55c36dbff6e904452f31d79e95a983e0d6e10aeb
+2 -4
--- src/backlink.c
+++ src/backlink.c
@@ -62,12 +62,11 @@
6262
blob_zero(&sql);
6363
blob_append(&sql, timeline_query_for_www(), -1);
6464
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
6565
db_prepare(&q, "%s", blob_sql_text(&sql));
6666
www_print_timeline(&q,
67
- TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL|TIMELINE_REFS,
68
- 0, 0, 0, 0, 0, 0);
67
+ TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL|TIMELINE_REFS, 0);
6968
db_finalize(&q);
7069
if( needEndPanel ){
7170
cgi_printf("</div>\n");
7271
}
7372
}
@@ -99,12 +98,11 @@
9998
);
10099
blob_zero(&sql);
101100
blob_append(&sql, timeline_query_for_www(), -1);
102101
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
103102
db_prepare(&q, "%s", blob_sql_text(&sql));
104
- www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
105
- 0, 0, 0, 0, 0, 0);
103
+ www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,0);
106104
db_finalize(&q);
107105
style_finish_page();
108106
}
109107
110108
/*
111109
--- src/backlink.c
+++ src/backlink.c
@@ -62,12 +62,11 @@
62 blob_zero(&sql);
63 blob_append(&sql, timeline_query_for_www(), -1);
64 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
65 db_prepare(&q, "%s", blob_sql_text(&sql));
66 www_print_timeline(&q,
67 TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL|TIMELINE_REFS,
68 0, 0, 0, 0, 0, 0);
69 db_finalize(&q);
70 if( needEndPanel ){
71 cgi_printf("</div>\n");
72 }
73 }
@@ -99,12 +98,11 @@
99 );
100 blob_zero(&sql);
101 blob_append(&sql, timeline_query_for_www(), -1);
102 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
103 db_prepare(&q, "%s", blob_sql_text(&sql));
104 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
105 0, 0, 0, 0, 0, 0);
106 db_finalize(&q);
107 style_finish_page();
108 }
109
110 /*
111
--- src/backlink.c
+++ src/backlink.c
@@ -62,12 +62,11 @@
62 blob_zero(&sql);
63 blob_append(&sql, timeline_query_for_www(), -1);
64 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
65 db_prepare(&q, "%s", blob_sql_text(&sql));
66 www_print_timeline(&q,
67 TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL|TIMELINE_REFS, 0);
 
68 db_finalize(&q);
69 if( needEndPanel ){
70 cgi_printf("</div>\n");
71 }
72 }
@@ -99,12 +98,11 @@
98 );
99 blob_zero(&sql);
100 blob_append(&sql, timeline_query_for_www(), -1);
101 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
102 db_prepare(&q, "%s", blob_sql_text(&sql));
103 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,0);
 
104 db_finalize(&q);
105 style_finish_page();
106 }
107
108 /*
109
+4 -1
--- src/branch.c
+++ src/branch.c
@@ -1076,10 +1076,11 @@
10761076
Blob sql = empty_blob;
10771077
Stmt q;
10781078
int tmFlags; /* Timeline display flags */
10791079
int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */
10801080
int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */
1081
+ TimelineXtra xtra;
10811082
10821083
login_check_credentials();
10831084
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
10841085
if( robot_restrict("timelineX") ) return;
10851086
@@ -1109,11 +1110,13 @@
11091110
if( PB("ubg")!=0 ){
11101111
tmFlags |= TIMELINE_UCOLOR;
11111112
}else{
11121113
tmFlags |= TIMELINE_BRCOLOR;
11131114
}
1114
- www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
1115
+ memset(&xtra, 0, sizeof(xtra));
1116
+ xtra.xExtra = brtimeline_extra;
1117
+ www_print_timeline(&q, tmFlags, &xtra);
11151118
db_finalize(&q);
11161119
style_finish_page();
11171120
}
11181121
11191122
/*
11201123
--- src/branch.c
+++ src/branch.c
@@ -1076,10 +1076,11 @@
1076 Blob sql = empty_blob;
1077 Stmt q;
1078 int tmFlags; /* Timeline display flags */
1079 int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */
1080 int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */
 
1081
1082 login_check_credentials();
1083 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1084 if( robot_restrict("timelineX") ) return;
1085
@@ -1109,11 +1110,13 @@
1109 if( PB("ubg")!=0 ){
1110 tmFlags |= TIMELINE_UCOLOR;
1111 }else{
1112 tmFlags |= TIMELINE_BRCOLOR;
1113 }
1114 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
 
 
1115 db_finalize(&q);
1116 style_finish_page();
1117 }
1118
1119 /*
1120
--- src/branch.c
+++ src/branch.c
@@ -1076,10 +1076,11 @@
1076 Blob sql = empty_blob;
1077 Stmt q;
1078 int tmFlags; /* Timeline display flags */
1079 int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */
1080 int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */
1081 TimelineXtra xtra;
1082
1083 login_check_credentials();
1084 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1085 if( robot_restrict("timelineX") ) return;
1086
@@ -1109,11 +1110,13 @@
1110 if( PB("ubg")!=0 ){
1111 tmFlags |= TIMELINE_UCOLOR;
1112 }else{
1113 tmFlags |= TIMELINE_BRCOLOR;
1114 }
1115 memset(&xtra, 0, sizeof(xtra));
1116 xtra.xExtra = brtimeline_extra;
1117 www_print_timeline(&q, tmFlags, &xtra);
1118 db_finalize(&q);
1119 style_finish_page();
1120 }
1121
1122 /*
1123
+1 -1
--- src/default.css
+++ src/default.css
@@ -35,11 +35,11 @@
3535
font-size: small;
3636
}
3737
tr.timelineCurrent {
3838
padding: .1em .2em;
3939
border: 1px dashed #446979;
40
- box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5);
40
+ box-shadow: 1px 1px 4px 1px rgba(0, 0, 0, 0.5);
4141
}
4242
.timelineSelected {
4343
padding: .1em .2em;
4444
border: 2px solid lightgray;
4545
background-color: #ffc;
4646
--- src/default.css
+++ src/default.css
@@ -35,11 +35,11 @@
35 font-size: small;
36 }
37 tr.timelineCurrent {
38 padding: .1em .2em;
39 border: 1px dashed #446979;
40 box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5);
41 }
42 .timelineSelected {
43 padding: .1em .2em;
44 border: 2px solid lightgray;
45 background-color: #ffc;
46
--- src/default.css
+++ src/default.css
@@ -35,11 +35,11 @@
35 font-size: small;
36 }
37 tr.timelineCurrent {
38 padding: .1em .2em;
39 border: 1px dashed #446979;
40 box-shadow: 1px 1px 4px 1px rgba(0, 0, 0, 0.5);
41 }
42 .timelineSelected {
43 padding: .1em .2em;
44 border: 2px solid lightgray;
45 background-color: #ffc;
46
--- src/descendants.c
+++ src/descendants.c
@@ -640,11 +640,11 @@
640640
** many descenders to (off-screen) parents. */
641641
tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
642642
if( fNg==0 ) tmFlags |= TIMELINE_GRAPH;
643643
if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR;
644644
if( fUBg ) tmFlags |= TIMELINE_UCOLOR;
645
- www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
645
+ www_print_timeline(&q, tmFlags, 0);
646646
db_finalize(&q);
647647
@ <br>
648648
style_finish_page();
649649
}
650650
651651
--- src/descendants.c
+++ src/descendants.c
@@ -640,11 +640,11 @@
640 ** many descenders to (off-screen) parents. */
641 tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
642 if( fNg==0 ) tmFlags |= TIMELINE_GRAPH;
643 if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR;
644 if( fUBg ) tmFlags |= TIMELINE_UCOLOR;
645 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
646 db_finalize(&q);
647 @ <br>
648 style_finish_page();
649 }
650
651
--- src/descendants.c
+++ src/descendants.c
@@ -640,11 +640,11 @@
640 ** many descenders to (off-screen) parents. */
641 tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
642 if( fNg==0 ) tmFlags |= TIMELINE_GRAPH;
643 if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR;
644 if( fUBg ) tmFlags |= TIMELINE_UCOLOR;
645 www_print_timeline(&q, tmFlags, 0);
646 db_finalize(&q);
647 @ <br>
648 style_finish_page();
649 }
650
651
+139 -103
--- src/info.c
+++ src/info.c
@@ -271,17 +271,24 @@
271271
}
272272
}
273273
274274
/*
275275
** Show the context graph (immediate parents and children) for
276
-** check-in rid and rid2
276
+** check-in rid and rid2. rid2 might be zero, in which case only
277
+** show the context for rid1.
277278
*/
278
-void render_checkin_context(int rid, int rid2, int parentsOnly, int mFlags){
279
+void render_checkin_context(
280
+ int rid, int rid2, /* One or two checkins for which to show context */
281
+ int mRCCFlags, /* Flags. 1: parents only, 2: shadow-select rid */
282
+ int mFlags /* Graph flags */
283
+){
279284
Blob sql;
280285
Stmt q;
281286
int rx[2];
282287
int i, n;
288
+ TimelineXtra xtra;
289
+
283290
rx[0] = rid;
284291
rx[1] = rid2;
285292
n = rid2 ? 2 : 1;
286293
blob_zero(&sql);
287294
blob_append(&sql, timeline_query_for_www(), -1);
@@ -295,11 +302,11 @@
295302
"INSERT OR IGNORE INTO ok VALUES(%d);"
296303
"INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
297304
rx[i], rx[i]
298305
);
299306
}
300
- if( !parentsOnly ){
307
+ if( (mRCCFlags & 0x01)==0 ){
301308
for(i=0; i<n; i++){
302309
db_multi_exec(
303310
"INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rx[i]
304311
);
305312
if( db_table_exists("repository","cherrypick") ){
@@ -313,18 +320,20 @@
313320
}
314321
}
315322
}
316323
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
317324
db_prepare(&q, "%s", blob_sql_text(&sql));
318
- www_print_timeline(&q,
319
- mFlags
320
- |TIMELINE_GRAPH
321
- |TIMELINE_FILLGAPS
322
- |TIMELINE_NOSCROLL
323
- |TIMELINE_XMERGE
324
- |TIMELINE_CHPICK,
325
- 0, 0, 0, rid, rid2, 0);
325
+ memset(&xtra, 0, sizeof(xtra));
326
+ if( mRCCFlags & 0x02 ){
327
+ xtra.currentRid = rid;
328
+ }else{
329
+ xtra.selectedRid = rid;
330
+ xtra.secondRid = rid2;
331
+ }
332
+ mFlags |= TIMELINE_GRAPH|TIMELINE_FILLGAPS|TIMELINE_NOSCROLL|
333
+ TIMELINE_XMERGE|TIMELINE_CHPICK;
334
+ www_print_timeline(&q, mFlags, &xtra);
326335
db_finalize(&q);
327336
blob_reset(&sql);
328337
}
329338
330339
@@ -508,10 +517,11 @@
508517
int rid;
509518
Stmt q;
510519
int cnt = 0;
511520
Blob sql;
512521
char const *zType;
522
+ TimelineXtra xtra;
513523
514524
login_check_credentials();
515525
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
516526
rid = name_to_rid_www("name");
517527
if( rid==0 ){
@@ -607,12 +617,14 @@
607617
#endif
608618
blob_zero(&sql);
609619
blob_append(&sql, timeline_query_for_www(), -1);
610620
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
611621
db_prepare(&q, "%s", blob_sql_text(&sql));
622
+ memset(&xtra, 0, sizeof(xtra));
623
+ xtra.selectedRid = rid;
612624
www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
613
- 0, 0, 0, rid, 0, 0);
625
+ &xtra);
614626
db_finalize(&q);
615627
style_finish_page();
616628
}
617629
618630
/*
@@ -876,11 +888,11 @@
876888
if( zHostname ){
877889
style_header("Checkout Status: %h on %h", zCwd, zHostname);
878890
}else{
879891
style_header("Checkout Status: %h", zCwd);
880892
}
881
- render_checkin_context(vid, 0, 0, 0);
893
+ render_checkin_context(vid, 0, 2, 0);
882894
@ <hr>
883895
zExBase = P("exbase");
884896
if( zExBase && zExBase[0] ){
885897
char *zPath = decode16_dup(zExBase);
886898
char *zCBase = file_canonical_name_dup(zPath?zPath:zExBase);
@@ -1183,11 +1195,11 @@
11831195
wiki_render_associated("checkin", zUuid, 0);
11841196
}
11851197
render_backlink_graph(zUuid,
11861198
"<div class=\"section accordion\">References</div>\n");
11871199
@ <div class="section accordion">Context</div><div class="accordion_panel">
1188
- render_checkin_context(rid, 0, 0, 0);
1200
+ render_checkin_context(rid, 0, 2, 0);
11891201
@ </div><div class="section accordion" id="changes_section">Changes</div>
11901202
@ <div class="accordion_panel">
11911203
@ <div class="sectionmenu info-changes-menu">
11921204
/* ^^^ .info-changes-menu is used by diff scroll sync */
11931205
pCfg = construct_diff_flags(diffType, &DCfg);
@@ -3453,74 +3465,93 @@
34533465
while( fossil_isspace(zA[0]) ) zA++;
34543466
return zA[0]==0 && zB[0]==0;
34553467
}
34563468
34573469
/*
3458
-** The following methods operate on the newtags temporary table
3459
-** that is used to collect various changes to be added to a control
3460
-** artifact for a check-in edit.
3470
+*****************************************************************************
3471
+** The following methods operate on the "newtags" temporary table. This
3472
+** table collects changes for a control artifact that will implement an
3473
+** edit to a check-in.
3474
+**
3475
+** Initialize the newtags table
34613476
*/
34623477
static void init_newtags(void){
34633478
db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
34643479
}
34653480
3481
+/* Add a new changes to the newtags table */
34663482
static void change_special(
34673483
const char *zName, /* Name of the special tag */
34683484
const char *zOp, /* Operation prefix (e.g. +,-,*) */
34693485
const char *zValue /* Value of the tag */
34703486
){
34713487
db_multi_exec("REPLACE INTO newtags VALUES(%Q,'%q',%Q)", zName, zOp, zValue);
34723488
}
34733489
3490
+/* Change a symbolic tag */
34743491
static void change_sym_tag(const char *zTag, const char *zOp){
34753492
db_multi_exec("REPLACE INTO newtags VALUES('sym-%q',%Q,NULL)", zTag, zOp);
34763493
}
34773494
3495
+/* Cancel a tag */
34783496
static void cancel_special(const char *zTag){
34793497
change_special(zTag,"-",0);
34803498
}
34813499
3500
+/* Add a background color tag. This will be propagating tag
3501
+** if fPropagateColor is true. */
34823502
static void add_color(const char *zNewColor, int fPropagateColor){
34833503
change_special("bgcolor",fPropagateColor ? "*" : "+", zNewColor);
34843504
}
34853505
3506
+/* Cancel a background color tag */
34863507
static void cancel_color(void){
34873508
change_special("bgcolor","-",0);
34883509
}
34893510
3511
+/* Add a comment-change tag */
34903512
static void add_comment(const char *zNewComment){
34913513
change_special("comment","+",zNewComment);
34923514
}
34933515
3516
+/* Add a date-change tag */
34943517
static void add_date(const char *zNewDate){
34953518
change_special("date","+",zNewDate);
34963519
}
34973520
3521
+/* Add a change-user tag */
34983522
static void add_user(const char *zNewUser){
34993523
change_special("user","+",zNewUser);
35003524
}
35013525
3526
+/* Add a generic symbolic tag (one that has no value) */
35023527
static void add_tag(const char *zNewTag){
35033528
change_sym_tag(zNewTag,"+");
35043529
}
35053530
3531
+/* Cancel an existing symbolic tag associated with check-in rid */
35063532
static void cancel_tag(int rid, const char *zCancelTag){
35073533
if( db_exists("SELECT 1 FROM tagxref, tag"
35083534
" WHERE tagxref.rid=%d AND tagtype>0"
35093535
" AND tagxref.tagid=tag.tagid AND tagname='sym-%q'",
35103536
rid, zCancelTag)
3511
- ) change_sym_tag(zCancelTag,"-");
3537
+ ){
3538
+ change_sym_tag(zCancelTag,"-");
3539
+ }
35123540
}
35133541
3542
+/* Add a hidden tag that propagates to all ancestors */
35143543
static void hide_branch(void){
35153544
change_special("hidden","*",0);
35163545
}
35173546
3547
+/* Close the branch */
35183548
static void close_leaf(int rid){
35193549
change_special("closed",is_a_leaf(rid)?"+":"*",0);
35203550
}
35213551
3552
+/* Move the check-in to a different branch */
35223553
static void change_branch(int rid, const char *zNewBranch){
35233554
db_multi_exec(
35243555
"REPLACE INTO newtags "
35253556
" SELECT tagname, '-', NULL FROM tagxref, tag"
35263557
" WHERE tagxref.rid=%d AND tagtype==2"
@@ -3531,19 +3562,31 @@
35313562
change_special("branch","*",zNewBranch);
35323563
change_sym_tag(zNewBranch,"*");
35333564
}
35343565
35353566
/*
3536
-** The apply_newtags method is called after all newtags have been added
3537
-** and the control artifact is completed and then written to the DB.
3567
+** Construct a control artifact.
3568
+**
3569
+** Preconditions:
3570
+**
3571
+** (1) "ctrl" contains the beginning of a control artifact with
3572
+** just the D-card showing the timestamp on the control artifact
3573
+** itself.
3574
+**
3575
+** (2) The newtags temporary table has been constructed.
3576
+**
3577
+** (3) Zero or more methods may have been called to populate the
3578
+** newtags table. Or the newtags table might be empty.
3579
+**
3580
+** Construct the complete control artifact. Or, if there are not
3581
+** changes, just zero out the ctrl blob.
35383582
*/
3539
-static void apply_newtags(
3540
- Blob *ctrl,
3541
- int rid,
3542
- const char *zUuid,
3543
- const char *zUserOvrd, /* The user name on the control artifact */
3544
- int fDryRun /* Print control artifact, but make no changes */
3583
+static void construct_newtags_artifact(
3584
+ Blob *ctrl, /* The control artifact text */
3585
+ int rid, /* rid of the check-in that the artifact applies to */
3586
+ const char *zUuid, /* UUID of the check-in to which the artifact applies*/
3587
+ const char *zUserOvrd /* The user name on the control artifact */
35453588
){
35463589
Stmt q;
35473590
int nChng = 0;
35483591
35493592
db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
@@ -3559,33 +3602,46 @@
35593602
blob_appendf(ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
35603603
}
35613604
}
35623605
db_finalize(&q);
35633606
if( nChng>0 ){
3564
- int nrid;
35653607
Blob cksum;
35663608
if( zUserOvrd && zUserOvrd[0] ){
35673609
blob_appendf(ctrl, "U %F\n", zUserOvrd);
35683610
}else{
35693611
blob_appendf(ctrl, "U %F\n", login_name());
35703612
}
35713613
md5sum_blob(ctrl, &cksum);
35723614
blob_appendf(ctrl, "Z %b\n", &cksum);
3573
- if( fDryRun ){
3574
- assert( g.isHTTP==0 ); /* Only print control artifact in console mode. */
3575
- fossil_print("%s", blob_str(ctrl));
3576
- blob_reset(ctrl);
3577
- }else{
3578
- db_begin_transaction();
3579
- g.markPrivate = content_is_private(rid);
3580
- nrid = content_put(ctrl);
3581
- manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
3582
- db_end_transaction(0);
3583
- }
3584
- assert( blob_is_reset(ctrl) );
3615
+ blob_reset(&cksum);
3616
+ }else{
3617
+ blob_reset(ctrl);
3618
+ }
3619
+}
3620
+
3621
+/*
3622
+** Return true if the artifact is complete. We assume that any non-empty
3623
+** artifact is complete.
3624
+*/
3625
+static int artifact_is_complete(Blob *ctrl){
3626
+ return blob_size(ctrl)>0;
3627
+}
3628
+
3629
+/*
3630
+** Apply a control artifact to the repository database. Except, if the
3631
+** artifact is empty, this routine is a no-op.
3632
+*/
3633
+static void publish_newtags_artifact(Blob *ctrl, int rid){
3634
+ if( artifact_is_complete(ctrl) ){
3635
+ int nrid;
3636
+ g.markPrivate = content_is_private(rid);
3637
+ nrid = content_put(ctrl);
3638
+ manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
35853639
}
35863640
}
3641
+/* End of the newtags subsystem
3642
+******************************************************************************/
35873643
35883644
/*
35893645
** This method checks that the date can be parsed.
35903646
** Returns 1 if datetime() can validate, 0 otherwise.
35913647
*/
@@ -3644,14 +3700,17 @@
36443700
int fPropagateColor; /* True if color propagates before edit */
36453701
int fNewPropagateColor; /* True if color propagates after edit */
36463702
int fHasHidden = 0; /* True if hidden tag already set */
36473703
int fHasClosed = 0; /* True if closed tag already set */
36483704
const char *zChngTime = 0; /* Value of chngtime= query param, if any */
3705
+ int bApply = P("apply")!=0;
3706
+ int bPreview = P("preview")!=0;
36493707
char *zUuid;
36503708
Blob comment;
36513709
char *zBranchName = 0;
36523710
Stmt q;
3711
+ Blob ctrl; /* The generated control artifact */
36533712
36543713
login_check_credentials();
36553714
if( !g.perm.Write ){ login_needed(g.anon.Write); return; }
36563715
rid = name_to_typed_rid(P("r"), "ci");
36573716
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
@@ -3684,16 +3743,14 @@
36843743
zNewBrFlag = P("newbr") ? " checked" : "";
36853744
zNewBranch = PDT("brname","");
36863745
zBranchName = branch_of_rid(rid);
36873746
zCloseFlag = P("close") ? " checked" : "";
36883747
zHideFlag = P("hide") ? " checked" : "";
3689
- if( P("apply") && cgi_csrf_safe(2) ){
3690
- Blob ctrl;
3691
- char *zNow;
3692
-
3693
- blob_zero(&ctrl);
3694
- zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
3748
+ blob_zero(&ctrl);
3749
+ if( bApply && !cgi_csrf_safe(2) ) bApply = 0;
3750
+ if( bApply || bPreview ){
3751
+ char *zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
36953752
blob_appendf(&ctrl, "D %s\n", zNow);
36963753
init_newtags();
36973754
if( zNewColorFlag[0]
36983755
&& zNewColor[0]
36993756
&& (fPropagateColor!=fNewPropagateColor
@@ -3719,69 +3776,41 @@
37193776
db_finalize(&q);
37203777
if( zHideFlag[0] ) hide_branch();
37213778
if( zCloseFlag[0] ) close_leaf(rid);
37223779
if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
37233780
if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
3724
- apply_newtags(&ctrl, rid, zUuid, 0, 0);
3725
- cgi_redirectf("%R/ci/%S", zUuid);
3781
+ construct_newtags_artifact(&ctrl, rid, zUuid, 0);
3782
+ if( bApply ){
3783
+ if( artifact_is_complete(&ctrl) ){
3784
+ db_begin_transaction();
3785
+ publish_newtags_artifact(&ctrl, rid);
3786
+ db_end_transaction(0);
3787
+ blob_reset(&ctrl);
3788
+ }
3789
+ cgi_redirectf("%R/ci/%S", zUuid);
3790
+ }
37263791
}
37273792
blob_zero(&comment);
37283793
blob_append(&comment, zNewComment, -1);
3729
- zUuid[10] = 0;
3730
- style_header("Edit Check-in [%s]", zUuid);
3731
- if( P("preview") ){
3732
- Blob suffix;
3733
- int nTag = 0;
3734
- const char *zDplyBr; /* Branch name used to determine BG color */
3735
- const char *zMainBranch = db_main_branch();
3736
- if( zNewBrFlag[0] && zNewBranch[0] ){
3737
- zDplyBr = zNewBranch;
3738
- }else{
3739
- zDplyBr = zBranchName;
3740
- }
3741
- @ <b>Preview:</b>
3742
- @ <blockquote>
3743
- @ <table border=0>
3744
- if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
3745
- @ <tr><td style="background-color:%h(reasonable_bg_color(zNewColor,0));">
3746
- }else if( zColor[0] ){
3747
- @ <tr><td style="background-color:%h(reasonable_bg_color(zColor,0));">
3748
- }else if( zDplyBr && fossil_strcmp(zDplyBr, zMainBranch)!=0 ){
3749
- @ <tr><td style="background-color:%h(hash_color(zDplyBr));">
3750
- }else{
3751
- @ <tr><td>
3752
- }
3753
- @ %!W(blob_str(&comment))
3754
- blob_zero(&suffix);
3755
- blob_appendf(&suffix, "(user: %h", zNewUser);
3756
- db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
3757
- " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
3758
- " AND tagtype>1 AND tag.tagid=tagxref.tagid",
3759
- rid);
3760
- while( db_step(&q)==SQLITE_ROW ){
3761
- const char *zTag = db_column_text(&q, 0);
3762
- if( nTag==0 ){
3763
- blob_appendf(&suffix, ", tags: %h", zTag);
3764
- }else{
3765
- blob_appendf(&suffix, ", %h", zTag);
3766
- }
3767
- nTag++;
3768
- }
3769
- db_finalize(&q);
3770
- blob_appendf(&suffix, ")");
3771
- @ %s(blob_str(&suffix))
3772
- @ </td></tr></table>
3773
- if( zChngTime ){
3774
- @ <p>The timestamp on the tag used to make the changes above
3775
- @ will be overridden as: %s(date_in_standard_format(zChngTime))</p>
3776
- }
3777
- @ </blockquote>
3778
- @ <hr>
3779
- blob_reset(&suffix);
3780
- }
3781
- @ <p>Make changes to attributes of check-in
3782
- @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p>
3794
+ style_header("Edit Check-in %S", zUuid);
3795
+ @ <div class="section accordion">Original Context Around \
3796
+ @ Check-in %S(zUuid) on %s(zDate)</div>
3797
+ @ <div class="accordion_panel">
3798
+ render_checkin_context(rid, 0, 2, 0);
3799
+ @ </div>
3800
+ if( bPreview ){
3801
+ @ <div class="section accordion">After The Proposed Changes</div>
3802
+ @ <div class="accordion_panel">
3803
+ db_begin_transaction();
3804
+ publish_newtags_artifact(&ctrl, rid);
3805
+ render_checkin_context(rid, 0, 2, 0);
3806
+ db_end_transaction(1);
3807
+ @ </div>
3808
+ }
3809
+ @ <div class="section accordion">Proposed Changes For \
3810
+ @ Check-In %S(zUuid):</div>
3811
+ @ <div class="accordion_panel">
37833812
form_begin(0, "%R/ci_edit");
37843813
@ <div><input type="hidden" name="r" value="%s(zUuid)">
37853814
@ <table border="0" cellspacing="10">
37863815
37873816
@ <tr><th align="right" valign="top">User:</th>
@@ -3919,10 +3948,11 @@
39193948
if( P("preview") ){
39203949
@ <input type="submit" name="apply" value="Apply Changes">
39213950
}
39223951
@ </td></tr>
39233952
@ </table>
3953
+ login_insert_csrf_secret();
39243954
@ </div></form>
39253955
builtin_request_js("ci_edit.js");
39263956
style_finish_page();
39273957
}
39283958
@@ -4194,13 +4224,19 @@
41944224
fossil_free((void *)pzCancelTags);
41954225
}
41964226
if( fHide && !fHasHidden ) hide_branch();
41974227
if( fClose && !fHasClosed ) close_leaf(rid);
41984228
if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
4199
- apply_newtags(&ctrl, rid, zUuid, zUserOvrd, fDryRun);
4200
- if( fDryRun==0 ){
4229
+ construct_newtags_artifact(&ctrl, rid, zUuid, zUserOvrd);
4230
+ if( fDryRun ){
4231
+ fossil_print("%s", blob_str(&ctrl));
4232
+ blob_reset(&ctrl);
4233
+ }else{
4234
+ db_begin_transaction();
4235
+ publish_newtags_artifact(&ctrl, rid);
42014236
show_common_info(rid, "hash:", 1, 0);
4237
+ db_end_transaction(0);
42024238
}
42034239
if( g.localOpen ){
42044240
manifest_to_disk(rid);
42054241
}
42064242
}
42074243
--- src/info.c
+++ src/info.c
@@ -271,17 +271,24 @@
271 }
272 }
273
274 /*
275 ** Show the context graph (immediate parents and children) for
276 ** check-in rid and rid2
 
277 */
278 void render_checkin_context(int rid, int rid2, int parentsOnly, int mFlags){
 
 
 
 
279 Blob sql;
280 Stmt q;
281 int rx[2];
282 int i, n;
 
 
283 rx[0] = rid;
284 rx[1] = rid2;
285 n = rid2 ? 2 : 1;
286 blob_zero(&sql);
287 blob_append(&sql, timeline_query_for_www(), -1);
@@ -295,11 +302,11 @@
295 "INSERT OR IGNORE INTO ok VALUES(%d);"
296 "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
297 rx[i], rx[i]
298 );
299 }
300 if( !parentsOnly ){
301 for(i=0; i<n; i++){
302 db_multi_exec(
303 "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rx[i]
304 );
305 if( db_table_exists("repository","cherrypick") ){
@@ -313,18 +320,20 @@
313 }
314 }
315 }
316 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
317 db_prepare(&q, "%s", blob_sql_text(&sql));
318 www_print_timeline(&q,
319 mFlags
320 |TIMELINE_GRAPH
321 |TIMELINE_FILLGAPS
322 |TIMELINE_NOSCROLL
323 |TIMELINE_XMERGE
324 |TIMELINE_CHPICK,
325 0, 0, 0, rid, rid2, 0);
 
 
326 db_finalize(&q);
327 blob_reset(&sql);
328 }
329
330
@@ -508,10 +517,11 @@
508 int rid;
509 Stmt q;
510 int cnt = 0;
511 Blob sql;
512 char const *zType;
 
513
514 login_check_credentials();
515 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
516 rid = name_to_rid_www("name");
517 if( rid==0 ){
@@ -607,12 +617,14 @@
607 #endif
608 blob_zero(&sql);
609 blob_append(&sql, timeline_query_for_www(), -1);
610 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
611 db_prepare(&q, "%s", blob_sql_text(&sql));
 
 
612 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
613 0, 0, 0, rid, 0, 0);
614 db_finalize(&q);
615 style_finish_page();
616 }
617
618 /*
@@ -876,11 +888,11 @@
876 if( zHostname ){
877 style_header("Checkout Status: %h on %h", zCwd, zHostname);
878 }else{
879 style_header("Checkout Status: %h", zCwd);
880 }
881 render_checkin_context(vid, 0, 0, 0);
882 @ <hr>
883 zExBase = P("exbase");
884 if( zExBase && zExBase[0] ){
885 char *zPath = decode16_dup(zExBase);
886 char *zCBase = file_canonical_name_dup(zPath?zPath:zExBase);
@@ -1183,11 +1195,11 @@
1183 wiki_render_associated("checkin", zUuid, 0);
1184 }
1185 render_backlink_graph(zUuid,
1186 "<div class=\"section accordion\">References</div>\n");
1187 @ <div class="section accordion">Context</div><div class="accordion_panel">
1188 render_checkin_context(rid, 0, 0, 0);
1189 @ </div><div class="section accordion" id="changes_section">Changes</div>
1190 @ <div class="accordion_panel">
1191 @ <div class="sectionmenu info-changes-menu">
1192 /* ^^^ .info-changes-menu is used by diff scroll sync */
1193 pCfg = construct_diff_flags(diffType, &DCfg);
@@ -3453,74 +3465,93 @@
3453 while( fossil_isspace(zA[0]) ) zA++;
3454 return zA[0]==0 && zB[0]==0;
3455 }
3456
3457 /*
3458 ** The following methods operate on the newtags temporary table
3459 ** that is used to collect various changes to be added to a control
3460 ** artifact for a check-in edit.
 
 
 
3461 */
3462 static void init_newtags(void){
3463 db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
3464 }
3465
 
3466 static void change_special(
3467 const char *zName, /* Name of the special tag */
3468 const char *zOp, /* Operation prefix (e.g. +,-,*) */
3469 const char *zValue /* Value of the tag */
3470 ){
3471 db_multi_exec("REPLACE INTO newtags VALUES(%Q,'%q',%Q)", zName, zOp, zValue);
3472 }
3473
 
3474 static void change_sym_tag(const char *zTag, const char *zOp){
3475 db_multi_exec("REPLACE INTO newtags VALUES('sym-%q',%Q,NULL)", zTag, zOp);
3476 }
3477
 
3478 static void cancel_special(const char *zTag){
3479 change_special(zTag,"-",0);
3480 }
3481
 
 
3482 static void add_color(const char *zNewColor, int fPropagateColor){
3483 change_special("bgcolor",fPropagateColor ? "*" : "+", zNewColor);
3484 }
3485
 
3486 static void cancel_color(void){
3487 change_special("bgcolor","-",0);
3488 }
3489
 
3490 static void add_comment(const char *zNewComment){
3491 change_special("comment","+",zNewComment);
3492 }
3493
 
3494 static void add_date(const char *zNewDate){
3495 change_special("date","+",zNewDate);
3496 }
3497
 
3498 static void add_user(const char *zNewUser){
3499 change_special("user","+",zNewUser);
3500 }
3501
 
3502 static void add_tag(const char *zNewTag){
3503 change_sym_tag(zNewTag,"+");
3504 }
3505
 
3506 static void cancel_tag(int rid, const char *zCancelTag){
3507 if( db_exists("SELECT 1 FROM tagxref, tag"
3508 " WHERE tagxref.rid=%d AND tagtype>0"
3509 " AND tagxref.tagid=tag.tagid AND tagname='sym-%q'",
3510 rid, zCancelTag)
3511 ) change_sym_tag(zCancelTag,"-");
 
 
3512 }
3513
 
3514 static void hide_branch(void){
3515 change_special("hidden","*",0);
3516 }
3517
 
3518 static void close_leaf(int rid){
3519 change_special("closed",is_a_leaf(rid)?"+":"*",0);
3520 }
3521
 
3522 static void change_branch(int rid, const char *zNewBranch){
3523 db_multi_exec(
3524 "REPLACE INTO newtags "
3525 " SELECT tagname, '-', NULL FROM tagxref, tag"
3526 " WHERE tagxref.rid=%d AND tagtype==2"
@@ -3531,19 +3562,31 @@
3531 change_special("branch","*",zNewBranch);
3532 change_sym_tag(zNewBranch,"*");
3533 }
3534
3535 /*
3536 ** The apply_newtags method is called after all newtags have been added
3537 ** and the control artifact is completed and then written to the DB.
 
 
 
 
 
 
 
 
 
 
 
 
 
3538 */
3539 static void apply_newtags(
3540 Blob *ctrl,
3541 int rid,
3542 const char *zUuid,
3543 const char *zUserOvrd, /* The user name on the control artifact */
3544 int fDryRun /* Print control artifact, but make no changes */
3545 ){
3546 Stmt q;
3547 int nChng = 0;
3548
3549 db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
@@ -3559,33 +3602,46 @@
3559 blob_appendf(ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
3560 }
3561 }
3562 db_finalize(&q);
3563 if( nChng>0 ){
3564 int nrid;
3565 Blob cksum;
3566 if( zUserOvrd && zUserOvrd[0] ){
3567 blob_appendf(ctrl, "U %F\n", zUserOvrd);
3568 }else{
3569 blob_appendf(ctrl, "U %F\n", login_name());
3570 }
3571 md5sum_blob(ctrl, &cksum);
3572 blob_appendf(ctrl, "Z %b\n", &cksum);
3573 if( fDryRun ){
3574 assert( g.isHTTP==0 ); /* Only print control artifact in console mode. */
3575 fossil_print("%s", blob_str(ctrl));
3576 blob_reset(ctrl);
3577 }else{
3578 db_begin_transaction();
3579 g.markPrivate = content_is_private(rid);
3580 nrid = content_put(ctrl);
3581 manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
3582 db_end_transaction(0);
3583 }
3584 assert( blob_is_reset(ctrl) );
 
 
 
 
 
 
 
 
 
 
 
 
3585 }
3586 }
 
 
3587
3588 /*
3589 ** This method checks that the date can be parsed.
3590 ** Returns 1 if datetime() can validate, 0 otherwise.
3591 */
@@ -3644,14 +3700,17 @@
3644 int fPropagateColor; /* True if color propagates before edit */
3645 int fNewPropagateColor; /* True if color propagates after edit */
3646 int fHasHidden = 0; /* True if hidden tag already set */
3647 int fHasClosed = 0; /* True if closed tag already set */
3648 const char *zChngTime = 0; /* Value of chngtime= query param, if any */
 
 
3649 char *zUuid;
3650 Blob comment;
3651 char *zBranchName = 0;
3652 Stmt q;
 
3653
3654 login_check_credentials();
3655 if( !g.perm.Write ){ login_needed(g.anon.Write); return; }
3656 rid = name_to_typed_rid(P("r"), "ci");
3657 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
@@ -3684,16 +3743,14 @@
3684 zNewBrFlag = P("newbr") ? " checked" : "";
3685 zNewBranch = PDT("brname","");
3686 zBranchName = branch_of_rid(rid);
3687 zCloseFlag = P("close") ? " checked" : "";
3688 zHideFlag = P("hide") ? " checked" : "";
3689 if( P("apply") && cgi_csrf_safe(2) ){
3690 Blob ctrl;
3691 char *zNow;
3692
3693 blob_zero(&ctrl);
3694 zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
3695 blob_appendf(&ctrl, "D %s\n", zNow);
3696 init_newtags();
3697 if( zNewColorFlag[0]
3698 && zNewColor[0]
3699 && (fPropagateColor!=fNewPropagateColor
@@ -3719,69 +3776,41 @@
3719 db_finalize(&q);
3720 if( zHideFlag[0] ) hide_branch();
3721 if( zCloseFlag[0] ) close_leaf(rid);
3722 if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
3723 if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
3724 apply_newtags(&ctrl, rid, zUuid, 0, 0);
3725 cgi_redirectf("%R/ci/%S", zUuid);
 
 
 
 
 
 
 
 
3726 }
3727 blob_zero(&comment);
3728 blob_append(&comment, zNewComment, -1);
3729 zUuid[10] = 0;
3730 style_header("Edit Check-in [%s]", zUuid);
3731 if( P("preview") ){
3732 Blob suffix;
3733 int nTag = 0;
3734 const char *zDplyBr; /* Branch name used to determine BG color */
3735 const char *zMainBranch = db_main_branch();
3736 if( zNewBrFlag[0] && zNewBranch[0] ){
3737 zDplyBr = zNewBranch;
3738 }else{
3739 zDplyBr = zBranchName;
3740 }
3741 @ <b>Preview:</b>
3742 @ <blockquote>
3743 @ <table border=0>
3744 if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
3745 @ <tr><td style="background-color:%h(reasonable_bg_color(zNewColor,0));">
3746 }else if( zColor[0] ){
3747 @ <tr><td style="background-color:%h(reasonable_bg_color(zColor,0));">
3748 }else if( zDplyBr && fossil_strcmp(zDplyBr, zMainBranch)!=0 ){
3749 @ <tr><td style="background-color:%h(hash_color(zDplyBr));">
3750 }else{
3751 @ <tr><td>
3752 }
3753 @ %!W(blob_str(&comment))
3754 blob_zero(&suffix);
3755 blob_appendf(&suffix, "(user: %h", zNewUser);
3756 db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
3757 " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
3758 " AND tagtype>1 AND tag.tagid=tagxref.tagid",
3759 rid);
3760 while( db_step(&q)==SQLITE_ROW ){
3761 const char *zTag = db_column_text(&q, 0);
3762 if( nTag==0 ){
3763 blob_appendf(&suffix, ", tags: %h", zTag);
3764 }else{
3765 blob_appendf(&suffix, ", %h", zTag);
3766 }
3767 nTag++;
3768 }
3769 db_finalize(&q);
3770 blob_appendf(&suffix, ")");
3771 @ %s(blob_str(&suffix))
3772 @ </td></tr></table>
3773 if( zChngTime ){
3774 @ <p>The timestamp on the tag used to make the changes above
3775 @ will be overridden as: %s(date_in_standard_format(zChngTime))</p>
3776 }
3777 @ </blockquote>
3778 @ <hr>
3779 blob_reset(&suffix);
3780 }
3781 @ <p>Make changes to attributes of check-in
3782 @ [%z(href("%R/ci/%!S",zUuid))%s(zUuid)</a>]:</p>
3783 form_begin(0, "%R/ci_edit");
3784 @ <div><input type="hidden" name="r" value="%s(zUuid)">
3785 @ <table border="0" cellspacing="10">
3786
3787 @ <tr><th align="right" valign="top">User:</th>
@@ -3919,10 +3948,11 @@
3919 if( P("preview") ){
3920 @ <input type="submit" name="apply" value="Apply Changes">
3921 }
3922 @ </td></tr>
3923 @ </table>
 
3924 @ </div></form>
3925 builtin_request_js("ci_edit.js");
3926 style_finish_page();
3927 }
3928
@@ -4194,13 +4224,19 @@
4194 fossil_free((void *)pzCancelTags);
4195 }
4196 if( fHide && !fHasHidden ) hide_branch();
4197 if( fClose && !fHasClosed ) close_leaf(rid);
4198 if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
4199 apply_newtags(&ctrl, rid, zUuid, zUserOvrd, fDryRun);
4200 if( fDryRun==0 ){
 
 
 
 
 
4201 show_common_info(rid, "hash:", 1, 0);
 
4202 }
4203 if( g.localOpen ){
4204 manifest_to_disk(rid);
4205 }
4206 }
4207
--- src/info.c
+++ src/info.c
@@ -271,17 +271,24 @@
271 }
272 }
273
274 /*
275 ** Show the context graph (immediate parents and children) for
276 ** check-in rid and rid2. rid2 might be zero, in which case only
277 ** show the context for rid1.
278 */
279 void render_checkin_context(
280 int rid, int rid2, /* One or two checkins for which to show context */
281 int mRCCFlags, /* Flags. 1: parents only, 2: shadow-select rid */
282 int mFlags /* Graph flags */
283 ){
284 Blob sql;
285 Stmt q;
286 int rx[2];
287 int i, n;
288 TimelineXtra xtra;
289
290 rx[0] = rid;
291 rx[1] = rid2;
292 n = rid2 ? 2 : 1;
293 blob_zero(&sql);
294 blob_append(&sql, timeline_query_for_www(), -1);
@@ -295,11 +302,11 @@
302 "INSERT OR IGNORE INTO ok VALUES(%d);"
303 "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
304 rx[i], rx[i]
305 );
306 }
307 if( (mRCCFlags & 0x01)==0 ){
308 for(i=0; i<n; i++){
309 db_multi_exec(
310 "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rx[i]
311 );
312 if( db_table_exists("repository","cherrypick") ){
@@ -313,18 +320,20 @@
320 }
321 }
322 }
323 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
324 db_prepare(&q, "%s", blob_sql_text(&sql));
325 memset(&xtra, 0, sizeof(xtra));
326 if( mRCCFlags & 0x02 ){
327 xtra.currentRid = rid;
328 }else{
329 xtra.selectedRid = rid;
330 xtra.secondRid = rid2;
331 }
332 mFlags |= TIMELINE_GRAPH|TIMELINE_FILLGAPS|TIMELINE_NOSCROLL|
333 TIMELINE_XMERGE|TIMELINE_CHPICK;
334 www_print_timeline(&q, mFlags, &xtra);
335 db_finalize(&q);
336 blob_reset(&sql);
337 }
338
339
@@ -508,10 +517,11 @@
517 int rid;
518 Stmt q;
519 int cnt = 0;
520 Blob sql;
521 char const *zType;
522 TimelineXtra xtra;
523
524 login_check_credentials();
525 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
526 rid = name_to_rid_www("name");
527 if( rid==0 ){
@@ -607,12 +617,14 @@
617 #endif
618 blob_zero(&sql);
619 blob_append(&sql, timeline_query_for_www(), -1);
620 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
621 db_prepare(&q, "%s", blob_sql_text(&sql));
622 memset(&xtra, 0, sizeof(xtra));
623 xtra.selectedRid = rid;
624 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
625 &xtra);
626 db_finalize(&q);
627 style_finish_page();
628 }
629
630 /*
@@ -876,11 +888,11 @@
888 if( zHostname ){
889 style_header("Checkout Status: %h on %h", zCwd, zHostname);
890 }else{
891 style_header("Checkout Status: %h", zCwd);
892 }
893 render_checkin_context(vid, 0, 2, 0);
894 @ <hr>
895 zExBase = P("exbase");
896 if( zExBase && zExBase[0] ){
897 char *zPath = decode16_dup(zExBase);
898 char *zCBase = file_canonical_name_dup(zPath?zPath:zExBase);
@@ -1183,11 +1195,11 @@
1195 wiki_render_associated("checkin", zUuid, 0);
1196 }
1197 render_backlink_graph(zUuid,
1198 "<div class=\"section accordion\">References</div>\n");
1199 @ <div class="section accordion">Context</div><div class="accordion_panel">
1200 render_checkin_context(rid, 0, 2, 0);
1201 @ </div><div class="section accordion" id="changes_section">Changes</div>
1202 @ <div class="accordion_panel">
1203 @ <div class="sectionmenu info-changes-menu">
1204 /* ^^^ .info-changes-menu is used by diff scroll sync */
1205 pCfg = construct_diff_flags(diffType, &DCfg);
@@ -3453,74 +3465,93 @@
3465 while( fossil_isspace(zA[0]) ) zA++;
3466 return zA[0]==0 && zB[0]==0;
3467 }
3468
3469 /*
3470 *****************************************************************************
3471 ** The following methods operate on the "newtags" temporary table. This
3472 ** table collects changes for a control artifact that will implement an
3473 ** edit to a check-in.
3474 **
3475 ** Initialize the newtags table
3476 */
3477 static void init_newtags(void){
3478 db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
3479 }
3480
3481 /* Add a new changes to the newtags table */
3482 static void change_special(
3483 const char *zName, /* Name of the special tag */
3484 const char *zOp, /* Operation prefix (e.g. +,-,*) */
3485 const char *zValue /* Value of the tag */
3486 ){
3487 db_multi_exec("REPLACE INTO newtags VALUES(%Q,'%q',%Q)", zName, zOp, zValue);
3488 }
3489
3490 /* Change a symbolic tag */
3491 static void change_sym_tag(const char *zTag, const char *zOp){
3492 db_multi_exec("REPLACE INTO newtags VALUES('sym-%q',%Q,NULL)", zTag, zOp);
3493 }
3494
3495 /* Cancel a tag */
3496 static void cancel_special(const char *zTag){
3497 change_special(zTag,"-",0);
3498 }
3499
3500 /* Add a background color tag. This will be propagating tag
3501 ** if fPropagateColor is true. */
3502 static void add_color(const char *zNewColor, int fPropagateColor){
3503 change_special("bgcolor",fPropagateColor ? "*" : "+", zNewColor);
3504 }
3505
3506 /* Cancel a background color tag */
3507 static void cancel_color(void){
3508 change_special("bgcolor","-",0);
3509 }
3510
3511 /* Add a comment-change tag */
3512 static void add_comment(const char *zNewComment){
3513 change_special("comment","+",zNewComment);
3514 }
3515
3516 /* Add a date-change tag */
3517 static void add_date(const char *zNewDate){
3518 change_special("date","+",zNewDate);
3519 }
3520
3521 /* Add a change-user tag */
3522 static void add_user(const char *zNewUser){
3523 change_special("user","+",zNewUser);
3524 }
3525
3526 /* Add a generic symbolic tag (one that has no value) */
3527 static void add_tag(const char *zNewTag){
3528 change_sym_tag(zNewTag,"+");
3529 }
3530
3531 /* Cancel an existing symbolic tag associated with check-in rid */
3532 static void cancel_tag(int rid, const char *zCancelTag){
3533 if( db_exists("SELECT 1 FROM tagxref, tag"
3534 " WHERE tagxref.rid=%d AND tagtype>0"
3535 " AND tagxref.tagid=tag.tagid AND tagname='sym-%q'",
3536 rid, zCancelTag)
3537 ){
3538 change_sym_tag(zCancelTag,"-");
3539 }
3540 }
3541
3542 /* Add a hidden tag that propagates to all ancestors */
3543 static void hide_branch(void){
3544 change_special("hidden","*",0);
3545 }
3546
3547 /* Close the branch */
3548 static void close_leaf(int rid){
3549 change_special("closed",is_a_leaf(rid)?"+":"*",0);
3550 }
3551
3552 /* Move the check-in to a different branch */
3553 static void change_branch(int rid, const char *zNewBranch){
3554 db_multi_exec(
3555 "REPLACE INTO newtags "
3556 " SELECT tagname, '-', NULL FROM tagxref, tag"
3557 " WHERE tagxref.rid=%d AND tagtype==2"
@@ -3531,19 +3562,31 @@
3562 change_special("branch","*",zNewBranch);
3563 change_sym_tag(zNewBranch,"*");
3564 }
3565
3566 /*
3567 ** Construct a control artifact.
3568 **
3569 ** Preconditions:
3570 **
3571 ** (1) "ctrl" contains the beginning of a control artifact with
3572 ** just the D-card showing the timestamp on the control artifact
3573 ** itself.
3574 **
3575 ** (2) The newtags temporary table has been constructed.
3576 **
3577 ** (3) Zero or more methods may have been called to populate the
3578 ** newtags table. Or the newtags table might be empty.
3579 **
3580 ** Construct the complete control artifact. Or, if there are not
3581 ** changes, just zero out the ctrl blob.
3582 */
3583 static void construct_newtags_artifact(
3584 Blob *ctrl, /* The control artifact text */
3585 int rid, /* rid of the check-in that the artifact applies to */
3586 const char *zUuid, /* UUID of the check-in to which the artifact applies*/
3587 const char *zUserOvrd /* The user name on the control artifact */
 
3588 ){
3589 Stmt q;
3590 int nChng = 0;
3591
3592 db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
@@ -3559,33 +3602,46 @@
3602 blob_appendf(ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
3603 }
3604 }
3605 db_finalize(&q);
3606 if( nChng>0 ){
 
3607 Blob cksum;
3608 if( zUserOvrd && zUserOvrd[0] ){
3609 blob_appendf(ctrl, "U %F\n", zUserOvrd);
3610 }else{
3611 blob_appendf(ctrl, "U %F\n", login_name());
3612 }
3613 md5sum_blob(ctrl, &cksum);
3614 blob_appendf(ctrl, "Z %b\n", &cksum);
3615 blob_reset(&cksum);
3616 }else{
3617 blob_reset(ctrl);
3618 }
3619 }
3620
3621 /*
3622 ** Return true if the artifact is complete. We assume that any non-empty
3623 ** artifact is complete.
3624 */
3625 static int artifact_is_complete(Blob *ctrl){
3626 return blob_size(ctrl)>0;
3627 }
3628
3629 /*
3630 ** Apply a control artifact to the repository database. Except, if the
3631 ** artifact is empty, this routine is a no-op.
3632 */
3633 static void publish_newtags_artifact(Blob *ctrl, int rid){
3634 if( artifact_is_complete(ctrl) ){
3635 int nrid;
3636 g.markPrivate = content_is_private(rid);
3637 nrid = content_put(ctrl);
3638 manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
3639 }
3640 }
3641 /* End of the newtags subsystem
3642 ******************************************************************************/
3643
3644 /*
3645 ** This method checks that the date can be parsed.
3646 ** Returns 1 if datetime() can validate, 0 otherwise.
3647 */
@@ -3644,14 +3700,17 @@
3700 int fPropagateColor; /* True if color propagates before edit */
3701 int fNewPropagateColor; /* True if color propagates after edit */
3702 int fHasHidden = 0; /* True if hidden tag already set */
3703 int fHasClosed = 0; /* True if closed tag already set */
3704 const char *zChngTime = 0; /* Value of chngtime= query param, if any */
3705 int bApply = P("apply")!=0;
3706 int bPreview = P("preview")!=0;
3707 char *zUuid;
3708 Blob comment;
3709 char *zBranchName = 0;
3710 Stmt q;
3711 Blob ctrl; /* The generated control artifact */
3712
3713 login_check_credentials();
3714 if( !g.perm.Write ){ login_needed(g.anon.Write); return; }
3715 rid = name_to_typed_rid(P("r"), "ci");
3716 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
@@ -3684,16 +3743,14 @@
3743 zNewBrFlag = P("newbr") ? " checked" : "";
3744 zNewBranch = PDT("brname","");
3745 zBranchName = branch_of_rid(rid);
3746 zCloseFlag = P("close") ? " checked" : "";
3747 zHideFlag = P("hide") ? " checked" : "";
3748 blob_zero(&ctrl);
3749 if( bApply && !cgi_csrf_safe(2) ) bApply = 0;
3750 if( bApply || bPreview ){
3751 char *zNow = date_in_standard_format(zChngTime ? zChngTime : "now");
 
 
3752 blob_appendf(&ctrl, "D %s\n", zNow);
3753 init_newtags();
3754 if( zNewColorFlag[0]
3755 && zNewColor[0]
3756 && (fPropagateColor!=fNewPropagateColor
@@ -3719,69 +3776,41 @@
3776 db_finalize(&q);
3777 if( zHideFlag[0] ) hide_branch();
3778 if( zCloseFlag[0] ) close_leaf(rid);
3779 if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
3780 if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
3781 construct_newtags_artifact(&ctrl, rid, zUuid, 0);
3782 if( bApply ){
3783 if( artifact_is_complete(&ctrl) ){
3784 db_begin_transaction();
3785 publish_newtags_artifact(&ctrl, rid);
3786 db_end_transaction(0);
3787 blob_reset(&ctrl);
3788 }
3789 cgi_redirectf("%R/ci/%S", zUuid);
3790 }
3791 }
3792 blob_zero(&comment);
3793 blob_append(&comment, zNewComment, -1);
3794 style_header("Edit Check-in %S", zUuid);
3795 @ <div class="section accordion">Original Context Around \
3796 @ Check-in %S(zUuid) on %s(zDate)</div>
3797 @ <div class="accordion_panel">
3798 render_checkin_context(rid, 0, 2, 0);
3799 @ </div>
3800 if( bPreview ){
3801 @ <div class="section accordion">After The Proposed Changes</div>
3802 @ <div class="accordion_panel">
3803 db_begin_transaction();
3804 publish_newtags_artifact(&ctrl, rid);
3805 render_checkin_context(rid, 0, 2, 0);
3806 db_end_transaction(1);
3807 @ </div>
3808 }
3809 @ <div class="section accordion">Proposed Changes For \
3810 @ Check-In %S(zUuid):</div>
3811 @ <div class="accordion_panel">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3812 form_begin(0, "%R/ci_edit");
3813 @ <div><input type="hidden" name="r" value="%s(zUuid)">
3814 @ <table border="0" cellspacing="10">
3815
3816 @ <tr><th align="right" valign="top">User:</th>
@@ -3919,10 +3948,11 @@
3948 if( P("preview") ){
3949 @ <input type="submit" name="apply" value="Apply Changes">
3950 }
3951 @ </td></tr>
3952 @ </table>
3953 login_insert_csrf_secret();
3954 @ </div></form>
3955 builtin_request_js("ci_edit.js");
3956 style_finish_page();
3957 }
3958
@@ -4194,13 +4224,19 @@
4224 fossil_free((void *)pzCancelTags);
4225 }
4226 if( fHide && !fHasHidden ) hide_branch();
4227 if( fClose && !fHasClosed ) close_leaf(rid);
4228 if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
4229 construct_newtags_artifact(&ctrl, rid, zUuid, zUserOvrd);
4230 if( fDryRun ){
4231 fossil_print("%s", blob_str(&ctrl));
4232 blob_reset(&ctrl);
4233 }else{
4234 db_begin_transaction();
4235 publish_newtags_artifact(&ctrl, rid);
4236 show_common_info(rid, "hash:", 1, 0);
4237 db_end_transaction(0);
4238 }
4239 if( g.localOpen ){
4240 manifest_to_disk(rid);
4241 }
4242 }
4243
+1 -1
--- src/moderate.c
+++ src/moderate.c
@@ -186,11 +186,11 @@
186186
blob_append_sql(&sql,
187187
" AND event.objid IN (SELECT objid FROM modreq)"
188188
" ORDER BY event.mtime DESC"
189189
);
190190
db_prepare(&q, "%s", blob_sql_text(&sql));
191
- www_print_timeline(&q, 0, 0, 0, 0, 0, 0, 0);
191
+ www_print_timeline(&q, 0, 0);
192192
db_finalize(&q);
193193
}
194194
style_finish_page();
195195
}
196196
197197
--- src/moderate.c
+++ src/moderate.c
@@ -186,11 +186,11 @@
186 blob_append_sql(&sql,
187 " AND event.objid IN (SELECT objid FROM modreq)"
188 " ORDER BY event.mtime DESC"
189 );
190 db_prepare(&q, "%s", blob_sql_text(&sql));
191 www_print_timeline(&q, 0, 0, 0, 0, 0, 0, 0);
192 db_finalize(&q);
193 }
194 style_finish_page();
195 }
196
197
--- src/moderate.c
+++ src/moderate.c
@@ -186,11 +186,11 @@
186 blob_append_sql(&sql,
187 " AND event.objid IN (SELECT objid FROM modreq)"
188 " ORDER BY event.mtime DESC"
189 );
190 db_prepare(&q, "%s", blob_sql_text(&sql));
191 www_print_timeline(&q, 0, 0);
192 db_finalize(&q);
193 }
194 style_finish_page();
195 }
196
197
+1 -1
--- src/tag.c
+++ src/tag.c
@@ -938,11 +938,11 @@
938938
** many descenders to (off-screen) parents. */
939939
tmFlags = TIMELINE_XMERGE | TIMELINE_FILLGAPS | TIMELINE_NOSCROLL;
940940
if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
941941
if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
942942
if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
943
- www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
943
+ www_print_timeline(&q, tmFlags, 0);
944944
db_finalize(&q);
945945
@ <br>
946946
style_finish_page();
947947
}
948948
949949
--- src/tag.c
+++ src/tag.c
@@ -938,11 +938,11 @@
938 ** many descenders to (off-screen) parents. */
939 tmFlags = TIMELINE_XMERGE | TIMELINE_FILLGAPS | TIMELINE_NOSCROLL;
940 if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
941 if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
942 if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
943 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
944 db_finalize(&q);
945 @ <br>
946 style_finish_page();
947 }
948
949
--- src/tag.c
+++ src/tag.c
@@ -938,11 +938,11 @@
938 ** many descenders to (off-screen) parents. */
939 tmFlags = TIMELINE_XMERGE | TIMELINE_FILLGAPS | TIMELINE_NOSCROLL;
940 if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
941 if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
942 if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
943 www_print_timeline(&q, tmFlags, 0);
944 db_finalize(&q);
945 @ <br>
946 style_finish_page();
947 }
948
949
+4 -1
--- src/tar.c
+++ src/tar.c
@@ -1248,10 +1248,11 @@
12481248
12491249
n = db_int(0, "SELECT count(*) FROM tarlist");
12501250
if( n==0 ){
12511251
@ <h2>No tarball/ZIP suggestions are available at this time</h2>
12521252
}else{
1253
+ TimelineXtra xtra;
12531254
@ <h2>%d(n) Tarball/ZIP Download Suggestion%s(n>1?"s":""):</h2>
12541255
db_prepare(&q,
12551256
"WITH matches AS (%s AND blob.rid IN (SELECT rid FROM tarlist))\n"
12561257
"SELECT blobRid, uuid, timestamp,"
12571258
" com||comment,"
@@ -1261,11 +1262,13 @@
12611262
timeline_query_for_www()
12621263
);
12631264
12641265
tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL | TIMELINE_COLUMNAR
12651266
| TIMELINE_BRCOLOR;
1266
- www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, download_extra);
1267
+ memset(&xtra, 0, sizeof(xtra));
1268
+ xtra.xExtra = download_extra;
1269
+ www_print_timeline(&q, tmFlags, &xtra);
12671270
db_finalize(&q);
12681271
}
12691272
if( g.perm.Clone ){
12701273
char *zNm = fossil_strdup(db_get("project-name","clone"));
12711274
sanitize_name(zNm);
12721275
--- src/tar.c
+++ src/tar.c
@@ -1248,10 +1248,11 @@
1248
1249 n = db_int(0, "SELECT count(*) FROM tarlist");
1250 if( n==0 ){
1251 @ <h2>No tarball/ZIP suggestions are available at this time</h2>
1252 }else{
 
1253 @ <h2>%d(n) Tarball/ZIP Download Suggestion%s(n>1?"s":""):</h2>
1254 db_prepare(&q,
1255 "WITH matches AS (%s AND blob.rid IN (SELECT rid FROM tarlist))\n"
1256 "SELECT blobRid, uuid, timestamp,"
1257 " com||comment,"
@@ -1261,11 +1262,13 @@
1261 timeline_query_for_www()
1262 );
1263
1264 tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL | TIMELINE_COLUMNAR
1265 | TIMELINE_BRCOLOR;
1266 www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, download_extra);
 
 
1267 db_finalize(&q);
1268 }
1269 if( g.perm.Clone ){
1270 char *zNm = fossil_strdup(db_get("project-name","clone"));
1271 sanitize_name(zNm);
1272
--- src/tar.c
+++ src/tar.c
@@ -1248,10 +1248,11 @@
1248
1249 n = db_int(0, "SELECT count(*) FROM tarlist");
1250 if( n==0 ){
1251 @ <h2>No tarball/ZIP suggestions are available at this time</h2>
1252 }else{
1253 TimelineXtra xtra;
1254 @ <h2>%d(n) Tarball/ZIP Download Suggestion%s(n>1?"s":""):</h2>
1255 db_prepare(&q,
1256 "WITH matches AS (%s AND blob.rid IN (SELECT rid FROM tarlist))\n"
1257 "SELECT blobRid, uuid, timestamp,"
1258 " com||comment,"
@@ -1261,11 +1262,13 @@
1262 timeline_query_for_www()
1263 );
1264
1265 tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL | TIMELINE_COLUMNAR
1266 | TIMELINE_BRCOLOR;
1267 memset(&xtra, 0, sizeof(xtra));
1268 xtra.xExtra = download_extra;
1269 www_print_timeline(&q, tmFlags, &xtra);
1270 db_finalize(&q);
1271 }
1272 if( g.perm.Clone ){
1273 char *zNm = fossil_strdup(db_get("project-name","clone"));
1274 sanitize_name(zNm);
1275
+54 -30
--- src/timeline.c
+++ src/timeline.c
@@ -308,10 +308,27 @@
308308
if( (tmFlags & TIMELINE_INLINE)!=0 ){
309309
cgi_printf(")");
310310
}
311311
}
312312
313
+/*
314
+** Clients of the www_print_timeline() routine can create an instance
315
+** of the following object to pass supplemental information into
316
+** www_print_timeline() as the last (8th) argument.
317
+*/
318
+#if INTERFACE
319
+struct TimelineXtra {
320
+ void (*xExtra)(Stmt*,int,const char*,const char*); /* generate "extra" text */
321
+ const char *zThisUser; /* Suppress links to this user */
322
+ const char *zThisTag; /* Suppress links to this tag */
323
+ Matcher *pLeftBranch; /* Comparison function to use for zLeftBranch */
324
+ int selectedRid; /* CSS: timelineSelected */
325
+ int secondRid; /* CSS: timelineSelected timelineSecondary */
326
+ int currentRid; /* CSS: timelineCurrent */
327
+};
328
+#endif
329
+
313330
314331
/*
315332
** SETTING: timeline-truncate-at-blank boolean default=off
316333
**
317334
** If enabled, check-in comments displayed on the timeline are truncated
@@ -363,16 +380,11 @@
363380
** 10. Short comment to user for repeated tickets and wiki
364381
*/
365382
void www_print_timeline(
366383
Stmt *pQuery, /* Query to implement the timeline */
367384
int tmFlags, /* Flags controlling display behavior */
368
- const char *zThisUser, /* Suppress links to this user */
369
- const char *zThisTag, /* Suppress links to this tag */
370
- Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
371
- int selectedRid, /* Highlight the line with this RID value or zero */
372
- int secondRid, /* Secondary highlight (or zero) */
373
- void (*xExtra)(Stmt*,int,const char*,const char*) /* generate "extra" text */
385
+ TimelineXtra *pXtra /* Supplemental information */
374386
){
375387
int mxWikiLen;
376388
Blob comment;
377389
int prevTagid = 0;
378390
int suppressCnt = 0;
@@ -389,16 +401,21 @@
389401
const char *zDateFmt;
390402
int iTableId = timeline_tableid();
391403
int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info
392404
** page rather than the /timeline page */
393405
const char *zMainBranch = db_main_branch();
406
+ TimelineXtra zeroXtra; /* Substitute for pXtra==NULL */
394407
395
-
396
- if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
408
+ if( pXtra==0 ){
409
+ memset(&zeroXtra, 0, sizeof(zeroXtra));
410
+ pXtra = &zeroXtra;
411
+ }
412
+ if( pXtra->currentRid ){
413
+ vid = pXtra->currentRid;
414
+ }else if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
397415
vid = db_lget_int("checkout", 0);
398416
}
399
- if( xExtra==0 ) xExtra = timeline_extra;
400417
zPrevDate[0] = 0;
401418
mxWikiLen = db_get_int("timeline-max-comment", 0);
402419
dateFormat = db_get_int("timeline-date-format", 0);
403420
bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
404421
bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
@@ -442,11 +459,11 @@
442459
int commentColumn = 3; /* Column containing comment text */
443460
int modPending; /* Pending moderation */
444461
char *zDateLink; /* URL for the link on the timestamp */
445462
int drawDetailEllipsis; /* True to show ellipsis in place of detail */
446463
int gidx = 0; /* Graph row identifier */
447
- int isSelectedOrCurrent = 0; /* True if current row is selected */
464
+ int omitCommentColor = 0; /* True to skip added color to comments */
448465
const char *zExtraClass = "";
449466
char zTime[20];
450467
451468
if( zDate==0 ){
452469
zDate = "YYYY-MM-DD HH:MM:SS"; /* Something wrong with the repo */
@@ -513,19 +530,18 @@
513530
zTime[pos++] = 0;
514531
}else{
515532
zTime[0] = 0;
516533
}
517534
pendingEndTr = 1;
518
- if( rid==selectedRid ){
535
+ if( rid==pXtra->selectedRid ){
519536
@ <tr class="timelineSelected">
520
- isSelectedOrCurrent = 1;
521
- }else if( rid==secondRid ){
537
+ omitCommentColor = 1;
538
+ }else if( rid==pXtra->secondRid ){
522539
@ <tr class="timelineSelected timelineSecondary">
523
- isSelectedOrCurrent = 1;
540
+ omitCommentColor = 1;
524541
}else if( rid==vid ){
525542
@ <tr class="timelineCurrent">
526
- isSelectedOrCurrent = 1;
527543
}else {
528544
@ <tr>
529545
}
530546
if( zType[0]=='t' && tagid && (tmFlags & TIMELINE_NOTKT)==0 ){
531547
char *zTktid = db_text(0, "SELECT substr(tagname,5) FROM tag"
@@ -642,14 +658,14 @@
642658
gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0);
643659
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
644660
}
645661
fossil_free(zBr);
646662
@</td>
647
- if( !isSelectedOrCurrent ){
648
- @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
649
- }else{
663
+ if( omitCommentColor ){
650664
@ <td class="timeline%s(zStyle)Cell%s(zExtraClass)">
665
+ }else{
666
+ @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
651667
}
652668
if( pGraph ){
653669
if( zType[0]=='e' ){
654670
@ <b>Note:</b>
655671
}else if( zType[0]!='c' ){
@@ -771,21 +787,25 @@
771787
if( drawDetailEllipsis ){
772788
@ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
773789
@ data-id='%d(rid)'>...</span>
774790
}
775791
if( tmFlags & TIMELINE_COLUMNAR ){
776
- if( !isSelectedOrCurrent ){
777
- @ <td class="timelineDetailCell%s(zExtraClass)" id='md%d(gidx)'>
778
- }else{
792
+ if( omitCommentColor ){
779793
@ <td class="timelineDetailCell%s(zExtraClass)">
794
+ }else{
795
+ @ <td class="timelineDetailCell%s(zExtraClass)" id='md%d(gidx)'>
780796
}
781797
}
782798
if( tmFlags & TIMELINE_COMPACT ){
783799
cgi_printf("<span class='clutter' id='detail-%d'>",rid);
784800
}
785801
cgi_printf("<span class='timeline%sDetail'>", zStyle);
786
- xExtra(pQuery, tmFlags, zThisUser, zThisTag);
802
+ if( pXtra->xExtra ){
803
+ pXtra->xExtra(pQuery, tmFlags, pXtra->zThisUser, pXtra->zThisTag);
804
+ }else{
805
+ timeline_extra(pQuery, tmFlags, pXtra->zThisUser, pXtra->zThisTag);
806
+ }
787807
if( tmFlags & TIMELINE_COMPACT ){
788808
@ </span></span>
789809
}else{
790810
@ </span>
791811
}
@@ -904,11 +924,11 @@
904924
}
905925
if( pendingEndTr ){
906926
@ </td></tr>
907927
}
908928
if( pGraph ){
909
- graph_finish(pGraph, pLeftBranch, tmFlags);
929
+ graph_finish(pGraph, pXtra->pLeftBranch, tmFlags);
910930
if( pGraph->nErr ){
911931
graph_free(pGraph);
912932
pGraph = 0;
913933
}else{
914934
@ <tr class="timelineBottom" id="btm-%d(iTableId)">\
@@ -3256,26 +3276,30 @@
32563276
@ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
32573277
@ &nbsp;&uarr;</a>
32583278
}
32593279
cgi_check_for_malice();
32603280
{
3261
- Matcher *pLeftBranch;
3281
+ TimelineXtra xtra;
32623282
const char *zPattern = P("sl");
3283
+ memset(&xtra, 0, sizeof(xtra));
32633284
if( zPattern!=0 ){
32643285
MatchStyle ms;
32653286
if( zMatchStyle!=0 ){
32663287
ms = matchStyle;
32673288
}else{
32683289
ms = strpbrk(zPattern,"*[?")!=0 ? MS_GLOB : MS_BRLIST;
32693290
}
3270
- pLeftBranch = match_create(ms,zPattern);
3291
+ xtra.pLeftBranch = match_create(ms,zPattern);
32713292
}else{
3272
- pLeftBranch = match_create(matchStyle, zBrName?zBrName:zTagName);
3293
+ xtra.pLeftBranch = match_create(matchStyle, zBrName?zBrName:zTagName);
32733294
}
3274
- www_print_timeline(&q, tmFlags, zThisUser, zThisTag, pLeftBranch,
3275
- selectedRid, secondaryRid, 0);
3276
- match_free(pLeftBranch);
3295
+ xtra.zThisUser = zThisUser;
3296
+ xtra.zThisTag = zThisTag;
3297
+ xtra.selectedRid = selectedRid;
3298
+ xtra.secondRid = secondaryRid;
3299
+ www_print_timeline(&q, tmFlags, &xtra);
3300
+ match_free(xtra.pLeftBranch);
32773301
}
32783302
db_finalize(&q);
32793303
if( zOlderButton ){
32803304
@ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
32813305
@ &nbsp;&darr;</a>
@@ -4077,11 +4101,11 @@
40774101
zId = db_text(0, "SELECT timestamp FROM timeline"
40784102
" ORDER BY sortby DESC LIMIT 1");
40794103
@ <h2>%d(iAgo) Year%s(iAgo>1?"s":"") Ago
40804104
@ <small>%z(href("%R/timeline?c=%t",zId))(more context)</a>\
40814105
@ </small></h2>
4082
- www_print_timeline(&q, TIMELINE_GRAPH, 0, 0, 0, 0, 0, 0);
4106
+ www_print_timeline(&q, TIMELINE_GRAPH, 0);
40834107
}
40844108
db_finalize(&q);
40854109
style_finish_page();
40864110
}
40874111
40884112
--- src/timeline.c
+++ src/timeline.c
@@ -308,10 +308,27 @@
308 if( (tmFlags & TIMELINE_INLINE)!=0 ){
309 cgi_printf(")");
310 }
311 }
312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
314 /*
315 ** SETTING: timeline-truncate-at-blank boolean default=off
316 **
317 ** If enabled, check-in comments displayed on the timeline are truncated
@@ -363,16 +380,11 @@
363 ** 10. Short comment to user for repeated tickets and wiki
364 */
365 void www_print_timeline(
366 Stmt *pQuery, /* Query to implement the timeline */
367 int tmFlags, /* Flags controlling display behavior */
368 const char *zThisUser, /* Suppress links to this user */
369 const char *zThisTag, /* Suppress links to this tag */
370 Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
371 int selectedRid, /* Highlight the line with this RID value or zero */
372 int secondRid, /* Secondary highlight (or zero) */
373 void (*xExtra)(Stmt*,int,const char*,const char*) /* generate "extra" text */
374 ){
375 int mxWikiLen;
376 Blob comment;
377 int prevTagid = 0;
378 int suppressCnt = 0;
@@ -389,16 +401,21 @@
389 const char *zDateFmt;
390 int iTableId = timeline_tableid();
391 int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info
392 ** page rather than the /timeline page */
393 const char *zMainBranch = db_main_branch();
 
394
395
396 if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
 
 
 
 
 
397 vid = db_lget_int("checkout", 0);
398 }
399 if( xExtra==0 ) xExtra = timeline_extra;
400 zPrevDate[0] = 0;
401 mxWikiLen = db_get_int("timeline-max-comment", 0);
402 dateFormat = db_get_int("timeline-date-format", 0);
403 bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
404 bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
@@ -442,11 +459,11 @@
442 int commentColumn = 3; /* Column containing comment text */
443 int modPending; /* Pending moderation */
444 char *zDateLink; /* URL for the link on the timestamp */
445 int drawDetailEllipsis; /* True to show ellipsis in place of detail */
446 int gidx = 0; /* Graph row identifier */
447 int isSelectedOrCurrent = 0; /* True if current row is selected */
448 const char *zExtraClass = "";
449 char zTime[20];
450
451 if( zDate==0 ){
452 zDate = "YYYY-MM-DD HH:MM:SS"; /* Something wrong with the repo */
@@ -513,19 +530,18 @@
513 zTime[pos++] = 0;
514 }else{
515 zTime[0] = 0;
516 }
517 pendingEndTr = 1;
518 if( rid==selectedRid ){
519 @ <tr class="timelineSelected">
520 isSelectedOrCurrent = 1;
521 }else if( rid==secondRid ){
522 @ <tr class="timelineSelected timelineSecondary">
523 isSelectedOrCurrent = 1;
524 }else if( rid==vid ){
525 @ <tr class="timelineCurrent">
526 isSelectedOrCurrent = 1;
527 }else {
528 @ <tr>
529 }
530 if( zType[0]=='t' && tagid && (tmFlags & TIMELINE_NOTKT)==0 ){
531 char *zTktid = db_text(0, "SELECT substr(tagname,5) FROM tag"
@@ -642,14 +658,14 @@
642 gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0);
643 @ <div id="m%d(gidx)" class="tl-nodemark"></div>
644 }
645 fossil_free(zBr);
646 @</td>
647 if( !isSelectedOrCurrent ){
648 @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
649 }else{
650 @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)">
 
 
651 }
652 if( pGraph ){
653 if( zType[0]=='e' ){
654 @ <b>Note:</b>
655 }else if( zType[0]!='c' ){
@@ -771,21 +787,25 @@
771 if( drawDetailEllipsis ){
772 @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
773 @ data-id='%d(rid)'>...</span>
774 }
775 if( tmFlags & TIMELINE_COLUMNAR ){
776 if( !isSelectedOrCurrent ){
777 @ <td class="timelineDetailCell%s(zExtraClass)" id='md%d(gidx)'>
778 }else{
779 @ <td class="timelineDetailCell%s(zExtraClass)">
 
 
780 }
781 }
782 if( tmFlags & TIMELINE_COMPACT ){
783 cgi_printf("<span class='clutter' id='detail-%d'>",rid);
784 }
785 cgi_printf("<span class='timeline%sDetail'>", zStyle);
786 xExtra(pQuery, tmFlags, zThisUser, zThisTag);
 
 
 
 
787 if( tmFlags & TIMELINE_COMPACT ){
788 @ </span></span>
789 }else{
790 @ </span>
791 }
@@ -904,11 +924,11 @@
904 }
905 if( pendingEndTr ){
906 @ </td></tr>
907 }
908 if( pGraph ){
909 graph_finish(pGraph, pLeftBranch, tmFlags);
910 if( pGraph->nErr ){
911 graph_free(pGraph);
912 pGraph = 0;
913 }else{
914 @ <tr class="timelineBottom" id="btm-%d(iTableId)">\
@@ -3256,26 +3276,30 @@
3256 @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
3257 @ &nbsp;&uarr;</a>
3258 }
3259 cgi_check_for_malice();
3260 {
3261 Matcher *pLeftBranch;
3262 const char *zPattern = P("sl");
 
3263 if( zPattern!=0 ){
3264 MatchStyle ms;
3265 if( zMatchStyle!=0 ){
3266 ms = matchStyle;
3267 }else{
3268 ms = strpbrk(zPattern,"*[?")!=0 ? MS_GLOB : MS_BRLIST;
3269 }
3270 pLeftBranch = match_create(ms,zPattern);
3271 }else{
3272 pLeftBranch = match_create(matchStyle, zBrName?zBrName:zTagName);
3273 }
3274 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, pLeftBranch,
3275 selectedRid, secondaryRid, 0);
3276 match_free(pLeftBranch);
 
 
 
3277 }
3278 db_finalize(&q);
3279 if( zOlderButton ){
3280 @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
3281 @ &nbsp;&darr;</a>
@@ -4077,11 +4101,11 @@
4077 zId = db_text(0, "SELECT timestamp FROM timeline"
4078 " ORDER BY sortby DESC LIMIT 1");
4079 @ <h2>%d(iAgo) Year%s(iAgo>1?"s":"") Ago
4080 @ <small>%z(href("%R/timeline?c=%t",zId))(more context)</a>\
4081 @ </small></h2>
4082 www_print_timeline(&q, TIMELINE_GRAPH, 0, 0, 0, 0, 0, 0);
4083 }
4084 db_finalize(&q);
4085 style_finish_page();
4086 }
4087
4088
--- src/timeline.c
+++ src/timeline.c
@@ -308,10 +308,27 @@
308 if( (tmFlags & TIMELINE_INLINE)!=0 ){
309 cgi_printf(")");
310 }
311 }
312
313 /*
314 ** Clients of the www_print_timeline() routine can create an instance
315 ** of the following object to pass supplemental information into
316 ** www_print_timeline() as the last (8th) argument.
317 */
318 #if INTERFACE
319 struct TimelineXtra {
320 void (*xExtra)(Stmt*,int,const char*,const char*); /* generate "extra" text */
321 const char *zThisUser; /* Suppress links to this user */
322 const char *zThisTag; /* Suppress links to this tag */
323 Matcher *pLeftBranch; /* Comparison function to use for zLeftBranch */
324 int selectedRid; /* CSS: timelineSelected */
325 int secondRid; /* CSS: timelineSelected timelineSecondary */
326 int currentRid; /* CSS: timelineCurrent */
327 };
328 #endif
329
330
331 /*
332 ** SETTING: timeline-truncate-at-blank boolean default=off
333 **
334 ** If enabled, check-in comments displayed on the timeline are truncated
@@ -363,16 +380,11 @@
380 ** 10. Short comment to user for repeated tickets and wiki
381 */
382 void www_print_timeline(
383 Stmt *pQuery, /* Query to implement the timeline */
384 int tmFlags, /* Flags controlling display behavior */
385 TimelineXtra *pXtra /* Supplemental information */
 
 
 
 
 
386 ){
387 int mxWikiLen;
388 Blob comment;
389 int prevTagid = 0;
390 int suppressCnt = 0;
@@ -389,16 +401,21 @@
401 const char *zDateFmt;
402 int iTableId = timeline_tableid();
403 int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info
404 ** page rather than the /timeline page */
405 const char *zMainBranch = db_main_branch();
406 TimelineXtra zeroXtra; /* Substitute for pXtra==NULL */
407
408 if( pXtra==0 ){
409 memset(&zeroXtra, 0, sizeof(zeroXtra));
410 pXtra = &zeroXtra;
411 }
412 if( pXtra->currentRid ){
413 vid = pXtra->currentRid;
414 }else if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
415 vid = db_lget_int("checkout", 0);
416 }
 
417 zPrevDate[0] = 0;
418 mxWikiLen = db_get_int("timeline-max-comment", 0);
419 dateFormat = db_get_int("timeline-date-format", 0);
420 bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
421 bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
@@ -442,11 +459,11 @@
459 int commentColumn = 3; /* Column containing comment text */
460 int modPending; /* Pending moderation */
461 char *zDateLink; /* URL for the link on the timestamp */
462 int drawDetailEllipsis; /* True to show ellipsis in place of detail */
463 int gidx = 0; /* Graph row identifier */
464 int omitCommentColor = 0; /* True to skip added color to comments */
465 const char *zExtraClass = "";
466 char zTime[20];
467
468 if( zDate==0 ){
469 zDate = "YYYY-MM-DD HH:MM:SS"; /* Something wrong with the repo */
@@ -513,19 +530,18 @@
530 zTime[pos++] = 0;
531 }else{
532 zTime[0] = 0;
533 }
534 pendingEndTr = 1;
535 if( rid==pXtra->selectedRid ){
536 @ <tr class="timelineSelected">
537 omitCommentColor = 1;
538 }else if( rid==pXtra->secondRid ){
539 @ <tr class="timelineSelected timelineSecondary">
540 omitCommentColor = 1;
541 }else if( rid==vid ){
542 @ <tr class="timelineCurrent">
 
543 }else {
544 @ <tr>
545 }
546 if( zType[0]=='t' && tagid && (tmFlags & TIMELINE_NOTKT)==0 ){
547 char *zTktid = db_text(0, "SELECT substr(tagname,5) FROM tag"
@@ -642,14 +658,14 @@
658 gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0);
659 @ <div id="m%d(gidx)" class="tl-nodemark"></div>
660 }
661 fossil_free(zBr);
662 @</td>
663 if( omitCommentColor ){
 
 
664 @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)">
665 }else{
666 @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
667 }
668 if( pGraph ){
669 if( zType[0]=='e' ){
670 @ <b>Note:</b>
671 }else if( zType[0]!='c' ){
@@ -771,21 +787,25 @@
787 if( drawDetailEllipsis ){
788 @ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
789 @ data-id='%d(rid)'>...</span>
790 }
791 if( tmFlags & TIMELINE_COLUMNAR ){
792 if( omitCommentColor ){
 
 
793 @ <td class="timelineDetailCell%s(zExtraClass)">
794 }else{
795 @ <td class="timelineDetailCell%s(zExtraClass)" id='md%d(gidx)'>
796 }
797 }
798 if( tmFlags & TIMELINE_COMPACT ){
799 cgi_printf("<span class='clutter' id='detail-%d'>",rid);
800 }
801 cgi_printf("<span class='timeline%sDetail'>", zStyle);
802 if( pXtra->xExtra ){
803 pXtra->xExtra(pQuery, tmFlags, pXtra->zThisUser, pXtra->zThisTag);
804 }else{
805 timeline_extra(pQuery, tmFlags, pXtra->zThisUser, pXtra->zThisTag);
806 }
807 if( tmFlags & TIMELINE_COMPACT ){
808 @ </span></span>
809 }else{
810 @ </span>
811 }
@@ -904,11 +924,11 @@
924 }
925 if( pendingEndTr ){
926 @ </td></tr>
927 }
928 if( pGraph ){
929 graph_finish(pGraph, pXtra->pLeftBranch, tmFlags);
930 if( pGraph->nErr ){
931 graph_free(pGraph);
932 pGraph = 0;
933 }else{
934 @ <tr class="timelineBottom" id="btm-%d(iTableId)">\
@@ -3256,26 +3276,30 @@
3276 @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
3277 @ &nbsp;&uarr;</a>
3278 }
3279 cgi_check_for_malice();
3280 {
3281 TimelineXtra xtra;
3282 const char *zPattern = P("sl");
3283 memset(&xtra, 0, sizeof(xtra));
3284 if( zPattern!=0 ){
3285 MatchStyle ms;
3286 if( zMatchStyle!=0 ){
3287 ms = matchStyle;
3288 }else{
3289 ms = strpbrk(zPattern,"*[?")!=0 ? MS_GLOB : MS_BRLIST;
3290 }
3291 xtra.pLeftBranch = match_create(ms,zPattern);
3292 }else{
3293 xtra.pLeftBranch = match_create(matchStyle, zBrName?zBrName:zTagName);
3294 }
3295 xtra.zThisUser = zThisUser;
3296 xtra.zThisTag = zThisTag;
3297 xtra.selectedRid = selectedRid;
3298 xtra.secondRid = secondaryRid;
3299 www_print_timeline(&q, tmFlags, &xtra);
3300 match_free(xtra.pLeftBranch);
3301 }
3302 db_finalize(&q);
3303 if( zOlderButton ){
3304 @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
3305 @ &nbsp;&darr;</a>
@@ -4077,11 +4101,11 @@
4101 zId = db_text(0, "SELECT timestamp FROM timeline"
4102 " ORDER BY sortby DESC LIMIT 1");
4103 @ <h2>%d(iAgo) Year%s(iAgo>1?"s":"") Ago
4104 @ <small>%z(href("%R/timeline?c=%t",zId))(more context)</a>\
4105 @ </small></h2>
4106 www_print_timeline(&q, TIMELINE_GRAPH, 0);
4107 }
4108 db_finalize(&q);
4109 style_finish_page();
4110 }
4111
4112
+1 -1
--- src/tkt.c
+++ src/tkt.c
@@ -1218,11 +1218,11 @@
12181218
}
12191219
db_prepare(&q, "%z", zSQL/*safe-for-%s*/);
12201220
www_print_timeline(&q,
12211221
TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT |
12221222
TIMELINE_REFS,
1223
- 0, 0, 0, 0, 0, 0);
1223
+ 0);
12241224
db_finalize(&q);
12251225
fossil_free(zFullUuid);
12261226
}
12271227
12281228
/*
12291229
--- src/tkt.c
+++ src/tkt.c
@@ -1218,11 +1218,11 @@
1218 }
1219 db_prepare(&q, "%z", zSQL/*safe-for-%s*/);
1220 www_print_timeline(&q,
1221 TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT |
1222 TIMELINE_REFS,
1223 0, 0, 0, 0, 0, 0);
1224 db_finalize(&q);
1225 fossil_free(zFullUuid);
1226 }
1227
1228 /*
1229
--- src/tkt.c
+++ src/tkt.c
@@ -1218,11 +1218,11 @@
1218 }
1219 db_prepare(&q, "%z", zSQL/*safe-for-%s*/);
1220 www_print_timeline(&q,
1221 TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT |
1222 TIMELINE_REFS,
1223 0);
1224 db_finalize(&q);
1225 fossil_free(zFullUuid);
1226 }
1227
1228 /*
1229

Keyboard Shortcuts

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