Fossil SCM

Merged with [82fc5abb60].

aku 2008-02-27 04:15 trunk merge
Commit 2e84fb3e544008c383c28b49580020d608983ca8
--- src/descendents.c
+++ src/descendents.c
@@ -196,11 +196,11 @@
196196
" AND blob.rid IN"
197197
" (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
198198
" ORDER BY event.mtime DESC",
199199
timeline_query_for_www()
200200
);
201
- www_print_timeline(&q, 0, 0, 0, 0);
201
+ www_print_timeline(&q);
202202
db_finalize(&q);
203203
@ <script>
204204
@ function xin(id){
205205
@ }
206206
@ function xout(id){
207207
--- src/descendents.c
+++ src/descendents.c
@@ -196,11 +196,11 @@
196 " AND blob.rid IN"
197 " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
198 " ORDER BY event.mtime DESC",
199 timeline_query_for_www()
200 );
201 www_print_timeline(&q, 0, 0, 0, 0);
202 db_finalize(&q);
203 @ <script>
204 @ function xin(id){
205 @ }
206 @ function xout(id){
207
--- src/descendents.c
+++ src/descendents.c
@@ -196,11 +196,11 @@
196 " AND blob.rid IN"
197 " (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
198 " ORDER BY event.mtime DESC",
199 timeline_query_for_www()
200 );
201 www_print_timeline(&q);
202 db_finalize(&q);
203 @ <script>
204 @ function xin(id){
205 @ }
206 @ function xout(id){
207
+3 -2
--- src/info.c
+++ src/info.c
@@ -332,12 +332,13 @@
332332
}
333333
@ <tr><th>Original&nbsp;User:</th><td>%h(db_column_text(&q, 2))</td></tr>
334334
@ <tr><th>Original&nbsp;Comment:</th><td>%w(db_column_text(&q,3))</td></tr>
335335
@ </td></tr>
336336
@ <tr><th>Timelines:</th><td>
337
- @ <a href="%s(g.zBaseURL)/timeline?e=%d(rid)&r">ancestors</a>
338
- @ | <a href="%s(g.zBaseURL)/timeline?e=%d(rid)&r&a">descendents</a>
337
+ @ <a href="%s(g.zBaseURL)/timeline?p=%d(rid)">ancestors</a>
338
+ @ | <a href="%s(g.zBaseURL)/timeline?d=%d(rid)">descendents</a>
339
+ @ | <a href="%s(g.zBaseURL)/timeline?d=%d(rid)&p=%d(rid)">both</a>
339340
@ </td></tr>
340341
@ <tr><th>Commands:</th>
341342
@ <td>
342343
@ <a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a>
343344
@ | <a href="%s(g.zBaseURL)/zip/%s(zUuid).zip">ZIP archive</a>
344345
--- src/info.c
+++ src/info.c
@@ -332,12 +332,13 @@
332 }
333 @ <tr><th>Original&nbsp;User:</th><td>%h(db_column_text(&q, 2))</td></tr>
334 @ <tr><th>Original&nbsp;Comment:</th><td>%w(db_column_text(&q,3))</td></tr>
335 @ </td></tr>
336 @ <tr><th>Timelines:</th><td>
337 @ <a href="%s(g.zBaseURL)/timeline?e=%d(rid)&r">ancestors</a>
338 @ | <a href="%s(g.zBaseURL)/timeline?e=%d(rid)&r&a">descendents</a>
 
