dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Copyright (c) 2007 D. Richard Hipp dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This program is free software; you can redistribute it and/or dbda8d6ce9 2007-07-21 drh: ** modify it under the terms of the GNU General Public dbda8d6ce9 2007-07-21 drh: ** License version 2 as published by the Free Software Foundation. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This program is distributed in the hope that it will be useful, dbda8d6ce9 2007-07-21 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of dbda8d6ce9 2007-07-21 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dbda8d6ce9 2007-07-21 drh: ** General Public License for more details. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** You should have received a copy of the GNU General Public dbda8d6ce9 2007-07-21 drh: ** License along with this library; if not, write to the dbda8d6ce9 2007-07-21 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, dbda8d6ce9 2007-07-21 drh: ** Boston, MA 02111-1307, USA. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Author contact information: dbda8d6ce9 2007-07-21 drh: ** drh@hwaci.com dbda8d6ce9 2007-07-21 drh: ** http://www.hwaci.com/drh/ dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ******************************************************************************* dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This file contains code to implement the timeline web page dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: */ abce5105e2 2007-09-01 drh: #include <string.h> d23b8ba62b 2008-11-01 drh: #include <time.h> dbda8d6ce9 2007-07-21 drh: #include "config.h" dbda8d6ce9 2007-07-21 drh: #include "timeline.h" dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Generate a hyperlink to a version. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void hyperlink_to_uuid(const char *zUuid){ dbda8d6ce9 2007-07-21 drh: char zShortUuid[UUID_SIZE+1]; dbda8d6ce9 2007-07-21 drh: sprintf(zShortUuid, "%.10s", zUuid); fd36718ad9 2007-07-31 drh: if( g.okHistory ){ 72b3b1ad47 2007-09-22 drh: @ <a href="%s(g.zBaseURL)/info/%s(zUuid)">[%s(zShortUuid)]</a> fd36718ad9 2007-07-31 drh: }else{ fd36718ad9 2007-07-31 drh: @ <b>[%s(zShortUuid)]</b> bbdd4f9915 2007-08-27 drh: } bbdd4f9915 2007-08-27 drh: } bbdd4f9915 2007-08-27 drh: bbdd4f9915 2007-08-27 drh: /* bbdd4f9915 2007-08-27 drh: ** Generate a hyperlink that invokes javascript to highlight bbdd4f9915 2007-08-27 drh: ** a version on mouseover. bbdd4f9915 2007-08-27 drh: */ 4d051c1eda 2007-08-29 drh: void hyperlink_to_uuid_with_mouseover( 4d051c1eda 2007-08-29 drh: const char *zUuid, /* The UUID to display */ 4d051c1eda 2007-08-29 drh: const char *zIn, /* Javascript proc for mouseover */ 4d051c1eda 2007-08-29 drh: const char *zOut, /* Javascript proc for mouseout */ 4d051c1eda 2007-08-29 drh: int id /* Argument to javascript procs */ 4d051c1eda 2007-08-29 drh: ){ bbdd4f9915 2007-08-27 drh: char zShortUuid[UUID_SIZE+1]; bbdd4f9915 2007-08-27 drh: sprintf(zShortUuid, "%.10s", zUuid); bbdd4f9915 2007-08-27 drh: if( g.okHistory ){ 4d051c1eda 2007-08-29 drh: @ <a onmouseover='%s(zIn)("m%d(id)")' onmouseout='%s(zOut)("m%d(id)")' bbdd4f9915 2007-08-27 drh: @ href="%s(g.zBaseURL)/vinfo/%s(zUuid)">[%s(zShortUuid)]</a> bbdd4f9915 2007-08-27 drh: }else{ 4d051c1eda 2007-08-29 drh: @ <b onmouseover='%s(zIn)("m%d(id)")' onmouseout='%s(zOut)("m%d(id)")'> bbdd4f9915 2007-08-27 drh: @ [%s(zShortUuid)]</b> fd36718ad9 2007-07-31 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Generate a hyperlink to a diff between two versions. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void hyperlink_to_diff(const char *zV1, const char *zV2){ fd36718ad9 2007-07-31 drh: if( g.okHistory ){ fd36718ad9 2007-07-31 drh: if( zV2==0 ){ fd36718ad9 2007-07-31 drh: @ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a> fd36718ad9 2007-07-31 drh: }else{ fd36718ad9 2007-07-31 drh: @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a> fd36718ad9 2007-07-31 drh: } fd36718ad9 2007-07-31 drh: } fd36718ad9 2007-07-31 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* afcdc7ec97 2007-08-01 drh: ** Output a timeline in the web format given a query. The query 9395aba4f4 2007-09-22 drh: ** should return these columns: bbdd4f9915 2007-08-27 drh: ** bbdd4f9915 2007-08-27 drh: ** 0. rid bbdd4f9915 2007-08-27 drh: ** 1. UUID bbdd4f9915 2007-08-27 drh: ** 2. Date/Time bbdd4f9915 2007-08-27 drh: ** 3. Comment string bbdd4f9915 2007-08-27 drh: ** 4. User bbdd4f9915 2007-08-27 drh: ** 5. Number of non-merge children bbdd4f9915 2007-08-27 drh: ** 6. Number of parents bbdd4f9915 2007-08-27 drh: ** 7. True if is a leaf 5e2392307d 2007-09-22 drh: ** 8. background color dfea940da8 2007-10-09 drh: ** 9. type ("ci", "w") fbbd0318bd 2009-01-20 drh: ** 10. list of symbolic tags. dbda8d6ce9 2007-07-21 drh: */ bbdd4f9915 2007-08-27 drh: void www_print_timeline( 82fc5abb60 2008-02-26 drh: Stmt *pQuery fbbd0318bd 2009-01-20 drh: ){ ebb2765954 2007-12-04 drh: int wikiFlags; ebb2765954 2007-12-04 drh: int mxWikiLen; a84089319c 2007-10-11 drh: Blob comment; afcdc7ec97 2007-08-01 drh: char zPrevDate[20]; dbda8d6ce9 2007-07-21 drh: zPrevDate[0] = 0; ebb2765954 2007-12-04 drh: ebb2765954 2007-12-04 drh: mxWikiLen = db_get_int("timeline-max-comment", 0); ebb2765954 2007-12-04 drh: if( db_get_boolean("timeline-block-markup", 0) ){ ebb2765954 2007-12-04 drh: wikiFlags = WIKI_INLINE; ebb2765954 2007-12-04 drh: }else{ ebb2765954 2007-12-04 drh: wikiFlags = WIKI_INLINE | WIKI_NOBLOCK; ebb2765954 2007-12-04 drh: } 5e2392307d 2007-09-22 drh: e15fe43153 2007-08-31 drh: db_multi_exec( e15fe43153 2007-08-31 drh: "CREATE TEMP TABLE IF NOT EXISTS seen(rid INTEGER PRIMARY KEY);" e15fe43153 2007-08-31 drh: "DELETE FROM seen;" dbda8d6ce9 2007-07-21 drh: ); dbda8d6ce9 2007-07-21 drh: @ <table cellspacing=0 border=0 cellpadding=0> 6d58613757 2007-10-06 drh: blob_zero(&comment); afcdc7ec97 2007-08-01 drh: while( db_step(pQuery)==SQLITE_ROW ){ bbdd4f9915 2007-08-27 drh: int rid = db_column_int(pQuery, 0); 4d051c1eda 2007-08-29 drh: const char *zUuid = db_column_text(pQuery, 1); bbdd4f9915 2007-08-27 drh: int nPChild = db_column_int(pQuery, 5); bbdd4f9915 2007-08-27 drh: int nParent = db_column_int(pQuery, 6); bbdd4f9915 2007-08-27 drh: int isLeaf = db_column_int(pQuery, 7); 5e2392307d 2007-09-22 drh: const char *zBgClr = db_column_text(pQuery, 8); bbdd4f9915 2007-08-27 drh: const char *zDate = db_column_text(pQuery, 2); dfea940da8 2007-10-09 drh: const char *zType = db_column_text(pQuery, 9); dfea940da8 2007-10-09 drh: const char *zUser = db_column_text(pQuery, 4); fbbd0318bd 2009-01-20 drh: const char *zTagList = db_column_text(pQuery, 10); 82fc5abb60 2008-02-26 drh: db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid); dbda8d6ce9 2007-07-21 drh: if( memcmp(zDate, zPrevDate, 10) ){ dbda8d6ce9 2007-07-21 drh: sprintf(zPrevDate, "%.10s", zDate); dbda8d6ce9 2007-07-21 drh: @ <tr><td colspan=3> 34af72801d 2007-11-23 drh: @ <div class="divider">%s(zPrevDate)</div> dbda8d6ce9 2007-07-21 drh: @ </td></tr> dbda8d6ce9 2007-07-21 drh: } 4d03017923 2007-08-30 drh: @ <tr> bbdd4f9915 2007-08-27 drh: @ <td valign="top">%s(&zDate[11])</td> 4d03017923 2007-08-30 drh: @ <td width="20" align="center" valign="top"> 4d03017923 2007-08-30 drh: @ <font id="m%d(rid)" size="+1" color="white">*</font></td> 5e2392307d 2007-09-22 drh: if( zBgClr && zBgClr[0] ){ 5e2392307d 2007-09-22 drh: @ <td valign="top" align="left" bgcolor="%h(zBgClr)"> 5e2392307d 2007-09-22 drh: }else{ 5e2392307d 2007-09-22 drh: @ <td valign="top" align="left"> 5e2392307d 2007-09-22 drh: } dfea940da8 2007-10-09 drh: if( zType[0]=='c' ){ dfea940da8 2007-10-09 drh: hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid); dfea940da8 2007-10-09 drh: if( nParent>1 ){ dfea940da8 2007-10-09 drh: @ <b>Merge</b> dfea940da8 2007-10-09 drh: } dfea940da8 2007-10-09 drh: if( nPChild>1 ){ dfea940da8 2007-10-09 drh: @ <b>Fork</b> dfea940da8 2007-10-09 drh: } dfea940da8 2007-10-09 drh: if( isLeaf ){ dfea940da8 2007-10-09 drh: @ <b>Leaf</b> bbdd4f9915 2007-08-27 drh: } dfea940da8 2007-10-09 drh: }else{ dfea940da8 2007-10-09 drh: hyperlink_to_uuid(zUuid); 6d58613757 2007-10-06 drh: } 6d58613757 2007-10-06 drh: db_column_blob(pQuery, 3, &comment); ebb2765954 2007-12-04 drh: if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){ 20553a420c 2007-12-03 drh: Blob truncated; 20553a420c 2007-12-03 drh: blob_zero(&truncated); ebb2765954 2007-12-04 drh: blob_append(&truncated, blob_buffer(&comment), mxWikiLen); 20553a420c 2007-12-03 drh: blob_append(&truncated, "...", 3); ebb2765954 2007-12-04 drh: wiki_convert(&truncated, 0, wikiFlags); 20553a420c 2007-12-03 drh: blob_reset(&truncated); 20553a420c 2007-12-03 drh: }else{ ebb2765954 2007-12-04 drh: wiki_convert(&comment, 0, wikiFlags); 20553a420c 2007-12-03 drh: } 6d58613757 2007-10-06 drh: blob_reset(&comment); fbbd0318bd 2009-01-20 drh: if( zTagList && zTagList[0] ){ fbbd0318bd 2009-01-20 drh: @ (user: %h(zUser), tags: %h(zTagList))</td></tr> fbbd0318bd 2009-01-20 drh: }else{ fbbd0318bd 2009-01-20 drh: @ (user: %h(zUser))</td></tr> 3945057916 2007-08-01 drh: } afcdc7ec97 2007-08-01 drh: } dbda8d6ce9 2007-07-21 drh: @ </table> afcdc7ec97 2007-08-01 drh: } afcdc7ec97 2007-08-01 drh: bbdd4f9915 2007-08-27 drh: /* e6aa161204 2008-02-26 drh: ** Create a temporary table suitable for storing timeline data. 9395aba4f4 2007-09-22 drh: */ e6aa161204 2008-02-26 drh: static void timeline_temp_table(void){ e6aa161204 2008-02-26 drh: static const char zSql[] = e6aa161204 2008-02-26 drh: @ CREATE TEMP TABLE IF NOT EXISTS timeline( e6aa161204 2008-02-26 drh: @ rid INTEGER PRIMARY KEY, e6aa161204 2008-02-26 drh: @ uuid TEXT, e6aa161204 2008-02-26 drh: @ timestamp TEXT, e6aa161204 2008-02-26 drh: @ comment TEXT, e6aa161204 2008-02-26 drh: @ user TEXT, e6aa161204 2008-02-26 drh: @ nchild INTEGER, e6aa161204 2008-02-26 drh: @ nparent INTEGER, e6aa161204 2008-02-26 drh: @ isleaf BOOLEAN, e6aa161204 2008-02-26 drh: @ bgcolor TEXT, fbbd0318bd 2009-01-20 drh: @ etype TEXT, fbbd0318bd 2009-01-20 drh: @ taglist TEXT e6aa161204 2008-02-26 drh: @ ) e6aa161204 2008-02-26 drh: ; e6aa161204 2008-02-26 drh: db_multi_exec(zSql); 9395aba4f4 2007-09-22 drh: } 9395aba4f4 2007-09-22 drh: 9395aba4f4 2007-09-22 drh: /* 9395aba4f4 2007-09-22 drh: ** Return a pointer to a constant string that forms the basis 9395aba4f4 2007-09-22 drh: ** for a timeline query for the WWW interface. 9395aba4f4 2007-09-22 drh: */ 9395aba4f4 2007-09-22 drh: const char *timeline_query_for_www(void){ 9395aba4f4 2007-09-22 drh: static const char zBaseSql[] = 9395aba4f4 2007-09-22 drh: @ SELECT 9395aba4f4 2007-09-22 drh: @ blob.rid, 9395aba4f4 2007-09-22 drh: @ uuid, 20553a420c 2007-12-03 drh: @ datetime(event.mtime,'localtime') AS timestamp, 3b5514ed82 2007-09-22 drh: @ coalesce(ecomment, comment), 3b5514ed82 2007-09-22 drh: @ coalesce(euser, user), 9395aba4f4 2007-09-22 drh: @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim=1), 9395aba4f4 2007-09-22 drh: @ (SELECT count(*) FROM plink WHERE cid=blob.rid), 9395aba4f4 2007-09-22 drh: @ NOT EXISTS (SELECT 1 FROM plink WHERE pid=blob.rid), dfea940da8 2007-10-09 drh: @ coalesce(bgcolor, brbgcolor), fbbd0318bd 2009-01-20 drh: @ event.type, fbbd0318bd 2009-01-20 drh: @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref fbbd0318bd 2009-01-20 drh: @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid fbbd0318bd 2009-01-20 drh: @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0) 9395aba4f4 2007-09-22 drh: @ FROM event JOIN blob 9395aba4f4 2007-09-22 drh: @ WHERE blob.rid=event.objid 9395aba4f4 2007-09-22 drh: ; 9395aba4f4 2007-09-22 drh: return zBaseSql; 3b5514ed82 2007-09-22 drh: } 3b5514ed82 2007-09-22 drh: 3b5514ed82 2007-09-22 drh: /* c9cd128c2c 2008-11-02 drh: ** Generate a submenu element with a single parameter change. bbdd4f9915 2007-08-27 drh: */ c9cd128c2c 2008-11-02 drh: static void timeline_submenu( c9cd128c2c 2008-11-02 drh: HQuery *pUrl, /* Base URL */ c9cd128c2c 2008-11-02 drh: const char *zMenuName, /* Submenu name */ c9cd128c2c 2008-11-02 drh: const char *zParam, /* Parameter value to add or change */ c9cd128c2c 2008-11-02 drh: const char *zValue, /* Value of the new parameter */ c9cd128c2c 2008-11-02 drh: const char *zRemove /* Parameter to omit */ c9cd128c2c 2008-11-02 drh: ){ c9cd128c2c 2008-11-02 drh: style_submenu_element(zMenuName, zMenuName, "%s", c9cd128c2c 2008-11-02 drh: url_render(pUrl, zParam, zValue, zRemove, 0)); bbdd4f9915 2007-08-27 drh: } afcdc7ec97 2007-08-01 drh: afcdc7ec97 2007-08-01 drh: /* afcdc7ec97 2007-08-01 drh: ** WEBPAGE: timeline e15fe43153 2007-08-31 drh: ** e15fe43153 2007-08-31 drh: ** Query parameters: e15fe43153 2007-08-31 drh: ** e6aa161204 2008-02-26 drh: ** a=TIMESTAMP after this date e6aa161204 2008-02-26 drh: ** b=TIMESTAMP before this date. e6aa161204 2008-02-26 drh: ** n=COUNT number of events in output e6aa161204 2008-02-26 drh: ** p=RID artifact RID and up to COUNT parents and ancestors 6458f020fc 2008-05-14 drh: ** d=RID artifact RID and up to COUNT descendants e6aa161204 2008-02-26 drh: ** u=USER only if belonging to this user c9cd128c2c 2008-11-02 drh: ** y=TYPE 'ci', 'w', 't' e6aa161204 2008-02-26 drh: ** e6aa161204 2008-02-26 drh: ** p= and d= can appear individually or together. If either p= or d= e6aa161204 2008-02-26 drh: ** appear, then u=, y=, a=, and b= are ignored. e6aa161204 2008-02-26 drh: ** e6aa161204 2008-02-26 drh: ** If a= and b= appear, only a= is used. If neither appear, the most e6aa161204 2008-02-26 drh: ** recent events are choosen. e6aa161204 2008-02-26 drh: ** e6aa161204 2008-02-26 drh: ** If n= is missing, the default count is 20. afcdc7ec97 2007-08-01 drh: */ afcdc7ec97 2007-08-01 drh: void page_timeline(void){ e6aa161204 2008-02-26 drh: Stmt q; /* Query used to generate the timeline */ e6aa161204 2008-02-26 drh: Blob sql; /* text of SQL used to generate timeline */ e6aa161204 2008-02-26 drh: Blob desc; /* Description of the timeline */ e38c89130f 2008-02-24 drh: int nEntry = atoi(PD("n","20")); /* Max number of entries on timeline */ e6aa161204 2008-02-26 drh: int p_rid = atoi(PD("p","0")); /* artifact p and its parents */ 6458f020fc 2008-05-14 drh: int d_rid = atoi(PD("d","0")); /* artifact d and its descendants */ e38c89130f 2008-02-24 drh: const char *zUser = P("u"); /* All entries by this user if not NULL */ c9cd128c2c 2008-11-02 drh: const char *zType = PD("y","all"); /* Type of events. All if NULL */ e6aa161204 2008-02-26 drh: const char *zAfter = P("a"); /* Events after this time */ e6aa161204 2008-02-26 drh: const char *zBefore = P("b"); /* Events before this time */ c9cd128c2c 2008-11-02 drh: HQuery url; /* URL for various branch links */ afcdc7ec97 2007-08-01 drh: afcdc7ec97 2007-08-01 drh: /* To view the timeline, must have permission to read project data. afcdc7ec97 2007-08-01 drh: */ afcdc7ec97 2007-08-01 drh: login_check_credentials(); afcdc7ec97 2007-08-01 drh: if( !g.okRead ){ login_needed(); return; } afcdc7ec97 2007-08-01 drh: afcdc7ec97 2007-08-01 drh: style_header("Timeline"); 2b0d4519dc 2008-05-05 drh: login_anonymous_available(); e6aa161204 2008-02-26 drh: timeline_temp_table(); 20553a420c 2007-12-03 drh: blob_zero(&sql); e38c89130f 2008-02-24 drh: blob_zero(&desc); e6aa161204 2008-02-26 drh: blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1); 20553a420c 2007-12-03 drh: blob_append(&sql, timeline_query_for_www(), -1); e6aa161204 2008-02-26 drh: if( p_rid || d_rid ){ e6aa161204 2008-02-26 drh: /* If p= or d= is present, ignore all other parameters other than n= */ e38c89130f 2008-02-24 drh: char *zUuid; e6aa161204 2008-02-26 drh: int np, nd; e6aa161204 2008-02-26 drh: e6aa161204 2008-02-26 drh: if( p_rid && d_rid && p_rid!=d_rid ) p_rid = d_rid; abce5105e2 2007-09-01 drh: db_multi_exec( abce5105e2 2007-09-01 drh: "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" abce5105e2 2007-09-01 drh: ); e6aa161204 2008-02-26 drh: zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", e6aa161204 2008-02-26 drh: p_rid ? p_rid : d_rid); e6aa161204 2008-02-26 drh: blob_appendf(&sql, " AND event.objid IN ok"); e6aa161204 2008-02-26 drh: nd = 0; e6aa161204 2008-02-26 drh: if( d_rid ){ 6458f020fc 2008-05-14 drh: compute_descendants(d_rid, nEntry); e6aa161204 2008-02-26 drh: nd = db_int(0, "SELECT count(*)-1 FROM ok"); e6aa161204 2008-02-26 drh: if( nd>0 ){ e6aa161204 2008-02-26 drh: db_multi_exec("%s", blob_str(&sql)); 6458f020fc 2008-05-14 drh: blob_appendf(&desc, "%d descendants", nd); e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: db_multi_exec("DELETE FROM ok"); e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: if( p_rid ){ e6aa161204 2008-02-26 drh: compute_ancestors(p_rid, nEntry); e6aa161204 2008-02-26 drh: np = db_int(0, "SELECT count(*)-1 FROM ok"); e6aa161204 2008-02-26 drh: if( np>0 ){ e6aa161204 2008-02-26 drh: if( nd>0 ) blob_appendf(&desc, " and "); e6aa161204 2008-02-26 drh: blob_appendf(&desc, "%d ancestors", np); e6aa161204 2008-02-26 drh: db_multi_exec("%s", blob_str(&sql)); e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: } 2b0d4519dc 2008-05-05 drh: if( g.okHistory ){ 2b0d4519dc 2008-05-05 drh: blob_appendf(&desc, " of <a href='%s/info/%s'>[%.10s]</a>", 2b0d4519dc 2008-05-05 drh: g.zBaseURL, zUuid, zUuid); 20553a420c 2007-12-03 drh: }else{ 2b0d4519dc 2008-05-05 drh: blob_appendf(&desc, " of [%.10s]", zUuid); 2b0d4519dc 2008-05-05 drh: } e6aa161204 2008-02-26 drh: db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); 20553a420c 2007-12-03 drh: }else{ 7915bd0665 2008-02-26 drh: int n; e6aa161204 2008-02-26 drh: const char *zEType = "event"; c9cd128c2c 2008-11-02 drh: char *zDate; c9cd128c2c 2008-11-02 drh: char *zNEntry = mprintf("%d", nEntry); c9cd128c2c 2008-11-02 drh: url_initialize(&url, "timeline"); c9cd128c2c 2008-11-02 drh: url_add_parameter(&url, "n", zNEntry); c9cd128c2c 2008-11-02 drh: if( zType[0]!='a' ){ e6aa161204 2008-02-26 drh: blob_appendf(&sql, " AND event.type=%Q", zType); c9cd128c2c 2008-11-02 drh: url_add_parameter(&url, "y", zType); e6aa161204 2008-02-26 drh: if( zType[0]=='c' ){ e6aa161204 2008-02-26 drh: zEType = "checkin"; e6aa161204 2008-02-26 drh: }else if( zType[0]=='w' ){ e6aa161204 2008-02-26 drh: zEType = "wiki edit"; e6aa161204 2008-02-26 drh: }else if( zType[0]=='t' ){ e6aa161204 2008-02-26 drh: zEType = "ticket change"; e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: if( zUser ){ e6aa161204 2008-02-26 drh: blob_appendf(&sql, " AND event.user=%Q", zUser); c9cd128c2c 2008-11-02 drh: url_add_parameter(&url, "u", zUser); e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: if( zAfter ){ e6aa161204 2008-02-26 drh: while( isspace(zAfter[0]) ){ zAfter++; } e6aa161204 2008-02-26 drh: if( zAfter[0] ){ e6aa161204 2008-02-26 drh: blob_appendf(&sql, e6aa161204 2008-02-26 drh: " AND event.mtime>=(SELECT julianday(%Q, 'utc'))" e6aa161204 2008-02-26 drh: " ORDER BY event.mtime ASC", zAfter); c9cd128c2c 2008-11-02 drh: url_add_parameter(&url, "a", zAfter); e6aa161204 2008-02-26 drh: zBefore = 0; 7915bd0665 2008-02-26 drh: }else{ 7915bd0665 2008-02-26 drh: zAfter = 0; e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: }else if( zBefore ){ e6aa161204 2008-02-26 drh: while( isspace(zBefore[0]) ){ zBefore++; } e6aa161204 2008-02-26 drh: if( zBefore[0] ){ e6aa161204 2008-02-26 drh: blob_appendf(&sql, e6aa161204 2008-02-26 drh: " AND event.mtime<=(SELECT julianday(%Q, 'utc'))" e6aa161204 2008-02-26 drh: " ORDER BY event.mtime DESC", zBefore); c9cd128c2c 2008-11-02 drh: url_add_parameter(&url, "b", zBefore); 7915bd0665 2008-02-26 drh: }else{ 7915bd0665 2008-02-26 drh: zBefore = 0; e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: }else{ e6aa161204 2008-02-26 drh: blob_appendf(&sql, " ORDER BY event.mtime DESC"); e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: blob_appendf(&sql, " LIMIT %d", nEntry); e6aa161204 2008-02-26 drh: db_multi_exec("%s", blob_str(&sql)); 7915bd0665 2008-02-26 drh: 7915bd0665 2008-02-26 drh: n = db_int(0, "SELECT count(*) FROM timeline"); c9cd128c2c 2008-11-02 drh: if( n<nEntry && zAfter ){ c9cd128c2c 2008-11-02 drh: cgi_redirect(url_render(&url, "a", 0, "b", 0)); c9cd128c2c 2008-11-02 drh: } 7915bd0665 2008-02-26 drh: if( zAfter==0 && zBefore==0 ){ 7915bd0665 2008-02-26 drh: blob_appendf(&desc, "%d most recent %ss", n, zEType); abce5105e2 2007-09-01 drh: }else{ 7915bd0665 2008-02-26 drh: blob_appendf(&desc, "%d %ss", n, zEType); 7915bd0665 2008-02-26 drh: } 7915bd0665 2008-02-26 drh: if( zUser ){ 7915bd0665 2008-02-26 drh: blob_appendf(&desc, " by user %h", zUser); 7915bd0665 2008-02-26 drh: } 7915bd0665 2008-02-26 drh: if( zAfter ){ 7915bd0665 2008-02-26 drh: blob_appendf(&desc, " occurring on or after %h.<br>", zAfter); 7915bd0665 2008-02-26 drh: }else if( zBefore ){ 7915bd0665 2008-02-26 drh: blob_appendf(&desc, " occurring on or before %h.<br>", zBefore); 7915bd0665 2008-02-26 drh: } 2b0d4519dc 2008-05-05 drh: if( g.okHistory ){ 2b0d4519dc 2008-05-05 drh: if( zAfter || n==nEntry ){ 2b0d4519dc 2008-05-05 drh: zDate = db_text(0, "SELECT min(timestamp) FROM timeline"); c9cd128c2c 2008-11-02 drh: timeline_submenu(&url, "Older", "b", zDate, "a"); c9cd128c2c 2008-11-02 drh: free(zDate); 2b0d4519dc 2008-05-05 drh: } 2b0d4519dc 2008-05-05 drh: if( zBefore || (zAfter && n==nEntry) ){ 2b0d4519dc 2008-05-05 drh: zDate = db_text(0, "SELECT max(timestamp) FROM timeline"); c9cd128c2c 2008-11-02 drh: timeline_submenu(&url, "Newer", "a", zDate, "b"); c9cd128c2c 2008-11-02 drh: free(zDate); c9cd128c2c 2008-11-02 drh: }else{ c9cd128c2c 2008-11-02 drh: if( zType[0]!='a' ){ c9cd128c2c 2008-11-02 drh: timeline_submenu(&url, "All Types", "y", "all", 0); c9cd128c2c 2008-11-02 drh: } c9cd128c2c 2008-11-02 drh: if( zType[0]!='w' ){ c9cd128c2c 2008-11-02 drh: timeline_submenu(&url, "Wiki Only", "y", "w", 0); c9cd128c2c 2008-11-02 drh: } c9cd128c2c 2008-11-02 drh: if( zType[0]!='c' ){ c9cd128c2c 2008-11-02 drh: timeline_submenu(&url, "Checkins Only", "y", "ci", 0); c9cd128c2c 2008-11-02 drh: } c9cd128c2c 2008-11-02 drh: if( zType[0]!='t' ){ c9cd128c2c 2008-11-02 drh: timeline_submenu(&url, "Tickets Only", "y", "t", 0); c9cd128c2c 2008-11-02 drh: } c9cd128c2c 2008-11-02 drh: } c9cd128c2c 2008-11-02 drh: if( nEntry>20 ){ c9cd128c2c 2008-11-02 drh: timeline_submenu(&url, "20 Events", "n", "20", 0); c9cd128c2c 2008-11-02 drh: } c9cd128c2c 2008-11-02 drh: if( nEntry<200 ){ c9cd128c2c 2008-11-02 drh: timeline_submenu(&url, "200 Events", "n", "200", 0); 2b0d4519dc 2008-05-05 drh: } 3945057916 2007-08-01 drh: } 3945057916 2007-08-01 drh: } 20553a420c 2007-12-03 drh: blob_zero(&sql); e6aa161204 2008-02-26 drh: db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); e38c89130f 2008-02-24 drh: @ <h2>%b(&desc)</h2> e38c89130f 2008-02-24 drh: blob_reset(&desc); afcdc7ec97 2007-08-01 drh: www_print_timeline(&q); afcdc7ec97 2007-08-01 drh: db_finalize(&q); 82fc5abb60 2008-02-26 drh: bbdd4f9915 2007-08-27 drh: @ <script> bbdd4f9915 2007-08-27 drh: @ var parentof = new Object(); bbdd4f9915 2007-08-27 drh: @ var childof = new Object(); 82fc5abb60 2008-02-26 drh: db_prepare(&q, "SELECT rid FROM timeline"); 82fc5abb60 2008-02-26 drh: while( db_step(&q)==SQLITE_ROW ){ 82fc5abb60 2008-02-26 drh: int rid = db_column_int(&q, 0); 82fc5abb60 2008-02-26 drh: Stmt q2; 82fc5abb60 2008-02-26 drh: const char *zSep; 82fc5abb60 2008-02-26 drh: Blob *pOut = cgi_output_blob(); 82fc5abb60 2008-02-26 drh: 82fc5abb60 2008-02-26 drh: db_prepare(&q2, "SELECT pid FROM plink WHERE cid=%d", rid); 82fc5abb60 2008-02-26 drh: zSep = ""; 82fc5abb60 2008-02-26 drh: blob_appendf(pOut, "parentof[\"m%d\"] = [", rid); 82fc5abb60 2008-02-26 drh: while( db_step(&q2)==SQLITE_ROW ){ 82fc5abb60 2008-02-26 drh: int pid = db_column_int(&q2, 0); 82fc5abb60 2008-02-26 drh: blob_appendf(pOut, "%s\"m%d\"", zSep, pid); 82fc5abb60 2008-02-26 drh: zSep = ","; 82fc5abb60 2008-02-26 drh: } 82fc5abb60 2008-02-26 drh: db_finalize(&q2); 82fc5abb60 2008-02-26 drh: blob_appendf(pOut, "];\n"); 82fc5abb60 2008-02-26 drh: db_prepare(&q2, "SELECT cid FROM plink WHERE pid=%d", rid); 82fc5abb60 2008-02-26 drh: zSep = ""; 82fc5abb60 2008-02-26 drh: blob_appendf(pOut, "childof[\"m%d\"] = [", rid); 82fc5abb60 2008-02-26 drh: while( db_step(&q2)==SQLITE_ROW ){ 82fc5abb60 2008-02-26 drh: int pid = db_column_int(&q2, 0); 82fc5abb60 2008-02-26 drh: blob_appendf(pOut, "%s\"m%d\"", zSep, pid); 82fc5abb60 2008-02-26 drh: zSep = ","; 82fc5abb60 2008-02-26 drh: } 82fc5abb60 2008-02-26 drh: db_finalize(&q2); 82fc5abb60 2008-02-26 drh: blob_appendf(pOut, "];\n"); 3945057916 2007-08-01 drh: } 82fc5abb60 2008-02-26 drh: db_finalize(&q); bbdd4f9915 2007-08-27 drh: @ function setall(value){ bbdd4f9915 2007-08-27 drh: @ for(var x in parentof){ bbdd4f9915 2007-08-27 drh: @ setone(x,value); bbdd4f9915 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 drh: @ } 4d03017923 2007-08-30 drh: @ setall("#ffffff"); 4d051c1eda 2007-08-29 drh: @ function setone(id, clr){ 5341655085 2007-08-27 drh: @ if( parentof[id]==null ) return 0; bbdd4f9915 2007-08-27 drh: @ var w = document.getElementById(id); 5341655085 2007-08-27 drh: @ if( w.style.color==clr ){ 5341655085 2007-08-27 drh: @ return 0 bbdd4f9915 2007-08-27 drh: @ }else{ 5341655085 2007-08-27 drh: @ w.style.color = clr 5341655085 2007-08-27 drh: @ return 1 bbdd4f9915 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 drh: @ function xin(id) { 4d051c1eda 2007-08-29 drh: @ setall("#ffffff"); 4d03017923 2007-08-30 drh: @ setone(id,"#ff0000"); 4d03017923 2007-08-30 drh: @ set_children(id, "#b0b0b0"); 4d03017923 2007-08-30 drh: @ set_parents(id, "#b0b0b0"); 4d051c1eda 2007-08-29 drh: @ for(var x in parentof[id]){ 4d051c1eda 2007-08-29 drh: @ var pid = parentof[id][x] 4d051c1eda 2007-08-29 drh: @ var w = document.getElementById(pid); 4d051c1eda 2007-08-29 drh: @ if( w!=null ){ 4d03017923 2007-08-30 drh: @ w.style.color = "#000000"; 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ for(var x in childof[id]){ 4d051c1eda 2007-08-29 drh: @ var cid = childof[id][x] 4d051c1eda 2007-08-29 drh: @ var w = document.getElementById(cid); 4d051c1eda 2007-08-29 drh: @ if( w!=null ){ 4d03017923 2007-08-30 drh: @ w.style.color = "#000000"; 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ } bbdd4f9915 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 drh: @ function xout(id) { 4d03017923 2007-08-30 drh: @ /* setall("#000000"); */ bbdd4f9915 2007-08-27 drh: @ } 4d051c1eda 2007-08-29 drh: @ function set_parents(id, clr){ 5341655085 2007-08-27 drh: @ var plist = parentof[id]; 5341655085 2007-08-27 drh: @ if( plist==null ) return; 5341655085 2007-08-27 drh: @ for(var x in plist){ 5341655085 2007-08-27 drh: @ var pid = plist[x]; 4d051c1eda 2007-08-29 drh: @ if( setone(pid,clr)==1 ){ 4d051c1eda 2007-08-29 drh: @ set_parents(pid,clr); 5341655085 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 drh: @ } 4d051c1eda 2007-08-29 drh: @ function set_children(id,clr){ 5341655085 2007-08-27 drh: @ var clist = childof[id]; 5341655085 2007-08-27 drh: @ if( clist==null ) return; 5341655085 2007-08-27 drh: @ for(var x in clist){ 5341655085 2007-08-27 drh: @ var cid = clist[x]; 4d051c1eda 2007-08-29 drh: @ if( setone(cid,clr)==1 ){ 4d051c1eda 2007-08-29 drh: @ set_children(cid,clr); 5341655085 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 drh: @ } 4d051c1eda 2007-08-29 drh: @ </script> dbda8d6ce9 2007-07-21 drh: style_footer(); dbda8d6ce9 2007-07-21 drh: } 3945057916 2007-08-01 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** The input query q selects various records. Print a human-readable dbda8d6ce9 2007-07-21 drh: ** summary of those records. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Limit the number of entries printed to nLine. 9395aba4f4 2007-09-22 drh: ** 9395aba4f4 2007-09-22 drh: ** The query should return these columns: 9395aba4f4 2007-09-22 drh: ** 9395aba4f4 2007-09-22 drh: ** 0. rid 9395aba4f4 2007-09-22 drh: ** 1. uuid 9395aba4f4 2007-09-22 drh: ** 2. Date/Time 9395aba4f4 2007-09-22 drh: ** 3. Comment string and user 9395aba4f4 2007-09-22 drh: ** 4. Number of non-merge children 9395aba4f4 2007-09-22 drh: ** 5. Number of parents dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void print_timeline(Stmt *q, int mxLine){ dbda8d6ce9 2007-07-21 drh: int nLine = 0; dbda8d6ce9 2007-07-21 drh: char zPrevDate[20]; 097479f99a 2007-09-26 drh: const char *zCurrentUuid=0; dbda8d6ce9 2007-07-21 drh: zPrevDate[0] = 0; dbda8d6ce9 2007-07-21 drh: c1e85e4da5 2008-07-13 drh: if( g.localOpen ){ c1e85e4da5 2008-07-13 drh: int rid = db_lget_int("checkout", 0); c1e85e4da5 2008-07-13 drh: zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); 2db88ec639 2007-09-25 jnc: } b846db063c 2007-08-25 drh: dbda8d6ce9 2007-07-21 drh: while( db_step(q)==SQLITE_ROW && nLine<=mxLine ){ b846db063c 2007-08-25 drh: const char *zId = db_column_text(q, 1); b846db063c 2007-08-25 drh: const char *zDate = db_column_text(q, 2); b846db063c 2007-08-25 drh: const char *zCom = db_column_text(q, 3); b846db063c 2007-08-25 drh: int nChild = db_column_int(q, 4); b846db063c 2007-08-25 drh: int nParent = db_column_int(q, 5); b846db063c 2007-08-25 drh: char *zFree = 0; 097479f99a 2007-09-26 drh: int n = 0; 097479f99a 2007-09-26 drh: char zPrefix[80]; dbda8d6ce9 2007-07-21 drh: char zUuid[UUID_SIZE+1]; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: sprintf(zUuid, "%.10s", zId); dbda8d6ce9 2007-07-21 drh: if( memcmp(zDate, zPrevDate, 10) ){ dbda8d6ce9 2007-07-21 drh: printf("=== %.10s ===\n", zDate); dbda8d6ce9 2007-07-21 drh: memcpy(zPrevDate, zDate, 10); dbda8d6ce9 2007-07-21 drh: nLine++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( zCom==0 ) zCom = ""; fcabd4774c 2007-09-13 drh: printf("%.8s ", &zDate[11]); 097479f99a 2007-09-26 drh: zPrefix[0] = 0; 097479f99a 2007-09-26 drh: if( nParent>1 ){ 097479f99a 2007-09-26 drh: sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* "); 097479f99a 2007-09-26 drh: n = strlen(zPrefix); 097479f99a 2007-09-26 drh: } 097479f99a 2007-09-26 drh: if( nChild>1 ){ 097479f99a 2007-09-26 drh: sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*FORK* "); 097479f99a 2007-09-26 drh: n = strlen(zPrefix); 097479f99a 2007-09-26 drh: } c1e85e4da5 2008-07-13 drh: if( zCurrentUuid && strcmp(zCurrentUuid,zId)==0 ){ 097479f99a 2007-09-26 drh: sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*CURRENT* "); 097479f99a 2007-09-26 drh: n += strlen(zPrefix); b846db063c 2007-08-25 drh: } 097479f99a 2007-09-26 drh: zFree = sqlite3_mprintf("[%.10s] %s%s", zUuid, zPrefix, zCom); fcabd4774c 2007-09-13 drh: nLine += comment_print(zFree, 9, 79); b846db063c 2007-08-25 drh: sqlite3_free(zFree); dbda8d6ce9 2007-07-21 drh: } b846db063c 2007-08-25 drh: } b846db063c 2007-08-25 drh: 9395aba4f4 2007-09-22 drh: /* 9395aba4f4 2007-09-22 drh: ** Return a pointer to a static string that forms the basis for 9395aba4f4 2007-09-22 drh: ** a timeline query for display on a TTY. 9395aba4f4 2007-09-22 drh: */ 9395aba4f4 2007-09-22 drh: const char *timeline_query_for_tty(void){ 9395aba4f4 2007-09-22 drh: static const char zBaseSql[] = 9395aba4f4 2007-09-22 drh: @ SELECT 9395aba4f4 2007-09-22 drh: @ blob.rid, 9395aba4f4 2007-09-22 drh: @ uuid, 9395aba4f4 2007-09-22 drh: @ datetime(event.mtime,'localtime'), fbbd0318bd 2009-01-20 drh: @ coalesce(ecomment,comment) fbbd0318bd 2009-01-20 drh: @ || ' (user: ' || coalesce(euser,user,'?') fbbd0318bd 2009-01-20 drh: @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end fbbd0318bd 2009-01-20 drh: @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x fbbd0318bd 2009-01-20 drh: @ FROM tag, tagxref fbbd0318bd 2009-01-20 drh: @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid fbbd0318bd 2009-01-20 drh: @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) 9395aba4f4 2007-09-22 drh: @ || ')', 9395aba4f4 2007-09-22 drh: @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim), 9395aba4f4 2007-09-22 drh: @ (SELECT count(*) FROM plink WHERE cid=blob.rid) 9395aba4f4 2007-09-22 drh: @ FROM event, blob 9395aba4f4 2007-09-22 drh: @ WHERE blob.rid=event.objid 9395aba4f4 2007-09-22 drh: ; 9395aba4f4 2007-09-22 drh: return zBaseSql; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dfea940da8 2007-10-09 drh: /* bab8363876 2008-12-12 stephan: ** Equivalent to timeline_query_for_tty(), except that: bab8363876 2008-12-12 stephan: ** bab8363876 2008-12-12 stephan: ** a) accepts a the -type=XX flag to set the event type to filter on. bab8363876 2008-12-12 stephan: ** The values of XX are the same as supported by the /timeline page. bab8363876 2008-12-12 stephan: ** bab8363876 2008-12-12 stephan: ** b) The returned string must be freed using free(). bab8363876 2008-12-12 stephan: */ bab8363876 2008-12-12 stephan: char * timeline_query_for_tty_m(void){ bab8363876 2008-12-12 stephan: Blob bl; bab8363876 2008-12-12 stephan: char const * zType = 0; bab8363876 2008-12-12 stephan: blob_zero(&bl); bab8363876 2008-12-12 stephan: blob_append( &bl, timeline_query_for_tty(), -1 ); bab8363876 2008-12-12 stephan: zType = find_option( "type", "t", 1 ); bab8363876 2008-12-12 stephan: if( zType && *zType ) bab8363876 2008-12-12 stephan: { bab8363876 2008-12-12 stephan: blob_appendf( &bl, " AND event.type=%Q", zType ); bab8363876 2008-12-12 stephan: } bab8363876 2008-12-12 stephan: return blob_buffer(&bl); bab8363876 2008-12-12 stephan: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** COMMAND: timeline dbda8d6ce9 2007-07-21 drh: ** bab8363876 2008-12-12 stephan: ** Usage: %fossil timeline ?WHEN? ?BASELINE|DATETIME? ?-n|--count N? ?-t|--type TYPE? 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** Print a summary of activity going backwards in date and time 6607844a01 2007-08-18 drh: ** specified or from the current date and time if no arguments abce5105e2 2007-09-01 drh: ** are given. Show as many as N (default 20) check-ins. The abce5105e2 2007-09-01 drh: ** WHEN argument can be any unique abbreviation of one of these abce5105e2 2007-09-01 drh: ** keywords: abce5105e2 2007-09-01 drh: ** abce5105e2 2007-09-01 drh: ** before abce5105e2 2007-09-01 drh: ** after 6458f020fc 2008-05-14 drh: ** descendants | children abce5105e2 2007-09-01 drh: ** ancestors | parents abce5105e2 2007-09-01 drh: ** e8c4f69c50 2008-10-24 drh: ** The BASELINE can be any unique prefix of 4 characters or more. abce5105e2 2007-09-01 drh: ** The DATETIME should be in the ISO8601 format. For abce5105e2 2007-09-01 drh: ** examples: "2007-08-18 07:21:21". You can also say "current" abce5105e2 2007-09-01 drh: ** for the current version or "now" for the current time. bab8363876 2008-12-12 stephan: ** bab8363876 2008-12-12 stephan: ** The optional TYPE argument may any types supported by the /timeline bab8363876 2008-12-12 stephan: ** page. For example: 6607844a01 2007-08-18 drh: ** bab8363876 2008-12-12 stephan: ** w = wiki commits only bab8363876 2008-12-12 stephan: ** ci = file commits only bab8363876 2008-12-12 stephan: ** t = tickets only dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void timeline_cmd(void){ dbda8d6ce9 2007-07-21 drh: Stmt q; abce5105e2 2007-09-01 drh: int n, k; b846db063c 2007-08-25 drh: const char *zCount; bab8363876 2008-12-12 stephan: const char *zType; abce5105e2 2007-09-01 drh: char *zOrigin; 6607844a01 2007-08-18 drh: char *zDate; abce5105e2 2007-09-01 drh: char *zSQL; abce5105e2 2007-09-01 drh: int objid = 0; abce5105e2 2007-09-01 drh: Blob uuid; abce5105e2 2007-09-01 drh: int mode = 1 ; /* 1: before 2:after 3:children 4:parents */ 4e683ef07b 2008-05-05 drh: db_find_and_open_repository(1); bab8363876 2008-12-12 stephan: zCount = find_option("count","n",1); bab8363876 2008-12-12 stephan: zType = find_option("type","t",1); 6607844a01 2007-08-18 drh: if( zCount ){ 6607844a01 2007-08-18 drh: n = atoi(zCount); 6607844a01 2007-08-18 drh: }else{ 6607844a01 2007-08-18 drh: n = 20; 6607844a01 2007-08-18 drh: } bab8363876 2008-12-12 stephan: if( g.argc>=4 ){ abce5105e2 2007-09-01 drh: k = strlen(g.argv[2]); abce5105e2 2007-09-01 drh: if( strncmp(g.argv[2],"before",k)==0 ){ abce5105e2 2007-09-01 drh: mode = 1; abce5105e2 2007-09-01 drh: }else if( strncmp(g.argv[2],"after",k)==0 && k>1 ){ abce5105e2 2007-09-01 drh: mode = 2; 6458f020fc 2008-05-14 drh: }else if( strncmp(g.argv[2],"descendants",k)==0 ){ abce5105e2 2007-09-01 drh: mode = 3; abce5105e2 2007-09-01 drh: }else if( strncmp(g.argv[2],"children",k)==0 ){ abce5105e2 2007-09-01 drh: mode = 3; abce5105e2 2007-09-01 drh: }else if( strncmp(g.argv[2],"ancestors",k)==0 && k>1 ){ abce5105e2 2007-09-01 drh: mode = 4; abce5105e2 2007-09-01 drh: }else if( strncmp(g.argv[2],"parents",k)==0 ){ abce5105e2 2007-09-01 drh: mode = 4; bab8363876 2008-12-12 stephan: }else if(!zType && !zCount){ bab8363876 2008-12-12 stephan: usage("?WHEN? ?BASELINE|DATETIME? ?-n|--count N? ?-t TYPE?"); abce5105e2 2007-09-01 drh: } bab8363876 2008-12-12 stephan: if( '-' != *g.argv[3] ){ bab8363876 2008-12-12 stephan: zOrigin = g.argv[3]; bab8363876 2008-12-12 stephan: }else{ bab8363876 2008-12-12 stephan: zOrigin = "now"; bab8363876 2008-12-12 stephan: } abce5105e2 2007-09-01 drh: }else if( g.argc==3 ){ abce5105e2 2007-09-01 drh: zOrigin = g.argv[2]; abce5105e2 2007-09-01 drh: }else{ abce5105e2 2007-09-01 drh: zOrigin = "now"; 6607844a01 2007-08-18 drh: } abce5105e2 2007-09-01 drh: k = strlen(zOrigin); abce5105e2 2007-09-01 drh: blob_zero(&uuid); abce5105e2 2007-09-01 drh: blob_append(&uuid, zOrigin, -1); abce5105e2 2007-09-01 drh: if( strcmp(zOrigin, "now")==0 ){ abce5105e2 2007-09-01 drh: if( mode==3 || mode==4 ){ 6458f020fc 2008-05-14 drh: fossil_fatal("cannot compute descendants or ancestors of a date"); abce5105e2 2007-09-01 drh: } aad573b31c 2007-09-24 mjanssen: zDate = mprintf("(SELECT datetime('now'))"); abce5105e2 2007-09-01 drh: }else if( strncmp(zOrigin, "current", k)==0 ){ c1e85e4da5 2008-07-13 drh: if( !g.localOpen ){ c1e85e4da5 2008-07-13 drh: fossil_fatal("must be within a local checkout to use 'current'"); c1e85e4da5 2008-07-13 drh: } abce5105e2 2007-09-01 drh: objid = db_lget_int("checkout",0); abce5105e2 2007-09-01 drh: zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid); abce5105e2 2007-09-01 drh: }else if( name_to_uuid(&uuid, 0)==0 ){ abce5105e2 2007-09-01 drh: objid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &uuid); abce5105e2 2007-09-01 drh: zDate = mprintf("(SELECT mtime FROM plink WHERE cid=%d)", objid); 6607844a01 2007-08-18 drh: }else{ abce5105e2 2007-09-01 drh: if( mode==3 || mode==4 ){ 6458f020fc 2008-05-14 drh: fossil_fatal("cannot compute descendants or ancestors of a date"); abce5105e2 2007-09-01 drh: } abce5105e2 2007-09-01 drh: zDate = mprintf("(SELECT julianday(%Q, 'utc'))", zOrigin); 6607844a01 2007-08-18 drh: } bab8363876 2008-12-12 stephan: zSQL = mprintf("%z AND event.mtime %s %s", bab8363876 2008-12-12 stephan: timeline_query_for_tty_m(), 9395aba4f4 2007-09-22 drh: (mode==1 || mode==4) ? "<=" : ">=", 9395aba4f4 2007-09-22 drh: zDate dbda8d6ce9 2007-07-21 drh: ); abce5105e2 2007-09-01 drh: if( mode==3 || mode==4 ){ abce5105e2 2007-09-01 drh: db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)"); abce5105e2 2007-09-01 drh: if( mode==3 ){ 6458f020fc 2008-05-14 drh: compute_descendants(objid, n); abce5105e2 2007-09-01 drh: }else{ abce5105e2 2007-09-01 drh: compute_ancestors(objid, n); abce5105e2 2007-09-01 drh: } abce5105e2 2007-09-01 drh: zSQL = mprintf("%z AND blob.rid IN ok", zSQL); abce5105e2 2007-09-01 drh: } bab8363876 2008-12-12 stephan: if( zType && (zType[0]!='a') ){ bab8363876 2008-12-12 stephan: zSQL = mprintf( "%z AND event.type=%Q ", zSQL, zType); bab8363876 2008-12-12 stephan: } bab8363876 2008-12-12 stephan: abce5105e2 2007-09-01 drh: zSQL = mprintf("%z ORDER BY event.mtime DESC", zSQL); abce5105e2 2007-09-01 drh: db_prepare(&q, zSQL); bab8363876 2008-12-12 stephan: free( zSQL ); 6607844a01 2007-08-18 drh: print_timeline(&q, n); dbda8d6ce9 2007-07-21 drh: db_finalize(&q); d23b8ba62b 2008-11-01 drh: } d23b8ba62b 2008-11-01 drh: d23b8ba62b 2008-11-01 drh: /* d23b8ba62b 2008-11-01 drh: ** This is a version of the "localtime()" function from the standard d23b8ba62b 2008-11-01 drh: ** C library. It converts a unix timestamp (seconds since 1970) into d23b8ba62b 2008-11-01 drh: ** a broken-out local time structure. d23b8ba62b 2008-11-01 drh: ** d23b8ba62b 2008-11-01 drh: ** This modified version of localtime() works like the library localtime() d23b8ba62b 2008-11-01 drh: ** by default. Except if the timeline-utc property is set, this routine d23b8ba62b 2008-11-01 drh: ** uses gmttime() instead. Thus by setting the timeline-utc property, we d23b8ba62b 2008-11-01 drh: ** can get all localtimes to be displayed at UTC time. d23b8ba62b 2008-11-01 drh: */ d23b8ba62b 2008-11-01 drh: struct tm *fossil_localtime(const time_t *clock){ 0b36f02f15 2008-11-01 drh: if( g.fTimeFormat==0 ){ 0b36f02f15 2008-11-01 drh: if( db_get_int("timeline-utc", 1) ){ 0b36f02f15 2008-11-01 drh: g.fTimeFormat = 1; 0b36f02f15 2008-11-01 drh: }else{ 0b36f02f15 2008-11-01 drh: g.fTimeFormat = 2; 0b36f02f15 2008-11-01 drh: } d23b8ba62b 2008-11-01 drh: } 0b36f02f15 2008-11-01 drh: if( g.fTimeFormat==1 ){ d23b8ba62b 2008-11-01 drh: return gmtime(clock); d23b8ba62b 2008-11-01 drh: }else{ d23b8ba62b 2008-11-01 drh: return localtime(clock); d23b8ba62b 2008-11-01 drh: } dbda8d6ce9 2007-07-21 drh: }