/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License version 2 as published by the Free Software Foundation.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code to implement the "info" command. The
** "info" command gives command-line access to information about
** the current tree, or a particular artifact or baseline.
*/
#include "config.h"
#include "info.h"
#include
}
@
}
return cnt;
}
/*
** Show information about ancestors of a baseline. Do this recursively
** to a depth of N. Return true if ancestors are shown and false if not.
*/
static void showAncestors(int pid, int depth, const char *zTitle){
Stmt q;
int cnt = 0;
db_prepare(&q,
"SELECT plink.pid, blob.uuid, datetime(event.mtime, 'localtime'),"
" coalesce(event.euser,event.user),"
" coalesce(event.ecomment,event.comment)"
" FROM plink, blob, event"
" WHERE plink.cid=%d"
" AND blob.rid=plink.pid"
" AND event.objid=plink.pid"
" ORDER BY event.mtime DESC",
pid
);
while( db_step(&q)==SQLITE_ROW ){
int cid = db_column_int(&q, 0);
const char *zUuid = db_column_text(&q, 1);
const char *zDate = db_column_text(&q, 2);
const char *zUser = db_column_text(&q, 3);
const char *zCom = db_column_text(&q, 4);
cnt++;
if( cnt==1 ){
if( zTitle ){
@
}
@
}
}
/*
** Show information about baselines mentioned in the "leaves" table.
*/
static void showLeaves(int rid){
Stmt q;
int cnt = 0;
db_prepare(&q,
"SELECT blob.uuid, datetime(event.mtime, 'localtime'),"
" coalesce(event.euser, event.user),"
" coalesce(event.ecomment,event.comment)"
" FROM leaves, blob, event"
" WHERE blob.rid=leaves.rid AND blob.rid!=%d"
" AND event.objid=leaves.rid"
" ORDER BY event.mtime DESC",
rid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q, 0);
const char *zDate = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
const char *zCom = db_column_text(&q, 3);
cnt++;
if( cnt==1 ){
@
}
@
}
}
/*
** Show information about all tags on a given node.
*/
static void showTags(int rid, const char *zNotGlob){
Stmt q;
int cnt = 0;
db_prepare(&q,
"SELECT tag.tagid, tagname, "
" (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
" value, datetime(tagxref.mtime,'localtime'), tagtype,"
" (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
" FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
" WHERE tagxref.rid=%d AND tagname NOT GLOB '%s'"
" ORDER BY tagname", rid, rid, rid, zNotGlob
);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagname = db_column_text(&q, 1);
const char *zSrcUuid = db_column_text(&q, 2);
const char *zValue = db_column_text(&q, 3);
const char *zDate = db_column_text(&q, 4);
int tagtype = db_column_int(&q, 5);
const char *zOrigUuid = db_column_text(&q, 6);
cnt++;
if( cnt==1 ){
@
}
@
}
}
/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL: /ci?name=RID|ARTIFACTID
**
** Return information about a baseline
*/
void ci_page(void){
Stmt q;
int rid;
int isLeaf;
login_check_credentials();
if( !g.okRead ){ login_needed(); return; }
rid = name_to_rid(PD("name","0"));
if( rid==0 ){
style_header("Check-in Information Error");
@ No such object: %h(g.argv[2])
style_footer();
return;
}
isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
db_prepare(&q,
"SELECT uuid, datetime(mtime, 'localtime'), user, comment"
" FROM blob, event"
" WHERE blob.rid=%d"
" AND event.objid=%d",
rid, rid
);
if( db_step(&q)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q, 0);
char *zTitle = mprintf("Check-in [%.10s]", zUuid);
char *zEUser, *zEComment;
const char *zUser;
const char *zComment;
style_header(zTitle);
login_anonymous_available();
free(zTitle);
zEUser = db_text(0,
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
TAG_USER, rid);
zEComment = db_text(0,
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
TAG_COMMENT, rid);
zUser = db_column_text(&q, 2);
zComment = db_column_text(&q, 3);
@ %h(zTagname) cancelled
}else if( zValue ){
@ %h(zTagname)=%h(zValue)
}else {
@ %h(zTagname)
}
if( tagtype==2 ){
if( zOrigUuid && zOrigUuid[0] ){
@ inherited from
hyperlink_to_uuid(zOrigUuid);
}else{
@ propagates to descendants
}
}
if( zSrcUuid && zSrcUuid[0] ){
if( tagtype==0 ){
@ by
}else{
@ added by
}
hyperlink_to_uuid(zSrcUuid);
@ on %s(zDate)
}
}
db_finalize(&q);
if( cnt ){
@
@
@ SHA1 Hash: %s(zUuid)
if( g.okSetup ){
@ Date: %s(db_column_text(&q, 1))
}
if( zEUser ){
@ Record ID: %d(rid)
@ Edited User: %h(zEUser)
}else{
@ Original User: %h(zUser)
}
if( zEComment ){
@ User: %h(zUser)
@ Edited Comment: %w(zEComment)
}else{
@ Original Comment: %w(zComment)
}
@
if( g.okAdmin ){
db_prepare(&q,
"SELECT rcvfrom.ipaddr, user.login, datetime(rcvfrom.mtime)"
" FROM blob JOIN rcvfrom USING(rcvid) LEFT JOIN user USING(uid)"
" WHERE blob.rid=%d",
rid
);
if( db_step(&q)==SQLITE_ROW ){
const char *zIpAddr = db_column_text(&q, 0);
const char *zUser = db_column_text(&q, 1);
const char *zDate = db_column_text(&q, 2);
if( zUser==0 || zUser[0]==0 ) zUser = "unknown";
@ Comment: %w(zComment)
}
db_finalize(&q);
}
if( g.okHistory ){
char *zShortUuid = mprintf("%.10s", zUuid);
const char *zProjName = db_get("project-name", "unnamed");
@ Received From:
@ %h(zUser) @ %h(zIpAddr) on %s(zDate)
@ Timelines:
@ ancestors
@ | descendants
@ | both
db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
" WHERE rid=%d AND tagtype>0 "
" AND tag.tagid=tagxref.tagid "
" AND +tag.tagname GLOB 'sym-*'", rid);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagName = db_column_text(&q, 0);
@ | %h(zTagName)
}
db_finalize(&q);
@
free(zShortUuid);
}
@ Commands:
@
@ diff
@ | files
@ |
@ ZIP archive
@ | manifest
if( g.okWrite ){
@ | edit
}
@
@
Version: | %s(zUuid) |
---|---|
Date: | %s(db_column_text(&q, 2)) |
Record ID: | %d(rid) |
Original User: | %s(db_column_text(&q, 3)) |
Commands: | @/* @ diff | */ @ history @ | raw-text @ | @
@ %s(zPrevDate)
@ | ||
%s(&zDate[11]) | @@ | sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid); if( g.okHistory ){ @ [%s(zShort)] }else{ @ [%s(zShort)] } @ %h(zCom) (By: %h(zUser)) if( g.okHistory ){ if( fpid ){ @ [diff] } @ @ [annotate] @ | } } db_finalize(&q); @
Made by %h(zUser) on link_to_date(zDate, ":"); @ %w(zComment). [details]
@} db_finalize(&q); style_footer(); } /* ** Write a description of an object to the www reply. ** ** If the object is a file then mention: ** ** * It's artifact ID ** * All its filenames ** * The baselines it was checked-in on, with times and users ** ** If the object is a manifest, then mention: ** ** * It's artifact ID ** * date of check-in ** * Comment & user */ static void object_description( int rid, /* The artifact ID */ int linkToView, /* Add viewer link if true */ Blob *pDownloadName /* Fill with an appropriate download name */ ){ Stmt q; int cnt = 0; int nWiki = 0; db_prepare(&q, "SELECT filename.name, datetime(event.mtime), substr(a.uuid,1,10)," " coalesce(event.ecomment,event.comment)," " 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" " AND b.rid=mlink.mid" " AND mlink.fid=%d", rid ); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); const char *zDate = db_column_text(&q, 1); 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); if( cnt>0 ){ @ Also file }else{ @ File } @ %h(zName) @ artifact %s(zFuuid) part of check-in hyperlink_to_uuid(zVers); @ - %w(zCom) by %h(zUser) on %s(zDate). cnt++; if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_append(pDownloadName, zName, -1); } } db_finalize(&q); db_prepare(&q, "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 *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); if( cnt>0 ){ @ Also wiki page }else{ @ Wiki page } @ [%h(zPagename)] @ artifact %s(zUuid) by %h(zUser) on %s(zDate). nWiki++; cnt++; if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_append(pDownloadName, zPagename, -1); } } db_finalize(&q); if( nWiki==0 ){ db_prepare(&q, "SELECT datetime(mtime), user, comment, uuid, type" " 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); const char *zType = db_column_text(&q, 4); if( cnt>0 ){ @ Also } if( zType[0]=='w' ){ @ Wiki edit }else if( zType[0]=='t' ){ @ Ticket change }else if( zType[0]=='c' ){ @ Manifest of baseline }else{ @ Control file referencing } hyperlink_to_uuid(zUuid); @ - %w(zCom) by %h(zUser) on %s(zDate). if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_append(pDownloadName, zUuid, -1); } cnt++; } db_finalize(&q); } if( cnt==0 ){ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); @ Control file %s(zUuid). if( pDownloadName && blob_size(pDownloadName)==0 ){ blob_append(pDownloadName, zUuid, -1); } }else if( linkToView ){ @ [view] } } /* ** WEBPAGE: fdiff ** ** Two arguments, v1 and v2, are integers. Show the difference between ** the two records. */ void diff_page(void){ 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.okRead ){ login_needed(); return; } style_header("Diff"); @append_diff(pid, fid); @
object_description(v1, 1, 0); @@
object_description(v2, 1, 0); @@
blob_reset(&diff); style_footer(); } /* ** WEBPAGE: raw ** URL: /raw?name=ARTIFACTID&m=TYPE ** ** Return the uninterpreted content of an artifact. Used primarily ** to view artifacts that are images. */ void rawartifact_page(void){ int rid; const char *zMime; Blob content; rid = name_to_rid(PD("name","0")); zMime = PD("m","application/x-fossil-artifact"); login_check_credentials(); if( !g.okRead ){ login_needed(); return; } if( rid==0 ){ cgi_redirect("/home"); } content_get(rid, &content); cgi_set_content_type(zMime); cgi_set_content(&content); } /* ** Render a hex dump of a file. */ static void hexdump(Blob *pBlob){ const unsigned char *x; int n, i, j, k; char zLine[100]; static const char zHex[] = "0123456789abcdef"; x = (const unsigned char*)blob_buffer(pBlob); n = blob_size(pBlob); for(i=0; icontent_get(v1, &c1); content_get(v2, &c2); blob_zero(&diff); text_diff(&c1, &c2, &diff, 4); blob_reset(&c1); blob_reset(&c2); @ %h(blob_str(&diff)) @
blob_zero(&downloadName); object_description(rid, 0, &downloadName); style_submenu_element("Download", "Download", "%s/raw/%T?name=%d", g.zBaseURL, blob_str(&downloadName), rid); @@
style_footer(); } /* ** WEBPAGE: artifact ** URL: /artifact?name=ARTIFACTID ** ** Show the complete content of a file identified by ARTIFACTID ** as preformatted text. */ void artifact_page(void){ int rid; Blob content; const char *zMime; Blob downloadName; int renderAsWiki = 0; int renderAsHtml = 0; rid = name_to_rid(PD("name","0")); login_check_credentials(); if( !g.okRead ){ login_needed(); return; } if( rid==0 ){ cgi_redirect("/home"); } if( g.okAdmin ){ const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){ style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1", g.zTop, zUuid); }else{ style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); } } style_header("Artifact Content"); @hexdump(&content); @
blob_zero(&downloadName); object_description(rid, 0, &downloadName); style_submenu_element("Download", "Download", "%s/raw/%T?name=%d", g.zTop, blob_str(&downloadName), rid); zMime = mimetype_from_name(blob_str(&downloadName)); if( zMime ){ if( strcmp(zMime, "text/html")==0 ){ if( P("txt") ){ style_submenu_element("Html", "Html", "%s/artifact?name=%d", g.zTop, rid); }else{ renderAsHtml = 1; style_submenu_element("Text", "Text", "%s/artifact?name=%d&txt=1", g.zTop, rid); } }else if( strcmp(zMime, "application/x-fossil-wiki")==0 ){ if( P("txt") ){ style_submenu_element("Wiki", "Wiki", "%s/artifact?name=%d", g.zTop, rid); }else{ renderAsWiki = 1; style_submenu_element("Text", "Text", "%s/artifact?name=%d&txt=1", g.zTop, rid); } } } @@
if( zMime==0 ){ @} style_footer(); } /* ** WEBPAGE: tinfo ** URL: /tinfo?name=ARTIFACTID ** ** Show the details of a ticket change control artifact. */ void tinfo_page(void){ int rid; Blob content; char *zDate; const char *zUuid; char zTktName[20]; Manifest m; login_check_credentials(); if( !g.okRdTkt ){ login_needed(); return; } rid = name_to_rid(PD("name","0")); if( rid==0 ){ fossil_redirect_home(); } zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); if( g.okAdmin ){ if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){ style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1", g.zTop, zUuid); }else{ style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid); } } content_get(rid, &content); if( manifest_parse(&m, &content)==0 ){ fossil_redirect_home(); } if( m.type!=CFTYPE_TICKET ){ fossil_redirect_home(); } style_header("Ticket Change Details"); zDate = db_text(0, "SELECT datetime(%.12f)", m.rDate); memcpy(zTktName, m.zTicketUuid, 10); zTktName[10] = 0; @@ %h(blob_str(&content)) @style_submenu_element("Hex","Hex", "%s/hexdump?name=%d", g.zTop, rid); }else if( strncmp(zMime, "image/", 6)==0 ){ @style_submenu_element("Hex","Hex", "%s/hexdump?name=%d", g.zTop, rid); }else{ @
hexdump(&content); @} @
By %h(m.zUser) on %s(zDate). See also: @ artifact content, and @ ticket history @
@ @No such object: %h(zName)
style_footer(); return; } if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){ vdiff_page(); }else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){ winfo_page(); }else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" " WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){ tinfo_page(); }else if( db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) ){ ci_page(); }else if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ ci_page(); }else { artifact_page(); } } /* ** WEBPAGE: ci_edit ** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER ** ** Present a dialog for updating properties of a baseline: ** ** * The check-in user ** * The check-in comment ** * The background color. */ void ci_edit_page(void){ int rid; const char *zComment; /* Current comment on the check-in */ const char *zNewComment; /* Revised check-in comment */ const char *zUser; /* Current user for the check-in */ const char *zNewUser; /* Revised user */ const char *zDate; /* Current date of the check-in */ const char *zNewDate; /* Revised check-in date */ const char *zColor; const char *zNewColor; const char *zNewTagFlag; const char *zNewTag; const char *zNewBrFlag; const char *zNewBranch; const char *zCloseFlag; int fPropagateColor; char *zUuid; Blob comment; Stmt q; static const struct SampleColors { const char *zCName; const char *zColor; } aColor[] = { { "(none)", "" }, { "#f2dcdc", "#f2dcdc" }, { "#f0ffc0", "#f0ffc0" }, { "#bde5d6", "#bde5d6" }, { "#c0ffc0", "#c0ffc0" }, { "#c0fff0", "#c0fff0" }, { "#c0f0ff", "#c0f0ff" }, { "#d0c0ff", "#d0c0ff" }, { "#ffc0ff", "#ffc0ff" }, { "#ffc0d0", "#ffc0d0" }, { "#fff0c0", "#fff0c0" }, { "#c0c0c0", "#c0c0c0" }, }; int nColor = sizeof(aColor)/sizeof(aColor[0]); int i; login_check_credentials(); if( !g.okWrite ){ login_needed(); return; } rid = atoi(PD("r","0")); zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); zComment = db_text(0, "SELECT coalesce(ecomment,comment)" " FROM event WHERE objid=%d", rid); if( zComment==0 ) fossil_redirect_home(); if( P("cancel") ){ cgi_redirectf("ci?name=%d", rid); } zNewComment = PD("c",zComment); zUser = db_text(0, "SELECT coalesce(euser,user)" " FROM event WHERE objid=%d", rid); if( zUser==0 ) fossil_redirect_home(); zNewUser = PD("u",zUser); zDate = db_text(0, "SELECT datetime(mtime)" " FROM event WHERE objid=%d", rid); if( zDate==0 ) fossil_redirect_home(); zNewDate = PD("dt",zDate); zColor = db_text("", "SELECT bgcolor" " FROM event WHERE objid=%d", rid); zNewColor = PD("clr",zColor); fPropagateColor = P("pclr")!=0; zNewTagFlag = P("newtag") ? " checked" : ""; zNewTag = PD("tagname",""); zNewBrFlag = P("newbr") ? " checked" : ""; zNewBranch = PD("brname",""); zCloseFlag = P("close") ? " checked" : ""; if( P("apply") ){ Blob ctrl; char *zDate; int nChng = 0; login_verify_csrf_secret(); blob_zero(&ctrl); zDate = db_text(0, "SELECT datetime('now')"); zDate[10] = 'T'; blob_appendf(&ctrl, "D %s\n", zDate); db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)"); if( zNewColor[0] && strcmp(zColor,zNewColor)!=0 ){ char *zPrefix = "+"; if( fPropagateColor ){ zPrefix = "*"; } db_multi_exec("REPLACE INTO newtags VALUES('bgcolor',%Q,%Q)", zPrefix, zNewColor); } if( zNewColor[0]==0 && zColor[0]!=0 ){ db_multi_exec("REPLACE INTO newtags VALUES('bgcolor','-',NULL)"); } if( strcmp(zComment,zNewComment)!=0 ){ db_multi_exec("REPLACE INTO newtags VALUES('comment','+',%Q)", zNewComment); } if( strcmp(zDate,zNewDate)!=0 ){ db_multi_exec("REPLACE INTO newtags VALUES('date','+',%Q)", zNewDate); } if( strcmp(zUser,zNewUser)!=0 ){ db_multi_exec("REPLACE INTO newtags VALUES('user','+',%Q)", zNewUser); } db_prepare(&q, "SELECT tag.tagid, tagname FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid", rid ); while( db_step(&q)==SQLITE_ROW ){ int tagid = db_column_int(&q, 0); const char *zTag = db_column_text(&q, 1); char zLabel[30]; sprintf(zLabel, "c%d", tagid); if( P(zLabel) ){ db_multi_exec("REPLACE INTO newtags VALUES(%Q,'-',NULL)", zTag); } } db_finalize(&q); if( zCloseFlag[0] ){ db_multi_exec("REPLACE INTO newtags VALUES('closed','+',NULL)"); } if( zNewTagFlag[0] ){ db_multi_exec("REPLACE INTO newtags VALUES('sym-%q','+',NULL)", zNewTag); } if( zNewBrFlag[0] ){ db_multi_exec( "REPLACE INTO newtags " " SELECT tagname, '-', NULL FROM tagxref, tag" " WHERE tagxref.rid=%d AND tagtype==2" " AND tagname GLOB 'sym-*'" " AND tag.tagid=tagxref.tagid", rid ); db_multi_exec("REPLACE INTO newtags VALUES('branch','*',%Q)", zNewBranch); db_multi_exec("REPLACE INTO newtags VALUES('sym-%q','*',NULL)", zNewBranch); } db_prepare(&q, "SELECT tag, prefix, value FROM newtags" " ORDER BY prefix || tag"); while( db_step(&q)==SQLITE_ROW ){ const char *zTag = db_column_text(&q, 0); const char *zPrefix = db_column_text(&q, 1); const char *zValue = db_column_text(&q, 2); nChng++; if( zValue ){ blob_appendf(&ctrl, "T %s%F %s %F\n", zPrefix, zTag, zUuid, zValue); }else{ blob_appendf(&ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid); } } db_finalize(&q); if( nChng>0 ){ int nrid; Blob cksum; blob_appendf(&ctrl, "U %F\n", g.zLogin); md5sum_blob(&ctrl, &cksum); blob_appendf(&ctrl, "Z %b\n", &cksum); db_begin_transaction(); g.markPrivate = content_is_private(rid); nrid = content_put(&ctrl, 0, 0); manifest_crosslink(nrid, &ctrl); db_end_transaction(0); } cgi_redirectf("ci?name=%d", rid); } blob_zero(&comment); blob_append(&comment, zNewComment, -1); zUuid[10] = 0; style_header("Edit Check-in [%s]", zUuid); if( P("preview") ){ Blob suffix; int nTag = 0; @ Preview: @@@if( zNewColor && zNewColor[0] ){ @
@}else{ @ } wiki_convert(&comment, 0, WIKI_INLINE); blob_zero(&suffix); blob_appendf(&suffix, "(user: %h", zNewUser); db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag" " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d" " AND tagtype>1 AND tag.tagid=tagxref.tagid", rid); while( db_step(&q)==SQLITE_ROW ){ const char *zTag = db_column_text(&q, 0); if( nTag==0 ){ blob_appendf(&suffix, ", tags: %h", zTag); }else{ blob_appendf(&suffix, ", %h", zTag); } nTag++; } db_finalize(&q); blob_appendf(&suffix, ")"); @ %s(blob_str(&suffix)) @
Make changes to attributes of check-in @ [%s(zUuid)]:
@