Changes On Branch moderation
Not logged in

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

Changes In Branch moderation Excluding Merge-Ins

This is equivalent to a diff from fc0bffd995 to def256a23b

2012-11-02
02:36
Merge the "moderation" branch into trunk. This adds the ability to have an approval process for edits to Wiki and Tickets, including creating new Wiki and Tickets and adding attachments. Probably there are still some problems, but things are working well enough for trunk. check-in: ba418ee1ce user: drh tags: trunk
02:34
Make sure the MODREQ table exists before using it in the "modreq" webpage. Leaf check-in: def256a23b user: drh tags: moderation
02:26
Additional tweaks to the wiki/ticket editing logic with moderation control. check-in: d3d094ff55 user: drh tags: moderation
2012-11-01
03:44
Merge commit warning and looks_like_text() enhancements to trunk. Further changes based on these will occur on a branch. check-in: 6182584217 user: mistachkin tags: trunk
2012-10-31
20:49
Don't make it too complicated, too much risk to break other things. check-in: de5a2ef661 user: jan.nijtmans tags: improve_looks_like_binary
20:40
Initial infrastructure for adding the ability to moderate Wiki and Ticket changes. check-in: ef112586a0 user: drh tags: moderation
19:48
Add the wiki-moderator and ticket-moderator permissions. Not yet used. check-in: fc0bffd995 user: drh tags: trunk
14:56
Some more style en comment fixes, backported from [d57f0a9361], that I missed before. check-in: 1e2fdf98bc user: jan.nijtmans tags: trunk

Changes to src/attach.c.

    39     39     Blob sql;
    40     40     Stmt q;
    41     41   
    42     42     if( zPage && zTkt ) zTkt = 0;
    43     43     login_check_credentials();
    44     44     blob_zero(&sql);
    45     45     blob_append(&sql,
    46         -     "SELECT datetime(mtime,'localtime'), src, target, filename, comment, user"
           46  +     "SELECT datetime(mtime,'localtime'), src, target, filename,"
           47  +     "       comment, user,"
           48  +     "       (SELECT uuid FROM blob WHERE rid=attachid), attachid"
    47     49        "  FROM attachment",
    48     50        -1
    49     51     );
    50     52     if( zPage ){
    51     53       if( g.perm.RdWiki==0 ) login_needed();
    52     54       style_header("Attachments To %h", zPage);
    53     55       blob_appendf(&sql, " WHERE target=%Q", zPage);
................................................................................
    57     59       blob_appendf(&sql, " WHERE target GLOB '%q*'", zTkt);
    58     60     }else{
    59     61       if( g.perm.RdTkt==0 && g.perm.RdWiki==0 ) login_needed();
    60     62       style_header("All Attachments");
    61     63     }
    62     64     blob_appendf(&sql, " ORDER BY mtime DESC");
    63     65     db_prepare(&q, "%s", blob_str(&sql));
           66  +  @ <ol>
    64     67     while( db_step(&q)==SQLITE_ROW ){
    65     68       const char *zDate = db_column_text(&q, 0);
    66     69       const char *zSrc = db_column_text(&q, 1);
    67     70       const char *zTarget = db_column_text(&q, 2);
    68     71       const char *zFilename = db_column_text(&q, 3);
    69     72       const char *zComment = db_column_text(&q, 4);
    70     73       const char *zUser = db_column_text(&q, 5);
           74  +    const char *zUuid = db_column_text(&q, 6);
           75  +    int attachid = db_column_int(&q, 7);
    71     76       int i;
    72     77       char *zUrlTail;
    73     78       for(i=0; zFilename[i]; i++){
    74     79         if( zFilename[i]=='/' && zFilename[i+1]!=0 ){ 
    75     80           zFilename = &zFilename[i+1];
    76     81           i = -1;
    77     82         }
    78     83       }
    79     84       if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
    80     85         zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
    81     86       }else{
    82     87         zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
    83     88       }
    84         -    @
    85         -    @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
           89  +    @ <li><p>
           90  +    @ Attachment %z(href("%R/ainfo/%s",zUuid))%S(zUuid)</a>
           91  +    if( moderation_pending(attachid) ){
           92  +      @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
           93  +    }
           94  +    @ <br><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
    86     95       @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
    87     96       if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++;
    88     97       if( zComment && zComment[0] ){
    89     98         @ %w(zComment)<br />
    90     99       }
    91    100       if( zPage==0 && zTkt==0 ){
    92    101         if( zSrc==0 || zSrc[0]==0 ){
................................................................................
   109    118         }
   110    119       }
   111    120       @ by %h(zUser) on
   112    121       hyperlink_to_date(zDate, ".");
   113    122       free(zUrlTail);
   114    123     }
   115    124     db_finalize(&q);
          125  +  @ </ol>
   116    126     style_footer();
   117    127     return;
   118    128   }
   119    129   
   120    130   /*
   121    131   ** WEBPAGE: attachdownload
   122    132   ** WEBPAGE: attachimage
................................................................................
   180    190       artifact_page();
   181    191     }else{
   182    192       cgi_replace_parameter("m", mimetype_from_name(zFile));
   183    193       rawartifact_page();
   184    194     }
   185    195   }
   186    196   
          197  +/*
          198  +** Save an attachment control artifact into the repository
          199  +*/
          200  +static void attach_put(
          201  +  Blob *pAttach,     /* Text of the Attachment record */
          202  +  int attachRid,     /* RID for the file that is being attached */
          203  +  int needMod        /* True if the attachment is subject to moderation */
          204  +){
          205  +  int rid;
          206  +  if( needMod ){
          207  +    rid = content_put_ex(pAttach, 0, 0, 0, 1);
          208  +    moderation_table_create();
          209  +    db_multi_exec(
          210  +      "INSERT INTO modreq(objid,attachRid) VALUES(%d,%d);",
          211  +      rid, attachRid
          212  +    );
          213  +  }else{
          214  +    rid = content_put(pAttach);
          215  +    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
          216  +    db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
          217  +  }
          218  +  manifest_crosslink(rid, pAttach);
          219  +}
          220  +
   187    221   
   188    222   /*
   189    223   ** WEBPAGE: attachadd
   190    224   **
   191    225   **    tkt=TICKETUUID
   192    226   **    page=WIKIPAGE
   193    227   **    from=URL
................................................................................
   238    272       char *zUUID;
   239    273       const char *zComment;
   240    274       char *zDate;
   241    275       int rid;
   242    276       int i, n;
   243    277       int addCompress = 0;
   244    278       Manifest *pManifest;
          279  +    int needModerator;
   245    280   
   246    281       db_begin_transaction();
   247    282       blob_init(&content, aContent, szContent);
   248    283       pManifest = manifest_parse(&content, 0, 0);
   249    284       manifest_destroy(pManifest);
   250    285       blob_init(&content, aContent, szContent);
   251    286       if( pManifest ){
   252    287         blob_compress(&content, &content);
   253    288         addCompress = 1;
   254    289       }
   255         -    rid = content_put(&content);
          290  +    needModerator =
          291  +         (zTkt!=0 && g.perm.ModTkt==0 && db_get_boolean("modreq-tkt",0)==1) ||
          292  +         (zPage!=0 && g.perm.ModWiki==0 && db_get_boolean("modreq-wiki",0)==1);
          293  +    rid = content_put_ex(&content, 0, 0, 0, needModerator);
   256    294       zUUID = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
   257    295       blob_zero(&manifest);
   258    296       for(i=n=0; zName[i]; i++){
   259    297         if( zName[i]=='/' || zName[i]=='\\' ) n = i;
   260    298       }
   261    299       zName += n;
   262    300       if( zName[0]==0 ) zName = "unknown";
................................................................................
   270    308         blob_appendf(&manifest, "C %F\n", zComment);
   271    309       }
   272    310       zDate = date_in_standard_format("now");
   273    311       blob_appendf(&manifest, "D %s\n", zDate);
   274    312       blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody");
   275    313       md5sum_blob(&manifest, &cksum);
   276    314       blob_appendf(&manifest, "Z %b\n", &cksum);
   277         -    rid = content_put(&manifest);
   278         -    manifest_crosslink(rid, &manifest);
          315  +    attach_put(&manifest, rid, needModerator);
   279    316       assert( blob_is_reset(&manifest) );
   280    317       db_end_transaction(0);
   281    318       cgi_redirect(zFrom);
   282    319     }
   283    320     style_header("Add Attachment");
   284    321     @ <h2>Add Attachment To %s(zTargetType)</h2>
   285    322     @ <form action="%s(g.zTop)/attachadd" method="post"
................................................................................
   296    333     @ <input type="hidden" name="from" value="%h(zFrom)" />
   297    334     @ <input type="submit" name="ok" value="Add Attachment" />
   298    335     @ <input type="submit" name="cancel" value="Cancel" />
   299    336     @ </div></form>
   300    337     style_footer();
   301    338   }
   302    339   
   303         -
   304    340   /*
   305         -** WEBPAGE: attachdelete
          341  +** WEBPAGE: ainfo
          342  +** URL: /ainfo?name=ARTIFACTID
   306    343   **
   307         -**    tkt=TICKETUUID
   308         -**    page=WIKIPAGE
   309         -**    file=FILENAME
   310         -**
   311         -** "Delete" an attachment.  Because objects in Fossil are immutable
   312         -** the attachment isn't really deleted.  Instead, we change the content
   313         -** of the attachment to NULL, which the system understands as being
   314         -** deleted.  Historical values of the attachment are preserved.
          344  +** Show the details of an attachment artifact.
   315    345   */
   316         -void attachdel_page(void){
   317         -  const char *zPage = P("page");
   318         -  const char *zTkt = P("tkt");
   319         -  const char *zFile = P("file");
   320         -  const char *zFrom = P("from");
   321         -  const char *zTarget;
          346  +void ainfo_page(void){
          347  +  int rid;                       /* RID for the control artifact */
          348  +  int ridSrc;                    /* RID for the attached file */
          349  +  char *zDate;                   /* Date attached */
          350  +  const char *zUuid;             /* UUID of the control artifact */
          351  +  Manifest *pAttach;             /* Parse of the control artifact */
          352  +  const char *zTarget;           /* Wiki or ticket attached to */
          353  +  const char *zSrc;              /* UUID of the attached file */
          354  +  const char *zName;             /* Name of the attached file */
          355  +  const char *zDesc;             /* Description of the attached file */
          356  +  const char *zWikiName = 0;     /* Wiki page name when attached to Wiki */
          357  +  const char *zTktUuid = 0;      /* Ticket ID when attached to a ticket */
          358  +  int modPending;                /* True if awaiting moderation */
          359  +  const char *zModAction;        /* Moderation action or NULL */
          360  +  int isModerator;               /* TRUE if user is the moderator */
          361  +  const char *zMime;             /* MIME Type */
          362  +  Blob attach;                   /* Content of the attachment */
          363  +  int wantToDelete = P("del")!=0;/* Want to delete */
   322    364   
   323         -  if( zPage && zTkt ) fossil_redirect_home();
   324         -  if( zPage==0 && zTkt==0 ) fossil_redirect_home();
   325         -  if( zFile==0 ) fossil_redirect_home();
   326    365     login_check_credentials();
   327         -  if( zPage ){
   328         -    if( g.perm.WrWiki==0 || g.perm.Attach==0 ) login_needed();
   329         -    zTarget = zPage;
   330         -  }else{
   331         -    if( g.perm.WrTkt==0 || g.perm.Attach==0 ) login_needed();
   332         -    zTarget = zTkt;
   333         -  }
   334         -  if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
   335         -  if( P("cancel") ){
   336         -    cgi_redirect(zFrom);
   337         -  }
   338         -  if( P("confirm") ){
          366  +  if( !g.perm.RdTkt && !g.perm.RdWiki ){ login_needed(); return; }
          367  +  rid = name_to_rid_www("name");
          368  +  if( rid==0 ){ fossil_redirect_home(); }
          369  +  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
          370  +#if 0
          371  +  /* Shunning here needs to get both the attachment control artifact and
          372  +  ** the object that is attached. */
          373  +  if( g.perm.Admin ){
          374  +    if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
          375  +      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
          376  +            g.zTop, zUuid);
          377  +    }else{
          378  +      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
          379  +            g.zTop, zUuid);
          380  +    }
          381  +  }
          382  +#endif
          383  +  pAttach = manifest_get(rid, CFTYPE_ATTACHMENT);
          384  +  if( pAttach==0 ) fossil_redirect_home();
          385  +  zTarget = pAttach->zAttachTarget;
          386  +  zSrc = pAttach->zAttachSrc;
          387  +  ridSrc = db_int(0,"SELECT rid FROM blob WHERE uuid='%s'", zSrc);
          388  +  zName = pAttach->zAttachName;
          389  +  zDesc = pAttach->zComment;
          390  +  if( validate16(zTarget, strlen(zTarget))
          391  +   && db_exists("SELECT 1 FROM ticket WHERE tkt_uuid='%s'", zTarget)
          392  +  ){
          393  +    zTktUuid = zTarget;
          394  +    if( !g.perm.RdTkt ){ login_needed(); return; }
          395  +    if( g.perm.WrTkt ){
          396  +      style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
          397  +    }
          398  +  }else if( db_exists("SELECT 1 FROM tag WHERE tagname='wiki-%q'",zTarget) ){
          399  +    zWikiName = zTarget;
          400  +    if( !g.perm.RdWiki ){ login_needed(); return; }
          401  +    if( g.perm.WrWiki ){
          402  +      style_submenu_element("Delete","Delete","%R/ainfo/%s?del", zUuid);
          403  +    }
          404  +  }
          405  +  zDate = db_text(0, "SELECT datetime(%.12f)", pAttach->rDate);
          406  +
          407  +  if( P("confirm")
          408  +   && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
          409  +  ){
   339    410       int i, n, rid;
   340    411       char *zDate;
   341    412       Blob manifest;
   342    413       Blob cksum;
          414  +    const char *zFile = zName;
   343    415   
   344    416       db_begin_transaction();
   345    417       blob_zero(&manifest);
   346    418       for(i=n=0; zFile[i]; i++){
   347    419         if( zFile[i]=='/' || zFile[i]=='\\' ) n = i;
   348    420       }
   349    421       zFile += n;
................................................................................
   353    425       blob_appendf(&manifest, "D %s\n", zDate);
   354    426       blob_appendf(&manifest, "U %F\n", g.zLogin ? g.zLogin : "nobody");
   355    427       md5sum_blob(&manifest, &cksum);
   356    428       blob_appendf(&manifest, "Z %b\n", &cksum);
   357    429       rid = content_put(&manifest);
   358    430       manifest_crosslink(rid, &manifest);
   359    431       db_end_transaction(0);
   360         -    cgi_redirect(zFrom);
   361         -  }    
   362         -  style_header("Delete Attachment");
   363         -  @ <form action="%s(g.zTop)/attachdelete" method="post"><div>
   364         -  @ <p>Confirm that you want to delete the attachment named
   365         -  @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br /></p>
   366         -  if( zTkt ){
   367         -    @ <input type="hidden" name="tkt" value="%h(zTkt)" />
          432  +    @ <p>The attachment below has been deleted.</p>
          433  +  }
          434  +
          435  +  if( P("del")
          436  +   && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
          437  +  ){
          438  +    @ <form method="post" action="%R/ainfo/%s(zUuid)">
          439  +    @ <p>Confirm you want to delete the attachment shown below.
          440  +    @ <input type="submit" name="confirm" value="Confirm">
          441  +    @ </form>
          442  +  }
          443  +
          444  +  isModerator = (zTktUuid && g.perm.ModTkt) || (zWikiName && g.perm.ModWiki);
          445  +  if( isModerator && (zModAction = P("modaction"))!=0 ){
          446  +    if( strcmp(zModAction,"delete")==0 ){
          447  +      moderation_disapprove(rid);
          448  +      if( zTktUuid ){
          449  +        cgi_redirectf("%R/tktview/%s", zTktUuid);
          450  +      }else{
          451  +        cgi_redirectf("%R/wiki?name=%t", zWikiName);
          452  +      }
          453  +      return;
          454  +    }
          455  +    if( strcmp(zModAction,"approve")==0 ){
          456  +      moderation_approve(rid);
          457  +    }
          458  +  }
          459  +  style_header("Attachment Details");
          460  +  style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid);
          461  +
          462  +  @ <div class="section">Overview</div>
          463  +  @ <p><table class="label-value">
          464  +  @ <tr><th>Artifact&nbsp;ID:</th>
          465  +  @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
          466  +  if( g.perm.Setup ){
          467  +    @ (%d(rid))
          468  +  }
          469  +  modPending = moderation_pending(rid);
          470  +  if( modPending ){
          471  +    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
          472  +  }
          473  +  if( zTktUuid ){
          474  +    @ <tr><th>Ticket:</th>
          475  +    @ <td>%z(href("%R/tktview/%s",zTktUuid))%s(zTktUuid)</a></td></tr>
          476  +  }
          477  +  if( zWikiName ){
          478  +    @ <tr><th>Wiki&nbsp;Page:</th>
          479  +    @ <td>%z(href("%R/wiki?name=%t",zWikiName))%h(zWikiName)</a></td></tr>
          480  +  }
          481  +  @ <tr><th>Date:</th><td>
          482  +  hyperlink_to_date(zDate, "</td></tr>");
          483  +  free(zDate);
          484  +  @ <tr><th>User:</th><td>
          485  +  hyperlink_to_user(pAttach->zUser, zDate, "</td></tr>");
          486  +  @ <tr><th>Artifact&nbsp;Attached:</th>
          487  +  @ <td>%z(href("%R/artifact/%s",zSrc))%s(zSrc)</a>
          488  +  if( g.perm.Setup ){
          489  +    @ (%d(ridSrc))
          490  +  }
          491  +  @ <tr><th>Filename:</th><td>%h(zName)</td></tr>
          492  +  zMime = mimetype_from_name(zName);
          493  +  if( g.perm.Setup ){
          494  +    @ <tr><th>MIME-Type:</th><td>%h(zMime)</td></tr>
          495  +  }
          496  +  @ <tr><th valign="top">Description:</th><td valign="top">%h(zDesc)</td></tr>
          497  +  @ </table>
          498  +  
          499  +  if( isModerator && modPending ){
          500  +    @ <div class="section">Moderation</div>
          501  +    @ <blockquote>
          502  +    @ <form method="POST" action="%R/ainfo/%s(zUuid)">
          503  +    @ <label><input type="radio" name="modaction" value="delete">
          504  +    @ Delete this change</label><br />
          505  +    @ <label><input type="radio" name="modaction" value="approve">
          506  +    @ Approve this change</label><br />
          507  +    @ <input type="submit" value="Submit">
          508  +    @ </form>
          509  +    @ </blockquote>
          510  +  }
          511  +
          512  +  @ <div class="section">Content Appended</div>
          513  +  @ <blockquote>
          514  +  blob_zero(&attach);
          515  +  if( zMime==0 || strncmp(zMime,"text/", 5)==0 ){
          516  +    const char *z;
          517  +    const char *zLn = P("ln");
          518  +    content_get(ridSrc, &attach);
          519  +    blob_strip_bom(&attach, 0);
          520  +    z = blob_str(&attach);
          521  +    if( zLn ){
          522  +      output_text_with_line_numbers(z, zLn);
          523  +    }else{
          524  +      @ <pre>
          525  +      @ %h(z)
          526  +      @ </pre>
          527  +    }
          528  +  }else if( strncmp(zMime, "image/", 6)==0 ){
          529  +    @ <img src="%R/raw?name=%s(zSrc)&m=%s(zMime)"></img>
   368    530     }else{
   369         -    @ <input type="hidden" name="page" value="%h(zPage)" />
          531  +    int sz = db_int(0, "SELECT sz FROM blob WHERE rid=%d", ridSrc);
          532  +    @ <i>(file is %d(sz) bytes of binary data)</i>
   370    533     }
   371         -  @ <input type="hidden" name="file" value="%h(zFile)" />
   372         -  @ <input type="hidden" name="from" value="%h(zFrom)" />
   373         -  @ <input type="submit" name="confirm" value="Delete" />
   374         -  @ <input type="submit" name="cancel" value="Cancel" />
   375         -  @ </div></form>
          534  +  @ </blockquote>
          535  +  manifest_destroy(pAttach);
          536  +  blob_reset(&attach);
   376    537     style_footer();
          538  +}
   377    539   
          540  +/*
          541  +** Output HTML to show a list of attachments.
          542  +*/
          543  +void attachment_list(
          544  +  const char *zTarget,   /* Object that things are attached to */
          545  +  const char *zHeader    /* Header to display with attachments */
          546  +){
          547  +  int cnt = 0;
          548  +  Stmt q;
          549  +  db_prepare(&q,
          550  +     "SELECT datetime(mtime,'localtime'), filename, user,"
          551  +     "       (SELECT uuid FROM blob WHERE rid=attachid), src"
          552  +     "  FROM attachment"
          553  +     " WHERE isLatest AND src!='' AND target=%Q"
          554  +     " ORDER BY mtime DESC", 
          555  +     zTarget
          556  +  );
          557  +  while( db_step(&q)==SQLITE_ROW ){
          558  +    const char *zDate = db_column_text(&q, 0);
          559  +    const char *zFile = db_column_text(&q, 1);
          560  +    const char *zUser = db_column_text(&q, 2);
          561  +    const char *zUuid = db_column_text(&q, 3);
          562  +    const char *zSrc = db_column_text(&q, 4);
          563  +    if( cnt==0 ){
          564  +      @ %s(zHeader)
          565  +    }
          566  +    cnt++;
          567  +    @ <li>
          568  +    @ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
          569  +    @ added by %h(zUser) on
          570  +    hyperlink_to_date(zDate, ".");
          571  +    @ [%z(href("%R/ainfo/%s",zUuid))details</a>]
          572  +    @ </li>
          573  +  }
          574  +  if( cnt ){
          575  +    @ </ul>
          576  +  }
          577  +  db_finalize(&q);
          578  +  
   378    579   }

