Fossil
Check-in [dd2d3177b1]
Not logged in

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

Overview

SHA1 Hash:dd2d3177b1b8333ee909d14edfd5f3287b689498
Date: 2010-03-17 23:15:04
User: drh
Comment:Basic functionality of attachments is now in place.

Tags And Properties
Changes
[hide diffs]

Changes to src/attach.c

@@ -30,10 +30,11 @@
 /*
 ** WEBPAGE: attachlist
 **
 **    tkt=TICKETUUID
 **    page=WIKIPAGE
+**    all
 **
 ** List attachments.
 */
 void attachlist_page(void){
   const char *zPage = P("page");
@@ -79,22 +80,104 @@
       }
     }
     if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
       zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
     }else{
-      zUrlTail = mprintf("page=%s&file=%t", zTarget, zFilename);
+      zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
     }
+    @
     @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
-    @ [<a href="/attachdownload?%s(zUrlTail)">download</a>]<br>
+    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
     @ %w(zComment)<br>
-    @ Added by %h(zUser) on %s(zDate)</p>
-    @
+    if( zPage==0 && zTkt==0 ){
+      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(zShort)</a>
+      }else{
+        @ Added to wiki page <a href="%s(g.zTop)/wiki?name=%t(zTarget)">
+        @ %h(zTarget)</a>
+      }
+    }else{
+      @ Add
+    }
+    @ by %h(zUser) on
+    hyperlink_to_date(zDate, ".");
     free(zUrlTail);
   }
   db_finalize(&q);
   style_footer();
   return;
+}
+
+/*
+** WEBPAGE: attachdownload
+** WEBPAGE: attachimage
+** WEBPAGE: attachview
+**
+**    tkt=TICKETUUID
+**    page=WIKIPAGE
+**    file=FILENAME
+**    attachid=ID
+**
+** List attachments.
+*/
+void attachview_page(void){
+  const char *zPage = P("page");
+  const char *zTkt = P("tkt");
+  const char *zFile = P("file");
+  const char *zTarget;
+  int attachid = atoi(PD("attachid","0"));
+  char *zUUID;
+
+  if( zPage && zTkt ) zTkt = 0;
+  if( zFile==0 ) fossil_redirect_home();
+  login_check_credentials();
+  if( zPage ){
+    if( g.okRdWiki==0 ) login_needed();
+    zTarget = zPage;
+  }else if( zTkt ){
+    if( g.okRdTkt==0 ) login_needed();
+    zTarget = zTkt;
+  }else{
+    fossil_redirect_home();
+  }
+  if( attachid>0 ){
+    zUUID = db_text(0,
+       "SELECT coalesce(src,'x') FROM attachment"
+       " WHERE target=%Q AND attachid=%d",
+       zTarget, attachid
+    );
+  }else{
+    zUUID = db_text(0,
+       "SELECT coalesce(src,'x') FROM attachment"
+       " WHERE target=%Q AND filename=%Q"
+       " ORDER BY mtime DESC LIMIT 1",
+       zTarget, zFile
+    );
+  }
+  if( zUUID==0 ){
+    style_header("No Such Attachment");
+    @ No such attachment....
+    style_footer();
+    return;
+  }else if( zUUID[0]=='x' ){
+    style_header("Missing");
+    @ Attachment has been deleted
+    style_footer();
+    return;
+  }
+  g.okRead = 1;
+  cgi_replace_parameter("name",zUUID);
+  if( strcmp(g.zPath,"attachview")==0 ){
+    artifact_page();
+  }else{
+    cgi_replace_parameter("m", mimetype_from_name(zFile));
+    rawartifact_page();
+  }
 }
 
 /*
 ** WEBPAGE: attachadd
 **
@@ -173,11 +256,11 @@
     manifest_crosslink(rid, &manifest);
     db_end_transaction(0);
     cgi_redirect(zFrom);
   }
   style_header("Add Attachment");
-  @ <h1>Add Attachment To %s(zTargetType)</h1>
+  @ <h2>Add Attachment To %s(zTargetType)</h2>
   @ <form action="%s(g.zBaseURL)/attachadd" method="POST"
   @  enctype="multipart/form-data">
   @ File to Attach:
   @ <input type="file" name="f" size="60"><br>
   @ Description:<br>

Changes to src/info.c

@@ -732,10 +732,52 @@
       }
       cnt++;
     }
     db_finalize(&q);
   }
+  db_prepare(&q,
+    "SELECT target, filename, datetime(mtime), user, src"
+    "  FROM attachment"
+    " WHERE src=(SELECT uuid FROM blob WHERE rid=%d)"
+    " ORDER BY mtime DESC",
+    rid
+  );
+  while( db_step(&q)==SQLITE_ROW ){
+    const char *zTarget = db_column_text(&q, 0);
+    const char *zFilename = db_column_text(&q, 1);
+    const char *zDate = db_column_text(&q, 2);
+    const char *zUser = db_column_text(&q, 3);
+    const char *zSrc = db_column_text(&q, 4);
+    if( cnt>0 ){
+      @ Also attachment "%h(zFilename)" to
+    }else{
+      @ Attachment "%h(zFilename)" to
+    }
+    if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
+      char zShort[20];
+      memcpy(zShort, zTarget, 10);
+      if( g.okHistory && g.okRdTkt ){
+        @ ticket [<a href="%s(g.zTop)/tktview?name=%s(zShort)">%s(zShort)</a>]
+      }else{
+        @ ticket [%s(zShort)]
+      }
+    }else{
+      if( g.okHistory && g.okRdWiki ){
+        @ wiki page [<a href="%s(g.zTop)/wiki?name=%t(zTarget)">%h(zTarget)</a>]
+      }else{
+        @ wiki page [%h(zTarget)]
+      }
+    }
+    @ added by
+    hyperlink_to_user(zUser,zDate," on");
+    hyperlink_to_date(zDate,".");
+    cnt++;
+    if( pDownloadName && blob_size(pDownloadName)==0 ){
+      blob_append(pDownloadName, zSrc, -1);
+    }
+  }
+  db_finalize(&q);
   if( cnt==0 ){
     char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
     @ Control artifact.
     if( pDownloadName && blob_size(pDownloadName)==0 ){
       blob_append(pDownloadName, zUuid, -1);

Changes to src/tkt.c

@@ -296,18 +296,20 @@
 **
 ** View a ticket.
 */
 void tktview_page(void){
   const char *zScript;
+  char *zFullName;
+  const char *zUuid = PD("name","");
+
   login_check_credentials();
   if( !g.okRdTkt ){ login_needed(); return; }
   if( g.okWrTkt || g.okApndTkt ){
     style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
         g.zTop, PD("name",""));
   }
   if( g.okHistory ){
-    const char *zUuid = PD("name","");
     style_submenu_element("History", "History Of This Ticket",
         "%s/tkthistory/%T", g.zTop, zUuid);
     style_submenu_element("Timeline", "Timeline Of This Ticket",
         "%s/tkttimeline/%T", g.zTop, zUuid);
     style_submenu_element("Check-ins", "Check-ins Of This Ticket",
@@ -315,18 +317,54 @@
   }
   if( g.okNewTkt ){
     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);
+  }
   style_header("View Ticket");
   if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
   ticket_init();
   initializeVariablesFromDb();
   zScript = ticket_viewpage_code();
   if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1);
   Th_Render(zScript);
   if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