339 @ </td></tr>
340 @ <tr><th>Commands:</th>
341 @ <td>
342 @ <a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a>
343 @ | <a href="%s(g.zBaseURL)/zip/%s(zUuid).zip">ZIP archive</a>
344
--- src/info.c
+++ src/info.c
@@ -332,12 +332,13 @@
332 }
333 @ <tr><th>Original&nbsp;User:</th><td>%h(db_column_text(&q, 2))</td></tr>
334 @ <tr><th>Original&nbsp;Comment:</th><td>%w(db_column_text(&q,3))</td></tr>
335 @ </td></tr>
336 @ <tr><th>Timelines:</th><td>
337 @ <a href="%s(g.zBaseURL)/timeline?p=%d(rid)">ancestors</a>
338 @ | <a href="%s(g.zBaseURL)/timeline?d=%d(rid)">descendents</a>
339 @ | <a href="%s(g.zBaseURL)/timeline?d=%d(rid)&p=%d(rid)">both</a>
340 @ </td></tr>
341 @ <tr><th>Commands:</th>
342 @ <td>
343 @ <a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a>
344 @ | <a href="%s(g.zBaseURL)/zip/%s(zUuid).zip">ZIP archive</a>
345
--- src/stat.c
+++ src/stat.c
--- src/stat.c
+++ src/stat.c
0
--- src/stat.c
+++ src/stat.c
0
+175 -161
--- src/timeline.c
+++ src/timeline.c
@@ -89,17 +89,12 @@
8989
** 7. True if is a leaf
9090
** 8. background color
9191
** 9. type ("ci", "w")
9292
*/
9393
void www_print_timeline(
94
- Stmt *pQuery,
95
- int *pFirstEvent,
96
- int *pLastEvent,
97
- int (*xCallback)(int, Blob*),
98
- Blob *pArg
94
+ Stmt *pQuery
9995
){
100
- int cnt = 0;
10196
int wikiFlags;
10297
int mxWikiLen;
10398
Blob comment;
10499
char zPrevDate[20];
105100
zPrevDate[0] = 0;
@@ -125,21 +120,11 @@
125120
int isLeaf = db_column_int(pQuery, 7);
126121
const char *zBgClr = db_column_text(pQuery, 8);
127122
const char *zDate = db_column_text(pQuery, 2);
128123
const char *zType = db_column_text(pQuery, 9);
129124
const char *zUser = db_column_text(pQuery, 4);
130
- if( cnt==0 && pFirstEvent ){
131
- *pFirstEvent = rid;
132
- }
133
- cnt++;
134
- if( pLastEvent ){
135
- *pLastEvent = rid;
136
- }
137125
db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid);
138
- if( xCallback ){
139
- xCallback(rid, pArg);
140
- }
141126
if( memcmp(zDate, zPrevDate, 10) ){
142127
sprintf(zPrevDate, "%.10s", zDate);
143128
@ <tr><td colspan=3>
144129
@ <div class="divider">%s(zPrevDate)</div>
145130
@ </td></tr>
@@ -183,38 +168,28 @@
183168
}
184169
@ </table>
185170
}
186171
187172
/*
188
-** Generate javascript code that records the parents and children
189
-** of the version rid.
173
+** Create a temporary table suitable for storing timeline data.
190174
*/
191
-static int save_parentage_javascript(int rid, Blob *pOut){
192
- const char *zSep;
193
- Stmt q;
194
-
195
- db_prepare(&q, "SELECT pid FROM plink WHERE cid=%d", rid);
196
- zSep = "";
197
- blob_appendf(pOut, "parentof[\"m%d\"] = [", rid);
198
- while( db_step(&q)==SQLITE_ROW ){
199
- int pid = db_column_int(&q, 0);
200
- blob_appendf(pOut, "%s\"m%d\"", zSep, pid);
201
- zSep = ",";
202
- }
203
- db_finalize(&q);
204
- blob_appendf(pOut, "];\n");
205
- db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d", rid);
206
- zSep = "";
207
- blob_appendf(pOut, "childof[\"m%d\"] = [", rid);
208
- while( db_step(&q)==SQLITE_ROW ){
209
- int pid = db_column_int(&q, 0);
210
- blob_appendf(pOut, "%s\"m%d\"", zSep, pid);
211
- zSep = ",";
212
- }
213
- db_finalize(&q);
214
- blob_appendf(pOut, "];\n");
215
- return 0;
175
+static void timeline_temp_table(void){
176
+ static const char zSql[] =
177
+ @ CREATE TEMP TABLE IF NOT EXISTS timeline(
178
+ @ rid INTEGER PRIMARY KEY,
179
+ @ uuid TEXT,
180
+ @ timestamp TEXT,
181
+ @ comment TEXT,
182
+ @ user TEXT,
183
+ @ nchild INTEGER,
184
+ @ nparent INTEGER,
185
+ @ isleaf BOOLEAN,
186
+ @ bgcolor TEXT,
187
+ @ etype TEXT
188
+ @ )
189
+ ;
190
+ db_multi_exec(zSql);
216191
}
217192
218193
/*
219194
** Return a pointer to a constant string that forms the basis
220195
** for a timeline query for the WWW interface.
@@ -241,36 +216,37 @@
241216
/*
242217
** WEBPAGE: timeline
243218
**
244219
** Query parameters:
245220
**
246
-** d=STARTDATE date in iso8601 notation. dflt: newest event
247
-** n=INTEGER number of events to show. dflt: 25
248
-** e=INTEGER starting event id. dflt: nil
249
-** u=NAME show only events from user. dflt: nil
250
-** a show events after and including. dflt: false
251
-** r show only related events. dflt: false
252
-** y=TYPE show only TYPE ('ci' or 'w') dflt: nil
253
-** s show the SQL dflt: nil
221
+** a=TIMESTAMP after this date
222
+** b=TIMESTAMP before this date.
223
+** n=COUNT number of events in output
224
+** p=RID artifact RID and up to COUNT parents and ancestors
225
+** d=RID artifact RID and up to COUNT descendents
226
+** u=USER only if belonging to this user
227
+** y=TYPE 'ci', 'w', 'tkt'
228
+**
229
+** p= and d= can appear individually or together. If either p= or d=
230
+** appear, then u=, y=, a=, and b= are ignored.
231
+**
232
+** If a= and b= appear, only a= is used. If neither appear, the most
233
+** recent events are choosen.
234
+**
235
+** If n= is missing, the default count is 20.
254236
*/
255237
void page_timeline(void){
256
- Stmt q;
257
- Blob sql; /* text of SQL used to generate timeline */
258
- char *zSQL; /* Rendered copy of sql */
259
- Blob scriptInit;
260
- char zDate[100];
261
- const char *zStart = P("d"); /* Starting date */
238
+ Stmt q; /* Query used to generate the timeline */
239
+ Blob sql; /* text of SQL used to generate timeline */
240
+ Blob desc; /* Description of the timeline */
262241
int nEntry = atoi(PD("n","20")); /* Max number of entries on timeline */
242
+ int p_rid = atoi(PD("p","0")); /* artifact p and its parents */
243
+ int d_rid = atoi(PD("d","0")); /* artifact d and its descendents */
263244
const char *zUser = P("u"); /* All entries by this user if not NULL */
264
- int objid = atoi(PD("e","0")); /* Entries related to this event */
265
- int relatedEvents = P("r")!=0; /* Must be directly related to of objid */
266
- int afterFlag = P("a")!=0; /* After objid if true */
267245
const char *zType = P("y"); /* Type of events. All if NULL */
268
- int firstEvent; /* First event displayed */
269
- int lastEvent; /* Last event displayed */
270
- Blob desc; /* Human readable description of the timeline */
271
- const char *zEType; /* Human readable event type */
246
+ const char *zAfter = P("a"); /* Events after this time */
247
+ const char *zBefore = P("b"); /* Events before this time */
272248
273249
/* To view the timeline, must have permission to read project data.
274250
*/
275251
login_check_credentials();
276252
if( !g.okRead ){ login_needed(); return; }
@@ -281,100 +257,158 @@
281257
" WHERE login='anonymous'"
282258
" AND cap LIKE '%%h%%'") ){
283259
@ <p><b>Note:</b> You will be able to access <u>much</u> more
284260
@ historical information if you <a href="%s(g.zTop)/login">login</a>.</p>
285261
}
262
+ timeline_temp_table();
286263
blob_zero(&sql);
287264
blob_zero(&desc);
265
+ blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
288266
blob_append(&sql, timeline_query_for_www(), -1);
289
- zEType = "events";
290
- if( zType ){
291
- blob_appendf(&sql, " AND event.type=%Q", zType);
292
- if( zType[0]=='c' ){
293
- zEType = "checkins";
294
- }else if( zType[0]=='w' ){
295
- zEType = "wiki edits";
296
- }
297
- }
298
- blob_appendf(&desc, "Timeline of up to %d %s", nEntry, zEType);
299
- if( zUser ){
300
- blob_appendf(&sql, " AND event.user=%Q", zUser);
301
- blob_appendf(&desc, " by user %h", zUser);
302
- }
303
- if( objid ){
304
- char *z = db_text(0, "SELECT datetime(event.mtime, 'localtime') FROM event"
305
- " WHERE objid=%d", objid);
306
- if( z ){
307
- zStart = z;
308
- }
309
- }
310
- if( zStart ){
311
- while( isspace(zStart[0]) ){ zStart++; }
312
- if( zStart[0] ){
313
- blob_appendf(&sql,
314
- " AND event.mtime %s (SELECT julianday(%Q, 'utc'))",
315
- afterFlag ? ">=" : "<=", zStart);
316
- blob_appendf(&desc, " occurring on or %s %h",
317
- afterFlag ? "after": "before",
318
- zStart);
319
- }
320
- }
321
- if( relatedEvents && objid ){
267
+ if( p_rid || d_rid ){
268
+ /* If p= or d= is present, ignore all other parameters other than n= */
322269
char *zUuid;
270
+ int np, nd;
271
+
272
+ if( p_rid && d_rid && p_rid!=d_rid ) p_rid = d_rid;
323273
db_multi_exec(
324274
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
325275
);
326
- zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", objid);
327
- if( afterFlag ){
328
- compute_descendents(objid, nEntry);
329
- blob_appendf(&desc,
330
- " and decended from <a href='%s/vinfo/%d'>[%.10s]</a>",
331
- g.zBaseURL, objid, zUuid);
332
- }else{
333
- compute_ancestors(objid, nEntry);
334
- blob_appendf(&desc,
335
- " and a ancestor of <a href='%s/vinfo/%d'>[%.10s]</a>",
336
- g.zBaseURL, objid, zUuid);
337
- }
338
- blob_append(&sql, " AND event.objid IN ok", -1);
339
- }
340
- if( afterFlag ){
341
- blob_appendf(&sql, " ORDER BY event.mtime ASC LIMIT %d",
342
- nEntry);
343
- }else{
344
- blob_appendf(&sql, " ORDER BY event.mtime DESC LIMIT %d",
345
- nEntry);
346
- }
347
- zSQL = blob_str(&sql);
348
- if( afterFlag ){
349
- zSQL = mprintf("SELECT * FROM (%s) ORDER BY timestamp DESC", zSQL);
350
- }
351
- db_prepare(&q, zSQL);
352
- if( P("s")!=0 ){
353
- @ <hr><p>%h(zSQL)</p><hr>
354
- }
276
+ zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
277
+ p_rid ? p_rid : d_rid);
278
+ blob_appendf(&sql, " AND event.objid IN ok");
279
+ nd = 0;
280
+ if( d_rid ){
281
+ compute_descendents(d_rid, nEntry);
282
+ nd = db_int(0, "SELECT count(*)-1 FROM ok");
283
+ if( nd>0 ){
284
+ db_multi_exec("%s", blob_str(&sql));
285
+ blob_appendf(&desc, "%d descendents", nd);
286
+ }
287
+ db_multi_exec("DELETE FROM ok");
288
+ }
289
+ if( p_rid ){
290
+ compute_ancestors(p_rid, nEntry);
291
+ np = db_int(0, "SELECT count(*)-1 FROM ok");
292
+ if( np>0 ){
293
+ if( nd>0 ) blob_appendf(&desc, " and ");
294
+ blob_appendf(&desc, "%d ancestors", np);
295
+ db_multi_exec("%s", blob_str(&sql));
296
+ }
297
+ }
298
+ blob_appendf(&desc, " of <a href='%s/info/%s'>[%.10s]</a>",
299
+ g.zBaseURL, zUuid, zUuid);
300
+ db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC");
301
+ }else{
302
+ int n;
303
+ Blob url;
304
+ const char *zEType = "event";
305
+ const char *zDate;
306
+ blob_zero(&url);
307
+ blob_appendf(&url, "%s/timeline?n=%d", g.zBaseURL, nEntry);
308
+ if( zType ){
309
+ blob_appendf(&sql, " AND event.type=%Q", zType);
310
+ blob_appendf(&url, "&y=%T", zType);
311
+ if( zType[0]=='c' ){
312
+ zEType = "checkin";
313
+ }else if( zType[0]=='w' ){
314
+ zEType = "wiki edit";
315
+ }else if( zType[0]=='t' ){
316
+ zEType = "ticket change";
317
+ }
318
+ }
319
+ if( zUser ){
320
+ blob_appendf(&sql, " AND event.user=%Q", zUser);
321
+ blob_appendf(&url, "&u=%T", zUser);
322
+ }
323
+ if( zAfter ){
324
+ while( isspace(zAfter[0]) ){ zAfter++; }
325
+ if( zAfter[0] ){
326
+ blob_appendf(&sql,
327
+ " AND event.mtime>=(SELECT julianday(%Q, 'utc'))"
328
+ " ORDER BY event.mtime ASC", zAfter);
329
+ zBefore = 0;
330
+ }else{
331
+ zAfter = 0;
332
+ }
333
+ }else if( zBefore ){
334
+ while( isspace(zBefore[0]) ){ zBefore++; }
335
+ if( zBefore[0] ){
336
+ blob_appendf(&sql,
337
+ " AND event.mtime<=(SELECT julianday(%Q, 'utc'))"
338
+ " ORDER BY event.mtime DESC", zBefore);
339
+ }else{
340
+ zBefore = 0;
341
+ }
342
+ }else{
343
+ blob_appendf(&sql, " ORDER BY event.mtime DESC");
344
+ }
345
+ blob_appendf(&sql, " LIMIT %d", nEntry);
346
+ db_multi_exec("%s", blob_str(&sql));
347
+
348
+ n = db_int(0, "SELECT count(*) FROM timeline");
349
+ if( zAfter==0 && zBefore==0 ){
350
+ blob_appendf(&desc, "%d most recent %ss", n, zEType);
351
+ }else{
352
+ blob_appendf(&desc, "%d %ss", n, zEType);
353
+ }
354
+ if( zUser ){
355
+ blob_appendf(&desc, " by user %h", zUser);
356
+ }
357
+ if( zAfter ){
358
+ blob_appendf(&desc, " occurring on or after %h.<br>", zAfter);
359
+ }else if( zBefore ){
360
+ blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
361
+ }
362
+ if( zAfter || n==nEntry ){
363
+ zDate = db_text(0, "SELECT min(timestamp) FROM timeline");
364
+ blob_appendf(&desc, " <a href='%b&b=%s'>[older]</a>", &url, zDate);
365
+ }
366
+ if( zBefore || (zAfter && n==nEntry) ){
367
+ zDate = db_text(0, "SELECT max(timestamp) FROM timeline");
368
+ blob_appendf(&desc, " <a href='%b&a=%s'>[more recent]</a>", &url, zDate);
369
+ }
370
+ }
371
+ blob_zero(&sql);
372
+ db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC");
355373
@ <h2>%b(&desc)</h2>
356374
blob_reset(&desc);
357
- blob_zero(&sql);
358
- if( afterFlag ){
359
- free(zSQL);
360
- }
361
- zDate[0] = 0;
362
- blob_zero(&scriptInit);
363
- zDate[0] = 0;
364
- www_print_timeline(&q, &firstEvent, &lastEvent,
365
- save_parentage_javascript, &scriptInit);
375
+ www_print_timeline(&q);
366376
db_finalize(&q);
367
- /* @ <p>firstEvent=%d(firstEvent) lastEvent=%d(lastEvent)</p> */
368
- if( zStart==0 ){
369
- zStart = zDate;
370
- }
377
+
371378
@ <script>
372379
@ var parentof = new Object();
373380
@ var childof = new Object();
374
- cgi_append_content(blob_buffer(&scriptInit), blob_size(&scriptInit));
375
- blob_reset(&scriptInit);
381
+ db_prepare(&q, "SELECT rid FROM timeline");
382
+ while( db_step(&q)==SQLITE_ROW ){
383
+ int rid = db_column_int(&q, 0);
384
+ Stmt q2;
385
+ const char *zSep;
386
+ Blob *pOut = cgi_output_blob();
387
+
388
+ db_prepare(&q2, "SELECT pid FROM plink WHERE cid=%d", rid);
389
+ zSep = "";
390
+ blob_appendf(pOut, "parentof[\"m%d\"] = [", rid);
391
+ while( db_step(&q2)==SQLITE_ROW ){
392
+ int pid = db_column_int(&q2, 0);
393
+ blob_appendf(pOut, "%s\"m%d\"", zSep, pid);
394
+ zSep = ",";
395
+ }
396
+ db_finalize(&q2);
397
+ blob_appendf(pOut, "];\n");
398
+ db_prepare(&q2, "SELECT cid FROM plink WHERE pid=%d", rid);
399
+ zSep = "";
400
+ blob_appendf(pOut, "childof[\"m%d\"] = [", rid);
401
+ while( db_step(&q2)==SQLITE_ROW ){
402
+ int pid = db_column_int(&q2, 0);
403
+ blob_appendf(pOut, "%s\"m%d\"", zSep, pid);
404
+ zSep = ",";
405
+ }
406
+ db_finalize(&q2);
407
+ blob_appendf(pOut, "];\n");
408
+ }
409
+ db_finalize(&q);
376410
@ function setall(value){
377411
@ for(var x in parentof){
378412
@ setone(x,value);
379413
@ }
380414
@ }
@@ -431,30 +465,10 @@
431465
@ set_children(cid,clr);
432466
@ }
433467
@ }
434468
@ }
435469
@ </script>
436
- @ <hr>
437
- @ <form method="GET" action="%s(g.zBaseURL)/timeline">
438
- @ Start Date:
439
- @ <input type="text" size="30" value="%h(zStart)" name="d">
440
- @ Number Of Entries:
441
- @ <input type="text" size="4" value="%d(nEntry)" name="n">
442
- @ <br><input type="submit" value="Submit">
443
- @ </form>
444
- @ <table><tr><td>
445
- @ <form method="GET" action="%s(g.zBaseURL)/timeline">
446
- @ <input type="hidden" value="%d(lastEvent)" name="e">
447
- @ <input type="hidden" value="%d(nEntry)" name="n">
448
- @ <input type="submit" value="Next %d(nEntry) Rows">
449
- @ </form></td><td>
450
- @ <form method="GET" action="%s(g.zBaseURL)/timeline">
451
- @ <input type="hidden" value="%d(firstEvent)" name="e">
452
- @ <input type="hidden" value="%d(nEntry)" name="n">
453
- @ <input type="hidden" value="1" name="a">
454
- @ <input type="submit" value="Previous %d(nEntry) Rows">
455
- @ </form></td></tr></table>
456470
style_footer();
457471
}
458472
459473
/*
460474
** The input query q selects various records. Print a human-readable
461475
--- src/timeline.c
+++ src/timeline.c
@@ -89,17 +89,12 @@
89 ** 7. True if is a leaf
90 ** 8. background color
91 ** 9. type ("ci", "w")
92 */
93 void www_print_timeline(
94 Stmt *pQuery,
95 int *pFirstEvent,
96 int *pLastEvent,
97 int (*xCallback)(int, Blob*),
98 Blob *pArg
99 ){
100 int cnt = 0;
101 int wikiFlags;
102 int mxWikiLen;
103 Blob comment;
104 char zPrevDate[20];
105 zPrevDate[0] = 0;
@@ -125,21 +120,11 @@
125 int isLeaf = db_column_int(pQuery, 7);
126 const char *zBgClr = db_column_text(pQuery, 8);
127 const char *zDate = db_column_text(pQuery, 2);
128 const char *zType = db_column_text(pQuery, 9);
129 const char *zUser = db_column_text(pQuery, 4);
130 if( cnt==0 && pFirstEvent ){
131 *pFirstEvent = rid;
132 }
133 cnt++;
134 if( pLastEvent ){
135 *pLastEvent = rid;
136 }
137 db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid);
138 if( xCallback ){
139 xCallback(rid, pArg);
140 }
141 if( memcmp(zDate, zPrevDate, 10) ){
142 sprintf(zPrevDate, "%.10s", zDate);
143 @ <tr><td colspan=3>
144 @ <div class="divider">%s(zPrevDate)</div>
145 @ </td></tr>
@@ -183,38 +168,28 @@
183 }
184 @ </table>
185 }
186
187 /*
188 ** Generate javascript code that records the parents and children
189 ** of the version rid.
190 */
191 static int save_parentage_javascript(int rid, Blob *pOut){
192 const char *zSep;
193 Stmt q;
194
195 db_prepare(&q, "SELECT pid FROM plink WHERE cid=%d", rid);
196 zSep = "";
197 blob_appendf(pOut, "parentof[\"m%d\"] = [", rid);
198 while( db_step(&q)==SQLITE_ROW ){
199 int pid = db_column_int(&q, 0);
200 blob_appendf(pOut, "%s\"m%d\"", zSep, pid);
201 zSep = ",";
202 }
203 db_finalize(&q);
204 blob_appendf(pOut, "];\n");
205 db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d", rid);
206 zSep = "";
207 blob_appendf(pOut, "childof[\"m%d\"] = [", rid);
208 while( db_step(&q)==SQLITE_ROW ){
209 int pid = db_column_int(&q, 0);
210 blob_appendf(pOut, "%s\"m%d\"", zSep, pid);
211 zSep = ",";
212 }
213 db_finalize(&q);
214 blob_appendf(pOut, "];\n");
215 return 0;
216 }
217
218 /*
219 ** Return a pointer to a constant string that forms the basis
220 ** for a timeline query for the WWW interface.
@@ -241,36 +216,37 @@
241 /*
242 ** WEBPAGE: timeline
243 **
244 ** Query parameters:
245 **
246 ** d=STARTDATE date in iso8601 notation. dflt: newest event
247 ** n=INTEGER number of events to show. dflt: 25
248 ** e=INTEGER starting event id. dflt: nil
249 ** u=NAME show only events from user. dflt: nil
250 ** a show events after and including. dflt: false
251 ** r show only related events. dflt: false
252 ** y=TYPE show only TYPE ('ci' or 'w') dflt: nil
253 ** s show the SQL dflt: nil
 
 
 
 
 
 
 
254 */
255 void page_timeline(void){
256 Stmt q;
257 Blob sql; /* text of SQL used to generate timeline */
258 char *zSQL; /* Rendered copy of sql */
259 Blob scriptInit;
260 char zDate[100];
261 const char *zStart = P("d"); /* Starting date */
262 int nEntry = atoi(PD("n","20")); /* Max number of entries on timeline */
 
 
263 const char *zUser = P("u"); /* All entries by this user if not NULL */
264 int objid = atoi(PD("e","0")); /* Entries related to this event */
265 int relatedEvents = P("r")!=0; /* Must be directly related to of objid */
266 int afterFlag = P("a")!=0; /* After objid if true */
267 const char *zType = P("y"); /* Type of events. All if NULL */
268 int firstEvent; /* First event displayed */
269 int lastEvent; /* Last event displayed */
270 Blob desc; /* Human readable description of the timeline */
271 const char *zEType; /* Human readable event type */
272
273 /* To view the timeline, must have permission to read project data.
274 */
275 login_check_credentials();
276 if( !g.okRead ){ login_needed(); return; }
@@ -281,100 +257,158 @@
281 " WHERE login='anonymous'"
282 " AND cap LIKE '%%h%%'") ){
283 @ <p><b>Note:</b> You will be able to access <u>much</u> more
284 @ historical information if you <a href="%s(g.zTop)/login">login</a>.</p>
285 }
 
