Check-in [e6aa161204]
Not logged in
Overview

SHA1 Hash:e6aa16120430efe6ac3dc896dd6f9b76aa7a2579
Date: 2008-02-26 16:48:21
User: drh
Comment:Work on a new timeline implementation. Currently implemented as "ntimeline" since it is not yet ready to replace the existing timeline.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/timeline.c from [d5cc80fe9b] to [06d966f168].

@@ -214,10 +214,31 @@
   blob_appendf(pOut, "];\n");
   return 0;
 }
 
 /*
+** Create a temporary table suitable for storing timeline data.
+*/
+static void timeline_temp_table(void){
+  static const char zSql[] =
+    @ CREATE TEMP TABLE IF NOT EXISTS timeline(
+    @   rid INTEGER PRIMARY KEY,
+    @   uuid TEXT,
+    @   timestamp TEXT,
+    @   comment TEXT,
+    @   user TEXT,
+    @   nchild INTEGER,
+    @   nparent INTEGER,
+    @   isleaf BOOLEAN,
+    @   bgcolor TEXT,
+    @   etype TEXT
+    @ )
+  ;
+  db_multi_exec(zSql);
+}
+
+/*
 ** Return a pointer to a constant string that forms the basis
 ** for a timeline query for the WWW interface.
 */
 const char *timeline_query_for_www(void){
   static const char zBaseSql[] =
@@ -235,10 +256,212 @@
     @  FROM event JOIN blob
     @ WHERE blob.rid=event.objid
   ;
   return zBaseSql;
 }
