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> 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: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 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") dbda8d6ce9 2007-07-21 drh: */ bbdd4f9915 2007-08-27 drh: void www_print_timeline( bbdd4f9915 2007-08-27 drh: Stmt *pQuery, e15fe43153 2007-08-31 drh: int *pFirstEvent, e15fe43153 2007-08-31 drh: int *pLastEvent, bbdd4f9915 2007-08-27 drh: int (*xCallback)(int, Blob*), bbdd4f9915 2007-08-27 drh: Blob *pArg bbdd4f9915 2007-08-27 drh: ){ 9a76119307 2007-09-10 drh: int cnt = 0; ebb2765954 2007-12-04 drh: int wikiFlags; ebb2765954 2007-12-04 drh: int mxWikiLen; 6d58613757 2007-10-06 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); e15fe43153 2007-08-31 drh: if( cnt==0 && pFirstEvent ){ e15fe43153 2007-08-31 drh: *pFirstEvent = rid; e15fe43153 2007-08-31 drh: } 20553a420c 2007-12-03 drh: cnt++; e15fe43153 2007-08-31 drh: if( pLastEvent ){ e15fe43153 2007-08-31 drh: *pLastEvent = rid; e15fe43153 2007-08-31 drh: } e15fe43153 2007-08-31 drh: db_multi_exec("INSERT OR IGNORE INTO seen VALUES(%d)", rid); bbdd4f9915 2007-08-27 drh: if( xCallback ){ bbdd4f9915 2007-08-27 drh: xCallback(rid, pArg); bbdd4f9915 2007-08-27 drh: } 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); dfea940da8 2007-10-09 drh: @ (by %h(zUser))</td></tr> afcdc7ec97 2007-08-01 drh: } dbda8d6ce9 2007-07-21 drh: @ </table> afcdc7ec97 2007-08-01 drh: } afcdc7ec97 2007-08-01 drh: b846db063c 2007-08-25 drh: /* bbdd4f9915 2007-08-27 drh: ** Generate javascript code that records the parents and children bbdd4f9915 2007-08-27 drh: ** of the version rid. b846db063c 2007-08-25 drh: */ bbdd4f9915 2007-08-27 drh: static int save_parentage_javascript(int rid, Blob *pOut){ bbdd4f9915 2007-08-27 drh: const char *zSep; b846db063c 2007-08-25 drh: Stmt q; bbdd4f9915 2007-08-27 drh: 5341655085 2007-08-27 drh: db_prepare(&q, "SELECT pid FROM plink WHERE cid=%d", rid); bbdd4f9915 2007-08-27 drh: zSep = ""; a028affcf2 2007-08-27 drh: blob_appendf(pOut, "parentof[\"m%d\"] = [", rid); bbdd4f9915 2007-08-27 drh: while( db_step(&q)==SQLITE_ROW ){ bbdd4f9915 2007-08-27 drh: int pid = db_column_int(&q, 0); bbdd4f9915 2007-08-27 drh: blob_appendf(pOut, "%s\"m%d\"", zSep, pid); bbdd4f9915 2007-08-27 drh: zSep = ","; bbdd4f9915 2007-08-27 drh: } bbdd4f9915 2007-08-27 drh: db_finalize(&q); bbdd4f9915 2007-08-27 drh: blob_appendf(pOut, "];\n"); 5341655085 2007-08-27 drh: db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d", rid); bbdd4f9915 2007-08-27 drh: zSep = ""; a028affcf2 2007-08-27 drh: blob_appendf(pOut, "childof[\"m%d\"] = [", rid); bbdd4f9915 2007-08-27 drh: while( db_step(&q)==SQLITE_ROW ){ bbdd4f9915 2007-08-27 drh: int pid = db_column_int(&q, 0); bbdd4f9915 2007-08-27 drh: blob_appendf(pOut, "%s\"m%d\"", zSep, pid); bbdd4f9915 2007-08-27 drh: zSep = ","; bbdd4f9915 2007-08-27 drh: } bbdd4f9915 2007-08-27 drh: db_finalize(&q); bbdd4f9915 2007-08-27 drh: blob_appendf(pOut, "];\n"); bbdd4f9915 2007-08-27 drh: return 0; bbdd4f9915 2007-08-27 drh: } bbdd4f9915 2007-08-27 drh: bbdd4f9915 2007-08-27 drh: /* e6aa161204 2008-02-26 drh: ** Create a temporary table suitable for storing timeline data. e6aa161204 2008-02-26 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, e6aa161204 2008-02-26 drh: @ etype TEXT e6aa161204 2008-02-26 drh: @ ) e6aa161204 2008-02-26 drh: ; e6aa161204 2008-02-26 drh: db_multi_exec(zSql); e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: e6aa161204 2008-02-26 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), dfea940da8 2007-10-09 drh: @ event.type 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; 9395aba4f4 2007-09-22 drh: } 9395aba4f4 2007-09-22 drh: 9395aba4f4 2007-09-22 drh: /* e6aa161204 2008-02-26 drh: ** WEBPAGE: ntimeline e6aa161204 2008-02-26 drh: ** e6aa161204 2008-02-26 drh: ** Query parameters: e6aa161204 2008-02-26 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 e6aa161204 2008-02-26 drh: ** d=RID artifact RID and up to COUNT descendents e6aa161204 2008-02-26 drh: ** u=USER only if belonging to this user e6aa161204 2008-02-26 drh: ** y=TYPE 'ci', 'w', 'tkt' 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. bbdd4f9915 2007-08-27 drh: */ e6aa161204 2008-02-26 drh: void page_ntimeline(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 */ e6aa161204 2008-02-26 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 */ e6aa161204 2008-02-26 drh: int d_rid = atoi(PD("d","0")); /* artifact d and its descendents */ e6aa161204 2008-02-26 drh: const char *zUser = P("u"); /* All entries by this user if not NULL */ e6aa161204 2008-02-26 drh: const char *zType = P("y"); /* 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 */ e6aa161204 2008-02-26 drh: Blob scriptInit; b846db063c 2007-08-25 drh: b846db063c 2007-08-25 drh: /* To view the timeline, must have permission to read project data. b846db063c 2007-08-25 drh: */ b846db063c 2007-08-25 drh: login_check_credentials(); b846db063c 2007-08-25 drh: if( !g.okRead ){ login_needed(); return; } b846db063c 2007-08-25 drh: b846db063c 2007-08-25 drh: style_header("Timeline"); b846db063c 2007-08-25 drh: if( !g.okHistory && b846db063c 2007-08-25 drh: db_exists("SELECT 1 FROM user" b846db063c 2007-08-25 drh: " WHERE login='anonymous'" b846db063c 2007-08-25 drh: " AND cap LIKE '%%h%%'") ){ b846db063c 2007-08-25 drh: @ <p><b>Note:</b> You will be able to access <u>much</u> more e6aa161204 2008-02-26 drh: @ historical information if you <a href="%s(g.zTop)/login">login</a>.</p> e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: timeline_temp_table(); e6aa161204 2008-02-26 drh: blob_zero(&sql); e6aa161204 2008-02-26 drh: blob_zero(&desc); e6aa161204 2008-02-26 drh: blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1); e6aa161204 2008-02-26 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= */ e6aa161204 2008-02-26 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; e6aa161204 2008-02-26 drh: db_multi_exec( e6aa161204 2008-02-26 drh: "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" e6aa161204 2008-02-26 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 ){ e6aa161204 2008-02-26 drh: compute_descendents(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)); e6aa161204 2008-02-26 drh: blob_appendf(&desc, "%d descendents", 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: } e6aa161204 2008-02-26 drh: blob_appendf(&desc, " of <a href='%s/info/%s'>[%.10s]</a>", e6aa161204 2008-02-26 drh: g.zBaseURL, zUuid, zUuid); e6aa161204 2008-02-26 drh: db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); e6aa161204 2008-02-26 drh: }else{ 7915bd0665 2008-02-26 drh: int n; 7915bd0665 2008-02-26 drh: Blob url; e6aa161204 2008-02-26 drh: const char *zEType = "event"; 7915bd0665 2008-02-26 drh: const char *zDate; 7915bd0665 2008-02-26 drh: blob_zero(&url); 7915bd0665 2008-02-26 drh: blob_appendf(&url, "%s/ntimeline?n=%d", g.zBaseURL, nEntry); e6aa161204 2008-02-26 drh: if( zType ){ e6aa161204 2008-02-26 drh: blob_appendf(&sql, " AND event.type=%Q", zType); 7915bd0665 2008-02-26 drh: blob_appendf(&url, "&y=%T", 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); 7915bd0665 2008-02-26 drh: blob_appendf(&url, "&u=%T", 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); e6aa161204 2008-02-26 drh: zBefore = 0; 7915bd0665 2008-02-26 drh: }else{ 7915bd0665 2008-02-26 drh: zAfter = 0; 5341655085 2007-08-27 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); 7915bd0665 2008-02-26 drh: }else{ 7915bd0665 2008-02-26 drh: zBefore = 0; b846db063c 2007-08-25 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"); 7915bd0665 2008-02-26 drh: if( zAfter==0 && zBefore==0 ){ 7915bd0665 2008-02-26 drh: blob_appendf(&desc, "%d most recent %ss", n, zEType); 7915bd0665 2008-02-26 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: } 7915bd0665 2008-02-26 drh: if( zAfter || n==nEntry ){ 7915bd0665 2008-02-26 drh: zDate = db_text(0, "SELECT min(timestamp) FROM timeline"); 7915bd0665 2008-02-26 drh: blob_appendf(&desc, " <a href='%b&b=%s'>[older]</a>", &url, zDate); 7915bd0665 2008-02-26 drh: } 7915bd0665 2008-02-26 drh: if( zBefore || (zAfter && n==nEntry) ){ 7915bd0665 2008-02-26 drh: zDate = db_text(0, "SELECT max(timestamp) FROM timeline"); 7915bd0665 2008-02-26 drh: blob_appendf(&desc, " <a href='%b&a=%s'>[more recent]</a>", &url, zDate); 7915bd0665 2008-02-26 drh: } e6aa161204 2008-02-26 drh: } e6aa161204 2008-02-26 drh: blob_zero(&sql); e6aa161204 2008-02-26 drh: db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC"); e6aa161204 2008-02-26 drh: @ <h2>%b(&desc)</h2> e6aa161204 2008-02-26 drh: blob_reset(&desc); e6aa161204 2008-02-26 drh: blob_zero(&scriptInit); e6aa161204 2008-02-26 drh: www_print_timeline(&q, 0, 0, save_parentage_javascript, &scriptInit); e6aa161204 2008-02-26 drh: db_finalize(&q); e6aa161204 2008-02-26 drh: @ <script> e6aa161204 2008-02-26 drh: @ var parentof = new Object(); e6aa161204 2008-02-26 drh: @ var childof = new Object(); e6aa161204 2008-02-26 drh: cgi_append_content(blob_buffer(&scriptInit), blob_size(&scriptInit)); e6aa161204 2008-02-26 drh: blob_reset(&scriptInit); e6aa161204 2008-02-26 drh: @ function setall(value){ e6aa161204 2008-02-26 drh: @ for(var x in parentof){ e6aa161204 2008-02-26 drh: @ setone(x,value); e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ setall("#ffffff"); e6aa161204 2008-02-26 drh: @ function setone(id, clr){ e6aa161204 2008-02-26 drh: @ if( parentof[id]==null ) return 0; e6aa161204 2008-02-26 drh: @ var w = document.getElementById(id); e6aa161204 2008-02-26 drh: @ if( w.style.color==clr ){ e6aa161204 2008-02-26 drh: @ return 0 e6aa161204 2008-02-26 drh: @ }else{ e6aa161204 2008-02-26 drh: @ w.style.color = clr e6aa161204 2008-02-26 drh: @ return 1 e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ function xin(id) { e6aa161204 2008-02-26 drh: @ setall("#ffffff"); e6aa161204 2008-02-26 drh: @ setone(id,"#ff0000"); e6aa161204 2008-02-26 drh: @ set_children(id, "#b0b0b0"); e6aa161204 2008-02-26 drh: @ set_parents(id, "#b0b0b0"); e6aa161204 2008-02-26 drh: @ for(var x in parentof[id]){ e6aa161204 2008-02-26 drh: @ var pid = parentof[id][x] e6aa161204 2008-02-26 drh: @ var w = document.getElementById(pid); e6aa161204 2008-02-26 drh: @ if( w!=null ){ e6aa161204 2008-02-26 drh: @ w.style.color = "#000000"; e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ for(var x in childof[id]){ e6aa161204 2008-02-26 drh: @ var cid = childof[id][x] e6aa161204 2008-02-26 drh: @ var w = document.getElementById(cid); e6aa161204 2008-02-26 drh: @ if( w!=null ){ e6aa161204 2008-02-26 drh: @ w.style.color = "#000000"; e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ function xout(id) { e6aa161204 2008-02-26 drh: @ /* setall("#000000"); */ e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ function set_parents(id, clr){ e6aa161204 2008-02-26 drh: @ var plist = parentof[id]; e6aa161204 2008-02-26 drh: @ if( plist==null ) return; e6aa161204 2008-02-26 drh: @ for(var x in plist){ e6aa161204 2008-02-26 drh: @ var pid = plist[x]; e6aa161204 2008-02-26 drh: @ if( setone(pid,clr)==1 ){ e6aa161204 2008-02-26 drh: @ set_parents(pid,clr); e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ function set_children(id,clr){ e6aa161204 2008-02-26 drh: @ var clist = childof[id]; e6aa161204 2008-02-26 drh: @ if( clist==null ) return; e6aa161204 2008-02-26 drh: @ for(var x in clist){ e6aa161204 2008-02-26 drh: @ var cid = clist[x]; e6aa161204 2008-02-26 drh: @ if( setone(cid,clr)==1 ){ e6aa161204 2008-02-26 drh: @ set_children(cid,clr); e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ } e6aa161204 2008-02-26 drh: @ </script> e6aa161204 2008-02-26 drh: style_footer(); e6aa161204 2008-02-26 drh: } afcdc7ec97 2007-08-01 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: ** e15fe43153 2007-08-31 drh: ** d=STARTDATE date in iso8601 notation. dflt: newest event e15fe43153 2007-08-31 drh: ** n=INTEGER number of events to show. dflt: 25 e15fe43153 2007-08-31 drh: ** e=INTEGER starting event id. dflt: nil e15fe43153 2007-08-31 drh: ** u=NAME show only events from user. dflt: nil e15fe43153 2007-08-31 drh: ** a show events after and including. dflt: false e15fe43153 2007-08-31 drh: ** r show only related events. dflt: false 50a58adb76 2007-10-10 drh: ** y=TYPE show only TYPE ('ci' or 'w') dflt: nil 20553a420c 2007-12-03 drh: ** s show the SQL dflt: nil afcdc7ec97 2007-08-01 drh: */ afcdc7ec97 2007-08-01 drh: void page_timeline(void){ afcdc7ec97 2007-08-01 drh: Stmt q; e38c89130f 2008-02-24 drh: Blob sql; /* text of SQL used to generate timeline */ e38c89130f 2008-02-24 drh: char *zSQL; /* Rendered copy of sql */ 4d051c1eda 2007-08-29 drh: Blob scriptInit; 3945057916 2007-08-01 drh: char zDate[100]; e38c89130f 2008-02-24 drh: const char *zStart = P("d"); /* Starting date */ e38c89130f 2008-02-24 drh: int nEntry = atoi(PD("n","20")); /* Max number of entries on timeline */ e38c89130f 2008-02-24 drh: const char *zUser = P("u"); /* All entries by this user if not NULL */ e38c89130f 2008-02-24 drh: int objid = atoi(PD("e","0")); /* Entries related to this event */ e38c89130f 2008-02-24 drh: int relatedEvents = P("r")!=0; /* Must be directly related to of objid */ e38c89130f 2008-02-24 drh: int afterFlag = P("a")!=0; /* After objid if true */ e38c89130f 2008-02-24 drh: const char *zType = P("y"); /* Type of events. All if NULL */ e38c89130f 2008-02-24 drh: int firstEvent; /* First event displayed */ e38c89130f 2008-02-24 drh: int lastEvent; /* Last event displayed */ e38c89130f 2008-02-24 drh: Blob desc; /* Human readable description of the timeline */ e38c89130f 2008-02-24 drh: const char *zEType; /* Human readable event type */ 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"); 3945057916 2007-08-01 drh: if( !g.okHistory && 3945057916 2007-08-01 drh: db_exists("SELECT 1 FROM user" 3945057916 2007-08-01 drh: " WHERE login='anonymous'" 3945057916 2007-08-01 drh: " AND cap LIKE '%%h%%'") ){ e319e8e870 2007-08-25 drh: @ <p><b>Note:</b> You will be able to access <u>much</u> more c1d9e0f4f8 2008-02-09 bch: @ historical information if you <a href="%s(g.zTop)/login">login</a>.</p> 20553a420c 2007-12-03 drh: } 20553a420c 2007-12-03 drh: blob_zero(&sql); e38c89130f 2008-02-24 drh: blob_zero(&desc); 20553a420c 2007-12-03 drh: blob_append(&sql, timeline_query_for_www(), -1); e38c89130f 2008-02-24 drh: zEType = "events"; 50a58adb76 2007-10-10 drh: if( zType ){ 20553a420c 2007-12-03 drh: blob_appendf(&sql, " AND event.type=%Q", zType); e38c89130f 2008-02-24 drh: if( zType[0]=='c' ){ e38c89130f 2008-02-24 drh: zEType = "checkins"; e38c89130f 2008-02-24 drh: }else if( zType[0]=='w' ){ e38c89130f 2008-02-24 drh: zEType = "wiki edits"; e38c89130f 2008-02-24 drh: } e38c89130f 2008-02-24 drh: } e38c89130f 2008-02-24 drh: blob_appendf(&desc, "Timeline of up to %d %s", nEntry, zEType); e15fe43153 2007-08-31 drh: if( zUser ){ 20553a420c 2007-12-03 drh: blob_appendf(&sql, " AND event.user=%Q", zUser); e38c89130f 2008-02-24 drh: blob_appendf(&desc, " by user %h", zUser); e15fe43153 2007-08-31 drh: } e15fe43153 2007-08-31 drh: if( objid ){ 20553a420c 2007-12-03 drh: char *z = db_text(0, "SELECT datetime(event.mtime, 'localtime') FROM event" e15fe43153 2007-08-31 drh: " WHERE objid=%d", objid); e15fe43153 2007-08-31 drh: if( z ){ e15fe43153 2007-08-31 drh: zStart = z; e15fe43153 2007-08-31 drh: } e15fe43153 2007-08-31 drh: } 3945057916 2007-08-01 drh: if( zStart ){ 3945057916 2007-08-01 drh: while( isspace(zStart[0]) ){ zStart++; } 3945057916 2007-08-01 drh: if( zStart[0] ){ 20553a420c 2007-12-03 drh: blob_appendf(&sql, 20553a420c 2007-12-03 drh: " AND event.mtime %s (SELECT julianday(%Q, 'utc'))", 20553a420c 2007-12-03 drh: afterFlag ? ">=" : "<=", zStart); e38c89130f 2008-02-24 drh: blob_appendf(&desc, " occurring on or %s %h", e38c89130f 2008-02-24 drh: afterFlag ? "after": "before", e38c89130f 2008-02-24 drh: zStart); abce5105e2 2007-09-01 drh: } abce5105e2 2007-09-01 drh: } abce5105e2 2007-09-01 drh: if( relatedEvents && objid ){ e38c89130f 2008-02-24 drh: char *zUuid; 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: ); e38c89130f 2008-02-24 drh: zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", objid); abce5105e2 2007-09-01 drh: if( afterFlag ){ abce5105e2 2007-09-01 drh: compute_descendents(objid, nEntry); e38c89130f 2008-02-24 drh: blob_appendf(&desc, e38c89130f 2008-02-24 drh: " and decended from <a href='%s/vinfo/%d'>[%.10s]</a>", e38c89130f 2008-02-24 drh: g.zBaseURL, objid, zUuid); abce5105e2 2007-09-01 drh: }else{ abce5105e2 2007-09-01 drh: compute_ancestors(objid, nEntry); e38c89130f 2008-02-24 drh: blob_appendf(&desc, e38c89130f 2008-02-24 drh: " and a ancestor of <a href='%s/vinfo/%d'>[%.10s]</a>", e38c89130f 2008-02-24 drh: g.zBaseURL, objid, zUuid); 3945057916 2007-08-01 drh: } 20553a420c 2007-12-03 drh: blob_append(&sql, " AND event.objid IN ok", -1); 20553a420c 2007-12-03 drh: } 20553a420c 2007-12-03 drh: if( afterFlag ){ 20553a420c 2007-12-03 drh: blob_appendf(&sql, " ORDER BY event.mtime ASC LIMIT %d", 20553a420c 2007-12-03 drh: nEntry); 20553a420c 2007-12-03 drh: }else{ 20553a420c 2007-12-03 drh: blob_appendf(&sql, " ORDER BY event.mtime DESC LIMIT %d", 20553a420c 2007-12-03 drh: nEntry); 3945057916 2007-08-01 drh: } 20553a420c 2007-12-03 drh: zSQL = blob_str(&sql); 20553a420c 2007-12-03 drh: if( afterFlag ){ 20553a420c 2007-12-03 drh: zSQL = mprintf("SELECT * FROM (%s) ORDER BY timestamp DESC", zSQL); 20553a420c 2007-12-03 drh: } 3945057916 2007-08-01 drh: db_prepare(&q, zSQL); 20553a420c 2007-12-03 drh: if( P("s")!=0 ){ 20553a420c 2007-12-03 drh: @ <hr><p>%h(zSQL)</p><hr> 20553a420c 2007-12-03 drh: } e38c89130f 2008-02-24 drh: @ <h2>%b(&desc)</h2> e38c89130f 2008-02-24 drh: blob_reset(&desc); 20553a420c 2007-12-03 drh: blob_zero(&sql); 20553a420c 2007-12-03 drh: if( afterFlag ){ 20553a420c 2007-12-03 drh: free(zSQL); 20553a420c 2007-12-03 drh: } 4d051c1eda 2007-08-29 drh: zDate[0] = 0; 4d051c1eda 2007-08-29 drh: blob_zero(&scriptInit); f5e8b1d736 2007-08-04 drh: zDate[0] = 0; e15fe43153 2007-08-31 drh: www_print_timeline(&q, &firstEvent, &lastEvent, e15fe43153 2007-08-31 drh: save_parentage_javascript, &scriptInit); afcdc7ec97 2007-08-01 drh: db_finalize(&q); e487b77b1a 2008-02-04 drh: /* @ <p>firstEvent=%d(firstEvent) lastEvent=%d(lastEvent)</p> */ 3945057916 2007-08-01 drh: if( zStart==0 ){ 3945057916 2007-08-01 drh: zStart = zDate; 3945057916 2007-08-01 drh: } 4d051c1eda 2007-08-29 drh: @ <script> 4d051c1eda 2007-08-29 drh: @ var parentof = new Object(); 4d051c1eda 2007-08-29 drh: @ var childof = new Object(); 4d051c1eda 2007-08-29 drh: cgi_append_content(blob_buffer(&scriptInit), blob_size(&scriptInit)); 4d051c1eda 2007-08-29 drh: blob_reset(&scriptInit); 4d051c1eda 2007-08-29 drh: @ function setall(value){ 4d051c1eda 2007-08-29 drh: @ for(var x in parentof){ 4d051c1eda 2007-08-29 drh: @ setone(x,value); 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ } 4d03017923 2007-08-30 drh: @ setall("#ffffff"); 4d051c1eda 2007-08-29 drh: @ function setone(id, clr){ 4d051c1eda 2007-08-29 drh: @ if( parentof[id]==null ) return 0; 4d051c1eda 2007-08-29 drh: @ var w = document.getElementById(id); 4d051c1eda 2007-08-29 drh: @ if( w.style.color==clr ){ 4d051c1eda 2007-08-29 drh: @ return 0 4d051c1eda 2007-08-29 drh: @ }else{ 4d051c1eda 2007-08-29 drh: @ w.style.color = clr 4d051c1eda 2007-08-29 drh: @ return 1 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 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: @ } 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ function xout(id) { 4d03017923 2007-08-30 drh: @ /* setall("#000000"); */ 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ function set_parents(id, clr){ 4d051c1eda 2007-08-29 drh: @ var plist = parentof[id]; 4d051c1eda 2007-08-29 drh: @ if( plist==null ) return; 4d051c1eda 2007-08-29 drh: @ for(var x in plist){ 4d051c1eda 2007-08-29 drh: @ var pid = plist[x]; 4d051c1eda 2007-08-29 drh: @ if( setone(pid,clr)==1 ){ 4d051c1eda 2007-08-29 drh: @ set_parents(pid,clr); 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ function set_children(id,clr){ 4d051c1eda 2007-08-29 drh: @ var clist = childof[id]; 4d051c1eda 2007-08-29 drh: @ if( clist==null ) return; 4d051c1eda 2007-08-29 drh: @ for(var x in clist){ 4d051c1eda 2007-08-29 drh: @ var cid = clist[x]; 4d051c1eda 2007-08-29 drh: @ if( setone(cid,clr)==1 ){ 4d051c1eda 2007-08-29 drh: @ set_children(cid,clr); 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ } 4d051c1eda 2007-08-29 drh: @ </script> 3945057916 2007-08-01 drh: @ <hr> 3945057916 2007-08-01 drh: @ <form method="GET" action="%s(g.zBaseURL)/timeline"> 3945057916 2007-08-01 drh: @ Start Date: 3945057916 2007-08-01 drh: @ <input type="text" size="30" value="%h(zStart)" name="d"> 3945057916 2007-08-01 drh: @ Number Of Entries: 3945057916 2007-08-01 drh: @ <input type="text" size="4" value="%d(nEntry)" name="n"> 3945057916 2007-08-01 drh: @ <br><input type="submit" value="Submit"> 3945057916 2007-08-01 drh: @ </form> 20553a420c 2007-12-03 drh: @ <table><tr><td> 3945057916 2007-08-01 drh: @ <form method="GET" action="%s(g.zBaseURL)/timeline"> ff4cc5fae2 2007-09-23 drh: @ <input type="hidden" value="%d(lastEvent)" name="e"> 3945057916 2007-08-01 drh: @ <input type="hidden" value="%d(nEntry)" name="n"> 3945057916 2007-08-01 drh: @ <input type="submit" value="Next %d(nEntry) Rows"> 20553a420c 2007-12-03 drh: @ </form></td><td> ff4cc5fae2 2007-09-23 drh: @ <form method="GET" action="%s(g.zBaseURL)/timeline"> ff4cc5fae2 2007-09-23 drh: @ <input type="hidden" value="%d(firstEvent)" name="e"> ff4cc5fae2 2007-09-23 drh: @ <input type="hidden" value="%d(nEntry)" name="n"> 20553a420c 2007-12-03 drh: @ <input type="hidden" value="1" name="a"> ff4cc5fae2 2007-09-23 drh: @ <input type="submit" value="Previous %d(nEntry) Rows"> 20553a420c 2007-12-03 drh: @ </form></td></tr></table> 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]; e63a9fd9d0 2007-09-25 jnc: const char *zCurrentUuid=0; 2db88ec639 2007-09-25 jnc: Stmt currentQ; 2db88ec639 2007-09-25 jnc: int rid = db_lget_int("checkout", 0); dbda8d6ce9 2007-07-21 drh: zPrevDate[0] = 0; dbda8d6ce9 2007-07-21 drh: 2db88ec639 2007-09-25 jnc: db_prepare(¤tQ, 2db88ec639 2007-09-25 jnc: "SELECT uuid" 2db88ec639 2007-09-25 jnc: " FROM blob WHERE rid=%d", rid 2db88ec639 2007-09-25 jnc: ); 2db88ec639 2007-09-25 jnc: if( db_step(¤tQ)==SQLITE_ROW ){ 2db88ec639 2007-09-25 jnc: zCurrentUuid = db_column_text(¤tQ, 0); 2db88ec639 2007-09-25 jnc: } 4d051c1eda 2007-08-29 drh: dbda8d6ce9 2007-07-21 drh: while( db_step(q)==SQLITE_ROW && nLine<=mxLine ){ 4d051c1eda 2007-08-29 drh: const char *zId = db_column_text(q, 1); 4d051c1eda 2007-08-29 drh: const char *zDate = db_column_text(q, 2); 4d051c1eda 2007-08-29 drh: const char *zCom = db_column_text(q, 3); 4d051c1eda 2007-08-29 drh: int nChild = db_column_int(q, 4); 4d051c1eda 2007-08-29 drh: int nParent = db_column_int(q, 5); 4d051c1eda 2007-08-29 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: } 097479f99a 2007-09-26 drh: if( 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: } 2db88ec639 2007-09-25 jnc: db_finalize(¤tQ); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 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'), 6d58613757 2007-10-06 drh: @ coalesce(ecomment,comment) || ' (by ' || coalesce(euser,user,'?') ||')', 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; 9395aba4f4 2007-09-22 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** COMMAND: timeline dbda8d6ce9 2007-07-21 drh: ** abce5105e2 2007-09-01 drh: ** Usage: %fossil timeline ?WHEN? ?UUID|DATETIME? ?-n|--count N? 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 abce5105e2 2007-09-01 drh: ** descendents | children abce5105e2 2007-09-01 drh: ** ancestors | parents 6607844a01 2007-08-18 drh: ** abce5105e2 2007-09-01 drh: ** The UUID 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. 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; 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 */ 6607844a01 2007-08-18 drh: db_find_and_open_repository(); 6607844a01 2007-08-18 drh: zCount = find_option("n","count",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: } abce5105e2 2007-09-01 drh: 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; abce5105e2 2007-09-01 drh: }else if( strncmp(g.argv[2],"descendents",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; abce5105e2 2007-09-01 drh: }else{ abce5105e2 2007-09-01 drh: usage("?WHEN? ?UUID|DATETIME?"); abce5105e2 2007-09-01 drh: } abce5105e2 2007-09-01 drh: zOrigin = g.argv[3]; 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 ){ abce5105e2 2007-09-01 drh: fossil_fatal("cannot compute descendents 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 ){ 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 ){ abce5105e2 2007-09-01 drh: fossil_fatal("cannot compute descendents 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: } 9395aba4f4 2007-09-22 drh: zSQL = mprintf("%s AND event.mtime %s %s", 9395aba4f4 2007-09-22 drh: timeline_query_for_tty(), 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 ){ abce5105e2 2007-09-01 drh: compute_descendents(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: } abce5105e2 2007-09-01 drh: zSQL = mprintf("%z ORDER BY event.mtime DESC", zSQL); abce5105e2 2007-09-01 drh: db_prepare(&q, zSQL); 6607844a01 2007-08-18 drh: print_timeline(&q, n); dbda8d6ce9 2007-07-21 drh: db_finalize(&q); dbda8d6ce9 2007-07-21 drh: }