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: afcdc7ec97 2007-08-01 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: bbdd4f9915 2007-08-27 drh: /* bbdd4f9915 2007-08-27 drh: ** Generate javascript code that records the parents and children bbdd4f9915 2007-08-27 drh: ** of the version rid. bbdd4f9915 2007-08-27 drh: */ bbdd4f9915 2007-08-27 drh: static int save_parentage_javascript(int rid, Blob *pOut){ bbdd4f9915 2007-08-27 drh: const char *zSep; bbdd4f9915 2007-08-27 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: } afcdc7ec97 2007-08-01 drh: bbdd4f9915 2007-08-27 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: } 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; 20553a420c 2007-12-03 drh: Blob sql; 3945057916 2007-08-01 drh: char *zSQL; bbdd4f9915 2007-08-27 drh: Blob scriptInit; 3945057916 2007-08-01 drh: char zDate[100]; 3945057916 2007-08-01 drh: const char *zStart = P("d"); e15fe43153 2007-08-31 drh: int nEntry = atoi(PD("n","20")); e15fe43153 2007-08-31 drh: const char *zUser = P("u"); e15fe43153 2007-08-31 drh: int objid = atoi(PD("e","0")); e15fe43153 2007-08-31 drh: int relatedEvents = P("r")!=0; e15fe43153 2007-08-31 drh: int afterFlag = P("a")!=0; 50a58adb76 2007-10-10 drh: const char *zType = P("y"); e15fe43153 2007-08-31 drh: int firstEvent; e15fe43153 2007-08-31 drh: int lastEvent; 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 50a58adb76 2007-10-10 drh: @ historical information if <a href="%s(g.zTop)/login">login</a>.</p> 50a58adb76 2007-10-10 drh: } 20553a420c 2007-12-03 drh: blob_zero(&sql); 20553a420c 2007-12-03 drh: blob_append(&sql, timeline_query_for_www(), -1); 50a58adb76 2007-10-10 drh: if( zType ){ 20553a420c 2007-12-03 drh: blob_appendf(&sql, " AND event.type=%Q", zType); 50a58adb76 2007-10-10 drh: } e15fe43153 2007-08-31 drh: if( zUser ){ 20553a420c 2007-12-03 drh: blob_appendf(&sql, " AND event.user=%Q", 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); abce5105e2 2007-09-01 drh: } abce5105e2 2007-09-01 drh: } abce5105e2 2007-09-01 drh: if( relatedEvents && objid ){ 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: ); abce5105e2 2007-09-01 drh: if( afterFlag ){ abce5105e2 2007-09-01 drh: compute_descendents(objid, nEntry); abce5105e2 2007-09-01 drh: }else{ abce5105e2 2007-09-01 drh: compute_ancestors(objid, nEntry); 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: } 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: } bbdd4f9915 2007-08-27 drh: zDate[0] = 0; bbdd4f9915 2007-08-27 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); 20553a420c 2007-12-03 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: } bbdd4f9915 2007-08-27 drh: @ <script> bbdd4f9915 2007-08-27 drh: @ var parentof = new Object(); bbdd4f9915 2007-08-27 drh: @ var childof = new Object(); bbdd4f9915 2007-08-27 drh: cgi_append_content(blob_buffer(&scriptInit), blob_size(&scriptInit)); bbdd4f9915 2007-08-27 drh: blob_reset(&scriptInit); 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"); bbdd4f9915 2007-08-27 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: @ } bbdd4f9915 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 drh: @ } 5341655085 2007-08-27 drh: @ function xout(id) { 4d03017923 2007-08-30 drh: @ /* setall("#000000"); */ 5341655085 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: @ } 5341655085 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 drh: @ } bbdd4f9915 2007-08-27 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: } 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: } 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: }