Check-in [8745d0d579]
Not logged in
Overview

SHA1 Hash:8745d0d579e1b95e8fb766be7ef69eb7ed361744
Date: 2008-09-06 13:29:29
User: eric
Comment:Merge tagview branch into mainline
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/cgi.c from [5d10f16d56] to [cf67a58e94].

@@ -200,13 +200,15 @@
   int lifetime          /* Expiration of the cookie in seconds from now */
 ){
   if( zPath==0 ) zPath = g.zTop;
   if( lifetime>0 ){
     lifetime += (int)time(0);
+    char * zDate = cgi_rfc822_datestamp(lifetime);
     blob_appendf(&extraHeader,
-       "Set-Cookie: %s=%t; Path=%s; expires=%s; Version=1\r\n",
-        zName, zValue, zPath, cgi_rfc822_datestamp(lifetime));
+       "Set-Cookie: %s=%t; Path=%s; expires=%z; Version=1\r\n",
+        zName, zValue, zPath, zDate);
+    if( zDate[0] ) free( zDate );
   }else{
     blob_appendf(&extraHeader,
        "Set-Cookie: %s=%t; Path=%s; Version=1\r\n",
        zName, zValue, zPath);
   }
@@ -286,11 +288,13 @@
   }
 #endif
 
   if( g.fullHttpReply ){
     fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus);
-    fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0)));
+    char * zDate = cgi_rfc822_datestamp(time(0));
+    fprintf(g.httpOut, "Date: %s\r\n", zDate );
+    if( zDate[0] ) free( zDate );
     fprintf(g.httpOut, "Connection: close\r\n");
   }else{
     fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
   }
 
@@ -307,11 +311,13 @@
     ** stale cache is the least of the problem. So we provide an Expires
     ** header set to a reasonable period (default: one week).
     */
     /*time_t expires = time(0) + atoi(db_config("constant_expires","604800"));*/
     time_t expires = time(0) + 604800;
-    fprintf(g.httpOut, "Expires: %s\r\n", cgi_rfc822_datestamp(expires));
+    char * zDate = cgi_rfc822_datestamp(expires);
+    fprintf(g.httpOut, "Expires: %s\r\n", zDate );
+    if( zDate[0] ) free( zDate );
   }
 
   /* Content intended for logged in users should only be cached in
   ** the browser, not some shared location.
   */
@@ -1267,10 +1273,12 @@
 
 /*
 ** Returns an RFC822-formatted time string suitable for HTTP headers, among
 ** other things.
 ** Returned timezone is always GMT as required by HTTP/1.1 specification.
+** The returned string is allocated with malloc() and must be freed
+** with free().
 **
 ** See http://www.faqs.org/rfcs/rfc822.html, section 5
 ** and http://www.faqs.org/rfcs/rfc2616.html, section 3.3.
 */
 char *cgi_rfc822_datestamp(time_t now){

Modified src/info.c from [25c4dfa44a] to [33a76dcbd4].

@@ -915,17 +915,34 @@
 **
 ** Figure out what the UUID is and jump to it.
 */
 void info_page(void){
   const char *zName;
+  Blob uuid;
   int rid, nName;
 
   zName = P("name");
   if( zName==0 ) fossil_redirect_home();
   nName = strlen(zName);
   if( nName<4 || nName>UUID_SIZE || !validate16(zName, nName) ){
-    fossil_redirect_home();
+    switch( sym_tag_to_uuid(zName, &uuid) ){
+      case 1: {
+        /* got one UUID, use it */
+        zName = blob_str(&uuid);
+        break;
+      }
+      case 2: {
+        /* go somewhere to show the multiple UUIDs */
+        tagview_page();
+        return;
+        break;
+      }
+      default: {
+        fossil_redirect_home();
+        break;
+      }
+    }
   }
   if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%s*'", zName) ){
     tktview_page();
     return;
   }

Modified src/name.c from [825efda6ea] to [68dd0b644f].

@@ -44,34 +44,20 @@
 int name_to_uuid(Blob *pName, int iErrPriority){
   int rc;
   int sz;
   sz = blob_size(pName);
   if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){
-    Stmt q;
     Blob uuid;
     static const char prefix[] = "tag:";
     static const int preflen = sizeof(prefix)-1;
     const char *zName = blob_str(pName);
 
     if( strncmp(zName, prefix, preflen)==0 ){
       zName += preflen;
     }
 
-    db_prepare(&q,
-      "SELECT (SELECT uuid FROM blob WHERE rid=objid)"
-      "  FROM tagxref JOIN event ON rid=objid"
-      " WHERE tagid=(SELECT tagid FROM tag WHERE tagname='sym-'||%Q)"
-      "   AND tagtype>0"
-      "   AND value IS NULL"
-      " ORDER BY event.mtime DESC",
-      zName
-    );
-    blob_zero(&uuid);
-    if( db_step(&q)==SQLITE_ROW ){
-      db_column_blob(&q, 0, &uuid);
-    }
-    db_finalize(&q);
+    sym_tag_to_uuid(zName, &uuid);
     if( blob_size(&uuid)==0 ){
       fossil_error(iErrPriority, "not a valid object name: %s", zName);
       blob_reset(&uuid);
       return 1;
     }else{
@@ -110,10 +96,55 @@
     }
   }else{
     rc = 0;
   }
   return rc;
+}
+
+/*
+** This routine takes a name which might be a tag and attempts to
+** produce a UUID. The UUID (if any) is returned in the blob pointed
+** to by the second argument.
+**
+** Return as follows:
+**      0   Name is not a tag
+**      1   A single UUID was found
+**      2   More than one UUID was found, so this is presumably a
+**          propagating tag. The return UUID is the most recent,
+**          which is most likely to be the one wanted.
+*/
+int tag_to_uuid(const char *pName, Blob *pUuid,const char *pPrefix){
+  Stmt q;
+  int count = 0;
+  db_prepare(&q,
+    "SELECT (SELECT uuid FROM blob WHERE rid=objid)"
+    "  FROM tagxref JOIN event ON rid=objid"
+    " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q||%Q)"
+    "   AND tagtype>0"
+    "   AND value IS NULL"
+    " ORDER BY event.mtime DESC",
+    pPrefix,
+    pName
+  );
+  blob_zero(pUuid);
+  while( db_step(&q)==SQLITE_ROW ){
+    count++;
+    if(count>1){
+      break;
+    }
+    db_column_blob(&q, 0, pUuid);
+  }
+  db_finalize(&q);
+  return count;
+}
+
+/*
+** This routine takes a name which might be a symbolic tag and
+** attempts to produce a UUID. See tag_to_uuid.
+*/
+int sym_tag_to_uuid(const char *pName, Blob *pUuid){
+    return tag_to_uuid(pName,pUuid,"sym-");
 }
 
 /*
 ** COMMAND:  test-name-to-uuid
 **

Modified src/schema.c from [d06483adac] to [73d36a924e].

@@ -296,10 +296,13 @@
 # define TAG_COMMENT    2     /* The check-in comment */
 # define TAG_USER       3     /* User who made a checking */
 # define TAG_HIDDEN     4     /* Do not display or sync */
 # define TAG_PRIVATE    5     /* Display but do not sync */
 # define TAG_CLUSTER    6     /* A cluster */
