Index: src/attach.c
==================================================================
--- src/attach.c
+++ src/attach.c
@@ -41,11 +41,13 @@
 
   if( zPage && zTkt ) zTkt = 0;
   login_check_credentials();
   blob_zero(&sql);
   blob_append(&sql,
-     "SELECT datetime(mtime,'localtime'), src, target, filename, comment, user"
+     "SELECT datetime(mtime,'localtime'), src, target, filename,"
+     "       comment, user,"
+     "       (SELECT uuid FROM blob WHERE rid=attachid), attachid"
      "  FROM attachment",
      -1
   );
   if( zPage ){
     if( g.perm.RdWiki==0 ) login_needed();
@@ -59,17 +61,20 @@
     if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ) login_needed();
     style_header("All Attachments");
   }
   blob_appendf(&sql, " ORDER BY mtime DESC");
   db_prepare(&q, "%s", blob_str(&sql));
+  @ <ol>
   while( db_step(&q)==SQLITE_ROW ){
     const char *zDate = db_column_text(&q, 0);
     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);
+    const char *zUuid = db_column_text(&q, 6);
+    int attachid = db_column_int(&q, 7);
     int i;
     char *zUrlTail;
     for(i=0; zFilename[i]; i++){
       if( zFilename[i]=='/' && zFilename[i+1]!=0 ){ 
         zFilename = &zFilename[i+1];
@@ -79,12 +84,16 @@
     if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
       zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
     }else{
       zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
     }
-    @
-    @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
+    @ <li><p>
+    @ Attachment %z(href("%R/ainfo/%s",zUuid))%S(zUuid)</a>
+    if( moderation_pending(attachid) ){
+      @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
+    }
+    @ <br><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
     @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
     if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
     if( zComment && zComment[0] ){
       @ %w(zComment)<br />
     }
@@ -111,10 +120,11 @@
     @ by %h(zUser) on
     hyperlink_to_date(zDate, ".");
     free(zUrlTail);
   }
   db_finalize(&q);
+  @ </ol>
   style_footer();
   return;
 }
 
 /*
@@ -182,10 +192,34 @@
     cgi_replace_parameter("m", mimetype_from_name(zFile));
     rawartifact_page();
   }
 }
 
+/*
+** Save an attachment control artifact into the repository
+*/
+static void attach_put(
+  Blob *pAttach,     /* Text of the Attachment record */
+  int attachRid,     /* RID for the file that is being attached */
+  int needMod        /* True if the attachment is subject to moderation */
+){
+  int rid;
+  if( needMod ){
+    rid = content_put_ex(pAttach, 0, 0, 0, 1);
+    moderation_table_create();
+    db_multi_exec(
+      "INSERT INTO modreq(objid,attachRid) VALUES(%d,%d);",
+      rid, attachRid
+    );
+  }else{
+    rid = content_put(pAttach);
+    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
+    db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
+  }
+  manifest_crosslink(rid, pAttach);
+}
+
 
 /*
 ** WEBPAGE: attachadd
 **
 **    tkt=TICKETUUID
@@ -240,10 +274,11 @@
     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);
@@ -250,11 +285,14 @@
     blob_init(&content, aContent, szContent);
     if( pManifest ){
       blob_compress(&content, &content);
       addCompress = 1;
     }
-    rid = content_put(&content);
+    needModerator =
+         (zTkt!=0 && g.perm.ModTkt==0 && db_get_boolean("modreq-tkt",0)==1) ||
+         (zPage!=0 && g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==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;
     }
@@ -272,12 +310,11 @@
     zDate = date_in_standard_format("now");
     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);
-    manifest_crosslink(rid, &manifest);
+    attach_put(&manifest, rid, needModerator);
     assert( blob_is_reset(&manifest) );
     db_end_transaction(0);
     cgi_redirect(zFrom);
   }
   style_header("Add Attachment");
@@ -298,50 +335,85 @@
   @ <input type="submit" name="cancel" value="Cancel" />
   @ </div></form>
   style_footer();
 }
 
-
 /*
-** WEBPAGE: attachdelete
+** WEBPAGE: ainfo
+** URL: /ainfo?name=ARTIFACTID
 **
-**    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.
+** Show the details of an attachment artifact.
 */
-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;
+void ainfo_page(void){
+  int rid;                       /* RID for the control artifact */
+  int ridSrc;                    /* RID for the attached file */
+  char *zDate;                   /* Date attached */
+  const char *zUuid;             /* UUID of the control artifact */
+  Manifest *pAttach;             /* Parse of the control artifact */
+  const char *zTarget;           /* Wiki or ticket attached to */
+  const char *zSrc;              /* UUID of the attached file */
+  const char *zName;             /* Name of the attached file */
+  const char *zDesc;             /* Description of the attached file */
+  const char *zWikiName = 0;     /* Wiki page name when attached to Wiki */
+  const char *zTktUuid = 0;      /* Ticket ID when attached to a ticket */
+  int modPending;                /* True if awaiting moderation */
+  const char *zModAction;        /* Moderation action or NULL */
+  int isModerator;               /* TRUE if user is the moderator */
+  const char *zMime;             /* MIME Type */
+  Blob attach;                   /* Content of the attachment */
+  int wantToDelete = P("del")!=0;/* Want to delete */
 
-  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.perm.WrWiki==0 || g.perm.Attach==0 ) login_needed();
-    zTarget = zPage;
-  }else{
-    if( g.perm.WrTkt==0 || g.perm.Attach==0 ) login_needed();
-    zTarget = zTkt;
-  }
-  if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
-  if( P("cancel") ){
-    cgi_redirect(zFrom);
-  }
-  if( P("confirm") ){
+  if( !g.perm.RdTkt && !g.perm.RdWiki ){ login_needed(); return; }
+  rid = name_to_rid_www("name");
+  if( rid==0 ){ fossil_redirect_home(); }
+  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
+#if 0
+  /* Shunning here needs to get both the attachment control artifact and
+  ** the object that is attached. */
+  if( g.perm.Admin ){
+    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);
+    }
+  }
+#endif
+  pAttach = manifest_get(rid, CFTYPE_ATTACHMENT);
+  if( pAttach==0 ) fossil_redirect_home();
+  zTarget = pAttach->zAttachTarget;
+  zSrc = pAttach->zAttachSrc;
+  ridSrc = db_int(0,"SELECT rid FROM blob WHERE uuid='%s'", zSrc);
+  zName = pAttach->zAttachName;
+  zDesc = pAttach->zComment;
+  if( validate16(zTarget, strlen(zTarget))
+   && db_exists("SELECT 1 FROM ticket WHERE tkt_uuid='%s'", zTarget)
+  ){
+    zTktUuid = zTarget;
+    if( !g.perm.RdTkt ){ login_needed(); return; }
+    if( g.perm.WrTkt ){
+      style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
+    }
+  }else if( db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'",zTarget) ){
+    zWikiName = zTarget;
+    if( !g.perm.RdWiki ){ login_needed(); return; }
+    if( g.perm.WrWiki ){
+      style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
+    }
+  }
+  zDate = db_text(0, "SELECT datetime(%.12f)", pAttach->rDate);
+
+  if( P("confirm")
+   && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
+  ){
     int i, n, rid;
     char *zDate;
     Blob manifest;
     Blob cksum;
+    const char *zFile = zName;
 
     db_begin_transaction();
     blob_zero(&manifest);
     for(i=n=0; zFile[i]; i++){
       if( zFile[i]=='/' || zFile[i]=='\\' ) n = i;
@@ -355,24 +427,153 @@
     md5sum_blob(&manifest, &cksum);
     blob_appendf(&manifest, "Z %b\n", &cksum);
     rid = content_put(&manifest);
     manifest_crosslink(rid, &manifest);
     db_end_transaction(0);
-    cgi_redirect(zFrom);
-  }    
-  style_header("Delete Attachment");
-  @ <form action="%s(g.zTop)/attachdelete" method="post"><div>
-  @ <p>Confirm that you want to delete the attachment named
-  @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br /></p>
-  if( zTkt ){
-    @ <input type="hidden" name="tkt" value="%h(zTkt)" />
+    @ <p>The attachment below has been deleted.</p>
+  }
+
+  if( P("del")
+   && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
+  ){
+    @ <form method="post" action="%R/ainfo/%s(zUuid)">
+    @ <p>Confirm you want to delete the attachment shown below.
+    @ <input type="submit" name="confirm" value="Confirm">
+    @ </form>
+  }
+
+  isModerator = (zTktUuid && g.perm.ModTkt) || (zWikiName && g.perm.ModWiki);
+  if( isModerator && (zModAction = P("modaction"))!=0 ){
+    if( strcmp(zModAction,"delete")==0 ){
+      moderation_disapprove(rid);
+      if( zTktUuid ){
+        cgi_redirectf("%R/tktview/%s", zTktUuid);
+      }else{
+        cgi_redirectf("%R/wiki?name=%t", zWikiName);
+      }
+      return;
+    }
+    if( strcmp(zModAction,"approve")==0 ){
+      moderation_approve(rid);
+    }
+  }
+  style_header("Attachment Details");
+  style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid);
+
+  @ <div class="section">Overview</div>
+  @ <p><table class="label-value">
+  @ <tr><th>Artifact&nbsp;ID:</th>
+  @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
+  if( g.perm.Setup ){
+    @ (%d(rid))
+  }
+  modPending = moderation_pending(rid);
+  if( modPending ){
+    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
+  }
+  if( zTktUuid ){
+    @ <tr><th>Ticket:</th>
+    @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr>
+  }
+  if( zWikiName ){
+    @ <tr><th>Wiki&nbsp;Page:</th>
+    @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr>
+  }
+  @ <tr><th>Date:</th><td>
+  hyperlink_to_date(zDate, "</td></tr>");
+  free(zDate);
+  @ <tr><th>User:</th><td>
+  hyperlink_to_user(pAttach->zUser, zDate, "</td></tr>");
+  @ <tr><th>Artifact&nbsp;Attached:</th>
+  @ <td>%z(href("%R/artifact/%s",zSrc))%s(zSrc)</a>
+  if( g.perm.Setup ){
+    @ (%d(ridSrc))
+  }
+  @ <tr><th>Filename:</th><td>%h(zName)</td></tr>
+  zMime = mimetype_from_name(zName);
+  if( g.perm.Setup ){
+    @ <tr><th>MIME-Type:</th><td>%h(zMime)</td></tr>
+  }
+  @ <tr><th valign="top">Description:</th><td valign="top">%h(zDesc)</td></tr>
+  @ </table>
+  
+  if( isModerator && modPending ){
+    @ <div class="section">Moderation</div>
+    @ <blockquote>
+    @ <form method="POST" action="%R/ainfo/%s(zUuid)">
+    @ <label><input type="radio" name="modaction" value="delete">
+    @ Delete this change</label><br />
+    @ <label><input type="radio" name="modaction" value="approve">
+    @ Approve this change</label><br />
+    @ <input type="submit" value="Submit">
+    @ </form>
+    @ </blockquote>
+  }
+
+  @ <div class="section">Content Appended</div>
+  @ <blockquote>
+  blob_zero(&attach);
+  if( zMime==0 || strncmp(zMime,"text/", 5)==0 ){
+    const char *z;
+    const char *zLn = P("ln");
+    content_get(ridSrc, &attach);
+    blob_strip_bom(&attach, 0);
+    z = blob_str(&attach);
+    if( zLn ){
+      output_text_with_line_numbers(z, zLn);
+    }else{
+      @ <pre>
+      @ %h(z)
+      @ </pre>
+    }
+  }else if( strncmp(zMime, "image/", 6)==0 ){
+    @ <img src="%R/raw?name=%s(zSrc)&m=%s(zMime)"></img>
   }else{
-    @ <input type="hidden" name="page" value="%h(zPage)" />
+    int sz = db_int(0, "SELECT sz FROM blob WHERE rid=%d", ridSrc);
+    @ <i>(file is %d(sz) bytes of binary data)</i>
   }
