Fossil
Check-in [01c0180b3f]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview

SHA1 Hash:01c0180b3f084975f5fbb9f251681df02f694fb5
Date: 2010-03-18 02:11:47
User: drh
Comment:Add the ability to delete attachments and fix issues with timelines.

Tags And Properties
Changes
[hide diffs]

Changes to src/attach.c

@@ -64,11 +64,11 @@
   }
   blob_appendf(&sql, " ORDER BY mtime DESC");
   db_prepare(&q, "%s", blob_str(&sql));
   while( db_step(&q)==SQLITE_ROW ){
     const char *zDate = db_column_text(&q, 0);
-    /* const char *zSrc = db_column_text(&q, 1); */
+    const char *zSrc = db_column_text(&q, 1);
     const char *zTarget = db_column_text(&q, 2);
     const char *zFilename = db_column_text(&q, 3);
     const char *zComment = db_column_text(&q, 4);
     const char *zUser = db_column_text(&q, 5);
     int i;
@@ -85,24 +85,36 @@
       zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
     }
     @
     @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
     @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
-    @ %w(zComment)<br>
+    if( zComment ) while( isspace(zComment[0]) ) zComment++;
+    if( zComment && zComment[0] ){
+      @ %w(zComment)<br>
+    }
     if( zPage==0 && zTkt==0 ){
+      if( zSrc==0 || zSrc[0]==0 ){
+        zSrc = "Deleted from";
+      }else {
+        zSrc = "Added to";
+      }
       if( strlen(zTarget)==UUID_SIZE && validate16(zTarget, UUID_SIZE) ){
         char zShort[20];
         memcpy(zShort, zTarget, 10);
         zShort[10] = 0;
-        @ Added to ticket <a href="%s(g.zTop)/tktview?name=%s(zTarget)">
+        @ %s(zSrc) ticket <a href="%s(g.zTop)/tktview?name=%s(zTarget)">
         @ %s(zShort)</a>
       }else{
-        @ Added to wiki page <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
+        @ %s(zSrc) wiki page <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
         @ %h(zTarget)</a>
       }
     }else{
-      @ Add
+      if( zSrc==0 || zSrc[0]==0 ){
+        @ Deleted
+      }else {
+        @ Added
+      }
     }
     @ by %h(zUser) on
     hyperlink_to_date(zDate, ".");
     free(zUrlTail);
   }
@@ -155,11 +167,11 @@
        " WHERE target=%Q AND filename=%Q"
        " ORDER BY mtime DESC LIMIT 1",
        zTarget, zFile
     );
   }
-  if( zUUID==0 ){
+  if( zUUID==0 || zUUID[0]==0 ){
     style_header("No Such Attachment");
     @ No such attachment....
     style_footer();
     return;
   }else if( zUUID[0]=='x' ){
@@ -176,10 +188,11 @@
     cgi_replace_parameter("m", mimetype_from_name(zFile));
     rawartifact_page();
   }
 }
 
+
 /*
 ** WEBPAGE: attachadd
 **
 **    tkt=TICKETUUID
 **    page=WIKIPAGE
@@ -188,11 +201,11 @@
 ** Add a new attachment.
 */
 void attachadd_page(void){
   const char *zPage = P("page");
   const char *zTkt = P("tkt");
-  const char *zFrom = PD("from", "/home");
+  const char *zFrom = P("from");
   const char *aContent = P("f");
   const char *zName = PD("f:filename","unknown");
   const char *zTarget;
   const char *zTargetType;
   int szContent = atoi(PD("f:bytes","0"));
@@ -215,10 +228,14 @@
       fossil_redirect_home();
     }
     zTarget = zTkt;
     zTargetType = mprintf("Ticket <a href=\"%s/tktview?name=%.10s\">%.10s</a>",
                           g.zTop, zTkt, zTkt);
+  }
+  if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
+  if( P("cancel") ){
+    cgi_redirect(zFrom);
   }
   if( P("ok") && szContent>0 ){
     Blob content;
     Blob manifest;
     Blob cksum;
@@ -270,9 +287,87 @@
   }else{
     @ <input type="hidden" name="page" value="%h(zPage)">
   }
   @ <input type="hidden" name="from" value="%h(zFrom)">
   @ <input type="submit" name="ok" value="Add Attachment">