286 blob_zero(&sql);
287 blob_zero(&desc);
 
288 blob_append(&sql, timeline_query_for_www(), -1);
289 zEType = "events";
290 if( zType ){
291 blob_appendf(&sql, " AND event.type=%Q", zType);
292 if( zType[0]=='c' ){
293 zEType = "checkins";
294 }else if( zType[0]=='w' ){
295 zEType = "wiki edits";
296 }
297 }
298 blob_appendf(&desc, "Timeline of up to %d %s", nEntry, zEType);
299 if( zUser ){
300 blob_appendf(&sql, " AND event.user=%Q", zUser);
301 blob_appendf(&desc, " by user %h", zUser);
302 }
303 if( objid ){
304 char *z = db_text(0, "SELECT datetime(event.mtime, 'localtime') FROM event"
305 " WHERE objid=%d", objid);
306 if( z ){
307 zStart = z;
308 }
309 }
310 if( zStart ){
311 while( isspace(zStart[0]) ){ zStart++; }
312 if( zStart[0] ){
313 blob_appendf(&sql,
314 " AND event.mtime %s (SELECT julianday(%Q, 'utc'))",
315 afterFlag ? ">=" : "<=", zStart);
316 blob_appendf(&desc, " occurring on or %s %h",
317 afterFlag ? "after": "before",
318 zStart);
319 }
320 }
321 if( relatedEvents && objid ){
322 char *zUuid;
 
 
 
323 db_multi_exec(
324 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
325 );
326 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", objid);
327 if( afterFlag ){
328 compute_descendents(objid, nEntry);
329 blob_appendf(&desc,
330 " and decended from <a href='%s/vinfo/%d'>[%.10s]</a>",
331 g.zBaseURL, objid, zUuid);
332 }else{
333 compute_ancestors(objid, nEntry);
334 blob_appendf(&desc,
335 " and a ancestor of <a href='%s/vinfo/%d'>[%.10s]</a>",
336 g.zBaseURL, objid, zUuid);
337 }
338 blob_append(&sql, " AND event.objid IN ok", -1);
339 }
340 if( afterFlag ){
341 blob_appendf(&sql, " ORDER BY event.mtime ASC LIMIT %d",
342 nEntry);
343 }else{
344 blob_appendf(&sql, " ORDER BY event.mtime DESC LIMIT %d",
345 nEntry);
346 }
347 zSQL = blob_str(&sql);
348 if( afterFlag ){
349 zSQL = mprintf("SELECT * FROM (%s) ORDER BY timestamp DESC", zSQL);
350 }
351 db_prepare(&q, zSQL);
352 if( P("s")!=0 ){
353 @ <hr><p>%h(zSQL)</p><hr>
354 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355 @ <h2>%b(&desc)</h2>
356 blob_reset(&desc);
357 blob_zero(&sql);
358 if( afterFlag ){
359 free(zSQL);
360 }
361 zDate[0] = 0;
362 blob_zero(&scriptInit);
363 zDate[0] = 0;
364 www_print_timeline(&q, &firstEvent, &lastEvent,
365 save_parentage_javascript, &scriptInit);
366 db_finalize(&q);
367 /* @ <p>firstEvent=%d(firstEvent) lastEvent=%d(lastEvent)</p> */
368 if( zStart==0 ){
369 zStart = zDate;
370 }
371 @ <script>
372 @ var parentof = new Object();
373 @ var childof = new Object();
374 cgi_append_content(blob_buffer(&scriptInit), blob_size(&scriptInit));
375 blob_reset(&scriptInit);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376 @ function setall(value){
377 @ for(var x in parentof){
378 @ setone(x,value);
379 @ }
380 @ }
@@ -431,30 +465,10 @@
431 @ set_children(cid,clr);
432 @ }
433 @ }
434 @ }
435 @ </script>
436 @ <hr>
437 @ <form method="GET" action="%s(g.zBaseURL)/timeline">
438 @ Start Date:
439 @ <input type="text" size="30" value="%h(zStart)" name="d">
440 @ Number Of Entries:
441 @ <input type="text" size="4" value="%d(nEntry)" name="n">
442 @ <br><input type="submit" value="Submit">
443 @ </form>
444 @ <table><tr><td>
445 @ <form method="GET" action="%s(g.zBaseURL)/timeline">
446 @ <input type="hidden" value="%d(lastEvent)" name="e">
447 @ <input type="hidden" value="%d(nEntry)" name="n">
448 @ <input type="submit" value="Next %d(nEntry) Rows">
449 @ </form></td><td>
450 @ <form method="GET" action="%s(g.zBaseURL)/timeline">
451 @ <input type="hidden" value="%d(firstEvent)" name="e">
452 @ <input type="hidden" value="%d(nEntry)" name="n">
453 @ <input type="hidden" value="1" name="a">
454 @ <input type="submit" value="Previous %d(nEntry) Rows">
455 @ </form></td></tr></table>
456 style_footer();
457 }
458
459 /*
460 ** The input query q selects various records. Print a human-readable
461
--- src/timeline.c
+++ src/timeline.c
@@ -89,17 +89,12 @@
89 ** 7. True if is a leaf
90 ** 8. background color
91 ** 9. type ("ci", "w")
92 */
93 void www_print_timeline(
94 Stmt *pQuery
 
 
 
 
95 ){
 
96 int wikiFlags;
97 int mxWikiLen;
98 Blob comment;
99 char zPrevDate[20];
100 zPrevDate[0] = 0;
@@ -125,21 +120,11 @@
120 int isLeaf = db_column_int(pQuery, 7);
121 const char *zBgClr = db_column_text(pQuery, 8);
122 const char *zDate = db_column_text(pQuery, 2);
123 const char *zType = db_column_text(pQuery, 9);
124 const char *zUser = db_column_text(pQuery, 4);
 
 
 
 
 
 
 
125 db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid);
 
 
 