-  @ <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" />
-  @ </div></form>
+  @ </blockquote>
+  manifest_destroy(pAttach);
+  blob_reset(&attach);
   style_footer();
+}
 
+/*
+** Output HTML to show a list of attachments.
+*/
+void attachment_list(
+  const char *zTarget,   /* Object that things are attached to */
+  const char *zHeader    /* Header to display with attachments */
+){
+  int cnt = 0;
+  Stmt q;
+  db_prepare(&q,
+     "SELECT datetime(mtime,'localtime'), filename, user,"
+     "       (SELECT uuid FROM blob WHERE rid=attachid), src"
+     "  FROM attachment"
+     " WHERE isLatest AND src!='' AND target=%Q"
+     " ORDER BY mtime DESC", 
+     zTarget
+  );
+  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);
+    const char *zUuid = db_column_text(&q, 3);
+    const char *zSrc = db_column_text(&q, 4);
+    if( cnt==0 ){
+      @ %s(zHeader)
+    }
+    cnt++;
+    @ <li>
+    @ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
+    @ added by %h(zUser) on
+    hyperlink_to_date(zDate, ".");
+    @ [%z(href("%R/ainfo/%s",zUuid))details</a>]
+    @ </li>
+  }
+  if( cnt ){
+    @ </ul>
+  }
+  db_finalize(&q);
+  
 }

Index: src/clone.c
==================================================================
--- src/clone.c
+++ src/clone.c
@@ -72,10 +72,11 @@
   fix_private_blob_dependencies(1);
   db_multi_exec(
     "DELETE FROM blob WHERE rid IN private;"
     "DELETE FROM delta wHERE rid IN private;"
     "DELETE FROM private;"
+    "DROP TABLE IF EXISTS modreq;"
   );
 }
 
 
 /*

Index: src/info.c
==================================================================
--- src/info.c
+++ src/info.c
@@ -713,84 +713,98 @@
   style_footer();
 }
 
 /*
 ** WEBPAGE: winfo
-** URL:  /winfo?name=RID
+** URL:  /winfo?name=UUID
 **
 ** Return information about a wiki page.
 */
 void winfo_page(void){
-  Stmt q;
   int rid;
+  Manifest *pWiki;
+  char *zUuid;
+  char *zDate;
+  Blob wiki;
+  int modPending;
+  const char *zModAction;
 
   login_check_credentials();
   if( !g.perm.RdWiki ){ login_needed(); return; }
   rid = name_to_rid_www("name");
-  if( rid==0 ){
+  if( rid==0 || (pWiki = manifest_get(rid, CFTYPE_WIKI))==0 ){
     style_header("Wiki Page Information Error");
-    @ No such object: %h(g.argv[2])
+    @ No such object: %h(P("name"))
     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);
-    const char *zDate = db_column_text(&q,2);
-    const char *zUser = db_column_text(&q,3);
-    style_header(zTitle);
-    free(zTitle);
-    login_anonymous_available();
-    @ <div class="section">Overview</div>
-    @ <p><table class="label-value">
-    @ <tr><th>Version:</th><td>%s(zUuid)</td></tr>
-    @ <tr><th>Date:</th><td>
-    hyperlink_to_date(zDate, "</td></tr>");
-    if( g.perm.Setup ){
-      @ <tr><th>Record ID:</th><td>%d(rid)</td></tr>
-    }
-    @ <tr><th>Original&nbsp;User:</th><td>
-    hyperlink_to_user(zUser, zDate, "</td></tr>");
-    if( g.perm.Hyperlink ){
-      @ <tr><th>Commands:</th>
-      @   <td>
-      @     %z(href("%R/whistory?name=%t",zName))history</a>
-      @     | %z(href("%R/artifact/%S",zUuid))raw-text</a>
-      @   </td>
-      @ </tr>
-    }
-    @ </table></p>
-  }else{
-    style_header("Wiki Information");
-    rid = 0;
-  }
-  db_finalize(&q);
-  showTags(rid, "wiki-*");
-  if( rid ){
-    Manifest *pWiki;
-    pWiki = manifest_get(rid, CFTYPE_WIKI);
-    if( pWiki ){
-      Blob wiki;
-      blob_init(&wiki, pWiki->zWiki, -1);
-      @ <div class="section">Content</div>
-      wiki_convert(&wiki, 0, 0);
-      blob_reset(&wiki);
-    }
-    manifest_destroy(pWiki);
-  }
+  if( g.perm.ModWiki && (zModAction = P("modaction"))!=0 ){
+    if( strcmp(zModAction,"delete")==0 ){
+      moderation_disapprove(rid);
+      cgi_redirectf("%R/wiki?name=%T", pWiki->zWikiTitle);
+      /*NOTREACHED*/
+    }
+    if( strcmp(zModAction,"approve")==0 ){
+      moderation_approve(rid);
+    }
+  }
+  style_header("Update of \"%h\"", pWiki->zWikiTitle);
+  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
+  zDate = db_text(0, "SELECT datetime(%.17g)", pWiki->rDate);
+  style_submenu_element("Raw", "Raw", "artifact/%S", zUuid);
+  style_submenu_element("History", "History", "whistory?name=%t",
+                        pWiki->zWikiTitle);
+  style_submenu_element("Page", "Page", "wiki?name=%t",
+                        pWiki->zWikiTitle);
+  login_anonymous_available();
+  @ <div class="section">Overview</div>
+  @ <p><table class="label-value">
+  @ <tr><th>Artifact&nbsp;ID:</th>
+  @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
+  if( g.perm.Setup ){
+    @ (%d(rid))
+  }
+  modPending = moderation_pending(rid);
+  if( modPending ){
+    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
+  }
+  @ </td></tr>
+  @ <tr><th>Page&nbsp;Name:</th><td>%h(pWiki->zWikiTitle)</td></tr>
+  @ <tr><th>Date:</th><td>
+  hyperlink_to_date(zDate, "</td></tr>");
+  @ <tr><th>Original&nbsp;User:</th><td>
+  hyperlink_to_user(pWiki->zUser, zDate, "</td></tr>");
+  if( pWiki->nParent>0 ){
+    int i;
+    @ <tr><th>Parent%s(pWiki->nParent==1?"":"s"):</th><td>
+    for(i=0; i<pWiki->nParent; i++){
+      char *zParent = pWiki->azParent[i];
+      @ %z(href("info/%S",zParent))%s(zParent)</a>
+    }
+    @ </td></tr>
+  }
+  @ </table>
+
+  if( g.perm.ModWiki && modPending ){
+    @ <div class="section">Moderation</div>
+    @ <blockquote>
+    @ <form method="POST" action="%R/winfo/%s(zUuid)">
+    @ <label><input type="radio" name="modaction" value="delete">
+    @ Delete this change</label><br />
+    @ <label><input type="radio" name="modaction" value="approve">
+    @ Approve this change</label><br />
+    @ <input type="submit" value="Submit">
+    @ </form>
+    @ </blockquote>
+  }
+
+
+  @ <div class="section">Content</div>
+  blob_init(&wiki, pWiki->zWiki, -1);
+  wiki_convert(&wiki, 0, 0);
+  blob_reset(&wiki);
+  manifest_destroy(pWiki);
   style_footer();
 }
 
 /*
 ** Show a webpage error message
@@ -993,10 +1007,25 @@
   manifest_destroy(pTo);
 
   style_footer();
 }
 
+#if INTERFACE
+/*
+** Possible return values from object_description()
+*/
+#define OBJTYPE_CHECKIN    0x0001
+#define OBJTYPE_CONTENT    0x0002
+#define OBJTYPE_WIKI       0x0004
+#define OBJTYPE_TICKET     0x0008
+#define OBJTYPE_ATTACHMENT 0x0010
+#define OBJTYPE_EVENT      0x0020
+#define OBJTYPE_TAG        0x0040
+#define OBJTYPE_SYMLINK    0x0080
+#define OBJTYPE_EXE        0x0100
+#endif
+
 /*
 ** Write a description of an object to the www reply.
 **
 ** If the object is a file then mention:
 **
@@ -1008,18 +1037,19 @@
 **
 **     * It's artifact ID
 **     * date of check-in
 **     * Comment & user
 */