-  @ <input type="submit" name="can" value="Cancel">
+  @ <input type="submit" name="cancel" value="Cancel">
+  @ </form>
+  style_footer();
+}
+
+
+/*
+** WEBPAGE: attachdelete
+**
+**    tkt=TICKETUUID
+**    page=WIKIPAGE
+**    file=FILENAME
+**
+** "Delete" an attachment.  Because objects in Fossil are immutable
+** the attachment isn't really deleted.  Instead, we change the content
+** of the attachment to NULL, which the system understands as being
+** deleted.  Historical values of the attachment are preserved.
+*/
+void attachdel_page(void){
+  const char *zPage = P("page");
+  const char *zTkt = P("tkt");
+  const char *zFile = P("file");
+  const char *zFrom = P("from");
+  const char *zTarget;
+
+  if( zPage && zTkt ) fossil_redirect_home();
+  if( zPage==0 && zTkt==0 ) fossil_redirect_home();
+  if( zFile==0 ) fossil_redirect_home();
+  login_check_credentials();
+  if( zPage ){
+    if( g.okWrWiki==0 || g.okAttach==0 ) login_needed();
+    zTarget = zPage;
+  }else{
+    if( g.okWrTkt==0 || g.okAttach==0 ) login_needed();
+    zTarget = zTkt;
+  }
+  if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
+  if( P("cancel") ){
+    cgi_redirect(zFrom);
+  }
+  if( P("confirm") ){
+    int i, n, rid;
+    char *zDate;
+    Blob manifest;
+    Blob cksum;
+
+    db_begin_transaction();
+    blob_zero(&manifest);
+    for(i=n=0; zFile[i]; i++){
+      if( zFile[i]=='/' || zFile[i]=='\\' ) n = i;
+    }
+    zFile += n;
+    if( zFile[0]==0 ) zFile = "unknown";
+    blob_appendf(&manifest, "A %F %F\n", zFile, zTarget);
+    zDate = db_text(0, "SELECT datetime('now')");
+    zDate[10] = 'T';
+    blob_appendf(&manifest, "D %s\n", zDate);
+    blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody");
+    md5sum_blob(&manifest, &cksum);
+    blob_appendf(&manifest, "Z %b\n", &cksum);
+    rid = content_put(&manifest, 0, 0);
+    manifest_crosslink(rid, &manifest);
+    db_end_transaction(0);
+    cgi_redirect(zFrom);
+  }
+  style_header("Delete Attachment");
+  @ <form action="%s(g.zBaseURL)/attachdelete" method="POST">
+  @ <p>Confirm that you want to delete the attachment named
+  @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br>
+  if( zTkt ){
+    @ <input type="hidden" name="tkt" value="%h(zTkt)">
+  }else{
+    @ <input type="hidden" name="page" value="%h(zPage)">
+  }
+  @ <input type="hidden" name="file" value="%h(zFile)">
+  @ <input type="hidden" name="from" value="%h(zFrom)">
+  @ <input type="submit" name="confirm" value="Delete">
+  @ <input type="submit" name="cancel" value="Cancel">
   @ </form>
   style_footer();
+
 }

Changes to src/manifest.c

@@ -1136,28 +1136,45 @@
        m.zAttachTarget, m.zAttachName
     );
     if( strlen(m.zAttachTarget)!=UUID_SIZE
      || !validate16(m.zAttachTarget, UUID_SIZE)
     ){
-      char *zTag = mprintf("wiki-%s", m.zAttachTarget);
       char *zComment;
+#if 0
+      char *zTag = mprintf("wiki-%s", m.zAttachTarget);
       tag_findid(zTag, 1);
       free(zTag);
-      if( m.zAttachSrc ){
+#endif
+      if( m.zAttachSrc && m.zAttachSrc[0] ){
         zComment = mprintf("Add attachment \"%h\" to wiki page [%h]",
              m.zAttachName, m.zAttachTarget);
       }else{
         zComment = mprintf("Delete attachment \"%h\" from wiki page [%h]",
              m.zAttachName, m.zAttachTarget);
       }
       db_multi_exec(
         "REPLACE INTO event(type,mtime,objid,user,comment)"
         "VALUES('w',%.17g,%d,%Q,%Q)",
+        m.rDate, rid, m.zUser, zComment
+      );
+      free(zComment);
+    }else{
+      char *zComment;
+      if( m.zAttachSrc && m.zAttachSrc[0] ){
+        zComment = mprintf("Add attachment \"%h\" to ticket [%.10s]",
+             m.zAttachName, m.zAttachTarget);
+      }else{
+        zComment = mprintf("Delete attachment \"%h\" from ticket [%.10s]",
+             m.zAttachName, m.zAttachTarget);
+      }
+      db_multi_exec(
+        "REPLACE INTO event(type,mtime,objid,user,comment)"
+        "VALUES('t',%.17g,%d,%Q,%Q)",
         m.rDate, rid, m.zUser, zComment
       );
       free(zComment);
     }
   }
   db_end_transaction(0);
   manifest_clear(&m);
   return 1;
 }

Changes to src/tkt.c

@@ -242,10 +242,11 @@
     manifest_clear(&manifest);
     createFlag = 0;
   }
   db_finalize(&q);
 
+#if 0
   db_prepare(&q,
     "SELECT attachid, mtime, src IS NULL, filename, user"
     "  FROM attachment"
     " WHERE target=%Q",
     zTktUuid
@@ -271,10 +272,11 @@
       tagid, mtime, attachid, zUser, zCom, zCom
     );
     free(zCom);
   }
   db_finalize(&q);