Changes to src/clone.c.

    70     70   */
    71     71   void delete_private_content(void){
    72     72     fix_private_blob_dependencies(1);
    73     73     db_multi_exec(
    74     74       "DELETE FROM blob WHERE rid IN private;"
    75     75       "DELETE FROM delta wHERE rid IN private;"
    76     76       "DELETE FROM private;"
           77  +    "DROP TABLE IF EXISTS modreq;"
    77     78     );
    78     79   }
    79     80   
    80     81   
    81     82   /*
    82     83   ** COMMAND: clone
    83     84   **

Changes to src/info.c.

   711    711       db_finalize(&q);
   712    712     }
   713    713     style_footer();
   714    714   }
   715    715   
   716    716   /*
   717    717   ** WEBPAGE: winfo
   718         -** URL:  /winfo?name=RID
          718  +** URL:  /winfo?name=UUID
   719    719   **
   720    720   ** Return information about a wiki page.
   721    721   */
   722    722   void winfo_page(void){
   723         -  Stmt q;
   724    723     int rid;
          724  +  Manifest *pWiki;
          725  +  char *zUuid;
          726  +  char *zDate;
          727  +  Blob wiki;
          728  +  int modPending;
          729  +  const char *zModAction;
   725    730   
   726    731     login_check_credentials();
   727    732     if( !g.perm.RdWiki ){ login_needed(); return; }
   728    733     rid = name_to_rid_www("name");
   729         -  if( rid==0 ){
          734  +  if( rid==0 || (pWiki = manifest_get(rid, CFTYPE_WIKI))==0 ){
   730    735       style_header("Wiki Page Information Error");
   731         -    @ No such object: %h(g.argv[2])
          736  +    @ No such object: %h(P("name"))
   732    737       style_footer();
   733    738       return;
   734    739     }
   735         -  db_prepare(&q,
   736         -     "SELECT substr(tagname, 6, 1000), uuid,"
   737         -     "       datetime(event.mtime, 'localtime'), user"
   738         -     "  FROM tagxref, tag, blob, event"
   739         -     " WHERE tagxref.rid=%d"
   740         -     "   AND tag.tagid=tagxref.tagid"
   741         -     "   AND tag.tagname LIKE 'wiki-%%'"
   742         -     "   AND blob.rid=%d"
   743         -     "   AND event.objid=%d",
   744         -     rid, rid, rid
   745         -  );
   746         -  if( db_step(&q)==SQLITE_ROW ){
   747         -    const char *zName = db_column_text(&q, 0);
   748         -    const char *zUuid = db_column_text(&q, 1);
   749         -    char *zTitle = mprintf("Wiki Page %s", zName);
   750         -    const char *zDate = db_column_text(&q,2);
   751         -    const char *zUser = db_column_text(&q,3);
   752         -    style_header(zTitle);
   753         -    free(zTitle);
   754         -    login_anonymous_available();
   755         -    @ <div class="section">Overview</div>
   756         -    @ <p><table class="label-value">
   757         -    @ <tr><th>Version:</th><td>%s(zUuid)</td></tr>
   758         -    @ <tr><th>Date:</th><td>
   759         -    hyperlink_to_date(zDate, "</td></tr>");
   760         -    if( g.perm.Setup ){
   761         -      @ <tr><th>Record ID:</th><td>%d(rid)</td></tr>
   762         -    }
   763         -    @ <tr><th>Original&nbsp;User:</th><td>
   764         -    hyperlink_to_user(zUser, zDate, "</td></tr>");
   765         -    if( g.perm.Hyperlink ){
   766         -      @ <tr><th>Commands:</th>
   767         -      @   <td>
   768         -      @     %z(href("%R/whistory?name=%t",zName))history</a>
   769         -      @     | %z(href("%R/artifact/%S",zUuid))raw-text</a>
   770         -      @   </td>
   771         -      @ </tr>
   772         -    }
   773         -    @ </table></p>
   774         -  }else{
   775         -    style_header("Wiki Information");
   776         -    rid = 0;
   777         -  }
   778         -  db_finalize(&q);
   779         -  showTags(rid, "wiki-*");
   780         -  if( rid ){
   781         -    Manifest *pWiki;
   782         -    pWiki = manifest_get(rid, CFTYPE_WIKI);
   783         -    if( pWiki ){
   784         -      Blob wiki;
   785         -      blob_init(&wiki, pWiki->zWiki, -1);
   786         -      @ <div class="section">Content</div>
   787         -      wiki_convert(&wiki, 0, 0);
   788         -      blob_reset(&wiki);
   789         -    }
   790         -    manifest_destroy(pWiki);
   791         -  }
          740  +  if( g.perm.ModWiki && (zModAction = P("modaction"))!=0 ){
          741  +    if( strcmp(zModAction,"delete")==0 ){
          742  +      moderation_disapprove(rid);
          743  +      cgi_redirectf("%R/wiki?name=%T", pWiki->zWikiTitle);
          744  +      /*NOTREACHED*/
          745  +    }
          746  +    if( strcmp(zModAction,"approve")==0 ){
          747  +      moderation_approve(rid);
          748  +    }
          749  +  }
          750  +  style_header("Update of \"%h\"", pWiki->zWikiTitle);
          751  +  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
          752  +  zDate = db_text(0, "SELECT datetime(%.17g)", pWiki->rDate);
          753  +  style_submenu_element("Raw", "Raw", "artifact/%S", zUuid);
          754  +  style_submenu_element("History", "History", "whistory?name=%t",
          755  +                        pWiki->zWikiTitle);
          756  +  style_submenu_element("Page", "Page", "wiki?name=%t",
          757  +                        pWiki->zWikiTitle);
          758  +  login_anonymous_available();
          759  +  @ <div class="section">Overview</div>
          760  +  @ <p><table class="label-value">
          761  +  @ <tr><th>Artifact&nbsp;ID:</th>
          762  +  @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
          763  +  if( g.perm.Setup ){
          764  +    @ (%d(rid))
          765  +  }
          766  +  modPending = moderation_pending(rid);
          767  +  if( modPending ){
          768  +    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
          769  +  }
          770  +  @ </td></tr>
          771  +  @ <tr><th>Page&nbsp;Name:</th><td>%h(pWiki->zWikiTitle)</td></tr>
          772  +  @ <tr><th>Date:</th><td>
          773  +  hyperlink_to_date(zDate, "</td></tr>");
          774  +  @ <tr><th>Original&nbsp;User:</th><td>
          775  +  hyperlink_to_user(pWiki->zUser, zDate, "</td></tr>");
          776  +  if( pWiki->nParent>0 ){
          777  +    int i;
          778  +    @ <tr><th>Parent%s(pWiki->nParent==1?"":"s"):</th><td>
          779  +    for(i=0; i<pWiki->nParent; i++){
          780  +      char *zParent = pWiki->azParent[i];
          781  +      @ %z(href("info/%S",zParent))%s(zParent)</a>
          782  +    }
          783  +    @ </td></tr>
          784  +  }
          785  +  @ </table>
          786  +
          787  +  if( g.perm.ModWiki && modPending ){
          788  +    @ <div class="section">Moderation</div>
          789  +    @ <blockquote>
          790  +    @ <form method="POST" action="%R/winfo/%s(zUuid)">
          791  +    @ <label><input type="radio" name="modaction" value="delete">
          792  +    @ Delete this change</label><br />
          793  +    @ <label><input type="radio" name="modaction" value="approve">
          794  +    @ Approve this change</label><br />
          795  +    @ <input type="submit" value="Submit">
          796  +    @ </form>
          797  +    @ </blockquote>
          798  +  }
          799  +
          800  +
          801  +  @ <div class="section">Content</div>
          802  +  blob_init(&wiki, pWiki->zWiki, -1);
          803  +  wiki_convert(&wiki, 0, 0);
          804  +  blob_reset(&wiki);
          805  +  manifest_destroy(pWiki);
   792    806     style_footer();
   793    807   }
   794    808   
   795    809   /*
   796    810   ** Show a webpage error message
   797    811   */
   798    812   void webpage_error(const char *zFormat, ...){
................................................................................
   991   1005     }
   992   1006     manifest_destroy(pFrom);
   993   1007     manifest_destroy(pTo);
   994   1008   
   995   1009     style_footer();
   996   1010   }
   997   1011   
         1012  +#if INTERFACE
         1013  +/*
         1014  +** Possible return values from object_description()
         1015  +*/
         1016  +#define OBJTYPE_CHECKIN    0x0001
         1017  +#define OBJTYPE_CONTENT    0x0002
         1018  +#define OBJTYPE_WIKI       0x0004
         1019  +#define OBJTYPE_TICKET     0x0008
         1020  +#define OBJTYPE_ATTACHMENT 0x0010
         1021  +#define OBJTYPE_EVENT      0x0020
         1022  +#define OBJTYPE_TAG        0x0040
         1023  +#define OBJTYPE_SYMLINK    0x0080
         1024  +#define OBJTYPE_EXE        0x0100
         1025  +#endif
         1026  +
   998   1027   /*
   999   1028   ** Write a description of an object to the www reply.
  1000   1029   **
  1001   1030   ** If the object is a file then mention:
  1002   1031   **
  1003   1032   **     * It's artifact ID
  1004   1033   **     * All its filenames
................................................................................
  1006   1035   **
  1007   1036   ** If the object is a manifest, then mention:
  1008   1037   **
  1009   1038   **     * It's artifact ID
  1010   1039   **     * date of check-in
  1011   1040   **     * Comment & user
  1012   1041   */
  1013         -void object_description(
         1042  +int object_description(
  1014   1043     int rid,                 /* The artifact ID */
  1015   1044     int linkToView,          /* Add viewer link if true */
  1016   1045     Blob *pDownloadName      /* Fill with an appropriate download name */
  1017   1046   ){
  1018   1047     Stmt q;
  1019   1048     int cnt = 0;
  1020   1049     int nWiki = 0;
         1050  +  int objType = 0;
  1021   1051     char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  1022   1052   
  1023   1053     char *prevName = 0;
  1024   1054   
  1025   1055     db_prepare(&q,
  1026   1056       "SELECT filename.name, datetime(event.mtime),"
  1027   1057       "       coalesce(event.ecomment,event.comment),"
................................................................................
  1049   1079       const char *zBr = db_column_text(&q, 6);
  1050   1080       if( !prevName || fossil_strcmp(zName, prevName) ) {
  1051   1081         if( prevName ) {
  1052   1082           @ </ul>
  1053   1083         }
  1054   1084         if( mPerm==PERM_LNK ){
  1055   1085           @ <li>Symbolic link
         1086  +        objType |= OBJTYPE_SYMLINK;
  1056   1087         }else if( mPerm==PERM_EXE ){
  1057   1088           @ <li>Executable file
         1089  +        objType |= OBJTYPE_EXE;
  1058   1090         }else{
  1059   1091           @ <li>File
  1060   1092         }
         1093  +      objType |= OBJTYPE_CONTENT;
  1061   1094         @ %z(href("%R/finfo?name=%T",zName))%h(zName)</a>
  1062   1095         @ <ul>
  1063   1096         prevName = fossil_strdup(zName);
  1064   1097       }
  1065   1098       @ <li>
  1066   1099       hyperlink_to_date(zDate,"");
  1067   1100       @ - part of checkin
................................................................................
  1098   1131       const char *zDate = db_column_text(&q, 1);
  1099   1132       const char *zUser = db_column_text(&q, 2);
  1100   1133       if( cnt>0 ){
  1101   1134         @ Also wiki page
  1102   1135       }else{
  1103   1136         @ Wiki page
  1104   1137       }
         1138  +    objType |= OBJTYPE_WIKI;
  1105   1139       @ [%z(href("%R/wiki?name=%t",zPagename))%h(zPagename)</a>] by
  1106   1140       hyperlink_to_user(zUser,zDate," on");
  1107   1141       hyperlink_to_date(zDate,".");
  1108   1142       nWiki++;
  1109   1143       cnt++;
  1110   1144       if( pDownloadName && blob_size(pDownloadName)==0 ){
  1111         -      blob_appendf(pDownloadName, "%s.wiki", zPagename);
         1145  +      blob_appendf(pDownloadName, "%s.txt", zPagename);
  1112   1146       }
  1113   1147     }
  1114   1148     db_finalize(&q);
  1115   1149     if( nWiki==0 ){
  1116   1150       db_prepare(&q,
  1117   1151         "SELECT datetime(mtime), user, comment, type, uuid, tagid"
  1118   1152         "  FROM event, blob"
................................................................................
  1127   1161         const char *zType = db_column_text(&q, 3);
  1128   1162         const char *zUuid = db_column_text(&q, 4);
  1129   1163         if( cnt>0 ){
  1130   1164           @ Also
  1131   1165         }
  1132   1166         if( zType[0]=='w' ){
  1133   1167           @ Wiki edit
         1168  +        objType |= OBJTYPE_WIKI;
  1134   1169         }else if( zType[0]=='t' ){
  1135   1170           @ Ticket change
         1171  +        objType |= OBJTYPE_TICKET;
  1136   1172         }else if( zType[0]=='c' ){
  1137   1173           @ Manifest of check-in
         1174  +        objType |= OBJTYPE_CHECKIN;
  1138   1175         }else if( zType[0]=='e' ){
  1139   1176           @ Instance of event
         1177  +        objType |= OBJTYPE_EVENT;
  1140   1178           hyperlink_to_event_tagid(db_column_int(&q, 5));
  1141   1179         }else{
  1142   1180           @ Control file referencing
  1143   1181         }
  1144   1182         if( zType[0]!='e' ){
  1145   1183           hyperlink_to_uuid(zUuid);
  1146   1184         }
................................................................................
  1168   1206       const char *zUser = db_column_text(&q, 3);
  1169   1207       /* const char *zSrc = db_column_text(&q, 4); */
  1170   1208       if( cnt>0 ){
  1171   1209         @ Also attachment "%h(zFilename)" to
  1172   1210       }else{
  1173   1211         @ Attachment "%h(zFilename)" to
  1174   1212       }
         1213  +    objType |= OBJTYPE_ATTACHMENT;
  1175   1214       if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
  1176   1215         if( g.perm.Hyperlink && g.perm.RdTkt ){
  1177   1216           @ ticket [%z(href("%R/tktview?name=%S",zTarget))%S(zTarget)</a>]
  1178   1217         }else{
  1179   1218           @ ticket [%S(zTarget)]
  1180   1219         }
  1181   1220       }else{
................................................................................
  1198   1237       @ Control artifact.
  1199   1238       if( pDownloadName && blob_size(pDownloadName)==0 ){
  1200   1239         blob_appendf(pDownloadName, "%.10s.txt", zUuid);
  1201   1240       }
  1202   1241     }else if( linkToView && g.perm.Hyperlink ){
  1203   1242       @ %z(href("%R/artifact/%S",zUuid))[view]</a>
  1204   1243     }
         1244  +  return objType;
  1205   1245   }
  1206   1246   
  1207   1247   
  1208   1248   /*
  1209   1249   ** WEBPAGE: fdiff
  1210   1250   ** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN
  1211   1251   **
................................................................................
  1397   1437       }else{
  1398   1438         style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
  1399   1439               g.zTop, zUuid);
  1400   1440       }
  1401   1441     }
  1402   1442     style_header("Hex Artifact Content");
  1403   1443     zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
  1404         -  @ <h2>Artifact %s(zUuid):</h2>
         1444  +  if( g.perm.Setup ){
         1445  +    @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
         1446  +  }else{
         1447  +    @ <h2>Artifact %s(zUuid):</h2>
         1448  +  }
  1405   1449     blob_zero(&downloadName);
  1406   1450     object_description(rid, 0, &downloadName);
  1407   1451     style_submenu_element("Download", "Download",
  1408   1452           "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
  1409   1453     @ <hr />
  1410   1454     content_get(rid, &content);
  1411   1455     @ <blockquote><pre>
................................................................................
  1447   1491   ** The "z" argument is a string that contains the text of a source code
  1448   1492   ** file.  This routine appends that text to the HTTP reply with line numbering.
  1449   1493   **
  1450   1494   ** zLn is the ?ln= parameter for the HTTP query.  If there is an argument,
  1451   1495   ** then highlight that line number and scroll to it once the page loads.
  1452   1496   ** If there are two line numbers, highlight the range of lines.
  1453   1497   */
  1454         -static void output_text_with_line_numbers(
         1498  +void output_text_with_line_numbers(
  1455   1499     const char *z,
  1456   1500     const char *zLn
  1457   1501   ){
  1458   1502     int iStart, iEnd;    /* Start and end of region to highlight */
  1459   1503     int n = 0;           /* Current line number */
  1460   1504     int i;               /* Loop index */
  1461   1505     int iTop = 0;        /* Scroll so that this line is on top of screen. */
................................................................................
  1517   1561   void artifact_page(void){
  1518   1562     int rid = 0;
  1519   1563     Blob content;
  1520   1564     const char *zMime;
  1521   1565     Blob downloadName;
  1522   1566     int renderAsWiki = 0;
  1523   1567     int renderAsHtml = 0;
         1568  +  int objType;
         1569  +  int asText;
  1524   1570     const char *zUuid;
         1571  +  Manifest *pManifest;
         1572  +
  1525   1573     if( P("ci") && P("filename") ){
  1526   1574       rid = artifact_from_ci_and_filename();
  1527   1575     }
  1528   1576     if( rid==0 ){
  1529   1577       rid = name_to_rid_www("name");
  1530   1578     }
  1531   1579   
................................................................................
  1540   1588       }else{
  1541   1589         style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
  1542   1590               g.zTop, zUuid);
  1543   1591       }
  1544   1592     }
  1545   1593     style_header("Artifact Content");
  1546   1594     zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
  1547         -  @ <h2>Artifact %s(zUuid)</h2>
         1595  +  if( g.perm.Setup ){
         1596  +    @ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
         1597  +  }else{
         1598  +    @ <h2>Artifact %s(zUuid):</h2>
         1599  +  }
  1548   1600     blob_zero(&downloadName);
  1549         -  object_description(rid, 0, &downloadName);
         1601  +  objType = object_description(rid, 0, &downloadName);
  1550   1602     style_submenu_element("Download", "Download",
  1551   1603             "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
         1604  +  asText = P("txt")!=0;
  1552   1605     zMime = mimetype_from_name(blob_str(&downloadName));
  1553   1606     if( zMime ){
  1554   1607       if( fossil_strcmp(zMime, "text/html")==0 ){
  1555         -      if( P("txt") ){
         1608  +      if( asText ){
  1556   1609           style_submenu_element("Html", "Html",
  1557   1610                                 "%s/artifact/%s", g.zTop, zUuid);
  1558   1611         }else{
  1559   1612           renderAsHtml = 1;
  1560   1613           style_submenu_element("Text", "Text",
  1561   1614                                 "%s/artifact/%s?txt=1", g.zTop, zUuid);
  1562   1615         }
  1563   1616       }else if( fossil_strcmp(zMime, "application/x-fossil-wiki")==0 ){
  1564         -      if( P("txt") ){
         1617  +      if( asText ){
  1565   1618           style_submenu_element("Wiki", "Wiki",
  1566   1619                                 "%s/artifact/%s", g.zTop, zUuid);
  1567   1620         }else{
  1568   1621           renderAsWiki = 1;
  1569   1622           style_submenu_element("Text", "Text",
  1570   1623                                 "%s/artifact/%s?txt=1", g.zTop, zUuid);
  1571   1624         }
  1572   1625       }
  1573   1626     }
         1627  +  if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
         1628  +    style_submenu_element("Parsed", "Parsed", "%R/info/%s", zUuid);
         1629  +  }
  1574   1630     @ <hr />
  1575   1631     content_get(rid, &content);
  1576   1632     if( renderAsWiki ){
  1577   1633       wiki_convert(&content, 0, 0);
  1578   1634     }else if( renderAsHtml ){
  1579   1635       @ <div>
  1580   1636       blob_strip_bom(&content, 0);
................................................................................
  1612   1668   **
  1613   1669   ** Show the details of a ticket change control artifact.
  1614   1670   */
  1615   1671   void tinfo_page(void){
  1616   1672     int rid;
  1617   1673     char *zDate;
  1618   1674     const char *zUuid;
  1619         -  char zTktName[20];
         1675  +  char zTktName[UUID_SIZE+1];
  1620   1676     Manifest *pTktChng;
         1677  +  int modPending;
         1678  +  const char *zModAction;
  1621   1679   
  1622   1680     login_check_credentials();
  1623   1681     if( !g.perm.RdTkt ){ login_needed(); return; }
  1624   1682     rid = name_to_rid_www("name");
  1625   1683     if( rid==0 ){ fossil_redirect_home(); }
  1626   1684     zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
  1627   1685     if( g.perm.Admin ){
................................................................................
  1630   1688               g.zTop, zUuid);
  1631   1689       }else{
  1632   1690         style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
  1633   1691               g.zTop, zUuid);
  1634   1692       }
  1635   1693     }
  1636   1694     pTktChng = manifest_get(rid, CFTYPE_TICKET);
  1637         -  if( pTktChng==0 ){
  1638         -    fossil_redirect_home();
         1695  +  if( pTktChng==0 ) fossil_redirect_home();
         1696  +  zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate);
         1697  +  memcpy(zTktName, pTktChng->zTicketUuid, UUID_SIZE+1);
         1698  +  if( g.perm.ModTkt && (zModAction = P("modaction"))!=0 ){
         1699  +    if( strcmp(zModAction,"delete")==0 ){
         1700  +      moderation_disapprove(rid);
         1701  +      cgi_redirectf("%R/tktview/%s", zTktName);
         1702  +      /*NOTREACHED*/
         1703  +    }
         1704  +    if( strcmp(zModAction,"approve")==0 ){
         1705  +      moderation_approve(rid);
         1706  +    }
  1639   1707     }
  1640   1708     style_header("Ticket Change Details");
  1641         -  zDate = db_text(0, "SELECT datetime(%.12f)", pTktChng->rDate);
  1642         -  memcpy(zTktName, pTktChng->zTicketUuid, 10);
  1643         -  zTktName[10] = 0;
  1644         -  if( g.perm.Hyperlink ){
  1645         -    @ <h2>Changes to ticket
  1646         -    @ %z(href("%R/tktview/%s",pTktChng->zTicketUuid))%s(zTktName)</a></h2>
  1647         -    @
  1648         -    @ <p>By %h(pTktChng->zUser) on %s(zDate).  See also:
  1649         -    @ %z(href("%R/artifact/%T",zUuid))artifact content</a>, and
  1650         -    @ %z(href("%R/tkthistory/%s",pTktChng->zTicketUuid))ticket history</a></p>
  1651         -  }else{
  1652         -    @ <h2>Changes to ticket %s(zTktName)</h2>
  1653         -    @
  1654         -    @ <p>By %h(pTktChng->zUser) on %s(zDate).
  1655         -    @ </p>
         1709  +  style_submenu_element("Raw", "Raw", "%R/artifact/%S", zUuid);
         1710  +  style_submenu_element("History", "History", "%R/tkthistory/%s", zTktName);
         1711  +  style_submenu_element("Page", "Page", "%R/tktview/%t", zTktName);
         1712  +  style_submenu_element("Timeline", "Timeline", "%R/tkttimeline/%t", zTktName);
         1713  +
         1714  +  @ <div class="section">Overview</div>
         1715  +  @ <p><table class="label-value">
         1716  +  @ <tr><th>Artifact&nbsp;ID:</th>
         1717  +  @ <td>%z(href("%R/artifact/%s",zUuid))%s(zUuid)</a>
         1718  +  if( g.perm.Setup ){
         1719  +    @ (%d(rid))
  1656   1720     }
  1657         -  @
  1658         -  @ <ol>
         1721  +  modPending = moderation_pending(rid);
         1722  +  if( modPending ){
         1723  +    @ <span class="modpending">*** Awaiting Moderator Approval ***</span>
         1724  +  }
         1725  +  @ <tr><th>Ticket:</th>
         1726  +  @ <td>%z(href("%R/tktview/%s",zTktName))%s(zTktName)</a></td></tr>
         1727  +  @ <tr><th>Date:</th><td>
         1728  +  hyperlink_to_date(zDate, "</td></tr>");
  1659   1729     free(zDate);
         1730  +  @ <tr><th>User:</th><td>
         1731  +  hyperlink_to_user(pTktChng->zUser, zDate, "</td></tr>");
         1732  +  @ </table>
         1733  +  
         1734  +  if( g.perm.ModTkt && modPending ){
         1735  +    @ <div class="section">Moderation</div>
         1736  +    @ <blockquote>
         1737  +    @ <form method="POST" action="%R/tinfo/%s(zUuid)">
         1738  +    @ <label><input type="radio" name="modaction" value="delete">
         1739  +    @ Delete this change</label><br />
         1740  +    @ <label><input type="radio" name="modaction" value="approve">
         1741  +    @ Approve this change</label><br />
         1742  +    @ <input type="submit" value="Submit">
         1743  +    @ </form>
         1744  +    @ </blockquote>
         1745  +  }
         1746  +
         1747  +  @ <div class="section">Changes</div>
         1748  +  @ <p>
  1660   1749     ticket_output_change_artifact(pTktChng);
  1661   1750     manifest_destroy(pTktChng);
  1662   1751     style_footer();
  1663   1752   }
  1664   1753   
  1665   1754   
  1666   1755   /*
................................................................................
  1722   1811       tinfo_page();
  1723   1812     }else
  1724   1813     if( db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) ){
  1725   1814       ci_page();
  1726   1815     }else
  1727   1816     if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
  1728   1817       ci_page();
         1818  +  }else
         1819  +  if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
         1820  +    ainfo_page();
  1729   1821     }else
  1730   1822     {
  1731   1823       artifact_page();
  1732   1824     }
  1733   1825   }
  1734   1826   
  1735   1827   /*

Changes to src/login.c.

   975    975     for(i=0; zCap[i]; i++){
   976    976       switch( zCap[i] ){
   977    977         case 's':   g.perm.Setup = 1;  /* Fall thru into Admin */
   978    978         case 'a':   g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
   979    979                              g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
   980    980                              g.perm.ApndWiki = g.perm.Hyperlink = g.perm.Clone = 
   981    981                              g.perm.NewTkt = g.perm.Password = g.perm.RdAddr =
   982         -                           g.perm.TktFmt = g.perm.Attach = g.perm.ApndTkt = 1;
          982  +                           g.perm.TktFmt = g.perm.Attach = g.perm.ApndTkt =
          983  +                           g.perm.ModWiki = g.perm.ModTkt = 1;
   983    984                              /* Fall thru into Read/Write */
   984    985         case 'i':   g.perm.Read = g.perm.Write = 1;                     break;
   985    986         case 'o':   g.perm.Read = 1;                                 break;
   986    987         case 'z':   g.perm.Zip = 1;                                  break;
   987    988   
   988    989         case 'd':   g.perm.Delete = 1;                               break;
   989    990         case 'h':   g.perm.Hyperlink = 1;                            break;

Changes to src/main.mk.

    69     69     $(SRCDIR)/leaf.c \
    70     70     $(SRCDIR)/login.c \
    71     71     $(SRCDIR)/main.c \
    72     72     $(SRCDIR)/manifest.c \
    73     73     $(SRCDIR)/md5.c \
    74     74     $(SRCDIR)/merge.c \
    75     75     $(SRCDIR)/merge3.c \
           76  +  $(SRCDIR)/moderate.c \
    76     77     $(SRCDIR)/name.c \
    77     78     $(SRCDIR)/path.c \
    78     79     $(SRCDIR)/pivot.c \
    79     80     $(SRCDIR)/popen.c \
    80     81     $(SRCDIR)/pqueue.c \
    81     82     $(SRCDIR)/printf.c \
    82     83     $(SRCDIR)/rebuild.c \
................................................................................
   169    170     $(OBJDIR)/leaf_.c \
   170    171     $(OBJDIR)/login_.c \
   171    172     $(OBJDIR)/main_.c \
   172    173     $(OBJDIR)/manifest_.c \
   173    174     $(OBJDIR)/md5_.c \
   174    175     $(OBJDIR)/merge_.c \
   175    176     $(OBJDIR)/merge3_.c \
          177  +  $(OBJDIR)/moderate_.c \
   176    178     $(OBJDIR)/name_.c \
   177    179     $(OBJDIR)/path_.c \
   178    180     $(OBJDIR)/pivot_.c \
   179    181     $(OBJDIR)/popen_.c \
   180    182     $(OBJDIR)/pqueue_.c \
   181    183     $(OBJDIR)/printf_.c \
   182    184     $(OBJDIR)/rebuild_.c \
................................................................................
   269    271    $(OBJDIR)/leaf.o \
   270    272    $(OBJDIR)/login.o \
   271    273    $(OBJDIR)/main.o \
   272    274    $(OBJDIR)/manifest.o \
   273    275    $(OBJDIR)/md5.o \
   274    276    $(OBJDIR)/merge.o \
   275    277    $(OBJDIR)/merge3.o \
          278  + $(OBJDIR)/moderate.o \
   276    279    $(OBJDIR)/name.o \
   277    280    $(OBJDIR)/path.o \
   278    281    $(OBJDIR)/pivot.o \
   279    282    $(OBJDIR)/popen.o \
   280    283    $(OBJDIR)/pqueue.o \
   281    284    $(OBJDIR)/printf.o \
   282    285    $(OBJDIR)/rebuild.o \
................................................................................
   376    379   clean:	
   377    380   	rm -rf $(OBJDIR)/* $(APPNAME)
   378    381   
   379    382   
   380    383   $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
   381    384   	$(OBJDIR)/mkindex $(TRANS_SRC) >$@
   382    385   $(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
   383         -	$(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
          386  +	$(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
   384    387   	touch $(OBJDIR)/headers
   385    388   $(OBJDIR)/headers: Makefile
   386    389   $(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
   387    390   Makefile:
   388    391   $(OBJDIR)/add_.c:	$(SRCDIR)/add.c $(OBJDIR)/translate
   389    392   	$(OBJDIR)/translate $(SRCDIR)/add.c >$(OBJDIR)/add_.c
   390    393   
................................................................................
   794    797   $(OBJDIR)/merge3_.c:	$(SRCDIR)/merge3.c $(OBJDIR)/translate
   795    798   	$(OBJDIR)/translate $(SRCDIR)/merge3.c >$(OBJDIR)/merge3_.c
   796    799   
   797    800   $(OBJDIR)/merge3.o:	$(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h  $(SRCDIR)/config.h
   798    801   	$(XTCC) -o $(OBJDIR)/merge3.o -c $(OBJDIR)/merge3_.c
   799    802   
   800    803   $(OBJDIR)/merge3.h:	$(OBJDIR)/headers
          804  +$(OBJDIR)/moderate_.c:	$(SRCDIR)/moderate.c $(OBJDIR)/translate
          805  +	$(OBJDIR)/translate $(SRCDIR)/moderate.c >$(OBJDIR)/moderate_.c
          806  +
          807  +$(OBJDIR)/moderate.o:	$(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h  $(SRCDIR)/config.h
          808  +	$(XTCC) -o $(OBJDIR)/moderate.o -c $(OBJDIR)/moderate_.c
          809  +
          810  +$(OBJDIR)/moderate.h:	$(OBJDIR)/headers
   801    811   $(OBJDIR)/name_.c:	$(SRCDIR)/name.c $(OBJDIR)/translate
   802    812   	$(OBJDIR)/translate $(SRCDIR)/name.c >$(OBJDIR)/name_.c
   803    813   
   804    814   $(OBJDIR)/name.o:	$(OBJDIR)/name_.c $(OBJDIR)/name.h  $(SRCDIR)/config.h
   805    815   	$(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c
   806    816   
   807    817   $(OBJDIR)/name.h:	$(OBJDIR)/headers

Changes to src/makemake.tcl.

    72     72     leaf
    73     73     login
    74     74     main
    75     75     manifest
    76     76     md5
    77     77     merge
    78     78     merge3
           79  +  moderate
    79     80     name
    80     81     path
    81     82     pivot
    82     83     popen
    83     84     pqueue
    84     85     printf
    85     86     rebuild

Changes to src/manifest.c.

   316    316   
   317    317   /*
   318    318   ** Parse a blob into a Manifest object.  The Manifest object
   319    319   ** takes over the input blob and will free it when the
   320    320   ** Manifest object is freed.  Zeros are inserted into the blob
   321    321   ** as string terminators so that blob should not be used again.
   322    322   **
   323         -** Return TRUE if the content really is a control file of some
   324         -** kind.  Return FALSE if there are syntax errors.
          323  +** Return a pointer to an allocated Manifest object if the content
          324  +** really is a control file of some kind.  This object needs to be
          325  +** freed by a subsequent call to manifest_destroy().  Return NULL
          326  +** if there are syntax errors.
   325    327   **
   326    328   ** This routine is strict about the format of a control file.
   327    329   ** The format must match exactly or else it is rejected.  This
   328    330   ** rule minimizes the risk that a content file will be mistaken
   329    331   ** for a control file simply because they look the same.
   330    332   **
   331         -** The pContent is reset.  If TRUE is returned, then pContent will
   332         -** be reset when the Manifest object is cleared.  If FALSE is
          333  +** The pContent is reset.  If a pointer is returned, then pContent will
          334  +** be reset when the Manifest object is cleared.  If NULL is
   333    335   ** returned then the Manifest object is cleared automatically
   334    336   ** and pContent is reset before the return.
   335    337   **
   336    338   ** The entire file can be PGP clear-signed.  The signature is ignored.
   337    339   ** The file consists of zero or more cards, one card per line.
   338    340   ** (Except: the content of the W card can extend of multiple lines.)
   339    341   ** Each card is divided into tokens by a single space character.

Added src/moderate.c.

            1  +/*
            2  +** Copyright (c) 2012 D. Richard Hipp
            3  +**
            4  +** This program is free software; you can redistribute it and/or
            5  +** modify it under the terms of the Simplified BSD License (also
            6  +** known as the "2-Clause License" or "FreeBSD License".)
            7  +
            8  +** This program is distributed in the hope that it will be useful,
            9  +** but without any warranty; without even the implied warranty of
           10  +** merchantability or fitness for a particular purpose.
           11  +**
           12  +** Author contact information:
           13  +**   drh@hwaci.com
           14  +**   http://www.hwaci.com/drh/
           15  +**
           16  +*******************************************************************************
           17  +**
           18  +** This file contains code used to deal with moderator actions for
           19  +** Wiki and Tickets.
           20  +*/
           21  +#include "config.h"
           22  +#include "moderate.h"
           23  +#include <assert.h>
           24  +
           25  +/*
           26  +** Create a table to represent pending moderation requests, if the
           27  +** table does not already exist.
           28  +*/
           29  +void moderation_table_create(void){
           30  +  db_multi_exec(
           31  +     "CREATE TABLE IF NOT EXISTS modreq(\n"
           32  +     "  objid INTEGER PRIMARY KEY,\n"        /* Record pending approval */
           33  +     "  attachRid INT,\n"                    /* Object attached */
           34  +     "  tktid TEXT\n"                        /* Associated ticket id */
           35  +     ");\n"
           36  +  );
           37  +}
           38  +
           39  +/*
           40  +** Return TRUE if the modreq table exists
           41  +*/
           42  +int moderation_table_exists(void){
           43  +  static int modreqExists = -1;
           44  +  if( modreqExists<0 ){
           45  +    modreqExists = db_exists("SELECT 1 FROM %s.sqlite_master"
           46  +                             " WHERE name='modreq'", db_name("repository"));
           47  +  }
           48  +  return modreqExists;
           49  +}
           50  +
           51  +/*
           52  +** Return TRUE if the object specified is being held for moderation.
           53  +*/
           54  +int moderation_pending(int rid){
           55  +  static Stmt q;
           56  +  int rc;
           57  +  if( rid==0 || !moderation_table_exists() ) return 0;
           58  +  db_static_prepare(&q, "SELECT 1 FROM modreq WHERE objid=:objid");
           59  +  db_bind_int(&q, ":objid", rid);
           60  +  rc = db_step(&q)==SQLITE_ROW;
           61  +  db_reset(&q);
           62  +  return rc;
           63  +}
           64  +
           65  +/*
           66  +** Check to see if the object identified by RID is used for anything.
           67  +*/
           68  +static int object_used(int rid){
           69  +  static const char *aTabField[] = {
           70  +     "modreq",     "attachRid",
           71  +     "mlink",      "mid",
           72  +     "mlink",      "fid",
           73  +     "tagxref",    "srcid",
           74  +     "tagxref",    "rid",
           75  +  };
           76  +  int i;
           77  +  for(i=0; i<sizeof(aTabField)/sizeof(aTabField[0]); i+=2){
           78  +    if( db_exists("SELECT 1 FROM %s WHERE %s=%d",
           79  +                  aTabField[i], aTabField[i+1], rid) ) return 1;
           80  +  }
           81  +  return 0;
           82  +}
           83  +
           84  +/*
           85  +** Delete a moderation item given by objid
           86  +*/
           87  +void moderation_disapprove(int objid){
           88  +  Stmt q;
           89  +  char *zTktid;
           90  +  int attachRid = 0;
           91  +  int rid;
           92  +  if( !moderation_pending(objid) ) return;
           93  +  db_begin_transaction();
           94  +  rid = objid;
           95  +  while( rid && content_is_private(rid) ){
           96  +    db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
           97  +    while( db_step(&q)==SQLITE_ROW ){
           98  +      int ridUser = db_column_int(&q, 0);
           99  +      content_undelta(ridUser);
          100  +    }
          101  +    db_finalize(&q);
          102  +    db_multi_exec(
          103  +      "DELETE FROM blob WHERE rid=%d;"
          104  +      "DELETE FROM delta WHERE rid=%d;"
          105  +      "DELETE FROM event WHERE objid=%d;"
          106  +      "DELETE FROM tagxref WHERE rid=%d;"
          107  +      "DELETE FROM private WHERE rid=%d;"
          108  +      "DELETE FROM attachment WHERE attachid=%d;",
          109  +      rid, rid, rid, rid, rid, rid
          110  +    );
          111  +    zTktid = db_text(0, "SELECT tktid FROM modreq WHERE objid=%d", rid);
          112  +    if( zTktid && zTktid[0] ){
          113  +      ticket_rebuild_entry(zTktid);
          114  +      fossil_free(zTktid);
          115  +    }
          116  +    attachRid = db_int(0, "SELECT attachRid FROM modreq WHERE objid=%d", rid);
          117  +    if( rid==objid ){
          118  +      db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid);
          119  +    }
          120  +    if( attachRid && object_used(attachRid) ) attachRid = 0;
          121  +    rid = attachRid;
          122  +  }
          123  +  db_end_transaction(0);
          124  +}
          125  +
          126  +/*
          127  +** Approve an object held for moderation.
          128  +*/
          129  +void moderation_approve(int rid){
          130  +  if( !moderation_pending(rid) ) return;
          131  +  db_begin_transaction();
          132  +  db_multi_exec(
          133  +    "DELETE FROM private WHERE rid=%d;"
          134  +    "INSERT OR IGNORE INTO unclustered VALUES(%d);"
          135  +    "INSERT OR IGNORE INTO unsent VALUES(%d);",
          136  +    rid, rid, rid
          137  +  );
          138  +  db_multi_exec("DELETE FROM modreq WHERE objid=%d", rid);
          139  +  db_end_transaction(0);
          140  +}
          141  +
          142  +/*
          143  +** WEBPAGE: modreq
          144  +**
          145  +** Show all pending moderation request
          146  +*/
          147  +void modreq_page(void){
          148  +  Blob sql;
          149  +  Stmt q;
          150  +
          151  +  login_check_credentials();
          152  +  if( !g.perm.RdWiki && !g.perm.RdTkt ){ login_needed(); return; }
          153  +  style_header("Pending Moderation Requests");
          154  +  @ <h2>All Pending Moderation Requests</h2>
          155  +  if( moderation_table_exists() ){
          156  +    blob_init(&sql, timeline_query_for_www(), -1);
          157  +    blob_appendf(&sql,
          158  +        " AND event.objid IN (SELECT objid FROM modreq)"
          159  +        " ORDER BY event.mtime DESC"
          160  +    );
          161  +    db_prepare(&q, blob_str(&sql));
          162  +    www_print_timeline(&q, 0, 0, 0, 0);
          163  +    db_finalize(&q);
          164  +  }
          165  +  style_footer();
          166  +}

Changes to src/rebuild.c.

   345    345     rebuild_update_schema();
   346    346     for(;;){
   347    347       zTable = db_text(0,
   348    348          "SELECT name FROM sqlite_master /*scan*/"
   349    349          " WHERE type='table'"
   350    350          " AND name NOT IN ('blob','delta','rcvfrom','user',"
   351    351                            "'config','shun','private','reportfmt',"
   352         -                         "'concealed','accesslog')"
          352  +                         "'concealed','accesslog','modreq')"
   353    353          " AND name NOT GLOB 'sqlite_*'"
   354    354       );
   355    355       if( zTable==0 ) break;
   356    356       db_multi_exec("DROP TABLE %Q", zTable);
   357    357       free(zTable);
   358    358     }
   359    359     db_multi_exec(zRepositorySchema2);

Changes to src/setup.c.

    90     90       "Select from a menu of prepackaged \"skins\" for the web interface");
    91     91     setup_menu_entry("CSS", "setup_editcss",
    92     92       "Edit the Cascading Style Sheet used by all pages of this repository");
    93     93     setup_menu_entry("Header", "setup_header",
    94     94       "Edit HTML text inserted at the top of every page");
    95     95     setup_menu_entry("Footer", "setup_footer",
    96     96       "Edit HTML text inserted at the bottom of every page");
           97  +  setup_menu_entry("Moderation", "setup_modreq",
           98  +    "Enable/Disable requiring moderator approval of Wiki and/or Ticket"
           99  +    "edits and attachments.");
    97    100     setup_menu_entry("Ad-Unit", "setup_adunit",
    98    101       "Edit HTML text for an ad unit inserted after the menu bar");
    99    102     setup_menu_entry("Logo", "setup_logo",
   100    103       "Change the logo and background images for the server");
   101    104     setup_menu_entry("Shunned", "shun",
   102    105       "Show artifacts that are shunned by this repository");
   103    106     setup_menu_entry("Log", "rcvfromlist",
................................................................................
  1401   1404     @ See also the <a href="setup_editcss">CSS</a> and
  1402   1405     @ <a href="setup_header">header</a> editing screens.
  1403   1406     @ <blockquote><pre>
  1404   1407     @ %h(zDefaultFooter)
  1405   1408     @ </pre></blockquote>
  1406   1409     style_footer();
  1407   1410     db_end_transaction(0);
         1411  +}
         1412  +
         1413  +/*
         1414  +** WEBPAGE: setup_modreq
         1415  +*/
         1416  +void setup_modreq(void){
         1417  +  login_check_credentials();
         1418  +  if( !g.perm.Setup ){
         1419  +    login_needed();
         1420  +  }
         1421  +
         1422  +  style_header("Moderator For Wiki And Tickets");
         1423  +  db_begin_transaction();
         1424  +  @ <form action="%R/setup_modreq" method="post"><div>
         1425  +  login_insert_csrf_secret();
         1426  +  @ <hr />
         1427  +  onoff_attribute("Moderate ticket changes",
         1428  +     "modreq-tkt", "modreq-tkt", 0);
         1429  +  @ <p>When enabled, any change to tickets is subject to the approval
         1430  +  @ a ticket moderator - a user with the "q" or Mod-Tkt privilege.
         1431  +  @ Ticket changes enter the system and are shown locally, but are not
         1432  +  @ synced until they are approved.  The moderator has the option to 
         1433  +  @ delete the change rather than approve it.  Ticket changes made by
         1434  +  @ a user who hwas the Mod-Tkt privilege are never subject to
         1435  +  @ moderation.
         1436  +  @
         1437  +  @ <hr />
         1438  +  onoff_attribute("Moderate wiki changes",
         1439  +     "modreq-wiki", "modreq-wiki", 0);
         1440  +  @ <p>When enabled, any change to wiki is subject to the approval
         1441  +  @ a ticket moderator - a user with the "l" or Mod-Wiki privilege.
         1442  +  @ Wiki changes enter the system and are shown locally, but are not
         1443  +  @ synced until they are approved.  The moderator has the option to 
         1444  +  @ delete the change rather than approve it.  Wiki changes made by
         1445  +  @ a user who has the Mod-Wiki privilege are never subject to
         1446  +  @ moderation.
         1447  +  @ </p>
         1448  + 
         1449  +  @ <hr />
         1450  +  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
         1451  +  @ </div></form>
         1452  +  db_end_transaction(0);
         1453  +  style_footer();
         1454  +
  1408   1455   }
  1409   1456   
  1410   1457   /*
  1411   1458   ** WEBPAGE: setup_adunit
  1412   1459   */
  1413   1460   void setup_adunit(void){
  1414   1461     login_check_credentials();

Changes to src/style.c.

   928    928       @   background-color: #ffc8c8;
   929    929     },
   930    930     { "span.diffhr",
   931    931       "suppressed lines in a diff",
   932    932       @   color: #0000ff;
   933    933     },
   934    934     { "span.diffln",
   935         -    "line nubmers in a diff",
          935  +    "line numbers in a diff",
   936    936       @   color: #a0a0a0;
   937    937     },
          938  +  { "span.modpending",
          939  +    "Moderation Pending message on timelin",
          940  +    @   color: #b03800;
          941  +    @   font-style: italic;
          942  +  },
   938    943     { 0,
   939    944       0,
   940    945       0
   941    946     }
   942    947   };
   943    948   
   944    949   /*

Changes to src/th_main.c.

   190    190     }
   191    191     Th_SetResult(interp, zOut, -1);
   192    192     free(zOut);
   193    193     return TH_OK;
   194    194   }
   195    195   
   196    196   /*
   197         -** TH command:     hascap STRING
          197  +** TH command:     hascap STRING...
   198    198   **
   199    199   ** Return true if the user has all of the capabilities listed in STRING.
   200    200   */
   201    201   static int hascapCmd(
   202    202     Th_Interp *interp, 
   203    203     void *p, 
   204    204     int argc, 
   205    205     const char **argv, 
   206    206     int *argl
   207    207   ){
   208         -  int rc;
   209         -  if( argc!=2 ){
   210         -    return Th_WrongNumArgs(interp, "hascap STRING");
          208  +  int rc = 0, i;
          209  +  if( argc<2 ){
          210  +    return Th_WrongNumArgs(interp, "hascap STRING ...");
   211    211     }
   212         -  rc = login_has_capability((char*)argv[1],argl[1]);
          212  +  for(i=1; i<argc && rc==0; i++){
          213  +    rc = login_has_capability((char*)argv[i],argl[i]);
          214  +  }
   213    215     if( g.thTrace ){
   214    216       Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc);
   215    217     }
   216    218     Th_SetResultInt(interp, rc);
   217    219     return TH_OK;
   218    220   }
   219    221   