+
+  zFullName = db_text(0,
+       "SELECT tkt_uuid FROM ticket"
+       " WHERE tkt_uuid GLOB '%q*'", zUuid);
+  if( zFullName ){
+    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"
+       " 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);
+      const char *zUser = db_column_text(&q, 2);
+      if( cnt==0 ){
+        @ <hr><h2>Attachments:</h2>
+        @ <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>");
+    }
+    if( cnt ){
+      @ </ul>
+    }
+    db_finalize(&q);
+  }
+
   style_footer();
 }
 
 /*
 ** TH command:   append_field FIELD STRING

Changes to src/wiki.c

@@ -127,10 +127,12 @@
   int isSandbox;
   Blob wiki;
   Manifest m;
   const char *zPageName;
   char *zBody = mprintf("%s","<i>Empty Page</i>");
+  Stmt q;
+  int cnt = 0;
 
   login_check_credentials();
   if( !g.okRdWiki ){ login_needed(); return; }
   zPageName = P("name");
   if( zPageName==0 ){
@@ -152,11 +154,12 @@
       @ <li>  Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
     }
     @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
     @      available on this server.</li>
 	@ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
-	@     Search wiki titles: <input type="text" name="title"/> &nbsp; <input type="submit" />
+	@     Search wiki titles: <input type="text" name="title"/>
+        @  &nbsp; <input type="submit" />
 	@ </li>
     @ </ul>
     style_footer();
     return;
   }
@@ -186,10 +189,14 @@
   if( !g.isHome ){
     if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
       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);
+    }
     if( rid && g.okApndWiki ){
       style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
            g.zTop, zPageName);
     }
     if( g.okHistory ){
@@ -199,10 +206,35 @@
   }
   style_header(zPageName);
   blob_init(&wiki, zBody, -1);
   wiki_convert(&wiki, 0, 0);
   blob_reset(&wiki);
+
+  db_prepare(&q,
+     "SELECT datetime(mtime,'localtime'), filename, user"
+     "  FROM attachment"
+     " WHERE isLatest AND src NOT NULL 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);
+    const char *zUser = db_column_text(&q, 2);
+    if( cnt==0 ){
+      @ <hr><h2>Attachments:</h2>
+      @ <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>");
+  }
+  if( cnt ){
+    @ </ul>
+  }
+  db_finalize(&q);
+
   if( !isSandbox ){
     manifest_clear(&m);
   }
   style_footer();
 }