-void object_description(
+int 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;
+  int objType = 0;
   char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
 
   char *prevName = 0;
 
   db_prepare(&q,
@@ -1051,15 +1081,18 @@
       if( prevName ) {
         @ </ul>
       }
       if( mPerm==PERM_LNK ){
         @ <li>Symbolic link
+        objType |= OBJTYPE_SYMLINK;
       }else if( mPerm==PERM_EXE ){
         @ <li>Executable file
+        objType |= OBJTYPE_EXE;
       }else{
         @ <li>File
       }
+      objType |= OBJTYPE_CONTENT;
       @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
       @ <ul>
       prevName = fossil_strdup(zName);
     }
     @ <li>
@@ -1100,17 +1133,18 @@
     if( cnt>0 ){
       @ Also wiki page
     }else{
       @ Wiki page
     }
+    objType |= OBJTYPE_WIKI;
     @ [%z(href("%R/wiki?name=%t",zPagename))%h(zPagename)</a>] by
     hyperlink_to_user(zUser,zDate," on");
     hyperlink_to_date(zDate,".");
     nWiki++;
     cnt++;
     if( pDownloadName && blob_size(pDownloadName)==0 ){
-      blob_appendf(pDownloadName, "%s.wiki", zPagename);
+      blob_appendf(pDownloadName, "%s.txt", zPagename);
     }
   }
   db_finalize(&q);
   if( nWiki==0 ){
     db_prepare(&q,
@@ -1129,16 +1163,20 @@
       if( cnt>0 ){
         @ Also
       }
       if( zType[0]=='w' ){
         @ Wiki edit
+        objType |= OBJTYPE_WIKI;
       }else if( zType[0]=='t' ){
         @ Ticket change
+        objType |= OBJTYPE_TICKET;
       }else if( zType[0]=='c' ){
         @ Manifest of check-in
+        objType |= OBJTYPE_CHECKIN;
       }else if( zType[0]=='e' ){
         @ Instance of event
+        objType |= OBJTYPE_EVENT;
         hyperlink_to_event_tagid(db_column_int(&q, 5));
       }else{
         @ Control file referencing
       }
       if( zType[0]!='e' ){
@@ -1170,10 +1208,11 @@
     if( cnt>0 ){
       @ Also attachment "%h(zFilename)" to
     }else{
       @ Attachment "%h(zFilename)" to
     }
+    objType |= OBJTYPE_ATTACHMENT;
     if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
       if( g.perm.Hyperlink && g.perm.RdTkt ){
         @ ticket [%z(href("%R/tktview?name=%S",zTarget))%S(zTarget)</a>]
       }else{
         @ ticket [%S(zTarget)]
@@ -1200,10 +1239,11 @@
       blob_appendf(pDownloadName, "%.10s.txt", zUuid);
     }
   }else if( linkToView && g.perm.Hyperlink ){
     @ %z(href("%R/artifact/%S",zUuid))[view]</a>
   }
+  return objType;
 }
 
 
 /*
 ** WEBPAGE: fdiff
@@ -1399,11 +1439,15 @@
             g.zTop, zUuid);
     }
   }
   style_header("Hex Artifact Content");
   zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
-  @ <h2>Artifact %s(zUuid):</h2>
+  if( g.perm.Setup ){
+    @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
+  }else{
+    @ <h2>Artifact %s(zUuid):</h2>
+  }
   blob_zero(&downloadName);
   object_description(rid, 0, &downloadName);
   style_submenu_element("Download", "Download",
         "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
   @ <hr />
@@ -1449,11 +1493,11 @@
 **
 ** zLn is the ?ln= parameter for the HTTP query.  If there is an argument,
 ** then highlight that line number and scroll to it once the page loads.
 ** If there are two line numbers, highlight the range of lines.
 */
-static void output_text_with_line_numbers(
+void output_text_with_line_numbers(
   const char *z,
   const char *zLn
 ){
   int iStart, iEnd;    /* Start and end of region to highlight */
   int n = 0;           /* Current line number */
@@ -1519,11 +1563,15 @@
   Blob content;
   const char *zMime;
   Blob downloadName;
   int renderAsWiki = 0;
   int renderAsHtml = 0;
+  int objType;
+  int asText;
   const char *zUuid;
+  Manifest *pManifest;
+
   if( P("ci") && P("filename") ){
     rid = artifact_from_ci_and_filename();
   }
   if( rid==0 ){
     rid = name_to_rid_www("name");
@@ -1542,37 +1590,45 @@
             g.zTop, zUuid);
     }
   }
   style_header("Artifact Content");
   zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
-  @ <h2>Artifact %s(zUuid)</h2>
+  if( g.perm.Setup ){
+    @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
+  }else{
+    @ <h2>Artifact %s(zUuid):</h2>
+  }
   blob_zero(&downloadName);
-  object_description(rid, 0, &downloadName);
+  objType = object_description(rid, 0, &downloadName);
   style_submenu_element("Download", "Download",
           "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
+  asText = P("txt")!=0;
   zMime = mimetype_from_name(blob_str(&downloadName));
   if( zMime ){
     if( fossil_strcmp(zMime, "text/html")==0 ){
-      if( P("txt") ){
+      if( asText ){
         style_submenu_element("Html", "Html",
                               "%s/artifact/%s", g.zTop, zUuid);
       }else{
         renderAsHtml = 1;
         style_submenu_element("Text", "Text",
                               "%s/artifact/%s?txt=1", g.zTop, zUuid);
       }
     }else if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){
-      if( P("txt") ){
+      if( asText ){
         style_submenu_element("Wiki", "Wiki",
                               "%s/artifact/%s", g.zTop, zUuid);
       }else{
         renderAsWiki = 1;
         style_submenu_element("Text", "Text",
                               "%s/artifact/%s?txt=1", g.zTop, zUuid);
       }
     }
   }
+  if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
+    style_submenu_element("Parsed", "Parsed", "%R/info/%s", zUuid);
+  }
   @ <hr />
   content_get(rid, &content);
   if( renderAsWiki ){
     wiki_convert(&content, 0, 0);
   }else if( renderAsHtml ){
@@ -1614,12 +1670,14 @@
 */
 void tinfo_page(void){
   int rid;
   char *zDate;
   const char *zUuid;
-  char zTktName[20];
+  char zTktName[UUID_SIZE+1];
   Manifest *pTktChng;
+  int modPending;
+  const char *zModAction;
 
   login_check_credentials();
   if( !g.perm.RdTkt ){ login_needed(); return; }
   rid = name_to_rid_www("name");
   if( rid==0 ){ fossil_redirect_home(); }
@@ -1632,33 +1690,64 @@
       style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
             g.zTop, zUuid);
     }
   }
   pTktChng = manifest_get(rid, CFTYPE_TICKET);
-  if( pTktChng==0 ){
-    fossil_redirect_home();
+  if( pTktChng==0 ) fossil_redirect_home();
+  zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate);
+  memcpy(zTktName, pTktChng->zTicketUuid, UUID_SIZE+1);
+  if( g.perm.ModTkt && (zModAction = P("modaction"))!=0 ){
+    if( strcmp(zModAction,"delete")==0 ){
+      moderation_disapprove(rid);
+      cgi_redirectf("%R/tktview/%s", zTktName);
+      /*NOTREACHED*/
+    }
+    if( strcmp(zModAction,"approve")==0 ){
+      moderation_approve(rid);
+    }
   }
   style_header("Ticket Change Details");
-  zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate);
-  memcpy(zTktName, pTktChng->zTicketUuid, 10);
-  zTktName[10] = 0;
-  if( g.perm.Hyperlink ){
-    @ <h2>Changes to ticket
-    @ %z(href("%R/tktview/%s",pTktChng->zTicketUuid))%s(zTktName)</a></h2>
-    @
-    @ <p>By %h(pTktChng->zUser) on %s(zDate).  See also:
-    @ %z(href("%R/artifact/%T",zUuid))artifact content</a>, and
-    @ %z(href("%R/tkthistory/%s",pTktChng->zTicketUuid))ticket history</a></p>
-  }else{
-    @ <h2>Changes to ticket %s(zTktName)</h2>
-    @
-    @ <p>By %h(pTktChng->zUser) on %s(zDate).
-    @ </p>
+  style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid);
+  style_submenu_element("History", "History", "%R/tkthistory/%s", zTktName);
+  style_submenu_element("Page", "Page", "%R/tktview/%t", zTktName);
+  style_submenu_element("Timeline", "Timeline", "%R/tkttimeline/%t", zTktName);
+
+  @ <div class="section">Overview</div>
+  @ <p><table class="label-value">
+  @ <tr><th>Artifact&nbsp;ID:</th>
+  @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
+  if( g.perm.Setup ){
+    @ (%d(rid))
   }