Changes to src/timeline.c.

   234    234       const char *zDate = db_column_text(pQuery, 2);
   235    235       const char *zType = db_column_text(pQuery, 7);
   236    236       const char *zUser = db_column_text(pQuery, 4);
   237    237       const char *zTagList = db_column_text(pQuery, 8);
   238    238       int tagid = db_column_int(pQuery, 9);
   239    239       const char *zBr = 0;      /* Branch */
   240    240       int commentColumn = 3;    /* Column containing comment text */
          241  +    int modPending;           /* Pending moderation */
   241    242       char zTime[8];
          243  +
          244  +    modPending =  moderation_pending(rid);
   242    245       if( tagid ){
          246  +      if( modPending ) tagid = -tagid;
   243    247         if( tagid==prevTagid ){
   244    248           if( tmFlags & TIMELINE_BRIEF ){
   245    249             suppressCnt++;
   246    250             continue;
   247    251           }else{
   248    252             commentColumn = 10;
   249    253           }
................................................................................
   318    322         @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
   319    323       }else{
   320    324         @ <td class="timelineTableCell">
   321    325       }
   322    326       if( pGraph && zType[0]!='c' ){
   323    327         @ &bull;
   324    328       }
          329  +    if( modPending ){
          330  +      @ <span class="modpending">(Awaiting Moderator Approval)</span>
          331  +    }
   325    332       if( zType[0]=='c' ){
   326    333         hyperlink_to_uuid(zUuid);
   327    334         if( isLeaf ){
   328    335           if( db_exists("SELECT 1 FROM tagxref"
   329    336                         " WHERE rid=%d AND tagid=%d AND tagtype>0",
   330    337                         rid, TAG_CLOSED) ){
   331    338             @ <span class="timelineLeaf">Closed-Leaf:</span>
   332    339           }else{
   333    340             @ <span class="timelineLeaf">Leaf:</span>
   334    341           }
   335    342         }
   336    343       }else if( zType[0]=='e' && tagid ){
   337         -      hyperlink_to_event_tagid(tagid);
          344  +      hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
   338    345       }else if( (tmFlags & TIMELINE_ARTID)!=0 ){
   339    346         hyperlink_to_uuid(zUuid);
   340    347       }
   341    348       db_column_blob(pQuery, commentColumn, &comment);
   342    349       if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
   343    350         Blob truncated;
   344    351         blob_zero(&truncated);
................................................................................
   359    366         char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd", zUser, zDate);
   360    367         @ (user: %z(href("%z",zLink))%h(zUser)</a>%s(zTagList?",":"\051")
   361    368       }else{
   362    369         @ (user: %h(zUser)%s(zTagList?",":"\051")
   363    370       }
   364    371   
   365    372       /* Generate a "detail" link for tags. */
   366         -    if( zType[0]=='g' && g.perm.Hyperlink ){
          373  +    if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){
   367    374         @ [%z(href("%R/info/%S",zUuid))details</a>]
   368    375       }
   369    376   
   370    377       /* Generate the "tags: TAGLIST" at the end of the comment, together
   371    378       ** with hyperlinks to the tag list.
   372    379       */
   373    380       if( zTagList ){

Changes to src/tkt.c.

   333    333     Th_Render(zScript);
   334    334     if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
   335    335   
   336    336     zFullName = db_text(0, 
   337    337          "SELECT tkt_uuid FROM ticket"
   338    338          " WHERE tkt_uuid GLOB '%q*'", zUuid);
   339    339     if( zFullName ){
   340         -    int cnt = 0;
   341         -    Stmt q;
   342         -    db_prepare(&q,
   343         -       "SELECT datetime(mtime,'localtime'), filename, user"
   344         -       "  FROM attachment"
   345         -       " WHERE isLatest AND src!='' AND target=%Q"
   346         -       " ORDER BY mtime DESC",
   347         -       zFullName);
   348         -    while( db_step(&q)==SQLITE_ROW ){
   349         -      const char *zDate = db_column_text(&q, 0);
   350         -      const char *zFile = db_column_text(&q, 1);
   351         -      const char *zUser = db_column_text(&q, 2);
   352         -      if( cnt==0 ){
   353         -        @ <hr /><h2>Attachments:</h2>
   354         -        @ <ul>
   355         -      }
   356         -      cnt++;
   357         -      @ <li>
   358         -      if( g.perm.Read && g.perm.Hyperlink ){
   359         -        @ %z(href("%R/attachview?tkt=%s&file=%t",zFullName,zFile))
   360         -        @ %h(zFile)</a>
   361         -      }else{
   362         -        @ %h(zFile)
   363         -      }
   364         -      @ added by %h(zUser) on
   365         -      hyperlink_to_date(zDate, ".");
   366         -      if( g.perm.WrTkt && g.perm.Attach ){
   367         -        char *zH;
   368         -        zH = href("%R/attachdelete?tkt=%s&file=%t&from=%R/tktview%%3fname=%s",
   369         -                  zFullName, zFile, zFullName);
   370         -        @ [%z(zH)delete</a>]
   371         -      }
   372         -      @ </li>
   373         -    }
   374         -    if( cnt ){
   375         -      @ </ul>
   376         -    }
   377         -    db_finalize(&q);
          340  +    attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>");
   378    341     }
   379    342    
   380    343     style_footer();
   381    344   }
   382    345   
   383    346   /*
   384    347   ** TH command:   append_field FIELD STRING
................................................................................
   413    376     if( idx>=nField ){
   414    377       Th_ErrorMessage(g.interp, "no such TICKET column: ", argv[1], argl[1]);
   415    378       return TH_ERROR;
   416    379     }
   417    380     azAppend[idx] = mprintf("%.*s", argl[2], argv[2]);
   418    381     return TH_OK;
   419    382   }
          383  +
          384  +/*
          385  +** Write a ticket into the repository.
          386  +*/
          387  +static void ticket_put(
          388  +  Blob *pTicket,           /* The text of the ticket change record */
          389  +  const char *zTktId,      /* The ticket to which this change is applied */
          390  +  int needMod              /* True if moderation is needed */
          391  +){
          392  +  int rid = content_put_ex(pTicket, 0, 0, 0, needMod);
          393  +  if( rid==0 ){
          394  +    fossil_panic("trouble committing ticket: %s", g.zErrMsg);
          395  +  }
          396  +  if( needMod ){
          397  +    moderation_table_create();
          398  +    db_multi_exec(
          399  +      "INSERT INTO modreq(objid, tktid) VALUES(%d,'%s')",
          400  +      rid, zTktId
          401  +    );
          402  +  }else{
          403  +    db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d);", rid);
          404  +    db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", rid);
          405  +  }
          406  +  manifest_crosslink_begin();
          407  +  manifest_crosslink(rid, pTicket);
          408  +  assert( blob_is_reset(pTicket) );
          409  +  manifest_crosslink_end();
          410  +}
   420    411   
   421    412   /*
   422    413   ** Subscript command:   submit_ticket
   423    414   **
   424    415   ** Construct and submit a new ticket artifact.  The fields of the artifact
   425    416   ** are the names of the columns in the TICKET table.  The content is
   426    417   ** taken from TH variables.  If the content is unchanged, the field is
................................................................................
   496    487       @ <hr /></font>
   497    488       return TH_OK;
   498    489     }else if( g.thTrace ){
   499    490       Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n"
   500    491                "}<br />\n",
   501    492          blob_str(&tktchng));
   502    493     }else{
   503         -    rid = content_put(&tktchng);
   504         -    if( rid==0 ){
   505         -      fossil_panic("trouble committing ticket: %s", g.zErrMsg);
   506         -    }
   507         -    manifest_crosslink_begin();
   508         -    manifest_crosslink(rid, &tktchng);
   509         -    assert( blob_is_reset(&tktchng) );
   510         -    manifest_crosslink_end();
          494  +    ticket_put(&tktchng, zUuid,
          495  +               (g.perm.ModTkt==0 && db_get_boolean("modreq-tkt",0)==1));
   511    496     }
   512    497     return ticket_change();
   513    498   }
   514    499   
   515    500   
   516    501   /*
   517    502   ** WEBPAGE: tktnew
................................................................................
  1185   1170                          azField[i], strlen(zValue), zValue);
  1186   1171           }
  1187   1172         }
  1188   1173         blob_appendf(&tktchng, "K %s\n", zTktUuid);
  1189   1174         blob_appendf(&tktchng, "U %F\n", zUser);
  1190   1175         md5sum_blob(&tktchng, &cksum);
  1191   1176         blob_appendf(&tktchng, "Z %b\n", &cksum);
  1192         -      rid = content_put(&tktchng);
  1193         -      if( rid==0 ){
  1194         -        fossil_panic("trouble committing ticket: %s", g.zErrMsg);
  1195         -      }
  1196         -      manifest_crosslink_begin();
  1197         -      manifest_crosslink(rid, &tktchng);
  1198         -      manifest_crosslink_end();
  1199         -      assert( blob_is_reset(&tktchng) );
         1177  +      ticket_put(&tktchng, zTktUuid, 0);
  1200   1178         printf("ticket %s succeeded for %s\n",
  1201   1179                (eCmd==set?"set":"add"),zTktUuid);
  1202   1180       }
  1203   1181     }
  1204   1182   }

Changes to src/tktsetup.c.

   593    593   @ 
   594    594   @ <p>Choose a report format from the following list:</p>
   595    595   @ <ol>
   596    596   @ <th1>html $report_items</th1>
   597    597   @ </ol>
   598    598   @ 
   599    599   @ <th1>
   600         -@ if {[hascap t]} {
   601         -@   html "<p>Create a new ticket display format:</p>"
   602         -@   html "<ul><li><a href='rptnew'>New report format</a></li></ul>"
          600  +@ if {[hascap t q]} {
          601  +@   html "<p>Other options:</p>\n<ul>\n"
          602  +@   if {[hascap t]} {
          603  +@     html "<li><a href='rptnew'>New report format</a></li>\n"
          604  +@   }
          605  +@   if {[hascap q]} {
          606  +@     html "<li><a href='modreq'>Tend to pending moderation requests</a></li>\n"
          607  +@   }
   603    608   @ }
   604    609   @ </th1>
   605    610   ;
   606    611   
   607    612   /*
   608    613   ** Return the code used to generate the report list
   609    614   */

Changes to src/wiki.c.

   156    156         @ <li>  Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
   157    157         if( g.perm.Write ){
   158    158           @ <li>   Create a %z(href("%R/eventedit"))new event</a>.</li>
   159    159         }
   160    160       }
   161    161       @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
   162    162       @      available on this server.</li>
          163  +    if( g.perm.ModWiki ){
          164  +      @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
          165  +    }
   163    166       @ <li> <form method="get" action="%s(g.zTop)/wfind"><div>
   164    167       @     Search wiki titles: <input type="text" name="title"/>
   165    168       @  &nbsp; <input type="submit" /></div></form>
   166    169       @ </li>
   167    170       @ </ul>
   168    171       style_footer();
   169    172       return;
