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" d23b8ba62b 2008-11-01 drh: d23b8ba62b 2008-11-01 drh: /* 74534cc91e 2009-12-18 drh: ** Shorten a UUID so that is the minimum length needed to contain 74534cc91e 2009-12-18 drh: ** at least one digit in the range 'a'..'f'. The minimum length is 10. 74534cc91e 2009-12-18 drh: */ 74534cc91e 2009-12-18 drh: static void shorten_uuid(char *zDest, const char *zSrc){ 74534cc91e 2009-12-18 drh: int i; 74534cc91e 2009-12-18 drh: for(i=0; i<10 && zSrc[i]<='9'; i++){} 74534cc91e 2009-12-18 drh: memcpy(zDest, zSrc, 10); 74534cc91e 2009-12-18 drh: if( i==10 ){ 74534cc91e 2009-12-18 drh: do{ 74534cc91e 2009-12-18 drh: zDest[i] = zSrc[i]; 74534cc91e 2009-12-18 drh: i++; 74534cc91e 2009-12-18 drh: }while( zSrc[i-1]<='9' ); 74534cc91e 2009-12-18 drh: }else{ 74534cc91e 2009-12-18 drh: i = 10; 74534cc91e 2009-12-18 drh: } 74534cc91e 2009-12-18 drh: zDest[i] = 0; 74534cc91e 2009-12-18 drh: } 74534cc91e 2009-12-18 drh: 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]; 74534cc91e 2009-12-18 drh: shorten_uuid(zShortUuid, zUuid); fd36718ad9 2007-07-31 drh: if( g.okHistory ){ 74534cc91e 2009-12-18 drh: @ <a href="%s(g.zBaseURL)/info/%s(zShortUuid)">[%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]; 74534cc91e 2009-12-18 drh: shorten_uuid(zShortUuid, 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)")' 74534cc91e 2009-12-18 drh: @ href="%s(g.zBaseURL)/vinfo/%s(zShortUuid)">[%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: } fd36718ad9 2007-07-31 drh: afcdc7ec97 2007-08-01 drh: /* b5f4f910b7 2009-08-29 drh: ** Generate a hyperlink to a date & time. b5f4f910b7 2009-08-29 drh: */ b5f4f910b7 2009-08-29 drh: void hyperlink_to_date(const char *zDate, const char *zSuffix){ b5f4f910b7 2009-08-29 drh: if( zSuffix==0 ) zSuffix = ""; b5f4f910b7 2009-08-29 drh: if( g.okHistory ){ b5f4f910b7 2009-08-29 drh: @ <a href="%s(g.zTop)/timeline?c=%T(zDate)">%s(zDate)</a>%s(zSuffix) b5f4f910b7 2009-08-29 drh: }else{ b5f4f910b7 2009-08-29 drh: @ %s(zDate)%s(zSuffix) b5f4f910b7 2009-08-29 drh: } b5f4f910b7 2009-08-29 drh: } b5f4f910b7 2009-08-29 drh: b5f4f910b7 2009-08-29 drh: /* b5f4f910b7 2009-08-29 drh: ** Generate a hyperlink to a user. This will link to a timeline showing b5f4f910b7 2009-08-29 drh: ** events by that user. If the date+time is specified, then the timeline b5f4f910b7 2009-08-29 drh: ** is centered on that date+time. b5f4f910b7 2009-08-29 drh: */ b5f4f910b7 2009-08-29 drh: void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){ b5f4f910b7 2009-08-29 drh: if( zSuf==0 ) zSuf = ""; b5f4f910b7 2009-08-29 drh: if( g.okHistory ){ b5f4f910b7 2009-08-29 drh: if( zD && zD[0] ){ b5f4f910b7 2009-08-29 drh: @ <a href="%s(g.zTop)/timeline?c=%T(zD)&u=%T(zU)">%h(zU)</a>%s(zSuf) b5f4f910b7 2009-08-29 drh: }else{ b5f4f910b7 2009-08-29 drh: @ <a href="%s(g.zTop)/timeline?u=%T(zU)">%h(zU)</a>%s(zSuf) b5f4f910b7 2009-08-29 drh: } dbda8d6ce9 2007-07-21 drh: }else{ b5f4f910b7 2009-08-29 drh: @ %s(zU) 42c2a18e73 2009-01-22 drh: } 42c2a18e73 2009-01-22 drh: } 42c2a18e73 2009-01-22 drh: 42c2a18e73 2009-01-22 drh: /* 42c2a18e73 2009-01-22 drh: ** Count the number of primary non-branch children for the given check-in. 42c2a18e73 2009-01-22 drh: ** 42c2a18e73 2009-01-22 drh: ** A primary child is one where the parent is the primary parent, not 42c2a18e73 2009-01-22 drh: ** a merge parent. 42c2a18e73 2009-01-22 drh: ** 42c2a18e73 2009-01-22 drh: ** A non-branch child is one which is on the same branch as the parent. b6e22e62cf 2009-01-20 drh: */ b6e22e62cf 2009-01-20 drh: int count_nonbranch_children(int pid){ b6e22e62cf 2009-01-20 drh: int nNonBranch; 42c2a18e73 2009-01-22 drh: static const char zSql[] = 42c2a18e73 2009-01-22 drh: @ SELECT count(*) FROM plink 42c2a18e73 2009-01-22 drh: @ WHERE pid=%d AND isprim 42c2a18e73 2009-01-22 drh: @ AND coalesce((SELECT value FROM tagxref 42c2a18e73 2009-01-22 drh: @ WHERE tagid=%d AND rid=plink.pid), 'trunk') 42c2a18e73 2009-01-22 drh: @ =coalesce((SELECT value FROM tagxref 42c2a18e73 2009-01-22 drh: @ WHERE tagid=%d AND rid=plink.cid), 'trunk') 42c2a18e73 2009-01-22 drh: ; 42c2a18e73 2009-01-22 drh: nNonBranch = db_int(0, zSql, pid, TAG_BRANCH, TAG_BRANCH); b6e22e62cf 2009-01-20 drh: return nNonBranch; b6e22e62cf 2009-01-20 drh: } 580d6ad8c7 2009-01-21 drh: 580d6ad8c7 2009-01-21 drh: /* 580d6ad8c7 2009-01-21 drh: ** Allowed flags for the tmFlags argument to www_print_timeline 580d6ad8c7 2009-01-21 drh: */ 580d6ad8c7 2009-01-21 drh: #if INTERFACE 2fa4df1e47 2009-01-21 drh: #define TIMELINE_ARTID 0x0001 /* Show artifact IDs on non-check-in lines */ 2fa4df1e47 2009-01-21 drh: #define TIMELINE_LEAFONLY 0x0002 /* Show "Leaf", but not "Merge", "Fork" etc */ df3e34c2e8 2009-09-14 drh: #define TIMELINE_BRIEF 0x0004 /* Combine adjacent elements of same object */ 580d6ad8c7 2009-01-21 drh: #endif 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 df3e34c2e8 2009-09-14 drh: ** 9. type ("ci", "w", "t") fbbd0318bd 2009-01-20 drh: ** 10. list of symbolic tags. df3e34c2e8 2009-09-14 drh: ** 11. tagid for ticket or wiki df3e34c2e8 2009-09-14 drh: ** 12. Short comment to user for repeated tickets and wiki dbda8d6ce9 2007-07-21 drh: */ bbdd4f9915 2007-08-27 drh: void www_print_timeline( 580d6ad8c7 2009-01-21 drh: Stmt *pQuery, /* Query to implement the timeline */ 580d6ad8c7 2009-01-21 drh: int tmFlags, /* Flags controlling display behavior */ 580d6ad8c7 2009-01-21 drh: void (*xExtra)(int) /* Routine to call on each line of display */ 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; df3e34c2e8 2009-09-14 drh: int prevTagid = 0; df3e34c2e8 2009-09-14 drh: int suppressCnt = 0; dbda8d6ce9 2007-07-21 drh: char zPrevDate[20]; 5ebcedc33e 2007-07-31 dan: dbda8d6ce9 2007-07-21 drh: zPrevDate[0] = 0; 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); df3e34c2e8 2009-09-14 drh: int tagid = db_column_int(pQuery, 11); df3e34c2e8 2009-09-14 drh: int commentColumn = 3; /* Column containing comment text */ df3e34c2e8 2009-09-14 drh: if( tagid ){ df3e34c2e8 2009-09-14 drh: if( tagid==prevTagid ){ df3e34c2e8 2009-09-14 drh: if( tmFlags & TIMELINE_BRIEF ){ df3e34c2e8 2009-09-14 drh: suppressCnt++; df3e34c2e8 2009-09-14 drh: continue; df3e34c2e8 2009-09-14 drh: }else{ df3e34c2e8 2009-09-14 drh: commentColumn = 12; df3e34c2e8 2009-09-14 drh: } df3e34c2e8 2009-09-14 drh: } df3e34c2e8 2009-09-14 drh: } df3e34c2e8 2009-09-14 drh: prevTagid = tagid; df3e34c2e8 2009-09-14 drh: if( suppressCnt ){ df3e34c2e8 2009-09-14 drh: @ <tr><td><td><td> 8cf8ed785e 2009-09-14 drh: @ <small><i>... %d(suppressCnt) similar 8cf8ed785e 2009-09-14 drh: @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr> df3e34c2e8 2009-09-14 drh: suppressCnt = 0; df3e34c2e8 2009-09-14 drh: } b5f4f910b7 2009-08-29 drh: if( strcmp(zType,"div")==0 ){ b5f4f910b7 2009-08-29 drh: @ <tr><td colspan=3><hr></td></tr> b5f4f910b7 2009-08-29 drh: continue; b5f4f910b7 2009-08-29 drh: } 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' ){ cb31e90868 2009-01-23 drh: const char *azTag[5]; cb31e90868 2009-01-23 drh: int nTag = 0; dfea940da8 2007-10-09 drh: hyperlink_to_uuid_with_mouseover(zUuid, "xin", "xout", rid); 2fa4df1e47 2009-01-21 drh: if( (tmFlags & TIMELINE_LEAFONLY)==0 ){ 2fa4df1e47 2009-01-21 drh: if( nParent>1 ){ cb31e90868 2009-01-23 drh: azTag[nTag++] = "Merge"; 2fa4df1e47 2009-01-21 drh: } 2fa4df1e47 2009-01-21 drh: if( nPChild>1 ){ 2fa4df1e47 2009-01-21 drh: if( count_nonbranch_children(rid)>1 ){ cb31e90868 2009-01-23 drh: azTag[nTag++] = "Fork"; 2fa4df1e47 2009-01-21 drh: }else{ cb31e90868 2009-01-23 drh: azTag[nTag++] = "Branch-Point"; 2fa4df1e47 2009-01-21 drh: } b6e22e62cf 2009-01-20 drh: } dfea940da8 2007-10-09 drh: } dfea940da8 2007-10-09 drh: if( isLeaf ){ cb31e90868 2009-01-23 drh: if( db_exists("SELECT 1 FROM tagxref" cb31e90868 2009-01-23 drh: " WHERE rid=%d AND tagid=%d AND tagtype>0", cb31e90868 2009-01-23 drh: rid, TAG_CLOSED) ){ cb31e90868 2009-01-23 drh: azTag[nTag++] = "Closed-Leaf"; cb31e90868 2009-01-23 drh: }else{ cb31e90868 2009-01-23 drh: azTag[nTag++] = "Leaf"; cb31e90868 2009-01-23 drh: } cb31e90868 2009-01-23 drh: } cb31e90868 2009-01-23 drh: if( nTag>0 ){ cb31e90868 2009-01-23 drh: int i; cb31e90868 2009-01-23 drh: for(i=0; i<nTag; i++){ cb31e90868 2009-01-23 drh: @ <b>%s(azTag[i])%s(i==nTag-1?"":",")</b> cb31e90868 2009-01-23 drh: } 580d6ad8c7 2009-01-21 drh: } 580d6ad8c7 2009-01-21 drh: }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ dfea940da8 2007-10-09 drh: hyperlink_to_uuid(zUuid); dfea940da8 2007-10-09 drh: } df3e34c2e8 2009-09-14 drh: db_column_blob(pQuery, commentColumn, &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] ){ bdcac62937 2009-01-21 drh: @ (user: %h(zUser), tags: %h(zTagList)) fbbd0318bd 2009-01-20 drh: }else{ bdcac62937 2009-01-21 drh: @ (user: %h(zUser)) bdcac62937 2009-01-21 drh: } bdcac62937 2009-01-21 drh: if( xExtra ){ bdcac62937 2009-01-21 drh: xExtra(rid); bdcac62937 2009-01-21 drh: } bdcac62937 2009-01-21 drh: @ </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: /* 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, df3e34c2e8 2009-09-14 drh: @ taglist TEXT, df3e34c2e8 2009-09-14 drh: @ tagid INTEGER, df3e34c2e8 2009-09-14 drh: @ short 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){ 42c2a18e73 2009-01-22 drh: static char *zBase = 0; 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), 42c2a18e73 2009-01-22 drh: @ NOT EXISTS(SELECT 1 FROM plink 42c2a18e73 2009-01-22 drh: @ WHERE pid=blob.rid 42c2a18e73 2009-01-22 drh: @ AND coalesce((SELECT value FROM tagxref 42c2a18e73 2009-01-22 drh: @ WHERE tagid=%d AND rid=plink.pid), 'trunk') 42c2a18e73 2009-01-22 drh: @ = coalesce((SELECT value FROM tagxref 42c2a18e73 2009-01-22 drh: @ WHERE tagid=%d AND rid=plink.cid), 'trunk')), b7f32a71ab 2009-01-20 drh: @ bgcolor, 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 df3e34c2e8 2009-09-14 drh: @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0), df3e34c2e8 2009-09-14 drh: @ tagid, df3e34c2e8 2009-09-14 drh: @ brief 9395aba4f4 2007-09-22 drh: @ FROM event JOIN blob 9395aba4f4 2007-09-22 drh: @ WHERE blob.rid=event.objid 9395aba4f4 2007-09-22 drh: ; 42c2a18e73 2009-01-22 drh: if( zBase==0 ){ 42c2a18e73 2009-01-22 drh: zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH); 42c2a18e73 2009-01-22 drh: } 42c2a18e73 2009-01-22 drh: return zBase; c9cd128c2c 2008-11-02 drh: } c9cd128c2c 2008-11-02 drh: c9cd128c2c 2008-11-02 drh: /* c9cd128c2c 2008-11-02 drh: ** Generate a submenu element with a single parameter change. 4d051c1eda 2007-08-29 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)); 4d051c1eda 2007-08-29 drh: } c9cd128c2c 2008-11-02 drh: 4d051c1eda 2007-08-29 drh: 4d051c1eda 2007-08-29 drh: /* d42adc11c8 2009-08-29 drh: ** zDate is a localtime date. Insert records into the d42adc11c8 2009-08-29 drh: ** "timeline" table to cause <hr> to be inserted before and after d42adc11c8 2009-08-29 drh: ** entries of that date. bbdd4f9915 2007-08-27 drh: */ d42adc11c8 2009-08-29 drh: static void timeline_add_dividers(const char *zDate){ d42adc11c8 2009-08-29 drh: db_multi_exec( d42adc11c8 2009-08-29 drh: "INSERT INTO timeline(rid,timestamp,etype)" d42adc11c8 2009-08-29 drh: "VALUES(-1,datetime(%Q,'-1 second') || '.9','div')", d42adc11c8 2009-08-29 drh: zDate d42adc11c8 2009-08-29 drh: ); d42adc11c8 2009-08-29 drh: db_multi_exec( d42adc11c8 2009-08-29 drh: "INSERT INTO timeline(rid,timestamp,etype)" d42adc11c8 2009-08-29 drh: "VALUES(-2,datetime(%Q) || '.1','div')", d42adc11c8 2009-08-29 drh: zDate 4d051c1eda 2007-08-29 drh: ); bbdd4f9915 2007-08-27 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: ** e6aa161204 2008-02-26 drh: ** a=TIMESTAMP after this date e6aa161204 2008-02-26 drh: ** b=TIMESTAMP before this date. 5a539f82dc 2009-08-15 drh: ** c=TIMESTAMP "circa" 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 fecb3e5cc9 2009-01-20 drh: ** t=TAGID show only check-ins with the given tagid e6aa161204 2008-02-26 drh: ** u=USER only if belonging to this user c9cd128c2c 2008-11-02 drh: ** y=TYPE 'ci', 'w', 't' 1e2ec3ff87 2009-12-30 jeremy_c: ** s=TEXT string search (comment and brief) 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 */ 5a539f82dc 2009-08-15 drh: const char *zCirca = P("c"); /* Events near this time */ e631d8af6d 2009-01-21 drh: const char *zTagName = P("t"); /* Show events with this tag */ 1e2ec3ff87 2009-12-30 jeremy_c: const char *zString = P("s"); /* String text search of comment and brief */ c9cd128c2c 2008-11-02 drh: HQuery url; /* URL for various branch links */ e631d8af6d 2009-01-21 drh: int tagid; /* Tag ID */ df3e34c2e8 2009-09-14 drh: int tmFlags; /* Timeline flags */ 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; } e631d8af6d 2009-01-21 drh: if( zTagName ){ e631d8af6d 2009-01-21 drh: tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTagName); e631d8af6d 2009-01-21 drh: }else{ e631d8af6d 2009-01-21 drh: tagid = 0; df3e34c2e8 2009-09-14 drh: } df3e34c2e8 2009-09-14 drh: if( zType[0]=='a' ){ df3e34c2e8 2009-09-14 drh: tmFlags = TIMELINE_BRIEF; df3e34c2e8 2009-09-14 drh: }else{ df3e34c2e8 2009-09-14 drh: tmFlags = 0; e631d8af6d 2009-01-21 drh: } 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: d42adc11c8 2009-08-29 drh: if( p_rid && d_rid ){ d42adc11c8 2009-08-29 drh: if( p_rid!=d_rid ) p_rid = d_rid; d42adc11c8 2009-08-29 drh: if( P("n")==0 ) nEntry = 10; d42adc11c8 2009-08-29 drh: } 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 ){ d42adc11c8 2009-08-29 drh: compute_descendants(d_rid, nEntry+1); e6aa161204 2008-02-26 drh: nd = db_int(0, "SELECT count(*)-1 FROM ok"); a388e9da2e 2009-12-26 stephan: if( nd>=0 ){ e6aa161204 2008-02-26 drh: db_multi_exec("%s", blob_str(&sql)); a388e9da2e 2009-12-26 stephan: blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); d42adc11c8 2009-08-29 drh: } d42adc11c8 2009-08-29 drh: timeline_add_dividers( d42adc11c8 2009-08-29 drh: db_text("1","SELECT datetime(mtime,'localtime') FROM event" d42adc11c8 2009-08-29 drh: " WHERE objid=%d", d_rid) d42adc11c8 2009-08-29 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 ){ d42adc11c8 2009-08-29 drh: compute_ancestors(p_rid, nEntry+1); 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: } d42adc11c8 2009-08-29 drh: if( d_rid==0 ){ d42adc11c8 2009-08-29 drh: timeline_add_dividers( d42adc11c8 2009-08-29 drh: db_text("1","SELECT datetime(mtime,'localtime') FROM event" d42adc11c8 2009-08-29 drh: " WHERE objid=%d", p_rid) d42adc11c8 2009-08-29 drh: ); d42adc11c8 2009-08-29 drh: } 2b0d4519dc 2008-05-05 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{ d42adc11c8 2009-08-29 drh: blob_appendf(&desc, " of check-in [%.10s]", zUuid); e631d8af6d 2009-01-21 drh: } 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); e631d8af6d 2009-01-21 drh: if( tagid>0 ){ e631d8af6d 2009-01-21 drh: zType = "ci"; e631d8af6d 2009-01-21 drh: url_add_parameter(&url, "t", zTagName); e631d8af6d 2009-01-21 drh: blob_appendf(&sql, " AND EXISTS (SELECT 1 FROM tagxref WHERE tagid=%d" e631d8af6d 2009-01-21 drh: " AND tagtype>0 AND rid=blob.rid)", e631d8af6d 2009-01-21 drh: tagid); e631d8af6d 2009-01-21 drh: } 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); e631d8af6d 2009-01-21 drh: } 1e2ec3ff87 2009-12-30 jeremy_c: if ( zString ){ 1e2ec3ff87 2009-12-30 jeremy_c: blob_appendf(&sql, " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", 1e2ec3ff87 2009-12-30 jeremy_c: zString, zString); 1e2ec3ff87 2009-12-30 jeremy_c: url_add_parameter(&url, "s", zString); 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; c9cd128c2c 2008-11-02 drh: } 5a539f82dc 2009-08-15 drh: }else if( zCirca ){ 5a539f82dc 2009-08-15 drh: while( isspace(zCirca[0]) ){ zCirca++; } 5a539f82dc 2009-08-15 drh: if( zCirca[0] ){ 5a539f82dc 2009-08-15 drh: double rCirca = db_double(0.0, "SELECT julianday(%Q, 'utc')", zCirca); 5a539f82dc 2009-08-15 drh: Blob sql2; 5a539f82dc 2009-08-15 drh: blob_init(&sql2, blob_str(&sql), -1); 5a539f82dc 2009-08-15 drh: blob_appendf(&sql2, 5a539f82dc 2009-08-15 drh: " AND event.mtime<=%f ORDER BY event.mtime DESC LIMIT %d", 5a539f82dc 2009-08-15 drh: rCirca, (nEntry+1)/2 5a539f82dc 2009-08-15 drh: ); 5a539f82dc 2009-08-15 drh: db_multi_exec("%s", blob_str(&sql2)); 5a539f82dc 2009-08-15 drh: blob_reset(&sql2); 5a539f82dc 2009-08-15 drh: blob_appendf(&sql, 5a539f82dc 2009-08-15 drh: " AND event.mtime>=%f ORDER BY event.mtime ASC", 5a539f82dc 2009-08-15 drh: rCirca 5a539f82dc 2009-08-15 drh: ); 5a539f82dc 2009-08-15 drh: nEntry -= (nEntry+1)/2; d42adc11c8 2009-08-29 drh: timeline_add_dividers(zCirca); 5a539f82dc 2009-08-15 drh: url_add_parameter(&url, "c", zCirca); 5a539f82dc 2009-08-15 drh: }else{ 5a539f82dc 2009-08-15 drh: zCirca = 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: } 5a539f82dc 2009-08-15 drh: if( zAfter==0 && zBefore==0 && zCirca==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: } e631d8af6d 2009-01-21 drh: if( tagid>0 ){ e631d8af6d 2009-01-21 drh: blob_appendf(&desc, " tagged with \"%h\"", zTagName); e631d8af6d 2009-01-21 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); 5a539f82dc 2009-08-15 drh: }else if( zCirca ){ 5a539f82dc 2009-08-15 drh: blob_appendf(&desc, " occurring around %h.<br>", zCirca); 2b0d4519dc 2008-05-05 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); e631d8af6d 2009-01-21 drh: }else if( tagid==0 ){ 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); df3e34c2e8 2009-09-14 drh: www_print_timeline(&q, tmFlags, 0); 3945057916 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: } afcdc7ec97 2007-08-01 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: } 2db88ec639 2007-09-25 jnc: dbda8d6ce9 2007-07-21 drh: while( db_step(q)==SQLITE_ROW && nLine<=mxLine ){ b6e22e62cf 2009-01-20 drh: int rid = db_column_int(q, 0); 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 ){ b6e22e62cf 2009-01-20 drh: const char *zBrType; b6e22e62cf 2009-01-20 drh: if( count_nonbranch_children(rid)>1 ){ b6e22e62cf 2009-01-20 drh: zBrType = "*FORK* "; b6e22e62cf 2009-01-20 drh: }else{ b6e22e62cf 2009-01-20 drh: zBrType = "*BRANCH* "; b846db063c 2007-08-25 drh: } b6e22e62cf 2009-01-20 drh: sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], zBrType); 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: } 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'), 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; 9395aba4f4 2007-09-22 drh: } 9395aba4f4 2007-09-22 drh: dfea940da8 2007-10-09 drh: /* 0239325f58 2009-11-09 drh: ** Return true if the input string is a date in the ISO 8601 format: 0239325f58 2009-11-09 drh: ** YYYY-MM-DD. bab8363876 2008-12-12 stephan: */ 0239325f58 2009-11-09 drh: static int isIsoDate(const char *z){ 0239325f58 2009-11-09 drh: return strlen(z)==10 0239325f58 2009-11-09 drh: && z[4]=='-' 0239325f58 2009-11-09 drh: && z[7]=='-' 0239325f58 2009-11-09 drh: && isdigit(z[0]) 0239325f58 2009-11-09 drh: && isdigit(z[5]); 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: ** 0239325f58 2009-11-09 drh: ** Usage: %fossil timeline ?WHEN? ?BASELINE|DATETIME? ?-n N? ?-t 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; 02920e92b5 2009-12-18 drh: Blob sql; abce5105e2 2007-09-01 drh: int objid = 0; abce5105e2 2007-09-01 drh: Blob uuid; 0239325f58 2009-11-09 drh: int mode = 0 ; /* 0:none 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{ e2431b17df 2009-11-09 drh: const char *zShift = ""; 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"); 6458f020fc 2008-05-14 drh: } 0239325f58 2009-11-09 drh: if( mode==0 ){ e2431b17df 2009-11-09 drh: if( isIsoDate(zOrigin) ) zShift = ",'+1 day'"; abce5105e2 2007-09-01 drh: } e2431b17df 2009-11-09 drh: zDate = mprintf("(SELECT julianday(%Q%s, 'utc'))", zOrigin, zShift); 6607844a01 2007-08-18 drh: } e2431b17df 2009-11-09 drh: if( mode==0 ) mode = 1; 02920e92b5 2009-12-18 drh: blob_zero(&sql); 02920e92b5 2009-12-18 drh: blob_append(&sql, timeline_query_for_tty(), -1); 02920e92b5 2009-12-18 drh: blob_appendf(&sql, " AND event.mtime %s %s", 9395aba4f4 2007-09-22 drh: (mode==1 || mode==4) ? "<=" : ">=", 9395aba4f4 2007-09-22 drh: zDate dbda8d6ce9 2007-07-21 drh: ); 02920e92b5 2009-12-18 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: } 02920e92b5 2009-12-18 drh: blob_appendf(&sql, " AND blob.rid IN ok"); bab8363876 2008-12-12 stephan: } bab8363876 2008-12-12 stephan: if( zType && (zType[0]!='a') ){ 02920e92b5 2009-12-18 drh: blob_appendf(&sql, " AND event.type=%Q ", zType); abce5105e2 2007-09-01 drh: } bab8363876 2008-12-12 stephan: 02920e92b5 2009-12-18 drh: blob_appendf(&sql, " ORDER BY event.mtime DESC"); 02920e92b5 2009-12-18 drh: db_prepare(&q, blob_str(&sql)); 02920e92b5 2009-12-18 drh: blob_reset(&sql); 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: }