@@ -115,9 +115,10 @@
Stmt q;
int cnt = 0;
db_prepare(&q,
"SELECT plink.cid, blob.uuid, datetime(plink.mtime, 'localtime'),"
- " event.user, event.comment"
+ " coalesce(event.euser,event.user),"
+ " coalesce(event.comment,event.ecomment)"
" FROM plink, blob, event"
" WHERE plink.pid=%d"
" AND blob.rid=plink.cid"
" AND event.objid=plink.cid"
@@ -133,15 +134,15 @@
const char *zCom = db_column_text(&q, 4);
cnt++;
if( cnt==1 ){
if( zTitle ){
- @ <h2>%s(zTitle)</h2>
+ @ <div class="section">%s(zTitle)</div>
}
@ <ul>
}
@ <li>
hyperlink_to_uuid(zUuid);
- @ %s(zCom) (by %s(zUser) on %s(zDate))
+ @ %w(zCom) (by %s(zUser) on %s(zDate))
if( depth ){
n = showDescendents(cid, depth-1, 0);
}else{
n = db_int(0, "SELECT 1 FROM plink WHERE pid=%d", cid);
@@ -150,8 +151,9 @@
db_multi_exec("DELETE FROM leaves WHERE rid=%d", cid);
@ <b>leaf</b>
}
}
+ db_finalize(&q);
if( cnt ){
@ </ul>
}
return cnt;
@@ -165,9 +167,10 @@
Stmt q;
int cnt = 0;
db_prepare(&q,
"SELECT plink.pid, blob.uuid, datetime(event.mtime, 'localtime'),"
- " event.user, event.comment"
+ " coalesce(event.euser,event.user),"
+ " coalesce(event.comment,event.ecomment)"
" FROM plink, blob, event"
" WHERE plink.cid=%d"
" AND blob.rid=plink.pid"
" AND event.objid=plink.pid"
@@ -182,19 +185,20 @@
const char *zCom = db_column_text(&q, 4);
cnt++;
if( cnt==1 ){
if( zTitle ){
- @ <h2>%s(zTitle)</h2>
+ @ <div class="section">%s(zTitle)</div>
}
@ <ul>
}
@ <li>
hyperlink_to_uuid(zUuid);
- @ %s(zCom) (by %s(zUser) on %s(zDate))
+ @ %w(zCom) (by %s(zUser) on %s(zDate))
if( depth ){
showAncestors(cid, depth-1, 0);
}
}
+ db_finalize(&q);
if( cnt ){
@ </ul>
}
}
@@ -207,12 +211,12 @@
Stmt q;
int cnt = 0;
db_prepare(&q,
"SELECT blob.uuid, datetime(event.mtime, 'localtime'),"
- " event.user, event.comment"
- " FROM leaves, plink, blob, event"
- " WHERE plink.cid=leaves.rid"
- " AND blob.rid=leaves.rid"
+ " coalesce(event.euser, event.user),"
+ " coalesce(event.ecomment,event.comment)"
+ " FROM leaves, blob, event"
+ " WHERE blob.rid=leaves.rid"
" AND event.objid=leaves.rid"
" ORDER BY event.mtime DESC"
);
while( db_step(&q)==SQLITE_ROW ){
@@ -221,15 +225,65 @@
const char *zUser = db_column_text(&q, 2);
const char *zCom = db_column_text(&q, 3);
cnt++;
if( cnt==1 ){
- @ <h2>Leaves</h2>
+ @ <div class="section">Leaves</div>
@ <ul>
}
@ <li>
hyperlink_to_uuid(zUuid);
- @ %s(zCom) (by %s(zUser) on %s(zDate))
- }
+ @ %w(zCom) (by %s(zUser) on %s(zDate))
+ }
+ db_finalize(&q);
+ if( cnt ){
+ @ </ul>
+ }
+}
+
+/*
+** Show information about all tags on a given node.
+*/
+static void showTags(int rid){
+ Stmt q;
+ int cnt = 0;
+ db_prepare(&q,
+ "SELECT tag.tagid, tagname, srcid, blob.uuid, value,"
+ " datetime(tagxref.mtime,'localtime'), tagtype"
+ " FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
+ " LEFT JOIN blob ON blob.rid=tagxref.srcid"
+ " WHERE tagxref.rid=%d"
+ " ORDER BY tagname", rid
+ );
+ while( db_step(&q)==SQLITE_ROW ){
+ const char *zTagname = db_column_text(&q, 1);
+ int srcid = db_column_int(&q, 2);
+ const char *zUuid = db_column_text(&q, 3);
+ const char *zValue = db_column_text(&q, 4);
+ const char *zDate = db_column_text(&q, 5);
+ int tagtype = db_column_int(&q, 6);
+ cnt++;
+ if( cnt==1 ){
+ @ <div class="section">Tags And Properties</div>
+ @ <ul>
+ }
+ @ <li>
+ @ <b>%h(zTagname)</b>
+ if( zValue ){
+ @ = %h(zValue)<i>
+ }else if( tagtype==0 ){
+ @ <i>Cancelled
+ }else{
+ @ <i>
+ }
+ if( srcid==0 ){
+ @ Inherited
+ }else if( zUuid ){
+ @ From
+ hyperlink_to_uuid(zUuid);
+ }
+ @ on %s(zDate)</i>
+ }
+ db_finalize(&q);
if( cnt ){
@ </ul>
}
}
@@ -236,11 +290,11 @@
/*
** WEBPAGE: vinfo
+** URL: /vinfo?name=RID|UUID
**
-** Return information about a version. The version number is contained
-** in g.zExtra.
+** Return information about a version.
*/
void vinfo_page(void){
Stmt q;
int rid;
@@ -247,11 +301,11 @@
int isLeaf;
login_check_credentials();
if( !g.okHistory ){ login_needed(); return; }
- style_header("Version Information");
- rid = name_to_rid(g.zExtra);
+ rid = name_to_rid(PD("name","0"));
if( rid==0 ){
+ style_header("Version Information Error");
@ No such object: %h(g.argv[2])
style_footer();
return;
}
@@ -264,23 +318,36 @@
rid, rid
);
if( db_step(&q)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q, 0);
- @ <h2>Version %s(zUuid)</h2>
- @ <ul>
- @ <li><b>Date:</b> %s(db_column_text(&q, 1))</li>
- @ <li><b>User:</b> %s(db_column_text(&q, 2))</li>
- @ <li><b>Comment:</b> %s(db_column_text(&q, 3))</li>
- @ <li><a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a></li>
- @ <li><a href="%s(g.zBaseURL)/zip/%s(zUuid).zip">ZIP archive</a></li>
- @ <li><a href="%s(g.zBaseURL)/fview/%d(rid)">manifest</a></li>
+ char *zTitle = mprintf("Version: [%.10s]", zUuid);
+ style_header(zTitle);
+ free(zTitle);
+ /*@ <h2>Version %s(zUuid)</h2>*/
+ @ <div class="section">Overview</div>
+ @ <p><table class="label-value">
+ @ <tr><th>Version:</th><td>%s(zUuid)</td></tr>
+ @ <tr><th>Date:</th><td>%s(db_column_text(&q, 1))</td></tr>
if( g.okSetup ){
- @ <li><b>Record ID:</b> %d(rid)</li>
+ @ <tr><th>Record ID:</th><td>%d(rid)</td></tr>
}
- @ </ul>
+ @ <tr><th>Original User:</th><td>%h(db_column_text(&q, 2))</td></tr>
+ @ <tr><th>Original Comment:</th><td>%w(db_column_text(&q,3))</td></tr>
+ @ </td></tr>
+ @ <tr><th>Commands:</th>
+ @ <td>
+ @ <a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a>
+ @ | <a href="%s(g.zBaseURL)/zip/%s(zUuid).zip">ZIP archive</a>
+ @ | <a href="%s(g.zBaseURL)/fview/%d(rid)">manifest</a>
+ @ </td>
+ @ </tr>
+ @ </table></p>
+ }else{
+ style_header("Version Information");
}
db_finalize(&q);
- @ <p><h2>Changes:</h2>
+ showTags(rid);
+ @ <div class="section">Changes</div>
@ <ul>
db_prepare(&q,
"SELECT name, pid, fid"
" FROM mlink, filename"
@@ -299,9 +366,9 @@
@ <b>Added:</b>
}else{
@ <b>Deleted:</b>
}
- @ <a href="%s(g.zBaseURL)/finfo/%T(zName)">%h(zName)</a></li>
+ @ <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a></li>
}
@ </ul>
compute_leaves(rid);
showDescendents(rid, 2, "Descendents");
@@ -310,33 +377,97 @@
style_footer();
}
/*
+** WEBPAGE: winfo
+** URL: /winfo?name=RID
+**
+** Return information about a wiki page.
+*/
+void winfo_page(void){
+ Stmt q;
+ int rid;
+
+ login_check_credentials();
+ if( !g.okHistory ){ login_needed(); return; }
+ rid = name_to_rid(PD("name","0"));
+ if( rid==0 ){
+ style_header("Wiki Page Information Error");
+ @ No such object: %h(g.argv[2])
+ style_footer();
+ return;
+ }
+ db_prepare(&q,
+ "SELECT substr(tagname, 6, 1000), uuid,"
+ " datetime(event.mtime, 'localtime'), user"
+ " FROM tagxref, tag, blob, event"
+ " WHERE tagxref.rid=%d"
+ " AND tag.tagid=tagxref.tagid"
+ " AND tag.tagname LIKE 'wiki-%%'"
+ " AND blob.rid=%d"
+ " AND event.objid=%d",
+ rid, rid, rid
+ );
+ if( db_step(&q)==SQLITE_ROW ){
+ const char *zName = db_column_text(&q, 0);
+ const char *zUuid = db_column_text(&q, 1);
+ char *zTitle = mprintf("Wiki Page %s", zName);
+ style_header(zTitle);
+ free(zTitle);
+ @ <div class="section">Overview</div>
+ @ <p><table class="label-value">
+ @ <tr><th>Version:</th><td>%s(zUuid)</td></tr>
+ @ <tr><th>Date:</th><td>%s(db_column_text(&q, 2))</td></tr>
+ if( g.okSetup ){
+ @ <tr><th>Record ID:</th><td>%d(rid)</td></tr>
+ }
+ @ <tr><th>Original User:</th><td>%s(db_column_text(&q, 3))</td></tr>
+ @ <tr><th>Commands:</th>
+ @ <td>
+/* @ <a href="%s(g.zBaseURL)/wdiff/%d(rid)">diff</a> | */
+ @ <a href="%s(g.zBaseURL)/whistory?page=%t(zName)">history</a>
+ @ | <a href="%s(g.zBaseURL)/fview/%d(rid)">raw-text</a>
+ @ </td>
+ @ </tr>
+ @ </table></p>
+ }else{
+ style_header("Wiki Information");
+ }
+ db_finalize(&q);
+ showTags(rid);
+ style_footer();
+}
+
+/*
** WEBPAGE: finfo
+** URL: /finfo?name=FILENAME
**
-** Show the complete change history for a single file. The name
-** of the file is in g.zExtra
+** Show the complete change history for a single file.
*/
void finfo_page(void){
Stmt q;
+ const char *zFilename;
char zPrevDate[20];
login_check_credentials();
if( !g.okHistory ){ login_needed(); return; }
style_header("File History");
zPrevDate[0] = 0;
+ zFilename = PD("name","");
db_prepare(&q,
"SELECT a.uuid, substr(b.uuid,1,10), datetime(event.mtime,'localtime'),"
- " event.comment, event.user, mlink.pid, mlink.fid"
+ " coalesce(event.ecomment, event.comment),"
+ " coalesce(event.euser, event.user),"
+ " mlink.pid, mlink.fid"
" FROM mlink, blob a, blob b, event"
" WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)"
" AND a.rid=mlink.mid"
" AND b.rid=mlink.fid"
" AND event.objid=mlink.mid"
" ORDER BY event.mtime DESC",
- g.zExtra
+ zFilename
);
- @ <h2>History of %h(g.zExtra)</h2>
+ @ <h2>History of %h(zFilename)</h2>
@ <table cellspacing=0 border=0 cellpadding=0>
while( db_step(&q)==SQLITE_ROW ){
const char *zVers = db_column_text(&q, 0);
const char *zUuid = db_column_text(&q, 1);
@@ -362,9 +493,11 @@
hyperlink_to_uuid(zVers);
@ %h(zCom) (By: %h(zUser))
@ Id: %s(zUuid)/%d(frid)
@ <a href="%s(g.zBaseURL)/fview/%d(frid)">[view]</a>
- @ <a href="%s(g.zBaseURL)/fdiff?v1=%d(fpid)&v2=%d(frid)">[diff]</a>
+ if( fpid ){
+ @ <a href="%s(g.zBaseURL)/fdiff?v1=%d(fpid)&v2=%d(frid)">[diff]</a>
+ }
@ </td>
}
db_finalize(&q);
@ </table>
@@ -379,9 +512,9 @@
Blob from, to, out;
content_get(fromid, &from);
content_get(toid, &to);
blob_zero(&out);
- unified_diff(&from, &to, 5, &out);
+ text_diff(&from, &to, &out, 5);
@ %h(blob_str(&out))
blob_reset(&from);
blob_reset(&to);
blob_reset(&out);
@@ -388,10 +521,11 @@
}
/*
** WEBPAGE: vdiff
+** URL: /vdiff?name=RID
**
-** Show all differences for a particular check-in specified by g.zExtra
+** Show all differences for a particular check-in.
*/
void vdiff_page(void){
int rid;
Stmt q;
@@ -400,9 +534,9 @@
login_check_credentials();
if( !g.okHistory ){ login_needed(); return; }
style_header("Version Diff");
- rid = name_to_rid(g.zExtra);
+ rid = name_to_rid(PD("name",""));
if( rid==0 ){
cgi_redirect("index");
}
db_prepare(&q,
@@ -420,9 +554,9 @@
while( db_step(&q)==SQLITE_ROW ){
int pid = db_column_int(&q,0);
int fid = db_column_int(&q,1);
const char *zName = db_column_text(&q,2);
- @ <p><a href="%s(g.zBaseURL)/finfo/%T(zName)">%h(zName)</a></p>
+ @ <p><a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a></p>
@ <blockquote><pre>
append_diff(pid, fid);
@ </pre></blockquote>
}
@@ -448,11 +582,14 @@
*/
static void object_description(int rid, int linkToView){
Stmt q;
int cnt = 0;
+ int nWiki = 0;
db_prepare(&q,
"SELECT filename.name, datetime(event.mtime), substr(a.uuid,1,10),"
- " event.comment, event.user, b.uuid"
+ " coalesce(event.comment,event.ecomment),"
+ " coalesce(event.euser,event.user),"
+ " b.uuid"
" FROM mlink, filename, event, blob a, blob b"
" WHERE filename.fnid=mlink.fnid"
" AND event.objid=mlink.mid"
" AND a.rid=mlink.fid"
@@ -466,35 +603,61 @@
const char *zFuuid = db_column_text(&q, 2);
const char *zCom = db_column_text(&q, 3);
const char *zUser = db_column_text(&q, 4);
const char *zVers = db_column_text(&q, 5);
- @ File <a href="%s(g.zBaseURL)/finfo/%T(zName)">%h(zName)</a>
+ @ File <a href="%s(g.zBaseURL)/finfo?name=%T(zName)">%h(zName)</a>
@ uuid %s(zFuuid) part of check-in
hyperlink_to_uuid(zVers);
- @ %s(zCom) by %s(zUser) on %s(zDate).
+ @ %w(zCom) by %h(zUser) on %s(zDate)
cnt++;
}
db_finalize(&q);
db_prepare(&q,
- "SELECT datetime(mtime), user, comment, uuid"
- " FROM event, blob"
- " WHERE event.objid=%d"
- " AND blob.rid=%d",
- rid, rid
+ "SELECT substr(tagname, 6, 10000), datetime(event.mtime),"
+ " coalesce(event.euser, event.user), uuid"
+ " FROM tagxref, tag, event, blob"
+ " WHERE tagxref.rid=%d"
+ " AND tag.tagid=tagxref.tagid"
+ " AND tag.tagname LIKE 'wiki-%%'"
+ " AND event.objid=tagxref.rid"
+ " AND blob.rid=tagxref.rid",
+ rid
);
while( db_step(&q)==SQLITE_ROW ){
- const char *zDate = db_column_text(&q, 0);
+ const char *zPagename = db_column_text(&q, 0);
+ const char *zDate = db_column_text(&q, 1);
+ const char *zUser = db_column_text(&q, 2);
const char *zUuid = db_column_text(&q, 3);
- const char *zCom = db_column_text(&q, 2);
- const char *zUser = db_column_text(&q, 1);
- @ Version
- hyperlink_to_uuid(zUuid);
- @ %s(zCom) by %s(zUser) on %s(zDate).
+ @ Wiki page
+ @ [<a href="%s(g.zBaseURL)/wiki?page=%t(zPagename)">%h(zPagename)</a>]
+ @ uuid %s(zUuid) by %h(zUser) on %s(zDate)
+ nWiki++;
cnt++;
}
db_finalize(&q);
+ if( nWiki==0 ){
+ db_prepare(&q,
+ "SELECT datetime(mtime), user, comment, uuid"
+ " FROM event, blob"
+ " WHERE event.objid=%d"
+ " AND blob.rid=%d",
+ rid, rid
+ );
+ while( db_step(&q)==SQLITE_ROW ){
+ const char *zDate = db_column_text(&q, 0);
+ const char *zUuid = db_column_text(&q, 3);
+ const char *zUser = db_column_text(&q, 1);
+ const char *zCom = db_column_text(&q, 2);
+ @ Manifest of version
+ hyperlink_to_uuid(zUuid);
+ @ %w(zCom) by %h(zUser) on %s(zDate)
+ cnt++;
+ }
+ db_finalize(&q);
+ }
if( cnt==0 ){
- @ Empty file
+ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
+ @ Control file %s(zUuid).
}else if( linkToView ){
@ <a href="%s(g.zBaseURL)/fview/%d(rid)">[view]</a>
}
}
@@ -505,10 +668,10 @@
** Two arguments, v1 and v2, are integers. Show the difference between
** the two records.
*/
void diff_page(void){
- int v1 = atoi(PD("v1","0"));
- int v2 = atoi(PD("v2","0"));
+ int v1 = name_to_rid(PD("v1","0"));
+ int v2 = name_to_rid(PD("v2","0"));
Blob c1, c2, diff;
login_check_credentials();
if( !g.okHistory ){ login_needed(); return; }
@@ -525,9 +688,9 @@
@ <blockquote><pre>
content_get(v1, &c1);
content_get(v2, &c2);
blob_zero(&diff);
- unified_diff(&c1, &c2, 4, &diff);
+ text_diff(&c1, &c2, &diff, 4);
blob_reset(&c1);
blob_reset(&c2);
@ %h(blob_str(&diff))
@ </pre></blockquote>
@@ -536,9 +699,9 @@
}
/*
** WEBPAGE: fview
-** URL: /fview/UUID
+** URL: /fview?name=UUID
**
** Show the complete content of a file identified by UUID
** as preformatted text.
*/
@@ -545,11 +708,22 @@
void fview_page(void){
int rid;
Blob content;
- rid = name_to_rid(g.zExtra);
+ rid = name_to_rid(PD("name","0"));
login_check_credentials();
if( !g.okHistory ){ login_needed(); return; }
+ if( g.zPath[0]=='i' ){
+ if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
+ " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){
+ winfo_page();
+ return;
+ }
+ if( db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) ){
+ vinfo_page();
+ return;
+ }
+ }
style_header("File Content");
@ <h2>Content Of:</h2>
@ <blockquote>
object_description(rid, 0);
@@ -560,5 +734,90 @@
@ %h(blob_str(&content))
@ </pre></blockquote>
blob_reset(&content);
style_footer();
+}
+
+/*
+** WEBPAGE: info
+** URL: info/UUID
+**
+** The argument is a UUID which might be a baseline or a file or
+** a ticket or something else. It might also be a wiki page name.
+** Figure out what the UUID is an jump to it. If there is ambiguity,
+** draw a page and let the user select the interpretation.
+*/
+void info_page(void){
+ const char *zName;
+ int rc, nName, cnt;
+ Stmt q;
+
+ zName = P("name");
+ if( zName==0 ) cgi_redirect("index");
+ nName = strlen(zName);
+ if( nName<4 || nName>UUID_SIZE || !validate16(zName, nName) ){
+ cgi_redirect("index");
+ }
+ db_multi_exec(
+ "CREATE TEMP TABLE refs(type,link);"
+ "INSERT INTO refs "
+ " SELECT 'f', rid FROM blob WHERE uuid GLOB '%s*'"
+ " UNION ALL"
+ " SELECT 'w', substr(tagname,6) FROM tag"
+ " WHERE tagname='wiki-%q'"
+ " UNION ALL"
+ " SELECT 't', tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*';",
+ zName, zName, zName
+ );
+ cnt = db_int(0, "SELECT count(*) FROM refs");
+ if( cnt==0 ){
+ style_header("Broken Link");
+ @ <p>No such object: %h(zName)</p>
+ style_footer();
+ return;
+ }
+ db_prepare(&q, "SELECT type, link FROM refs");
+ db_step(&q);
+ if( cnt==1 ){
+ int type = *db_column_text(&q, 0);
+ int rid = db_column_int(&q, 1);
+ db_finalize(&q);
+ if( type=='w' ){
+ wiki_page();
+ }else if( type=='t' ){
+ tktview_page();
+ }else{
+ cgi_replace_parameter("name", mprintf("%d", rid));
+ if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
+ vinfo_page();
+ }else{
+ finfo_page();
+ }
+ }
+ return;
+ }
+ /* Multiple objects */
+ style_header("Ambiguous Link");
+ @ <h2>Ambiguous Link: %h(zName)</h2>
+ @ <ul>
+ while( rc==SQLITE_ROW ){
+ int type = *db_column_text(&q, 0);
+ if( type=='f' ){
+ @ <li><p>
+ object_description(db_column_int(&q, 1), 1);
+ @ </p></li>
+ }else if( type=='w' ){
+ @ <li><p>
+ @ Wiki page <a href="%s(g.zBaseURL)/wiki?name=%s(zName)">%s(zName)</a>.
+ @ </li><p>
+ }else if( type=='t' ){
+ const char *zUuid = db_column_text(&q, 1);
+ @ <li><p>
+ @ Ticket <a href="%s(g.zBaseURL)/tktview?name=%s(zUuid)">%s(zUuid)</a>.
+ @ </li><p>
+ }
+ rc = db_step(&q);
+ }
+ @ </ul>
+ style_footer();
+ db_finalize(&q);
}