+
+/*
+** WEBPAGE: ntimeline
+**
+** Query parameters:
+**
+**    a=TIMESTAMP    after this date
+**    b=TIMESTAMP    before this date.
+**    n=COUNT        number of events in output
+**    p=RID          artifact RID and up to COUNT parents and ancestors
+**    d=RID          artifact RID and up to COUNT descendents
+**    u=USER         only if belonging to this user
+**    y=TYPE         'ci', 'w', 'tkt'
+**
+** p= and d= can appear individually or together.  If either p= or d=
+** appear, then u=, y=, a=, and b= are ignored.
+**
+** If a= and b= appear, only a= is used.  If neither appear, the most
+** recent events are choosen.
+**
+** If n= is missing, the default count is 20.
+*/
+void page_ntimeline(void){
+  Stmt q;                            /* Query used to generate the timeline */
+  Blob sql;                          /* text of SQL used to generate timeline */
+  Blob desc;                         /* Description of the timeline */
+  int nEntry = atoi(PD("n","20"));   /* Max number of entries on timeline */
+  int p_rid = atoi(PD("p","0"));     /* artifact p and its parents */
+  int d_rid = atoi(PD("d","0"));     /* artifact d and its descendents */
+  const char *zUser = P("u");        /* All entries by this user if not NULL */
+  const char *zType = P("y");        /* Type of events.  All if NULL */
+  const char *zAfter = P("a");       /* Events after this time */
+  const char *zBefore = P("b");      /* Events before this time */
+  Blob scriptInit;
+
+  /* To view the timeline, must have permission to read project data.
+  */
+  login_check_credentials();
+  if( !g.okRead ){ login_needed(); return; }
+
+  style_header("Timeline");
+  if( !g.okHistory &&
+      db_exists("SELECT 1 FROM user"
+                " WHERE login='anonymous'"
+                "   AND cap LIKE '%%h%%'") ){
+    @ <p><b>Note:</b> You will be able to access <u>much</u> more
+    @ historical information if you <a href="%s(g.zTop)/login">login</a>.</p>
+  }
+  timeline_temp_table();
+  blob_zero(&sql);
+  blob_zero(&desc);
+  blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
+  blob_append(&sql, timeline_query_for_www(), -1);
+  if( p_rid || d_rid ){
+    /* If p= or d= is present, ignore all other parameters other than n= */
+    char *zUuid;
+    int np, nd;
+
+    if( p_rid && d_rid && p_rid!=d_rid ) p_rid = d_rid;
+    db_multi_exec(
+       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
+    );
+    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
+                         p_rid ? p_rid : d_rid);
+    blob_appendf(&sql, " AND event.objid IN ok");
+    nd = 0;
+    if( d_rid ){
+      compute_descendents(d_rid, nEntry);
+      nd = db_int(0, "SELECT count(*)-1 FROM ok");
+      if( nd>0 ){
+        db_multi_exec("%s", blob_str(&sql));
+        blob_appendf(&desc, "%d descendents", nd);
+      }
+      db_multi_exec("DELETE FROM ok");
+    }
+    if( p_rid ){
+      compute_ancestors(p_rid, nEntry);
+      np = db_int(0, "SELECT count(*)-1 FROM ok");
+      if( np>0 ){
+        if( nd>0 ) blob_appendf(&desc, " and ");
+        blob_appendf(&desc, "%d ancestors", np);
+        db_multi_exec("%s", blob_str(&sql));
+      }
+    }
+    blob_appendf(&desc, " of <a href='%s/info/%s'>[%.10s]</a>",
+                 g.zBaseURL, zUuid, zUuid);
+    db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC");
+  }else{
+    const char *zEType = "event";
+    if( zType ){
+      blob_appendf(&sql, " AND event.type=%Q", zType);
+      if( zType[0]=='c' ){
+        zEType = "checkin";
+      }else if( zType[0]=='w' ){
+        zEType = "wiki edit";
+      }else if( zType[0]=='t' ){
+        zEType = "ticket change";
+      }
+    }
+    blob_appendf(&desc, "Timeline of up to %d %s", nEntry, zEType);
+    if( zUser ){
+      blob_appendf(&sql, " AND event.user=%Q", zUser);
+      blob_appendf(&desc, " by user %h", zUser);
+    }
+    if( zAfter ){
+      while( isspace(zAfter[0]) ){ zAfter++; }
+      if( zAfter[0] ){
+        blob_appendf(&sql,
+           " AND event.mtime>=(SELECT julianday(%Q, 'utc'))"
+           " ORDER BY event.mtime ASC", zAfter);
+        blob_appendf(&desc, " occurring on or after %h", zAfter);
+        zBefore = 0;
+      }
+    }else if( zBefore ){
+      while( isspace(zBefore[0]) ){ zBefore++; }
+      if( zBefore[0] ){
+        blob_appendf(&sql,
+           " AND event.mtime<=(SELECT julianday(%Q, 'utc'))"
+           " ORDER BY event.mtime DESC", zBefore);
+        blob_appendf(&desc, " occurring on or before %h", zBefore);
+      }
+    }else{
+      blob_appendf(&sql, " ORDER BY event.mtime DESC");
+    }
+    blob_appendf(&sql, " LIMIT %d", nEntry);
+    db_multi_exec("%s", blob_str(&sql));
+  }
+  blob_zero(&sql);
+  db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC");
+  @ <h2>%b(&desc)</h2>
+  blob_reset(&desc);
+  blob_zero(&scriptInit);
+  www_print_timeline(&q, 0, 0, save_parentage_javascript, &scriptInit);
+  db_finalize(&q);
+  @ <script>
+  @ var parentof = new Object();
+  @ var childof = new Object();
+  cgi_append_content(blob_buffer(&scriptInit), blob_size(&scriptInit));
+  blob_reset(&scriptInit);
+  @ function setall(value){
+  @   for(var x in parentof){
+  @     setone(x,value);
+  @   }
+  @ }
+  @ setall("#ffffff");
+  @ function setone(id, clr){
+  @   if( parentof[id]==null ) return 0;
+  @   var w = document.getElementById(id);
+  @   if( w.style.color==clr ){
+  @     return 0
+  @   }else{
+  @     w.style.color = clr
+  @     return 1
+  @   }
+  @ }
+  @ function xin(id) {
+  @   setall("#ffffff");
+  @   setone(id,"#ff0000");
+  @   set_children(id, "#b0b0b0");
+  @   set_parents(id, "#b0b0b0");
+  @   for(var x in parentof[id]){
+  @     var pid = parentof[id][x]
+  @     var w = document.getElementById(pid);
+  @     if( w!=null ){
+  @       w.style.color = "#000000";
+  @     }
+  @   }
+  @   for(var x in childof[id]){
+  @     var cid = childof[id][x]
+  @     var w = document.getElementById(cid);
+  @     if( w!=null ){
+  @       w.style.color = "#000000";
+  @     }
+  @   }
+  @ }
+  @ function xout(id) {
+  @   /* setall("#000000"); */
+  @ }
+  @ function set_parents(id, clr){
+  @   var plist = parentof[id];
+  @   if( plist==null ) return;
+  @   for(var x in plist){
+  @     var pid = plist[x];
+  @     if( setone(pid,clr)==1 ){
+  @       set_parents(pid,clr);
+  @     }
+  @   }
+  @ }
+  @ function set_children(id,clr){
+  @   var clist = childof[id];
+  @   if( clist==null ) return;
+  @   for(var x in clist){
+  @     var cid = clist[x];
+  @     if( setone(cid,clr)==1 ){
+  @       set_children(cid,clr);
+  @     }
+  @   }
+  @ }
+  @ </script>
+  style_footer();
+}
+
 
 /*
 ** WEBPAGE: timeline
 **
 ** Query parameters: