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
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
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(); +}