126 if( memcmp(zDate, zPrevDate, 10) ){
127 sprintf(zPrevDate, "%.10s", zDate);
128 @ <tr><td colspan=3>
129 @ <div class="divider">%s(zPrevDate)</div>
130 @ </td></tr>
@@ -183,38 +168,28 @@
168 }
169 @ </table>
170 }
171
172 /*
173 ** Create a temporary table suitable for storing timeline data.
 
174 */
175 static void timeline_temp_table(void){
176 static const char zSql[] =
177 @ CREATE TEMP TABLE IF NOT EXISTS timeline(
178 @ rid INTEGER PRIMARY KEY,
179 @ uuid TEXT,
180 @ timestamp TEXT,
181 @ comment TEXT,
182 @ user TEXT,
183 @ nchild INTEGER,
184 @ nparent INTEGER,
185 @ isleaf BOOLEAN,
186 @ bgcolor TEXT,
187 @ etype TEXT
188 @ )
189 ;
190 db_multi_exec(zSql);
 
 
 
 
 
 
 
 
 
191 }
192
193 /*
194 ** Return a pointer to a constant string that forms the basis
195 ** for a timeline query for the WWW interface.
@@ -241,36 +216,37 @@
216 /*
217 ** WEBPAGE: timeline
218 **
219 ** Query parameters:
220 **
221 ** a=TIMESTAMP after this date
222 ** b=TIMESTAMP before this date.
223 ** n=COUNT number of events in output
224 ** p=RID artifact RID and up to COUNT parents and ancestors
225 ** d=RID artifact RID and up to COUNT descendents
226 ** u=USER only if belonging to this user
227 ** y=TYPE 'ci', 'w', 'tkt'
228 **
229 ** p= and d= can appear individually or together. If either p= or d=
230 ** appear, then u=, y=, a=, and b= are ignored.
231 **
232 ** If a= and b= appear, only a= is used. If neither appear, the most
233 ** recent events are choosen.
234 **
235 ** If n= is missing, the default count is 20.
236 */
237 void page_timeline(void){
238 Stmt q; /* Query used to generate the timeline */
239 Blob sql; /* text of SQL used to generate timeline */
240 Blob desc; /* Description of the timeline */
 
 
 
241 int nEntry = atoi(PD("n","20")); /* Max number of entries on timeline */
242 int p_rid = atoi(PD("p","0")); /* artifact p and its parents */
243 int d_rid = atoi(PD("d","0")); /* artifact d and its descendents */
244 const char *zUser = P("u"); /* All entries by this user if not NULL */
 
 
 
245 const char *zType = P("y"); /* Type of events. All if NULL */
246 const char *zAfter = P("a"); /* Events after this time */
247 const char *zBefore = P("b"); /* Events before this time */
 
 
248
249 /* To view the timeline, must have permission to read project data.
250 */
251 login_check_credentials();
252 if( !g.okRead ){ login_needed(); return; }
@@ -281,100 +257,158 @@
257 " WHERE login='anonymous'"
258 " AND cap LIKE '%%h%%'") ){
259 @ <p><b>Note:</b> You will be able to access <u>much</u> more
260 @ historical information if you <a href="%s(g.zTop)/login">login</a>.</p>
261 }
262 timeline_temp_table();
263 blob_zero(&sql);
264 blob_zero(&desc);
265 blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
266 blob_append(&sql, timeline_query_for_www(), -1);
267 if( p_rid || d_rid ){
268 /* If p= or d= is present, ignore all other parameters other than n= */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269 char *zUuid;
270 int np, nd;
271
272 if( p_rid && d_rid && p_rid!=d_rid ) p_rid = d_rid;
273 db_multi_exec(
274 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
275 );
276 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
277 p_rid ? p_rid : d_rid);
278 blob_appendf(&sql, " AND event.objid IN ok");
279 nd = 0;
280 if( d_rid ){
281 compute_descendents(d_rid, nEntry);
282 nd = db_int(0, "SELECT count(*)-1 FROM ok");
283 if( nd>0 ){
284 db_multi_exec("%s", blob_str(&sql));
285 blob_appendf(&desc, "%d descendents", nd);
286 }
287 db_multi_exec("DELETE FROM ok");
288 }
289 if( p_rid ){
290 compute_ancestors(p_rid, nEntry);
291 np = db_int(0, "SELECT count(*)-1 FROM ok");
292 if( np>0 ){
293 if( nd>0 ) blob_appendf(&desc, " and ");
294 blob_appendf(&desc, "%d ancestors", np);
295 db_multi_exec("%s", blob_str(&sql));
296 }
297 }
298 blob_appendf(&desc, " of <a href='%s/info/%s'>[%.10s]</a>",
299 g.zBaseURL, zUuid, zUuid);
300 db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC");
301 }else{
302 int n;
303 Blob url;
304 const char *zEType = "event";
305 const char *zDate;
306 blob_zero(&url);
307 blob_appendf(&url, "%s/timeline?n=%d", g.zBaseURL, nEntry);
308 if( zType ){
309 blob_appendf(&sql, " AND event.type=%Q", zType);
310 blob_appendf(&url, "&y=%T", zType);
311 if( zType[0]=='c' ){
312 zEType = "checkin";
313 }else if( zType[0]=='w' ){
314 zEType = "wiki edit";
315 }else if( zType[0]=='t' ){
316 zEType = "ticket change";
317 }
318 }
319 if( zUser ){
320 blob_appendf(&sql, " AND event.user=%Q", zUser);
321 blob_appendf(&url, "&u=%T", zUser);
322 }
323 if( zAfter ){
324 while( isspace(zAfter[0]) ){ zAfter++; }
325 if( zAfter[0] ){
326 blob_appendf(&sql,
327 " AND event.mtime>=(SELECT julianday(%Q, 'utc'))"
328 " ORDER BY event.mtime ASC", zAfter);
329 zBefore = 0;
330 }else{
331 zAfter = 0;
332 }
333 }else if( zBefore ){
334 while( isspace(zBefore[0]) ){ zBefore++; }
335 if( zBefore[0] ){
336 blob_appendf(&sql,
337 " AND event.mtime<=(SELECT julianday(%Q, 'utc'))"
338 " ORDER BY event.mtime DESC", zBefore);
339 }else{
340 zBefore = 0;
341 }
342 }else{
343 blob_appendf(&sql, " ORDER BY event.mtime DESC");
344 }
345 blob_appendf(&sql, " LIMIT %d", nEntry);
346 db_multi_exec("%s", blob_str(&sql));
347
348 n = db_int(0, "SELECT count(*) FROM timeline");
349 if( zAfter==0 && zBefore==0 ){
350 blob_appendf(&desc, "%d most recent %ss", n, zEType);
351 }else{
352 blob_appendf(&desc, "%d %ss", n, zEType);
353 }
354 if( zUser ){
355 blob_appendf(&desc, " by user %h", zUser);
356 }
357 if( zAfter ){
358 blob_appendf(&desc, " occurring on or after %h.<br>", zAfter);
359 }else if( zBefore ){
360 blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
361 }
362 if( zAfter || n==nEntry ){
363 zDate = db_text(0, "SELECT min(timestamp) FROM timeline");
364 blob_appendf(&desc, " <a href='%b&b=%s'>[older]</a>", &url, zDate);
365 }
366 if( zBefore || (zAfter && n==nEntry) ){
367 zDate = db_text(0, "SELECT max(timestamp) FROM timeline");
368 blob_appendf(&desc, " <a href='%b&a=%s'>[more recent]</a>", &url, zDate);
369 }
370 }
371 blob_zero(&sql);
372 db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC");
373 @ <h2>%b(&desc)</h2>
374 blob_reset(&desc);
375 www_print_timeline(&q);
 
 
 
 
 
 
 
 
376 db_finalize(&q);
377
 
 
 