................................................................................
   212    215       }
   213    216     }
   214    217     style_set_current_page("%s?name=%T", g.zPath, zPageName);
   215    218     style_header(zPageName);
   216    219     blob_init(&wiki, zBody, -1);
   217    220     wiki_convert(&wiki, 0, 0);
   218    221     blob_reset(&wiki);
   219         -
   220         -  db_prepare(&q,
   221         -     "SELECT datetime(mtime,'localtime'), filename, user"
   222         -     "  FROM attachment"
   223         -     " WHERE isLatest AND src!='' AND target=%Q"
   224         -     " ORDER BY mtime DESC",
   225         -     zPageName);
   226         -  while( db_step(&q)==SQLITE_ROW ){
   227         -    const char *zDate = db_column_text(&q, 0);
   228         -    const char *zFile = db_column_text(&q, 1);
   229         -    const char *zUser = db_column_text(&q, 2);
   230         -    if( cnt==0 ){
   231         -      @ <hr /><h2>Attachments:</h2>
   232         -      @ <ul>
   233         -    }
   234         -    cnt++;
   235         -    @ <li>
   236         -    if( g.perm.Hyperlink && g.perm.Read ){
   237         -      @ %z(href("%R/attachview?page=%T&file=%t",zPageName,zFile))
   238         -      @ %h(zFile)</a>
   239         -    }else{
   240         -      @ %h(zFile)
   241         -    }
   242         -    @ added by %h(zUser) on
   243         -    hyperlink_to_date(zDate, ".");
   244         -    if( g.perm.WrWiki && g.perm.Attach ){
   245         -      @ [%z(href("%R/attachdelete?page=%t&file=%t&from=%R/wiki%%3fname=%f",zPageName,zFile,zPageName))delete</a>]
   246         -    }
   247         -    @ </li>
   248         -  }
   249         -  if( cnt ){
   250         -    @ </ul>
   251         -  }
   252         -  db_finalize(&q);
   253         - 
          222  +  attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
   254    223     manifest_destroy(pWiki);
   255    224     style_footer();
   256    225   }
          226  +
          227  +/*
          228  +** Write a wiki artifact into the repository
          229  +*/
          230  +static void wiki_put(Blob *pWiki, int parent){
          231  +  int nrid;
          232  +  if( g.perm.ModWiki || db_get_boolean("modreq-wiki",0)==0 ){
          233  +    nrid = content_put_ex(pWiki, 0, 0, 0, 0);
          234  +    if( parent) content_deltify(parent, nrid, 0);
          235  +  }else{
          236  +    nrid = content_put_ex(pWiki, 0, 0, 0, 1);
          237  +    moderation_table_create();
          238  +    db_multi_exec("INSERT INTO modreq(objid) VALUES(%d)", nrid);
          239  +  }
          240  +  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
          241  +  db_multi_exec("INSERT OR IGNORE INTO unclustered VALUES(%d);", nrid);
          242  +  manifest_crosslink(nrid, pWiki);
          243  +}
   257    244   
   258    245   /*
   259    246   ** WEBPAGE: wikiedit
   260    247   ** URL: /wikiedit?name=PAGENAME
   261    248   */
   262    249   void wikiedit_page(void){
   263    250     char *zTag;
................................................................................
   333    320         if( g.zLogin ){
   334    321           blob_appendf(&wiki, "U %F\n", g.zLogin);
   335    322         }
   336    323         blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody);
   337    324         md5sum_blob(&wiki, &cksum);
   338    325         blob_appendf(&wiki, "Z %b\n", &cksum);
   339    326         blob_reset(&cksum);
   340         -      nrid = content_put(&wiki);
   341         -      db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
   342         -      manifest_crosslink(nrid, &wiki);
   343         -      assert( blob_is_reset(&wiki) );
   344         -      content_deltify(rid, nrid, 0);
          327  +      wiki_put(&wiki, 0);
   345    328       }
   346    329       db_end_transaction(0);
   347    330       cgi_redirectf("wiki?name=%T", zPageName);
   348    331     }
   349    332     if( P("cancel")!=0 ){
   350    333       cgi_redirectf("wiki?name=%T", zPageName);
   351    334       return;
................................................................................
   533    516           blob_appendf(&wiki, "U %F\n", g.zLogin);
   534    517         }
   535    518         appendRemark(&body);
   536    519         blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body));
   537    520         md5sum_blob(&wiki, &cksum);
   538    521         blob_appendf(&wiki, "Z %b\n", &cksum);
   539    522         blob_reset(&cksum);
   540         -      nrid = content_put(&wiki);
   541         -      db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
   542         -      manifest_crosslink(nrid, &wiki);
   543         -      assert( blob_is_reset(&wiki) );
   544         -      content_deltify(rid, nrid, 0);
          523  +      wiki_put(&wiki, rid);
   545    524         db_end_transaction(0);
   546    525       }
   547    526       cgi_redirectf("wiki?name=%T", zPageName);
   548    527     }
   549    528     if( P("cancel")!=0 ){
   550    529       cgi_redirectf("wiki?name=%T", zPageName);
   551    530       return;
................................................................................
   876    855     }
   877    856     blob_appendf( &wiki, "W %d\n%s\n", blob_size(pContent),
   878    857                   blob_str(pContent) );
   879    858     md5sum_blob(&wiki, &cksum);
   880    859     blob_appendf(&wiki, "Z %b\n", &cksum);
   881    860     blob_reset(&cksum);
   882    861     db_begin_transaction();
   883         -  nrid = content_put( &wiki);
   884         -  db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
   885         -  manifest_crosslink(nrid,&wiki);
   886         -  assert( blob_is_reset(&wiki) );
   887         -  content_deltify(rid,nrid,0);
          862  +  wiki_put(&wiki, 0);
   888    863     db_end_transaction(0);
   889    864     return 1;
   890    865   }
   891    866   
   892    867   /*
   893    868   ** COMMAND: wiki*
   894    869   **

Changes to win/Makefile.dmc.

    24     24   CFLAGS = -o
    25     25   BCC    = $(DMDIR)\bin\dmc $(CFLAGS)
    26     26   TCC    = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
    27     27   LIBS   = $(DMDIR)\extra\lib\ zlib wsock32 advapi32
    28     28   
    29     29   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
    30     30   
    31         -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 
           31  +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 
    32     32   
    33         -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 
           33  +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 
    34     34   
    35     35   
    36     36   RC=$(DMDIR)\bin\rcc
    37     37   RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
    38     38   
    39     39   APPNAME = $(OBJDIR)\fossil$(E)
    40     40   
................................................................................
    44     44   	cd $(OBJDIR) 
    45     45   	$(DMDIR)\bin\link @link
    46     46   
    47     47   $(OBJDIR)\fossil.res:	$B\win\fossil.rc
    48     48   	$(RC) $(RCFLAGS) -o$@ $**
    49     49   
    50     50   $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
    51         -	+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 > $@
           51  +	+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 > $@
    52     52   	+echo fossil >> $@
    53     53   	+echo fossil >> $@
    54     54   	+echo $(LIBS) >> $@
    55     55   	+echo. >> $@
    56     56   	+echo fossil >> $@
    57     57   
    58     58   translate$E: $(SRCDIR)\translate.c
................................................................................
   461    461   	+translate$E $** > $@
   462    462   
   463    463   $(OBJDIR)\merge3$O : merge3_.c merge3.h
   464    464   	$(TCC) -o$@ -c merge3_.c
   465    465   
   466    466   merge3_.c : $(SRCDIR)\merge3.c
   467    467   	+translate$E $** > $@
          468  +
          469  +$(OBJDIR)\moderate$O : moderate_.c moderate.h
          470  +	$(TCC) -o$@ -c moderate_.c
          471  +
          472  +moderate_.c : $(SRCDIR)\moderate.c
          473  +	+translate$E $** > $@
   468    474   
   469    475   $(OBJDIR)\name$O : name_.c name.h
   470    476   	$(TCC) -o$@ -c name_.c
   471    477   
   472    478   name_.c : $(SRCDIR)\name.c
   473    479   	+translate$E $** > $@
   474    480   
................................................................................
   697    703   $(OBJDIR)\zip$O : zip_.c zip.h
   698    704   	$(TCC) -o$@ -c zip_.c
   699    705   
   700    706   zip_.c : $(SRCDIR)\zip.c
   701    707   	+translate$E $** > $@
   702    708   
   703    709   headers: makeheaders$E page_index.h VERSION.h
   704         -	 +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
          710  +	 +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
   705    711   	@copy /Y nul: headers

Changes to win/Makefile.mingw.

   272    272     $(SRCDIR)/leaf.c \
   273    273     $(SRCDIR)/login.c \
   274    274     $(SRCDIR)/main.c \
   275    275     $(SRCDIR)/manifest.c \
   276    276     $(SRCDIR)/md5.c \
   277    277     $(SRCDIR)/merge.c \
   278    278     $(SRCDIR)/merge3.c \
          279  +  $(SRCDIR)/moderate.c \
   279    280     $(SRCDIR)/name.c \
   280    281     $(SRCDIR)/path.c \
   281    282     $(SRCDIR)/pivot.c \
   282    283     $(SRCDIR)/popen.c \
   283    284     $(SRCDIR)/pqueue.c \
   284    285     $(SRCDIR)/printf.c \
   285    286     $(SRCDIR)/rebuild.c \
................................................................................
   372    373     $(OBJDIR)/leaf_.c \
   373    374     $(OBJDIR)/login_.c \
   374    375     $(OBJDIR)/main_.c \
   375    376     $(OBJDIR)/manifest_.c \
   376    377     $(OBJDIR)/md5_.c \
   377    378     $(OBJDIR)/merge_.c \
   378    379     $(OBJDIR)/merge3_.c \
          380  +  $(OBJDIR)/moderate_.c \
   379    381     $(OBJDIR)/name_.c \
   380    382     $(OBJDIR)/path_.c \
   381    383     $(OBJDIR)/pivot_.c \
   382    384     $(OBJDIR)/popen_.c \
   383    385     $(OBJDIR)/pqueue_.c \
   384    386     $(OBJDIR)/printf_.c \
   385    387     $(OBJDIR)/rebuild_.c \
................................................................................
   472    474    $(OBJDIR)/leaf.o \
   473    475    $(OBJDIR)/login.o \
   474    476    $(OBJDIR)/main.o \
   475    477    $(OBJDIR)/manifest.o \
   476    478    $(OBJDIR)/md5.o \
   477    479    $(OBJDIR)/merge.o \
   478    480    $(OBJDIR)/merge3.o \
          481  + $(OBJDIR)/moderate.o \
   479    482    $(OBJDIR)/name.o \
   480    483    $(OBJDIR)/path.o \
   481    484    $(OBJDIR)/pivot.o \
   482    485    $(OBJDIR)/popen.o \
   483    486    $(OBJDIR)/pqueue.o \
   484    487    $(OBJDIR)/printf.o \
   485    488    $(OBJDIR)/rebuild.o \
................................................................................
   623    626   setup: $(OBJDIR) $(APPNAME)
   624    627   	$(MAKENSIS) ./fossil.nsi
   625    628   
   626    629   $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
   627    630   	$(MKINDEX) $(TRANS_SRC) >$@
   628    631   
   629    632   $(OBJDIR)/headers:	$(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
   630         -	$(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
          633  +	$(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
   631    634   	echo Done >$(OBJDIR)/headers
   632    635   
   633    636   $(OBJDIR)/headers: Makefile
   634    637   
   635    638   Makefile:
   636    639   
   637    640   $(OBJDIR)/add_.c:	$(SRCDIR)/add.c $(OBJDIR)/translate
................................................................................
  1101   1104   $(OBJDIR)/merge3_.c:	$(SRCDIR)/merge3.c $(OBJDIR)/translate
  1102   1105   	$(TRANSLATE) $(SRCDIR)/merge3.c >$(OBJDIR)/merge3_.c
  1103   1106   
  1104   1107   $(OBJDIR)/merge3.o:	$(OBJDIR)/merge3_.c $(OBJDIR)/merge3.h  $(SRCDIR)/config.h
  1105   1108   	$(XTCC) -o $(OBJDIR)/merge3.o -c $(OBJDIR)/merge3_.c
  1106   1109   
  1107   1110   $(OBJDIR)/merge3.h:	$(OBJDIR)/headers
         1111  +
         1112  +$(OBJDIR)/moderate_.c:	$(SRCDIR)/moderate.c $(OBJDIR)/translate
         1113  +	$(TRANSLATE) $(SRCDIR)/moderate.c >$(OBJDIR)/moderate_.c
         1114  +
         1115  +$(OBJDIR)/moderate.o:	$(OBJDIR)/moderate_.c $(OBJDIR)/moderate.h  $(SRCDIR)/config.h
         1116  +	$(XTCC) -o $(OBJDIR)/moderate.o -c $(OBJDIR)/moderate_.c
         1117  +
         1118  +$(OBJDIR)/moderate.h:	$(OBJDIR)/headers
  1108   1119   
  1109   1120   $(OBJDIR)/name_.c:	$(SRCDIR)/name.c $(OBJDIR)/translate
  1110   1121   	$(TRANSLATE) $(SRCDIR)/name.c >$(OBJDIR)/name_.c
  1111   1122   
  1112   1123   $(OBJDIR)/name.o:	$(OBJDIR)/name_.c $(OBJDIR)/name.h  $(SRCDIR)/config.h
  1113   1124   	$(XTCC) -o $(OBJDIR)/name.o -c $(OBJDIR)/name_.c
  1114   1125   

Changes to win/Makefile.msc.

    38     38   BCC    = $(CC) $(CFLAGS)
    39     39   TCC    = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL)
    40     40   LIBS   = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
    41     41   LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
    42     42   
    43     43   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
    44     44   
    45         -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 
           45  +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 
    46     46   
    47         -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 
           47  +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 
    48     48   
    49     49   
    50     50   APPNAME = $(OX)\fossil$(E)
    51     51   
    52     52   all: $(OX) $(APPNAME)
    53     53   
    54     54   $(APPNAME) : translate$E mkindex$E headers $(OBJ) $(OX)\linkopts
................................................................................
   111    111   	echo $(OX)\leaf.obj >> $@
   112    112   	echo $(OX)\login.obj >> $@
   113    113   	echo $(OX)\main.obj >> $@
   114    114   	echo $(OX)\manifest.obj >> $@
   115    115   	echo $(OX)\md5.obj >> $@
   116    116   	echo $(OX)\merge.obj >> $@
   117    117   	echo $(OX)\merge3.obj >> $@
          118  +	echo $(OX)\moderate.obj >> $@
   118    119   	echo $(OX)\name.obj >> $@
   119    120   	echo $(OX)\path.obj >> $@
   120    121   	echo $(OX)\pivot.obj >> $@
   121    122   	echo $(OX)\popen.obj >> $@
   122    123   	echo $(OX)\pqueue.obj >> $@
   123    124   	echo $(OX)\printf.obj >> $@
   124    125   	echo $(OX)\rebuild.obj >> $@
................................................................................
   571    572   	translate$E $** > $@
   572    573   
   573    574   $(OX)\merge3$O : merge3_.c merge3.h
   574    575   	$(TCC) /Fo$@ -c merge3_.c
   575    576   
   576    577   merge3_.c : $(SRCDIR)\merge3.c
   577    578   	translate$E $** > $@
          579  +
          580  +$(OX)\moderate$O : moderate_.c moderate.h
          581  +	$(TCC) /Fo$@ -c moderate_.c
          582  +
          583  +moderate_.c : $(SRCDIR)\moderate.c
          584  +	translate$E $** > $@
   578    585   
   579    586   $(OX)\name$O : name_.c name.h
   580    587   	$(TCC) /Fo$@ -c name_.c
   581    588   
   582    589   name_.c : $(SRCDIR)\name.c
   583    590   	translate$E $** > $@
   584    591   
................................................................................
   807    814   $(OX)\zip$O : zip_.c zip.h
   808    815   	$(TCC) /Fo$@ -c zip_.c
   809    816   
   810    817   zip_.c : $(SRCDIR)\zip.c
   811    818   	translate$E $** > $@
   812    819   
   813    820   headers: makeheaders$E page_index.h VERSION.h
   814         -	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
          821  +	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
   815    822   	@copy /Y nul: headers