-  @
-  @ <ol>
+  modPending = moderation_pending(rid);
+  if( modPending ){
+    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
+  }
+  @ <tr><th>Ticket:</th>
+  @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a></td></tr>
+  @ <tr><th>Date:</th><td>
+  hyperlink_to_date(zDate, "</td></tr>");
   free(zDate);
+  @ <tr><th>User:</th><td>
+  hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>");
+  @ </table>
+  
+  if( g.perm.ModTkt && modPending ){
+    @ <div class="section">Moderation</div>
+    @ <blockquote>
+    @ <form method="POST" action="%R/tinfo/%s(zUuid)">
+    @ <label><input type="radio" name="modaction" value="delete">
+    @ Delete this change</label><br />
+    @ <label><input type="radio" name="modaction" value="approve">
+    @ Approve this change</label><br />
+    @ <input type="submit" value="Submit">
+    @ </form>
+    @ </blockquote>
+  }
+
+  @ <div class="section">Changes</div>
+  @ <p>
   ticket_output_change_artifact(pTktChng);
   manifest_destroy(pTktChng);
   style_footer();
 }
 
@@ -1724,10 +1813,13 @@
   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
+  if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
+    ainfo_page();
   }else
   {
     artifact_page();
   }
 }

Index: src/login.c
==================================================================
--- src/login.c
+++ src/login.c
@@ -977,11 +977,12 @@
       case 's':   g.perm.Setup = 1;  /* Fall thru into Admin */
       case 'a':   g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
                            g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
                            g.perm.ApndWiki = g.perm.Hyperlink = g.perm.Clone = 
                            g.perm.NewTkt = g.perm.Password = g.perm.RdAddr =
-                           g.perm.TktFmt = g.perm.Attach = g.perm.ApndTkt = 1;
+                           g.perm.TktFmt = g.perm.Attach = g.perm.ApndTkt =
+                           g.perm.ModWiki = g.perm.ModTkt = 1;
                            /* Fall thru into Read/Write */
       case 'i':   g.perm.Read = g.perm.Write = 1;                     break;
       case 'o':   g.perm.Read = 1;                                 break;
       case 'z':   g.perm.Zip = 1;                                  break;
 

Index: src/main.mk
==================================================================
--- src/main.mk
+++ src/main.mk
@@ -71,10 +71,11 @@
   $(SRCDIR)/main.c \
   $(SRCDIR)/manifest.c \
   $(SRCDIR)/md5.c \
   $(SRCDIR)/merge.c \
   $(SRCDIR)/merge3.c \
+  $(SRCDIR)/moderate.c \
   $(SRCDIR)/name.c \
   $(SRCDIR)/path.c \
   $(SRCDIR)/pivot.c \
   $(SRCDIR)/popen.c \
   $(SRCDIR)/pqueue.c \
@@ -171,10 +172,11 @@
   $(OBJDIR)/main_.c \
   $(OBJDIR)/manifest_.c \
   $(OBJDIR)/md5_.c \
   $(OBJDIR)/merge_.c \
   $(OBJDIR)/merge3_.c \
+  $(OBJDIR)/moderate_.c \
   $(OBJDIR)/name_.c \
   $(OBJDIR)/path_.c \
   $(OBJDIR)/pivot_.c \
   $(OBJDIR)/popen_.c \
   $(OBJDIR)/pqueue_.c \
@@ -271,10 +273,11 @@
  $(OBJDIR)/main.o \
  $(OBJDIR)/manifest.o \
  $(OBJDIR)/md5.o \
  $(OBJDIR)/merge.o \
  $(OBJDIR)/merge3.o \
+ $(OBJDIR)/moderate.o \
  $(OBJDIR)/name.o \
  $(OBJDIR)/path.o \
  $(OBJDIR)/pivot.o \
  $(OBJDIR)/popen.o \
  $(OBJDIR)/pqueue.o \