378 @ <script>
379 @ var parentof = new Object();
380 @ var childof = new Object();
381 db_prepare(&q, "SELECT rid FROM timeline");
382 while( db_step(&q)==SQLITE_ROW ){
383 int rid = db_column_int(&q, 0);
384 Stmt q2;
385 const char *zSep;
386 Blob *pOut = cgi_output_blob();
387
388 db_prepare(&q2, "SELECT pid FROM plink WHERE cid=%d", rid);
389 zSep = "";
390 blob_appendf(pOut, "parentof[\"m%d\"] = [", rid);
391 while( db_step(&q2)==SQLITE_ROW ){
392 int pid = db_column_int(&q2, 0);
393 blob_appendf(pOut, "%s\"m%d\"", zSep, pid);
394 zSep = ",";
395 }
396 db_finalize(&q2);
397 blob_appendf(pOut, "];\n");
398 db_prepare(&q2, "SELECT cid FROM plink WHERE pid=%d", rid);
399 zSep = "";
400 blob_appendf(pOut, "childof[\"m%d\"] = [", rid);
401 while( db_step(&q2)==SQLITE_ROW ){
402 int pid = db_column_int(&q2, 0);
403 blob_appendf(pOut, "%s\"m%d\"", zSep, pid);
404 zSep = ",";
405 }
406 db_finalize(&q2);
407 blob_appendf(pOut, "];\n");
408 }
409 db_finalize(&q);
410 @ function setall(value){
411 @ for(var x in parentof){
412 @ setone(x,value);
413 @ }
414 @ }
@@ -431,30 +465,10 @@
465 @ set_children(cid,clr);
466 @ }
467 @ }
468 @ }
469 @ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470 style_footer();
471 }
472
473 /*
474 ** The input query q selects various records. Print a human-readable
475
+1 -1
--- src/wiki.c
+++ src/wiki.c
@@ -463,11 +463,11 @@
463463
"(SELECT tagid FROM tag WHERE tagname='wiki-%q'))"
464464
"ORDER BY mtime DESC",
465465
timeline_query_for_www(), zPageName);
466466
db_prepare(&q, zSQL);
467467
free(zSQL);
468
- www_print_timeline(&q, 0, 0, 0, 0);
468
+ www_print_timeline(&q);
469469
db_finalize(&q);
470470
style_footer();
471471
}
472472
473473
/*
474474
--- src/wiki.c
+++ src/wiki.c
@@ -463,11 +463,11 @@
463 "(SELECT tagid FROM tag WHERE tagname='wiki-%q'))"
464 "ORDER BY mtime DESC",
465 timeline_query_for_www(), zPageName);
466 db_prepare(&q, zSQL);
467 free(zSQL);
468 www_print_timeline(&q, 0, 0, 0, 0);
469 db_finalize(&q);
470 style_footer();
471 }
472
473 /*
474
--- src/wiki.c
+++ src/wiki.c
@@ -463,11 +463,11 @@
463 "(SELECT tagid FROM tag WHERE tagname='wiki-%q'))"
464 "ORDER BY mtime DESC",
465 timeline_query_for_www(), zPageName);
466 db_prepare(&q, zSQL);
467 free(zSQL);
468 www_print_timeline(&q);
469 db_finalize(&q);
470 style_footer();
471 }
472
473 /*
474

Keyboard Shortcuts

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