Index: src/attach.c ================================================================== --- src/attach.c +++ src/attach.c @@ -246,10 +246,66 @@ db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid); } manifest_crosslink(rid, pAttach, MC_NONE); } + +/* +** Commit a new attachment into the repository +*/ +void attach_commit( + const char *zName, /* The filename of the attachment */ + const char *zTarget, /* The artifact uuid to attach to */ + const char *aContent, /* The content of the attachment */ + int szContent, /* The length of the attachment */ + int needModerator, /* Moderate the attachment? */ + const char *zComment /* The comment for the attachment */ +){ + Blob content; + Blob manifest; + Blob cksum; + char *zUUID; + char *zDate; + int rid; + int i, n; + int addCompress = 0; + Manifest *pManifest; + + db_begin_transaction(); + blob_init(&content, aContent, szContent); + pManifest = manifest_parse(&content, 0, 0); + manifest_destroy(pManifest); + blob_init(&content, aContent, szContent); + if( pManifest ){ + blob_compress(&content, &content); + addCompress = 1; + } + rid = content_put_ex(&content, 0, 0, 0, needModerator); + zUUID = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); + blob_zero(&manifest); + for(i=n=0; zName[i]; i++){ + if( zName[i]=='/' || zName[i]=='\\' ) n = i+1; + } + zName += n; + if( zName[0]==0 ) zName = "unknown"; + blob_appendf(&manifest, "A %F%s %F %s\n", + zName, addCompress ? ".gz" : "", zTarget, zUUID); + while( fossil_isspace(zComment[0]) ) zComment++; + n = strlen(zComment); + while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; } + if( n>0 ){ + blob_appendf(&manifest, "C %#F\n", n, zComment); + } + zDate = date_in_standard_format("now"); + blob_appendf(&manifest, "D %s\n", zDate); + blob_appendf(&manifest, "U %F\n", login_name()); + md5sum_blob(&manifest, &cksum); + blob_appendf(&manifest, "Z %b\n", &cksum); + attach_put(&manifest, rid, needModerator); + assert( blob_is_reset(&manifest) ); + db_end_transaction(0); +} /* ** WEBPAGE: attachadd ** Add a new attachment. ** @@ -322,59 +378,14 @@ if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop); if( P("cancel") ){ cgi_redirect(zFrom); } if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){ - Blob content; - Blob manifest; - Blob cksum; - char *zUUID; - const char *zComment; - char *zDate; - int rid; - int i, n; - int addCompress = 0; - Manifest *pManifest; - int needModerator; - - db_begin_transaction(); - blob_init(&content, aContent, szContent); - pManifest = manifest_parse(&content, 0, 0); - manifest_destroy(pManifest); - blob_init(&content, aContent, szContent); - if( pManifest ){ - blob_compress(&content, &content); - addCompress = 1; - } - needModerator = - (zTkt!=0 && ticket_need_moderation(0)) || - (zPage!=0 && wiki_need_moderation(0)); - rid = content_put_ex(&content, 0, 0, 0, needModerator); - zUUID = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); - blob_zero(&manifest); - for(i=n=0; zName[i]; i++){ - if( zName[i]=='/' || zName[i]=='\\' ) n = i; - } - zName += n; - if( zName[0]==0 ) zName = "unknown"; - blob_appendf(&manifest, "A %F%s %F %s\n", - zName, addCompress ? ".gz" : "", zTarget, zUUID); - zComment = PD("comment", ""); - while( fossil_isspace(zComment[0]) ) zComment++; - n = strlen(zComment); - while( n>0 && fossil_isspace(zComment[n-1]) ){ n--; } - if( n>0 ){ - blob_appendf(&manifest, "C %#F\n", n, zComment); - } - zDate = date_in_standard_format("now"); - blob_appendf(&manifest, "D %s\n", zDate); - blob_appendf(&manifest, "U %F\n", login_name()); - md5sum_blob(&manifest, &cksum); - blob_appendf(&manifest, "Z %b\n", &cksum); - attach_put(&manifest, rid, needModerator); - assert( blob_is_reset(&manifest) ); - db_end_transaction(0); + int needModerator = (zTkt!=0 && ticket_need_moderation(0)) || + (zPage!=0 && wiki_need_moderation(0)); + const char *zComment = PD("comment", ""); + attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment); cgi_redirect(zFrom); } style_header("Add Attachment"); if( !goodCaptcha ){ @
Error: Incorrect security code.
@@ -668,5 +679,98 @@ @ } db_finalize(&q); } + +/* +** COMMAND: attachment* +** +** Usage: %fossil attachment add ?PAGENAME? FILENAME [-t|--technote DATETIME ] +** +** Add an attachment to an existing wiki page or tech note. One of +** PAGENAME or DATETIME must be specified. +** +*/ +void attachment_cmd(void){ + int n; + db_find_and_open_repository(0, 0); + if( g.argc<3 ){ + goto attachment_cmd_usage; + } + n = strlen(g.argv[2]); + if( n==0 ){ + goto attachment_cmd_usage; + } + + if( strncmp(g.argv[2],"add",n)==0 ){ + const char *zPageName; /* Name of the wiki page to attach to */ + const char *zFile; /* Name of the file to be attached */ + const char *zETime; /* The name of the technote to attach to */ + Manifest *pWiki = 0; /* Parsed wiki page content */ + char *zBody = 0; /* Wiki page content */ + int rid; + const char *zTarget; /* Target of the attachment */ + Blob content; /* The content of the attachment */ + zETime = find_option("technote","t",1); + if( !zETime ){ + if( g.argc!=5 ){ + usage("attachment add PAGENAME FILENAME"); + } + zPageName = g.argv[3]; + rid = db_int(0, "SELECT x.rid FROM tag t, tagxref x" + " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" + " ORDER BY x.mtime DESC LIMIT 1", + zPageName + ); + if( (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){ + zBody = pWiki->zWiki; + } + if( zBody==0 ){ + fossil_fatal("wiki page [%s] not found",zPageName); + } + zTarget = zPageName; + zFile = g.argv[4]; + }else{ + if( g.argc!=4 ){ + usage("attachment add FILENAME --technote DATETIME"); + } + rid = db_int(0, "SELECT objid FROM event" + " WHERE datetime(mtime)=datetime('%q') AND type='e'" + " ORDER BY mtime DESC LIMIT 1", + zETime + ); + if( (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0 ){ + zBody = pWiki->zWiki; + } + if( zBody==0 ){ + fossil_fatal("technote [%s] not found",zETime); + } + zTarget = db_text(0, + "SELECT substr(tagname,7) FROM tag WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')", + rid + ); + zFile = g.argv[3]; + } + blob_read_from_file(&content, zFile); + user_select(); + attach_commit( + zFile, /* The filename of the attachment */ + zTarget, /* The artifact uuid to attach to */ + blob_buffer(&content), /* The content of the attachment */ + blob_size(&content), /* The length of the attachment */ + 0, /* No need to moderate the attachment */ + "" /* Empty attachment comment */ + ); + if( !zETime ){ + fossil_print("Attached %s to wiki page %s.\n", zFile, zPageName); + }else{ + fossil_print("Attached %s to tech note %s.\n", zFile, zETime); + } + }else{ + goto attachment_cmd_usage; + } + return; + +attachment_cmd_usage: + usage("attachment add ?PAGENAME? FILENAME [-t|--technote DATETIME ]"); +} Index: src/info.c ================================================================== --- src/info.c +++ src/info.c @@ -1330,10 +1330,11 @@ const char *zDate = db_column_text(&q, 0); const char *zUser = db_column_text(&q, 1); const char *zCom = db_column_text(&q, 2); const char *zType = db_column_text(&q, 3); const char *zUuid = db_column_text(&q, 4); + int eventTagId = db_column_int(&q, 5); if( cnt>0 ){ @ Also } if( zType[0]=='w' ){ @ Wiki edit @@ -1343,17 +1344,21 @@ objType |= OBJTYPE_TICKET; }else if( zType[0]=='c' ){ @ Manifest of check-in objType |= OBJTYPE_CHECKIN; }else if( zType[0]=='e' ){ - @ Instance of technote - objType |= OBJTYPE_EVENT; - hyperlink_to_event_tagid(db_column_int(&q, 5)); + if( eventTagId != 0) { + @ Instance of technote + objType |= OBJTYPE_EVENT; + hyperlink_to_event_tagid(db_column_int(&q, 5)); + }else{ + @ Attachment to technote + } }else{ @ Tag referencing } - if( zType[0]!='e' ){ + if( zType[0]!='e' || eventTagId == 0){ hyperlink_to_uuid(zUuid); } @ - %!W(zCom) by hyperlink_to_user(zUser,zDate," on"); hyperlink_to_date(zDate, "."); @@ -1383,14 +1388,32 @@ }else{ @ Attachment "%h(zFilename)" to } objType |= OBJTYPE_ATTACHMENT; if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){ - if( g.perm.Hyperlink && g.anon.RdTkt ){ - @ ticket [%z(href("%R/tktview?name=%!S",zTarget))%S(zTarget)] + if ( db_exists("SELECT 1 FROM tag WHERE tagname='tkt-%q'", + zTarget) + ){ + if( g.perm.Hyperlink && g.anon.RdTkt ){ + @ ticket [%z(href("%R/tktview?name=%!S",zTarget))%S(zTarget)] + }else{ + @ ticket [%S(zTarget)] + } + }else if( db_exists("SELECT 1 FROM tag WHERE tagname='event-%q'", + zTarget) + ){ + if( g.perm.Hyperlink && g.anon.RdWiki ){ + @ tech note [%z(href("%R/technote/%h",zTarget))%S(zTarget)] + }else{ + @ tech note [%S(zTarget)] + } }else{ - @ ticket [%S(zTarget)] + if( g.perm.Hyperlink && g.anon.RdWiki ){ + @ wiki page [%z(href("%R/wiki?name=%t",zTarget))%h(zTarget)] + }else{ + @ wiki page [%h(zTarget)] + } } }else{ if( g.perm.Hyperlink && g.anon.RdWiki ){ @ wiki page [%z(href("%R/wiki?name=%t",zTarget))%h(zTarget)] }else{ Index: src/wiki.c ================================================================== --- src/wiki.c +++ src/wiki.c @@ -1137,21 +1137,21 @@ } /* ** COMMAND: wiki* ** -** Usage: ../fossil wiki (export|create|commit|list) WikiName +** Usage: %fossil wiki (export|create|commit|list) WikiName ** ** Run various subcommands to work with wiki entries or tech notes. ** -** ../fossil wiki export ?PAGENAME? ?FILE? [-t|--technote DATETIME ] +** %fossil wiki export ?PAGENAME? ?FILE? [-t|--technote DATETIME ] ** ** Sends the latest version of either the PAGENAME wiki entry ** or the DATETIME tech note to the given file or standard ** output. One of PAGENAME or DATETIME must be specified. ** -** ../fossil wiki (create|commit) PAGENAME ?FILE? ?OPTIONS? +** %fossil wiki (create|commit) PAGENAME ?FILE? ?OPTIONS? ** ** Create a new or commit changes to an existing wiki page or ** technote from FILE or from standard input. ** ** Options: @@ -1163,12 +1163,12 @@ ** to be created or updated. ** --technote-tags TAGS The set of tags for a technote. ** --technote-bgcolor COLOR The color used for the technote on the ** timeline. ** -** ../fossil wiki list ?--technote? -** ../fossil wiki ls ?--technote? +** %fossil wiki list ?--technote? +** %fossil wiki ls ?--technote? ** ** Lists all wiki entries, one per line, ordered ** case-insensitively by name. The --technote flag ** specifies that technotes will be listed instead of ** the wiki entries, which will be listed in order @@ -1225,11 +1225,11 @@ ); if( (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0 ){ zBody = pWiki->zWiki; } if( zBody==0 ){ - fossil_fatal("technote not found"); + fossil_fatal("technote [%s] not found",zETime); } zFile = (g.argc==3) ? "-" : g.argv[3]; } for(i=strlen(zBody); i>0 && fossil_isspace(zBody[i-1]); i--){} zBody[i] = 0;