@@ -378,11 +381,11 @@
 
 
 $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
 	$(OBJDIR)/mkindex $(TRANS_SRC) >$@
 $(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
-	$(OBJDIR)/makeheaders  $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h $(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
+	$(OBJDIR)/makeheaders  $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h $(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
 	touch $(OBJDIR)/headers
 $(OBJDIR)/headers: Makefile
 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_config.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_dir.o $(OBJDIR)/json_finfo.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
 Makefile:
 $(OBJDIR)/add_.c:	$(SRCDIR)/add.c $(OBJDIR)/translate
@@ -796,10 +799,17 @@
 
 $(OBJDIR)/merge3.o:	$(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h  $(SRCDIR)/config.h
 	$(XTCC) -o $(OBJDIR)/merge3.o -c $(OBJDIR)/merge3_.c
 
 $(OBJDIR)/merge3.h:	$(OBJDIR)/headers
+$(OBJDIR)/moderate_.c:	$(SRCDIR)/moderate.c $(OBJDIR)/translate
+	$(OBJDIR)/translate $(SRCDIR)/moderate.c >$(OBJDIR)/moderate_.c
+
+$(OBJDIR)/moderate.o:	$(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h  $(SRCDIR)/config.h
+	$(XTCC) -o $(OBJDIR)/moderate.o -c $(OBJDIR)/moderate_.c
+
+$(OBJDIR)/moderate.h:	$(OBJDIR)/headers
 $(OBJDIR)/name_.c:	$(SRCDIR)/name.c $(OBJDIR)/translate
 	$(OBJDIR)/translate $(SRCDIR)/name.c >$(OBJDIR)/name_.c
 
 $(OBJDIR)/name.o:	$(OBJDIR)/name_.c $(OBJDIR)/name.h  $(SRCDIR)/config.h
 	$(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c

Index: src/makemake.tcl
==================================================================
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -74,10 +74,11 @@
   main
   manifest
   md5
   merge
   merge3
+  moderate
   name
   path
   pivot
   popen
   pqueue

Index: src/manifest.c
==================================================================
--- src/manifest.c
+++ src/manifest.c
@@ -318,20 +318,22 @@
 ** Parse a blob into a Manifest object.  The Manifest object
 ** takes over the input blob and will free it when the
 ** Manifest object is freed.  Zeros are inserted into the blob
 ** as string terminators so that blob should not be used again.
 **
-** Return TRUE if the content really is a control file of some
-** kind.  Return FALSE if there are syntax errors.
+** Return a pointer to an allocated Manifest object if the content
+** really is a control file of some kind.  This object needs to be
+** freed by a subsequent call to manifest_destroy().  Return NULL
+** if there are syntax errors.
 **
 ** This routine is strict about the format of a control file.
 ** The format must match exactly or else it is rejected.  This
 ** rule minimizes the risk that a content file will be mistaken
 ** for a control file simply because they look the same.
 **
-** The pContent is reset.  If TRUE is returned, then pContent will
-** be reset when the Manifest object is cleared.  If FALSE is
+** The pContent is reset.  If a pointer is returned, then pContent will
+** be reset when the Manifest object is cleared.  If NULL is
 ** returned then the Manifest object is cleared automatically
 ** and pContent is reset before the return.
 **
 ** The entire file can be PGP clear-signed.  The signature is ignored.
 ** The file consists of zero or more cards, one card per line.

ADDED   src/moderate.c
Index: src/moderate.c
==================================================================
--- src/moderate.c
+++ src/moderate.c
@@ -0,0 +1,166 @@
+/*
+** Copyright (c) 2012 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+
+** 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.
+**
+** Author contact information:
+**   drh@hwaci.com
+**   http://www.hwaci.com/drh/
+**
+*******************************************************************************
+**
+** This file contains code used to deal with moderator actions for
+** Wiki and Tickets.
+*/
+#include "config.h"
+#include "moderate.h"
+#include <assert.h>
+
+/*
+** Create a table to represent pending moderation requests, if the
+** table does not already exist.
+*/
+void moderation_table_create(void){
+  db_multi_exec(
+     "CREATE TABLE IF NOT EXISTS modreq(\n"
+     "  objid INTEGER PRIMARY KEY,\n"        /* Record pending approval */
+     "  attachRid INT,\n"                    /* Object attached */
+     "  tktid TEXT\n"                        /* Associated ticket id */
+     ");\n"
+  );
+}
+
+/*
+** Return TRUE if the modreq table exists
+*/
+int moderation_table_exists(void){
+  static int modreqExists = -1;
+  if( modreqExists<0 ){
+    modreqExists = db_exists("SELECT 1 FROM %s.sqlite_master"
+                             " WHERE name='modreq'", db_name("repository"));
+  }
+  return modreqExists;
+}
+
+/*
+** Return TRUE if the object specified is being held for moderation.
+*/
+int moderation_pending(int rid){
+  static Stmt q;
+  int rc;
+  if( rid==0 || !moderation_table_exists() ) return 0;
+  db_static_prepare(&q, "SELECT 1 FROM modreq WHERE objid=:objid");
+  db_bind_int(&q, ":objid", rid);
+  rc = db_step(&q)==SQLITE_ROW;
+  db_reset(&q);
+  return rc;
+}
+
+/*
+** Check to see if the object identified by RID is used for anything.
+*/
+static int object_used(int rid){
+  static const char *aTabField[] = {
+     "modreq",     "attachRid",
+     "mlink",      "mid",
+     "mlink",      "fid",
+     "tagxref",    "srcid",
+     "tagxref",    "rid",
+  };
+  int i;
+  for(i=0; i<sizeof(aTabField)/sizeof(aTabField[0]); i+=2){
+    if( db_exists("SELECT 1 FROM %s WHERE %s=%d",
+                  aTabField[i], aTabField[i+1], rid) ) return 1;
+  }
+  return 0;
+}
+
+/*
+** Delete a moderation item given by objid
+*/
+void moderation_disapprove(int objid){
+  Stmt q;
+  char *zTktid;
+  int attachRid = 0;
+  int rid;
+  if( !moderation_pending(objid) ) return;
+  db_begin_transaction();
+  rid = objid;
+  while( rid && content_is_private(rid) ){
+    db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
+    while( db_step(&q)==SQLITE_ROW ){
+      int ridUser = db_column_int(&q, 0);
+      content_undelta(ridUser);
+    }
+    db_finalize(&q);
+    db_multi_exec(
+      "DELETE FROM blob WHERE rid=%d;"
+      "DELETE FROM delta WHERE rid=%d;"
+      "DELETE FROM event WHERE objid=%d;"
+      "DELETE FROM tagxref WHERE rid=%d;"
+      "DELETE FROM private WHERE rid=%d;"
+      "DELETE FROM attachment WHERE attachid=%d;",
+      rid, rid, rid, rid, rid, rid
+    );
+    zTktid = db_text(0, "SELECT tktid FROM modreq WHERE objid=%d", rid);
+    if( zTktid && zTktid[0] ){
+      ticket_rebuild_entry(zTktid);
+      fossil_free(zTktid);
+    }
+    attachRid = db_int(0, "SELECT attachRid FROM modreq WHERE objid=%d", rid);
+    if( rid==objid ){
+      db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid);
+    }
+    if( attachRid && object_used(attachRid) ) attachRid = 0;
+    rid = attachRid;
+  }
+  db_end_transaction(0);
+}
+
+/*
+** Approve an object held for moderation.
+*/
+void moderation_approve(int rid){
+  if( !moderation_pending(rid) ) return;
+  db_begin_transaction();
+  db_multi_exec(
+    "DELETE FROM private WHERE rid=%d;"
+    "INSERT OR IGNORE INTO unclustered VALUES(%d);"
+    "INSERT OR IGNORE INTO unsent VALUES(%d);",
+    rid, rid, rid
+  );
+  db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid);
+  db_end_transaction(0);
+}
+
+/*
+** WEBPAGE: modreq
+**
+** Show all pending moderation request
+*/
+void modreq_page(void){
+  Blob sql;
+  Stmt q;
+
+  login_check_credentials();
+  if( !g.perm.RdWiki && !g.perm.RdTkt ){ login_needed(); return; }
+  style_header("Pending Moderation Requests");
+  @ <h2>All Pending Moderation Requests</h2>
+  if( moderation_table_exists() ){
+    blob_init(&sql, timeline_query_for_www(), -1);
+    blob_appendf(&sql,
+        " AND event.objid IN (SELECT objid FROM modreq)"
+        " ORDER BY event.mtime DESC"
+    );
+    db_prepare(&q, blob_str(&sql));
+    www_print_timeline(&q, 0, 0, 0, 0);
+    db_finalize(&q);
+  }
+  style_footer();
+}

Index: src/rebuild.c
==================================================================
--- src/rebuild.c
+++ src/rebuild.c
@@ -347,11 +347,11 @@
     zTable = db_text(0,
        "SELECT name FROM sqlite_master /*scan*/"
        " WHERE type='table'"
        " AND name NOT IN ('blob','delta','rcvfrom','user',"
                          "'config','shun','private','reportfmt',"
-                         "'concealed','accesslog')"
+                         "'concealed','accesslog','modreq')"
        " AND name NOT GLOB 'sqlite_*'"
     );
     if( zTable==0 ) break;
     db_multi_exec("DROP TABLE %Q", zTable);
     free(zTable);

Index: src/setup.c
==================================================================
--- src/setup.c
+++ src/setup.c
@@ -92,10 +92,13 @@
     "Edit the Cascading Style Sheet used by all pages of this repository");
   setup_menu_entry("Header", "setup_header",
     "Edit HTML text inserted at the top of every page");
   setup_menu_entry("Footer", "setup_footer",
     "Edit HTML text inserted at the bottom of every page");
+  setup_menu_entry("Moderation", "setup_modreq",
+    "Enable/Disable requiring moderator approval of Wiki and/or Ticket"
+    "edits and attachments.");
   setup_menu_entry("Ad-Unit", "setup_adunit",
     "Edit HTML text for an ad unit inserted after the menu bar");
   setup_menu_entry("Logo", "setup_logo",
     "Change the logo and background images for the server");
   setup_menu_entry("Shunned", "shun",
@@ -1403,10 +1406,54 @@
   @ <blockquote><pre>
   @ %h(zDefaultFooter)
   @ </pre></blockquote>
   style_footer();
   db_end_transaction(0);
+}
+
+/*
+** WEBPAGE: setup_modreq
+*/
+void setup_modreq(void){
+  login_check_credentials();
+  if( !g.perm.Setup ){
+    login_needed();
+  }
+
+  style_header("Moderator For Wiki And Tickets");
+  db_begin_transaction();
+  @ <form action="%R/setup_modreq" method="post"><div>
+  login_insert_csrf_secret();
+  @ <hr />
+  onoff_attribute("Moderate ticket changes",
+     "modreq-tkt", "modreq-tkt", 0);
+  @ <p>When enabled, any change to tickets is subject to the approval
+  @ a ticket moderator - a user with the "q" or Mod-Tkt privilege.
+  @ Ticket changes enter the system and are shown locally, but are not
+  @ synced until they are approved.  The moderator has the option to 
+  @ delete the change rather than approve it.  Ticket changes made by
+  @ a user who hwas the Mod-Tkt privilege are never subject to
+  @ moderation.
+  @
+  @ <hr />
+  onoff_attribute("Moderate wiki changes",
+     "modreq-wiki", "modreq-wiki", 0);
+  @ <p>When enabled, any change to wiki is subject to the approval
+  @ a ticket moderator - a user with the "l" or Mod-Wiki privilege.
+  @ Wiki changes enter the system and are shown locally, but are not
+  @ synced until they are approved.  The moderator has the option to 
+  @ delete the change rather than approve it.  Wiki changes made by
+  @ a user who has the Mod-Wiki privilege are never subject to
+  @ moderation.
+  @ </p>
+ 
+  @ <hr />
+  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
+  @ </div></form>
+  db_end_transaction(0);
+  style_footer();
+
 }
 
 /*
 ** WEBPAGE: setup_adunit
 */

Index: src/style.c
==================================================================
--- src/style.c
+++ src/style.c
@@ -930,13 +930,18 @@
   { "span.diffhr",
     "suppressed lines in a diff",
     @   color: #0000ff;
   },
   { "span.diffln",
-    "line nubmers in a diff",
+    "line numbers in a diff",
     @   color: #a0a0a0;
   },
+  { "span.modpending",
+    "Moderation Pending message on timelin",
+    @   color: #b03800;
+    @   font-style: italic;
+  },
   { 0,
     0,
     0
   }
 };

Index: src/th_main.c
==================================================================
--- src/th_main.c
+++ src/th_main.c
@@ -192,11 +192,11 @@
   free(zOut);
   return TH_OK;
 }
 
 /*
-** TH command:     hascap STRING
+** TH command:     hascap STRING...
 **
 ** Return true if the user has all of the capabilities listed in STRING.
 */
 static int hascapCmd(
   Th_Interp *interp, 
@@ -203,15 +203,17 @@
   void *p, 
   int argc, 
   const char **argv, 
   int *argl
 ){
-  int rc;
-  if( argc!=2 ){
-    return Th_WrongNumArgs(interp, "hascap STRING");
+  int rc = 0, i;
+  if( argc<2 ){
+    return Th_WrongNumArgs(interp, "hascap STRING ...");
   }
-  rc = login_has_capability((char*)argv[1],argl[1]);
+  for(i=1; i<argc && rc==0; i++){
+    rc = login_has_capability((char*)argv[i],argl[i]);
+  }
   if( g.thTrace ){
     Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc);
   }
   Th_SetResultInt(interp, rc);
   return TH_OK;

Index: src/timeline.c
==================================================================
--- src/timeline.c
+++ src/timeline.c
@@ -236,12 +236,16 @@
     const char *zUser = db_column_text(pQuery, 4);
     const char *zTagList = db_column_text(pQuery, 8);
     int tagid = db_column_int(pQuery, 9);
     const char *zBr = 0;      /* Branch */
     int commentColumn = 3;    /* Column containing comment text */
+    int modPending;           /* Pending moderation */
     char zTime[8];
+
+    modPending =  moderation_pending(rid);
     if( tagid ){
+      if( modPending ) tagid = -tagid;
       if( tagid==prevTagid ){
         if( tmFlags & TIMELINE_BRIEF ){
           suppressCnt++;
           continue;
         }else{
@@ -320,10 +324,13 @@
       @ <td class="timelineTableCell">
     }
     if( pGraph && zType[0]!='c' ){
       @ &bull;
     }
+    if( modPending ){
+      @ <span class="modpending">(Awaiting Moderator Approval)</span>
+    }
     if( zType[0]=='c' ){
       hyperlink_to_uuid(zUuid);
       if( isLeaf ){
         if( db_exists("SELECT 1 FROM tagxref"
                       " WHERE rid=%d AND tagid=%d AND tagtype>0",
@@ -332,11 +339,11 @@
         }else{
           @ <span class="timelineLeaf">Leaf:</span>
         }
       }
     }else if( zType[0]=='e' && tagid ){
-      hyperlink_to_event_tagid(tagid);
+      hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
     }else if( (tmFlags & TIMELINE_ARTID)!=0 ){
       hyperlink_to_uuid(zUuid);
     }
     db_column_blob(pQuery, commentColumn, &comment);
     if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
@@ -361,11 +368,11 @@
     }else{
       @ (user: %h(zUser)%s(zTagList?",":"\051")
     }
 
     /* Generate a "detail" link for tags. */
-    if( zType[0]=='g' && g.perm.Hyperlink ){
+    if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){
       @ [%z(href("%R/info/%S",zUuid))details</a>]
     }
 
     /* Generate the "tags: TAGLIST" at the end of the comment, together
     ** with hyperlinks to the tag list.

Index: src/tkt.c
==================================================================
--- src/tkt.c
+++ src/tkt.c
@@ -335,48 +335,11 @@
 
   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!='' 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>
-      if( g.perm.Read && g.perm.Hyperlink ){
-        @ %z(href("%R/attachview?tkt=%s&file=%t",zFullName,zFile))
-        @ %h(zFile)</a>
-      }else{
-        @ %h(zFile)
-      }
-      @ added by %h(zUser) on
-      hyperlink_to_date(zDate, ".");
-      if( g.perm.WrTkt && g.perm.Attach ){
-        char *zH;
-        zH = href("%R/attachdelete?tkt=%s&file=%t&from=%R/tktview%%3fname=%s",
-                  zFullName, zFile, zFullName);
-        @ [%z(zH)delete</a>]
-      }
-      @ </li>
-    }
-    if( cnt ){
-      @ </ul>
-    }
-    db_finalize(&q);
+    attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>");
   }
  
   style_footer();
 }
 
@@ -415,10 +378,38 @@
     return TH_ERROR;
   }
   azAppend[idx] = mprintf("%.*s", argl[2], argv[2]);
   return TH_OK;
 }
+
+/*
+** Write a ticket into the repository.
+*/
+static void ticket_put(
+  Blob *pTicket,           /* The text of the ticket change record */
+  const char *zTktId,      /* The ticket to which this change is applied */
+  int needMod              /* True if moderation is needed */
+){
+  int rid = content_put_ex(pTicket, 0, 0, 0, needMod);
+  if( rid==0 ){
+    fossil_panic("trouble committing ticket: %s", g.zErrMsg);
+  }
+  if( needMod ){
+    moderation_table_create();
+    db_multi_exec(
+      "INSERT INTO modreq(objid, tktid) VALUES(%d,'%s')",
+      rid, zTktId
+    );
+  }else{
+    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
+    db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
+  }
+  manifest_crosslink_begin();
+  manifest_crosslink(rid, pTicket);
+  assert( blob_is_reset(pTicket) );
+  manifest_crosslink_end();
+}
 
 /*
 ** Subscript command:   submit_ticket
 **
 ** Construct and submit a new ticket artifact.  The fields of the artifact
@@ -498,18 +489,12 @@
   }else if( g.thTrace ){
     Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
              "}<br />\n",
        blob_str(&tktchng));
   }else{
-    rid = content_put(&tktchng);
-    if( rid==0 ){
-      fossil_panic("trouble committing ticket: %s", g.zErrMsg);
-    }
-    manifest_crosslink_begin();
-    manifest_crosslink(rid, &tktchng);
-    assert( blob_is_reset(&tktchng) );
-    manifest_crosslink_end();
+    ticket_put(&tktchng, zUuid,
+               (g.perm.ModTkt==0 && db_get_boolean("modreq-tkt",0)==1));
   }
   return ticket_change();
 }
 
 
@@ -1187,18 +1172,11 @@
       }
       blob_appendf(&tktchng, "K %s\n", zTktUuid);
       blob_appendf(&tktchng, "U %F\n", zUser);
       md5sum_blob(&tktchng, &cksum);
       blob_appendf(&tktchng, "Z %b\n", &cksum);
-      rid = content_put(&tktchng);
-      if( rid==0 ){
-        fossil_panic("trouble committing ticket: %s", g.zErrMsg);
-      }
-      manifest_crosslink_begin();
-      manifest_crosslink(rid, &tktchng);
-      manifest_crosslink_end();
-      assert( blob_is_reset(&tktchng) );
+      ticket_put(&tktchng, zTktUuid, 0);
       printf("ticket %s succeeded for %s\n",
              (eCmd==set?"set":"add"),zTktUuid);
     }
   }
 }

Index: src/tktsetup.c
==================================================================
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -595,13 +595,18 @@
 @ <ol>
 @ <th1>html $report_items</th1>
 @ </ol>
 @ 
 @ <th1>
-@ if {[hascap t]} {
-@   html "<p>Create a new ticket display format:</p>"
-@   html "<ul><li><a href='rptnew'>New report format</a></li></ul>"
+@ if {[hascap t q]} {
+@   html "<p>Other options:</p>\n<ul>\n"
+@   if {[hascap t]} {
+@     html "<li><a href='rptnew'>New report format</a></li>\n"
+@   }
+@   if {[hascap q]} {
+@     html "<li><a href='modreq'>Tend to pending moderation requests</a></li>\n"
+@   }
 @ }
 @ </th1>
 ;
 
 /*

Index: src/wiki.c
==================================================================
--- src/wiki.c
+++ src/wiki.c
@@ -158,10 +158,13 @@
         @ <li>   Create a %z(href("%R/eventedit"))new event</a>.</li>
       }
     }
     @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
     @      available on this server.</li>
+    if( g.perm.ModWiki ){
+      @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
+    }
     @ <li> <form method="get" action="%s(g.zTop)/wfind"><div>
     @     Search wiki titles: <input type="text" name="title"/>
     @  &nbsp; <input type="submit" /></div></form>
     @ </li>
     @ </ul>
@@ -214,48 +217,32 @@
   style_set_current_page("%s?name=%T", g.zPath, zPageName);
   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!='' 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>
-    if( g.perm.Hyperlink && g.perm.Read ){
-      @ %z(href("%R/attachview?page=%T&file=%t",zPageName,zFile))
-      @ %h(zFile)</a>
-    }else{
-      @ %h(zFile)
-    }
-    @ added by %h(zUser) on
-    hyperlink_to_date(zDate, ".");
-    if( g.perm.WrWiki && g.perm.Attach ){
-      @ [%z(href("%R/attachdelete?page=%t&file=%t&from=%R/wiki%%3fname=%f",zPageName,zFile,zPageName))delete</a>]
-    }
-    @ </li>
-  }
-  if( cnt ){
-    @ </ul>
-  }
-  db_finalize(&q);
- 
+  attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
   manifest_destroy(pWiki);
   style_footer();
 }
+
+/*
+** Write a wiki artifact into the repository
+*/
+static void wiki_put(Blob *pWiki, int parent){
+  int nrid;
+  if( g.perm.ModWiki || db_get_boolean("modreq-wiki",0)==0 ){
+    nrid = content_put_ex(pWiki, 0, 0, 0, 0);
+    if( parent) content_deltify(parent, nrid, 0);
+  }else{
+    nrid = content_put_ex(pWiki, 0, 0, 0, 1);
+    moderation_table_create();
+    db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid);
+  }
+  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
+  db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid);
+  manifest_crosslink(nrid, pWiki);
+}
 
 /*
 ** WEBPAGE: wikiedit
 ** URL: /wikiedit?name=PAGENAME
 */
@@ -335,15 +322,11 @@
       }
       blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody);
       md5sum_blob(&wiki, &cksum);
       blob_appendf(&wiki, "Z %b\n", &cksum);
       blob_reset(&cksum);
-      nrid = content_put(&wiki);
-      db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
-      manifest_crosslink(nrid, &wiki);
-      assert( blob_is_reset(&wiki) );
-      content_deltify(rid, nrid, 0);
+      wiki_put(&wiki, 0);
     }
     db_end_transaction(0);
     cgi_redirectf("wiki?name=%T", zPageName);
   }
   if( P("cancel")!=0 ){
@@ -535,15 +518,11 @@
       appendRemark(&body);
       blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body));
       md5sum_blob(&wiki, &cksum);
       blob_appendf(&wiki, "Z %b\n", &cksum);
       blob_reset(&cksum);
-      nrid = content_put(&wiki);
-      db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
-      manifest_crosslink(nrid, &wiki);
-      assert( blob_is_reset(&wiki) );
-      content_deltify(rid, nrid, 0);
+      wiki_put(&wiki, rid);
       db_end_transaction(0);
     }
     cgi_redirectf("wiki?name=%T", zPageName);
   }
   if( P("cancel")!=0 ){
@@ -878,15 +857,11 @@
                 blob_str(pContent) );
   md5sum_blob(&wiki, &cksum);
   blob_appendf(&wiki, "Z %b\n", &cksum);
   blob_reset(&cksum);
   db_begin_transaction();
-  nrid = content_put( &wiki);
-  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
-  manifest_crosslink(nrid,&wiki);
-  assert( blob_is_reset(&wiki) );
-  content_deltify(rid,nrid,0);
+  wiki_put(&wiki, 0);
   db_end_transaction(0);
   return 1;
 }
 
 /*

Index: win/Makefile.dmc
==================================================================
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -26,13 +26,13 @@
 TCC    = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
 LIBS   = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
 
 SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
 
-SRC   = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c 
+SRC   = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c 
 
-OBJ   = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O 
+OBJ   = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O 
 
 
 RC=$(DMDIR)\bin\rcc
 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
 
@@ -46,11 +46,11 @@
 
 $(OBJDIR)\fossil.res:	$B\win\fossil.rc
 	$(RC) $(RCFLAGS) -o$@ $**
 
 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
-	+echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_tag json_timeline json_user json_wiki leaf login main manifest md5 merge merge3 name path pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
+	+echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_tag json_timeline json_user json_wiki leaf login main manifest md5 merge merge3 moderate name path pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
 	+echo fossil >> $@
 	+echo fossil >> $@
 	+echo $(LIBS) >> $@
 	+echo. >> $@
 	+echo fossil >> $@
@@ -463,10 +463,16 @@
 $(OBJDIR)\merge3$O : merge3_.c merge3.h
 	$(TCC) -o$@ -c merge3_.c
 
 merge3_.c : $(SRCDIR)\merge3.c
 	+translate$E $** > $@
+
+$(OBJDIR)\moderate$O : moderate_.c moderate.h
+	$(TCC) -o$@ -c moderate_.c
+
+moderate_.c : $(SRCDIR)\moderate.c
+	+translate$E $** > $@
 
 $(OBJDIR)\name$O : name_.c name.h
 	$(TCC) -o$@ -c name_.c
 
 name_.c : $(SRCDIR)\name.c
@@ -699,7 +705,7 @@
 
 zip_.c : $(SRCDIR)\zip.c
 	+translate$E $** > $@
 
 headers: makeheaders$E page_index.h VERSION.h
-	 +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
+	 +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
 	@copy /Y nul: headers

Index: win/Makefile.mingw
==================================================================
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -274,10 +274,11 @@
   $(SRCDIR)/main.c \
   $(SRCDIR)/manifest.c \
   $(SRCDIR)/md5.c \
   $(SRCDIR)/merge.c \
   $(SRCDIR)/merge3.c \
+  $(SRCDIR)/moderate.c \
   $(SRCDIR)/name.c \
   $(SRCDIR)/path.c \
   $(SRCDIR)/pivot.c \
   $(SRCDIR)/popen.c \
   $(SRCDIR)/pqueue.c \
@@ -374,10 +375,11 @@
   $(OBJDIR)/main_.c \
   $(OBJDIR)/manifest_.c \
   $(OBJDIR)/md5_.c \
   $(OBJDIR)/merge_.c \
   $(OBJDIR)/merge3_.c \
+  $(OBJDIR)/moderate_.c \
   $(OBJDIR)/name_.c \
   $(OBJDIR)/path_.c \
   $(OBJDIR)/pivot_.c \
   $(OBJDIR)/popen_.c \
   $(OBJDIR)/pqueue_.c \
@@ -474,10 +476,11 @@
  $(OBJDIR)/main.o \
  $(OBJDIR)/manifest.o \
  $(OBJDIR)/md5.o \
  $(OBJDIR)/merge.o \
  $(OBJDIR)/merge3.o \
+ $(OBJDIR)/moderate.o \
  $(OBJDIR)/name.o \
  $(OBJDIR)/path.o \
  $(OBJDIR)/pivot.o \
  $(OBJDIR)/popen.o \
  $(OBJDIR)/pqueue.o \
@@ -625,11 +628,11 @@
 
 $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
 	$(MKINDEX) $(TRANS_SRC) >$@
 
 $(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
-	$(MAKEHEADERS)  $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h $(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
+	$(MAKEHEADERS)  $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_config_.c:$(OBJDIR)/json_config.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_dir_.c:$(OBJDIR)/json_dir.h $(OBJDIR)/json_finfo_.c:$(OBJDIR)/json_finfo.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/wysiwyg_.c:$(OBJDIR)/wysiwyg.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
 	echo Done >$(OBJDIR)/headers
 
 $(OBJDIR)/headers: Makefile
 
 Makefile:
@@ -1103,10 +1106,18 @@
 
 $(OBJDIR)/merge3.o:	$(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h  $(SRCDIR)/config.h
 	$(XTCC) -o $(OBJDIR)/merge3.o -c $(OBJDIR)/merge3_.c
 
 $(OBJDIR)/merge3.h:	$(OBJDIR)/headers
+
+$(OBJDIR)/moderate_.c:	$(SRCDIR)/moderate.c $(OBJDIR)/translate
+	$(TRANSLATE) $(SRCDIR)/moderate.c >$(OBJDIR)/moderate_.c
+
+$(OBJDIR)/moderate.o:	$(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h  $(SRCDIR)/config.h
+	$(XTCC) -o $(OBJDIR)/moderate.o -c $(OBJDIR)/moderate_.c
+
+$(OBJDIR)/moderate.h:	$(OBJDIR)/headers
 
 $(OBJDIR)/name_.c:	$(SRCDIR)/name.c $(OBJDIR)/translate
 	$(TRANSLATE) $(SRCDIR)/name.c >$(OBJDIR)/name_.c
 
 $(OBJDIR)/name.o:	$(OBJDIR)/name_.c $(OBJDIR)/name.h  $(SRCDIR)/config.h

Index: win/Makefile.msc
==================================================================
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -40,13 +40,13 @@
 LIBS   = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
 LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
 
 SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 /DSQLITE_THREADSAFE=0 /DSQLITE_DEFAULT_FILE_FORMAT=4 /DSQLITE_ENABLE_STAT3 /Dlocaltime=fossil_localtime /DSQLITE_ENABLE_LOCKING_STYLE=0
 
-SRC   = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c 
+SRC   = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c 
 
-OBJ   = $(OX)\add$O $(OX)\allrepo$O $(OX)\attach$O $(OX)\bag$O $(OX)\bisect$O $(OX)\blob$O $(OX)\branch$O $(OX)\browse$O $(OX)\captcha$O $(OX)\cgi$O $(OX)\checkin$O $(OX)\checkout$O $(OX)\clearsign$O $(OX)\clone$O $(OX)\comformat$O $(OX)\configure$O $(OX)\content$O $(OX)\db$O $(OX)\delta$O $(OX)\deltacmd$O $(OX)\descendants$O $(OX)\diff$O $(OX)\diffcmd$O $(OX)\doc$O $(OX)\encode$O $(OX)\event$O $(OX)\export$O $(OX)\file$O $(OX)\finfo$O $(OX)\glob$O $(OX)\graph$O $(OX)\gzip$O $(OX)\http$O $(OX)\http_socket$O $(OX)\http_ssl$O $(OX)\http_transport$O $(OX)\import$O $(OX)\info$O $(OX)\json$O $(OX)\json_artifact$O $(OX)\json_branch$O $(OX)\json_config$O $(OX)\json_diff$O $(OX)\json_dir$O $(OX)\json_finfo$O $(OX)\json_login$O $(OX)\json_query$O $(OX)\json_report$O $(OX)\json_tag$O $(OX)\json_timeline$O $(OX)\json_user$O $(OX)\json_wiki$O $(OX)\leaf$O $(OX)\login$O $(OX)\main$O $(OX)\manifest$O $(OX)\md5$O $(OX)\merge$O $(OX)\merge3$O $(OX)\name$O $(OX)\path$O $(OX)\pivot$O $(OX)\popen$O $(OX)\pqueue$O $(OX)\printf$O $(OX)\rebuild$O $(OX)\report$O $(OX)\rss$O $(OX)\schema$O $(OX)\search$O $(OX)\setup$O $(OX)\sha1$O $(OX)\shun$O $(OX)\skins$O $(OX)\sqlcmd$O $(OX)\stash$O $(OX)\stat$O $(OX)\style$O $(OX)\sync$O $(OX)\tag$O $(OX)\tar$O $(OX)\th_main$O $(OX)\timeline$O $(OX)\tkt$O $(OX)\tktsetup$O $(OX)\undo$O $(OX)\update$O $(OX)\url$O $(OX)\user$O $(OX)\verify$O $(OX)\vfile$O $(OX)\wiki$O $(OX)\wikiformat$O $(OX)\winhttp$O $(OX)\wysiwyg$O $(OX)\xfer$O $(OX)\xfersetup$O $(OX)\zip$O $(OX)\shell$O $(OX)\sqlite3$O $(OX)\th$O $(OX)\th_lang$O 
+OBJ   = $(OX)\add$O $(OX)\allrepo$O $(OX)\attach$O $(OX)\bag$O $(OX)\bisect$O $(OX)\blob$O $(OX)\branch$O $(OX)\browse$O $(OX)\captcha$O $(OX)\cgi$O $(OX)\checkin$O $(OX)\checkout$O $(OX)\clearsign$O $(OX)\clone$O $(OX)\comformat$O $(OX)\configure$O $(OX)\content$O $(OX)\db$O $(OX)\delta$O $(OX)\deltacmd$O $(OX)\descendants$O $(OX)\diff$O $(OX)\diffcmd$O $(OX)\doc$O $(OX)\encode$O $(OX)\event$O $(OX)\export$O $(OX)\file$O $(OX)\finfo$O $(OX)\glob$O $(OX)\graph$O $(OX)\gzip$O $(OX)\http$O $(OX)\http_socket$O $(OX)\http_ssl$O $(OX)\http_transport$O $(OX)\import$O $(OX)\info$O $(OX)\json$O $(OX)\json_artifact$O $(OX)\json_branch$O $(OX)\json_config$O $(OX)\json_diff$O $(OX)\json_dir$O $(OX)\json_finfo$O $(OX)\json_login$O $(OX)\json_query$O $(OX)\json_report$O $(OX)\json_tag$O $(OX)\json_timeline$O $(OX)\json_user$O $(OX)\json_wiki$O $(OX)\leaf$O $(OX)\login$O $(OX)\main$O $(OX)\manifest$O $(OX)\md5$O $(OX)\merge$O $(OX)\merge3$O $(OX)\moderate$O $(OX)\name$O $(OX)\path$O $(OX)\pivot$O $(OX)\popen$O $(OX)\pqueue$O $(OX)\printf$O $(OX)\rebuild$O $(OX)\report$O $(OX)\rss$O $(OX)\schema$O $(OX)\search$O $(OX)\setup$O $(OX)\sha1$O $(OX)\shun$O $(OX)\skins$O $(OX)\sqlcmd$O $(OX)\stash$O $(OX)\stat$O $(OX)\style$O $(OX)\sync$O $(OX)\tag$O $(OX)\tar$O $(OX)\th_main$O $(OX)\timeline$O $(OX)\tkt$O $(OX)\tktsetup$O $(OX)\undo$O $(OX)\update$O $(OX)\url$O $(OX)\user$O $(OX)\verify$O $(OX)\vfile$O $(OX)\wiki$O $(OX)\wikiformat$O $(OX)\winhttp$O $(OX)\wysiwyg$O $(OX)\xfer$O $(OX)\xfersetup$O $(OX)\zip$O $(OX)\shell$O $(OX)\sqlite3$O $(OX)\th$O $(OX)\th_lang$O 
 
 
 APPNAME = $(OX)\fossil$(E)
 
 all: $(OX) $(APPNAME)
@@ -113,10 +113,11 @@
 	echo $(OX)\main.obj >> $@
 	echo $(OX)\manifest.obj >> $@
 	echo $(OX)\md5.obj >> $@
 	echo $(OX)\merge.obj >> $@
 	echo $(OX)\merge3.obj >> $@
+	echo $(OX)\moderate.obj >> $@
 	echo $(OX)\name.obj >> $@
 	echo $(OX)\path.obj >> $@
 	echo $(OX)\pivot.obj >> $@
 	echo $(OX)\popen.obj >> $@
 	echo $(OX)\pqueue.obj >> $@
@@ -573,10 +574,16 @@
 $(OX)\merge3$O : merge3_.c merge3.h
 	$(TCC) /Fo$@ -c merge3_.c
 
 merge3_.c : $(SRCDIR)\merge3.c
 	translate$E $** > $@
+
+$(OX)\moderate$O : moderate_.c moderate.h
+	$(TCC) /Fo$@ -c moderate_.c
+
+moderate_.c : $(SRCDIR)\moderate.c
+	translate$E $** > $@
 
 $(OX)\name$O : name_.c name.h
 	$(TCC) /Fo$@ -c name_.c
 
 name_.c : $(SRCDIR)\name.c
@@ -809,7 +816,7 @@
 
 zip_.c : $(SRCDIR)\zip.c
 	translate$E $** > $@
 
 headers: makeheaders$E page_index.h VERSION.h
-	makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
+	makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
 	@copy /Y nul: headers