+#endif
+#if EXPORT_INTERFACE
+# define MAX_INT_TAG    6     /* The largest pre-assigned tag id */
 #endif
 
 /*
 ** The schema for the locate FOSSIL database file found at the root
 ** of very check-out.  This database contains the complete state of

Modified src/style.c from [1b159a6ade] to [89fb0defc5].

@@ -326,10 +326,16 @@
 @   padding: 5px 10px 5px 10px;
 @   text-align: right;
 @   background-color: #558195;
 @   color: white;
 @ }
+@
+@ /* Make the links in the footer less ugly... */
+@ div.footer a { color: white; }
+@ div.footer a:link { color: white; }
+@ div.footer a:visited { color: white; }
+@ div.footer a:hover { background-color: white; color: #558195; }
 @
 @ /* <verbatim> blocks */
 @ pre.verbatim {
 @    background-color: #f5f5f5;
 @    padding: 0.5em;

Modified src/tagview.c from [73b17a9efb] to [6eeaea74ec].

@@ -60,11 +60,11 @@
     "   linktagid(t.tagid) AS 'Tag ID',"
     "   linktagname(t.tagname) AS 'Name',"
     "   DATETIME(tx.mtime) AS 'Timestamp',"
     "   linkuuid(b.uuid) AS 'Version'"
     "  FROM tag t, tagxref tx, blob b "
-    " WHERE t.tagid=tx.tagid AND tx.srcid=b.rid"
+    " WHERE t.tagid=tx.tagid AND tx.rid=b.rid"
     "   AND tx.tagtype!=0 %s "
     TAGVIEW_DEFAULT_FILTER
     " ORDER BY tx.mtime DESC %s",
     zLikeClause, zLimit
   );
@@ -78,11 +78,11 @@
 ** A small search form which forwards to ?like=SEARCH_STRING
 */
 static void tagview_page_search_miniform(void){
   char const * like = P("like");
   @ <div style='font-size:smaller'>
-  @ <form action='/tagview' method='post'>
+  @ <form action='tagview' method='post'>
   @ Search for tags:
   @ <input type='text' name='like' value='%h((like?like:""))' size='10'/>
   @ <input type='submit'/>
   @ </form>
   @ </div>
@@ -105,11 +105,11 @@
     "SELECT DISTINCT"
     "       linktagname(t.tagname) AS 'Tag Name',"
     "       DATETIME(tx.mtime) AS 'Timestamp',"
     "       linkuuid(b.uuid) AS 'Version'"
     "  FROM tag t, tagxref tx, blob b"
-    " WHERE t.tagid=%d AND t.tagid=tx.tagid AND tx.srcid=b.rid "
+    " WHERE t.tagid=%d AND t.tagid=tx.tagid AND tx.rid=b.rid "
     TAGVIEW_DEFAULT_FILTER
     " ORDER BY tx.mtime DESC",
     tagid
   );
   db_generic_query_view(zSql, 1);
@@ -126,23 +126,22 @@
     "SELECT DISTINCT"
     "       linktagid(t.tagid) AS 'Tag ID',"
     "       DATETIME(tx.mtime) AS 'Timestamp',"
     "       linkuuid(b.uuid) AS 'Version'"
     "  FROM tag t, tagxref tx, blob b "
-    " WHERE t.tagname='%q' AND t.tagid=tx.tagid AND tx.srcid=b.rid "
+    " WHERE t.tagname='%q' AND t.tagid=tx.tagid AND tx.rid=b.rid "
     TAGVIEW_DEFAULT_FILTER
     " ORDER BY tx.mtime DESC",
     tagname);
   db_generic_query_view(zSql, 1);
   free(zSql);
 }
 
-
 /*
-** WEBPAGE: /tagview
+** WEBP AGE: /tagview
 */
-void tagview_page(void){
+void old_tagview_page(void){
   char const * check = 0;
   login_check_credentials();
   if( !g.okRdWiki ){
     login_needed();
   }
@@ -161,5 +160,108 @@
   }
   style_footer();
 }
 
 #undef TAGVIEW_DEFAULT_FILTER
+
+/*
+** Generate a timeline for the chosen tag
+*/
+void tagview_print_timeline(char const *pName, char const *pPrefix){
+  char *zSql;
+  Stmt q;
+  zSql = mprintf("%s AND EXISTS (SELECT 1"
+         " FROM tagxref"
+         "  WHERE tagxref.rid = event.objid"
+         "  AND tagxref.tagid = (SELECT tagid FROM tag"
+         "      WHERE tagname = %Q||%Q))"
+         " ORDER BY 3 desc",
+         timeline_query_for_www(), pPrefix, pName);
+  db_prepare(&q, zSql);
+  free(zSql);
+  www_print_timeline(&q);
+  db_finalize(&q);
+}
+
+/*
+** WEBPAGE: /tagview
+*/
+void tagview_page(void){
+  char const *zName = 0;
+  int zTcount = 0;
+  login_check_credentials();
+  if( !g.okRead ){
+    login_needed();
+  }
+  login_anonymous_available();
+  if( 0 != (zName = P("name")) ){
+    Blob uuid;
+    style_header("Tagged Baselines");
+    @ <h2>%s(zName):</h2>
+    if( sym_tag_to_uuid(zName, &uuid) > 0){
+      tagview_print_timeline(zName, "sym-");
+    }else if( tag_to_uuid(zName, &uuid, "") > 0){
+      tagview_print_timeline(zName, "");
+    }else{
+      @ There is no artifact with this tag.
+    }
+  }else{
+    Stmt q;
+    const char *prefix = "sym-";
+    int preflen = strlen(prefix);
+    style_header("Tags");
+    db_prepare(&q,
+      "SELECT tagname"
+      "  FROM tag"
+      " WHERE EXISTS(SELECT 1 FROM tagxref"
+      "               WHERE tagid=tag.tagid"
+      "                 AND tagtype>0)"
+      " AND tagid > %d"
+      " AND tagname NOT GLOB 'wiki-*'"
+      " AND tagname NOT GLOB 'tkt-*'"
+      " ORDER BY tagname",
+      MAX_INT_TAG
+    );
+    @ <ul>
+    while( db_step(&q)==SQLITE_ROW ){
+      zTcount++;
+      const char *name = db_column_text(&q, 0);
+      if( g.okHistory ){
+        if( strncmp(name, prefix, preflen)==0 ){
+          @ <li><a href=%s(g.zBaseURL)/tagview?name=%s(name+preflen)>
+          @ %s(name+preflen)</a>
+        }else{
+          @ <li><a href=%s(g.zBaseURL)/tagview?name=%s(name)>
+          @ %s(name)</a>
+        }
+      }else{
+        if( strncmp(name, prefix, preflen)==0 ){
+          @ <li><strong>%s(name+preflen)</strong>
+        }else{
+          @ <li><strong>%s(name)</strong>
+        }
+      }
+      if( strncmp(name, prefix, preflen)==0 ){
+        @ (symbolic label)
+      }
+      @ </li>
+    }
+    @ </ul>
+    if( zTcount == 0) {
+      @ There are no relevant tags.
+    }
+    db_finalize(&q);
+  }
+  /*
+   * Put in dummy functions since www_print_timeline has generated calls to
+   * them. Some browsers don't seem to care, but better to be safe.
+   * Actually, it would be nice to use the functions on this page, but at
+   * the moment it looks to be too difficult.
+   */
+  @ <script>
+  @ function xin(id){
+  @ }
+  @ function xout(id){
+  @ }
+  @ </script>
+  style_footer();
+}