+#endif
 }
 
 /*
 ** Create the subscript interpreter and load the "common" code.
 */
@@ -349,11 +351,12 @@
     style_submenu_element("New Ticket", "Create a new ticket",
         "%s/tktnew", g.zTop);
   }
   if( g.okApndTkt && g.okAttach ){
     style_submenu_element("Attach", "Add An Attachment",
-        "%s/attachadd?tkt=%T", g.zTop, zUuid);
+        "%s/attachadd?tkt=%T&from=%s/tktview%%3fname=%t",
+        g.zTop, zUuid, g.zTop, zUuid);
   }
   style_header("View Ticket");
   if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
   ticket_init();
   initializeVariablesFromDb();
@@ -369,11 +372,11 @@
     int cnt = 0;
     Stmt q;
     db_prepare(&q,
        "SELECT datetime(mtime,'localtime'), filename, user"
        "  FROM attachment"
-       " WHERE isLatest AND src NOT NULL AND target=%Q"
+       " WHERE isLatest AND src!='' AND target=%Q"
        " ORDER BY mtime DESC",
        zFullName);
     while( db_step(&q)==SQLITE_ROW ){
       const char *zDate = db_column_text(&q, 0);
       const char *zFile = db_column_text(&q, 1);
@@ -383,11 +386,14 @@
         @ <ul>
       }
       cnt++;
       @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
       @ %h(zFile)</a> add by %h(zUser) on
-      hyperlink_to_date(zDate, ".</li>");
+      hyperlink_to_date(zDate, ".");
+      if( g.okWrTkt && g.okAttach ){
+        @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
+      }
     }
     if( cnt ){
       @ </ul>
     }
     db_finalize(&q);
@@ -765,12 +771,11 @@
     " WHERE objid IN (SELECT rid FROM tagxref WHERE tagid=%d)"
     "   AND blob.rid=event.objid"
     " UNION "
     "SELECT datetime(mtime,'localtime'), attachid, uuid, src, filename, user"
     "  FROM attachment, blob"
-    " WHERE isLatest AND target=(SELECT substr(tagname,5) FROM tag"
-                                " WHERE tagid=%d)"
+    " WHERE target=(SELECT substr(tagname,5) FROM tag WHERE tagid=%d)"
     "   AND blob.rid=attachid"
     " ORDER BY 1 DESC",
     tagid, tagid
   );
   while( db_step(&q)==SQLITE_ROW ){
@@ -784,11 +789,11 @@
     memcpy(zShort, zChngUuid, 10);
     zShort[10] = 0;
     if( zFile!=0 ){
       const char *zSrc = db_column_text(&q, 3);
       const char *zUser = db_column_text(&q, 5);
-      if( zSrc==0 ){
+      if( zSrc==0 || zSrc[0]==0 ){
         @
         @ <p>Delete attachment "%h(zFile)"
       }else{
         @
         @ <p>Add attachment "%h(zFile)"

Changes to src/wiki.c

@@ -191,11 +191,12 @@
       style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
            g.zTop, zPageName);
     }
     if( rid && g.okWrWiki && g.okAttach ){
       style_submenu_element("Attach", "Add An Attachment",
-           "%s/attachadd?page=%T", g.zTop, zPageName);
+           "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
+           g.zTop, zPageName, g.zTop, zPageName);
     }
     if( rid && g.okApndWiki ){
       style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
            g.zTop, zPageName);
     }
@@ -210,11 +211,11 @@
   blob_reset(&wiki);
 
   db_prepare(&q,
      "SELECT datetime(mtime,'localtime'), filename, user"
      "  FROM attachment"
-     " WHERE isLatest AND src NOT NULL AND target=%Q"
+     " WHERE isLatest AND src!='' AND target=%Q"
      " ORDER BY mtime DESC",
      zPageName);
   while( db_step(&q)==SQLITE_ROW ){
     const char *zDate = db_column_text(&q, 0);
     const char *zFile = db_column_text(&q, 1);
@@ -224,11 +225,14 @@
       @ <ul>
     }
     cnt++;
     @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
     @ %h(zFile)</a> add by %h(zUser) on
-    hyperlink_to_date(zDate, ".</li>");
+    hyperlink_to_date(zDate, ".");
+    if( g.okWrWiki && g.okAttach ){
+      @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
+    }
   }
   if( cnt ){
     @ </ul>
   }
   db_finalize(&q);
@@ -547,11 +551,13 @@
 /*
 ** Function called to output extra text at the end of each line in
 ** a wiki history listing.
 */
 static void wiki_history_extra(int rid){
-  @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
+  if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
+    @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
+  }
 }
 
 /*
 ** WEBPAGE: whistory
 ** URL: /whistory?name=PAGENAME