Check-in [5fb14b9a0f]
Not logged in
Overview

SHA1 Hash:5fb14b9a0fa04a788de384897e65458a9d8f8eaa
Date: 2008-08-21 20:59:01
User: eric
Comment:Include non-sym- tags in tagview web page. Also merge mainline into tagview branch.
Timelines: ancestors | descendants | both | eric-tagview-rework | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/admin.c from [3d7573884c] to [8499f27819].

@@ -108,10 +108,10 @@
   }
   admin_prepare_submenu();
   style_header("Admin");
   @ <h2>Links:</h2>
   @ <ul>
-  @ <li><a href='%s(g.zBaseURL)/admin/setup'>Fossil WWW Setup</a></li>
+  @ <li><a href='%s(g.zBaseURL)/setup'>Fossil WWW Setup</a></li>
   @ <li><a href='%s(g.zBaseURL)/admin/sql'>Run SQL queries</a></li>
   @ </ul>
   style_footer();
 }

Modified src/checkin.c from [b72cce6453] to [425f4bbcea].

@@ -250,11 +250,15 @@
   }
   if( zEditor==0 ){
     zEditor = getenv("EDITOR");
   }
   if( zEditor==0 ){
+#ifdef __MINGW32__
+    zEditor = "notepad";
+#else
     zEditor = "ed";
+#endif
   }
   zFile = db_text(0, "SELECT '%qci-comment-' || hex(randomblob(6)) || '.txt'",
                    g.zLocalRoot);
 #ifdef __MINGW32__
   blob_add_cr(&text);
@@ -329,11 +333,11 @@
 **
 ** Usage: %fossil commit ?-m COMMENT? ?--nosign? ?FILE...?
 **
 ** Create a new version containing all of the changes in the current
 ** checkout.  You will be prompted to enter a check-in comment unless
-** the "-m" option is used to specify a command line.  You will be
+** the "-m" option is used to specify a comment line.  You will be
 ** prompted for your GPG passphrase in order to sign the new manifest
 ** unless the "--nosign" options is used.  All files that have
 ** changed will be committed unless some subset of files is specified
 ** on the command line.
 */
@@ -593,11 +597,11 @@
 /*
 ** COMMAND: test-import-manifest
 **
 ** Usage: %fossil test-import-manifest DATE COMMENT ?-p PARENT_RECORDID?... ?-f (FILE_RECORDID PATH)?...
 **
-** Create a new version containing containing the specified file
+** Create a new version containing the specified file
 ** revisions (if any), and child of the given PARENT version.
 */
 void import_manifest_cmd(void){
   const char* zDate;    /* argument - timestamp, as seconds since epoch (int) */
   const char* zComment; /* argument - manifest comment */

Modified src/db.c from [2755f169fc] to [1ccbb50ea2].

@@ -789,13 +789,15 @@
      "INSERT INTO user(login, pw, cap, info)"
      "VALUES(%Q,'','s','')", zUser
   );
   db_multi_exec(
      "INSERT INTO user(login,pw,cap,info)"
-     "   VALUES('anonymous','anonymous','hjkorw','Anon');"
+     "   VALUES('anonymous','anonymous','aghknw','Anon');"
      "INSERT INTO user(login,pw,cap,info)"
      "   VALUES('nobody','','jor','Nobody');"
+     "INSERT INTO user(login,pw,cap,info)"
+     "   VALUES('developer','','deipt','Dev');"
   );
   user_select();
 
   if (makeInitialVersion){
     blob_zero(&manifest);

Modified src/info.c from [3ed7c9edba] to [33a76dcbd4].

@@ -116,11 +116,11 @@
   Stmt q;
   int cnt = 0;
   db_prepare(&q,
     "SELECT plink.cid, blob.uuid, datetime(plink.mtime, 'localtime'),"
     "       coalesce(event.euser,event.user),"
-    "       coalesce(event.comment,event.ecomment)"
+    "       coalesce(event.ecomment,event.comment)"
     "  FROM plink, blob, event"
     " WHERE plink.pid=%d"
     "   AND blob.rid=plink.cid"
     "   AND event.objid=plink.cid"
     " ORDER BY plink.mtime ASC",
@@ -168,11 +168,11 @@
   Stmt q;
   int cnt = 0;
   db_prepare(&q,
     "SELECT plink.pid, blob.uuid, datetime(event.mtime, 'localtime'),"
     "       coalesce(event.euser,event.user),"
-    "       coalesce(event.comment,event.ecomment)"
+    "       coalesce(event.ecomment,event.comment)"
     "  FROM plink, blob, event"
     " WHERE plink.cid=%d"
     "   AND blob.rid=plink.pid"
     "   AND event.objid=plink.pid"
     " ORDER BY event.mtime DESC",
@@ -354,25 +354,29 @@
     }else{
       @ <tr><th>Comment:</th><td>%w(zComment)</td></tr>
     }
     @ </td></tr>
     if( g.okHistory ){
+      char *zShortUuid = mprintf("%.10s", zUuid);
+      const char *zProjName = db_get("project-name", "unnamed");
       @ <tr><th>Timelines:</th><td>
       @    <a href="%s(g.zBaseURL)/timeline?p=%d(rid)">ancestors</a>
       @    | <a href="%s(g.zBaseURL)/timeline?d=%d(rid)">descendants</a>
       @    | <a href="%s(g.zBaseURL)/timeline?d=%d(rid)&p=%d(rid)">both</a>
       @ </td></tr>
       @ <tr><th>Commands:</th>
       @   <td>
       @     <a href="%s(g.zBaseURL)/vdiff/%d(rid)">diff</a>
-      @     | <a href="%s(g.zBaseURL)/zip/%s(zUuid).zip">ZIP archive</a>
+      @     | <a href="%s(g.zBaseURL)/zip/%s(zProjName)-%s(zShortUuid).zip?uuid=%s(zUuid)">
+      @         ZIP archive</a>
       @     | <a href="%s(g.zBaseURL)/artifact/%d(rid)">manifest</a>
       if( g.okWrite ){
         @     | <a href="%s(g.zBaseURL)/vedit?r=%d(rid)">edit</a>
       }
       @   </td>
       @ </tr>
+      free(zShortUuid);
     }
     @ </table></p>
   }else{
     style_header("Baseline Information");
     login_anonymous_available();
@@ -655,11 +659,11 @@
   Stmt q;
   int cnt = 0;
   int nWiki = 0;
   db_prepare(&q,
     "SELECT filename.name, datetime(event.mtime), substr(a.uuid,1,10),"
-    "       coalesce(event.comment,event.ecomment),"
+    "       coalesce(event.ecomment,event.comment),"
     "       coalesce(event.euser,event.user),"
     "       b.uuid"
     "  FROM mlink, filename, event, blob a, blob b"
     " WHERE filename.fnid=mlink.fnid"
     "   AND event.objid=mlink.mid"

Modified src/login.c from [86fb4c3fd8] to [d0f9596e5f].

@@ -113,11 +113,11 @@
       );
       cgi_redirect(zGoto);
       return;
     }
   }
-  if( zUsername!=0 && zPasswd!=0 ){
+  if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
     int uid = db_int(0,
         "SELECT uid FROM user"
         " WHERE login=%Q AND pw=%Q", zUsername, zPasswd);
     if( uid<=0 || strcmp(zUsername,"nobody")==0 ){
       sleep(1);
@@ -300,13 +300,15 @@
   g.userUid = uid;
   if( g.zLogin && strcmp(g.zLogin,"nobody")==0 ){
     g.zLogin = 0;
   }
   if( uid && g.zLogin ){
+    /* All logged-in users inherit privileges from "nobody" */
     zNcap = db_text("", "SELECT cap FROM user WHERE login = 'nobody'");
     login_set_capabilities(zNcap);
-    if( db_get_int("inherit-anon",0) ){
+    if( strcmp(g.zLogin, "anonymous")!=0 ){
+      /* All logged-in users inherit privileges from "anonymous" */
       zAcap = db_text("", "SELECT cap FROM user WHERE login = 'anonymous'");
       login_set_capabilities(zAcap);
     }
   }
   login_set_capabilities(zCap);
@@ -314,10 +316,11 @@
 
 /*
 ** Set the global capability flags based on a capability string.
 */
 void login_set_capabilities(const char *zCap){
+  static char *zDev = 0;
   int i;
   for(i=0; zCap[i]; i++){
     switch( zCap[i] ){
       case 's':   g.okSetup = 1;
       case 'a':   g.okAdmin = g.okRdTkt = g.okWrTkt =
@@ -325,10 +328,11 @@
                               g.okApndWiki = g.okHistory = g.okClone =
                               g.okNewTkt = g.okPassword = g.okRdAddr =
                               g.okTktFmt = 1;
       case 'i':   g.okRead = g.okWrite = 1;                     break;
       case 'o':   g.okRead = 1;                                 break;
+      case 'z':   g.okZip = 1;                                  break;
 
       case 'd':   g.okDelete = 1;                               break;
       case 'h':   g.okHistory = 1;                              break;
       case 'g':   g.okClone = 1;                                break;
       case 'p':   g.okPassword = 1;                             break;
@@ -343,10 +347,20 @@
       case 'n':   g.okNewTkt = 1;                               break;
       case 'w':   g.okWrTkt = g.okRdTkt = g.okNewTkt =
                   g.okApndTkt = 1;                              break;
       case 'c':   g.okApndTkt = 1;                              break;
       case 't':   g.okTktFmt = 1;                               break;
+
+      /* The "v" privileges is a little different.  It recursively
+      ** inherits all privileges of the user named "developer" */
+      case 'v': {
+        if( zDev==0 ){
+          zDev = db_text("", "SELECT cap FROM user WHERE login='developer'");
+          login_set_capabilities(zDev);
+        }
+        break;
+      }
     }
   }
 }
 
 /*
@@ -359,27 +373,35 @@
   int rc = 1;
   if( nCap<0 ) nCap = strlen(zCap);
   for(i=0; i<nCap && rc && zCap[i]; i++){
     switch( zCap[i] ){
       case 'a':  rc = g.okAdmin;     break;
+      /* case 'b': */
       case 'c':  rc = g.okApndTkt;   break;
       case 'd':  rc = g.okDelete;    break;
       case 'e':  rc = g.okRdAddr;    break;
       case 'f':  rc = g.okNewWiki;   break;
       case 'g':  rc = g.okClone;     break;
       case 'h':  rc = g.okHistory;   break;
       case 'i':  rc = g.okWrite;     break;
       case 'j':  rc = g.okRdWiki;    break;
       case 'k':  rc = g.okWrWiki;    break;
+      /* case 'l': */
       case 'm':  rc = g.okApndWiki;  break;
       case 'n':  rc = g.okNewTkt;    break;
       case 'o':  rc = g.okRead;      break;
       case 'p':  rc = g.okPassword;  break;
+      /* case 'q': */
       case 'r':  rc = g.okRdTkt;     break;
       case 's':  rc = g.okSetup;     break;
       case 't':  rc = g.okTktFmt;    break;
+      /* case 'u': */
+      /* case 'v': */
       case 'w':  rc = g.okWrTkt;     break;
+      /* case 'x': */
+      /* case 'y': */
+      case 'z':  rc = g.okZip;       break;
       default:   rc = 0;             break;
     }
   }
   return rc;
 }

Modified src/main.c from [7cc029d992] to [e3e3bcd32f].

@@ -117,10 +117,11 @@
   int okNewTkt;           /* n: create new tickets */
   int okApndTkt;          /* c: append to tickets via the web */
   int okWrTkt;            /* w: make changes to tickets via web */
   int okTktFmt;           /* t: create new ticket report formats */
   int okRdAddr;           /* e: read email addresses or other private data */
+  int okZip;              /* z: download zipped artifact via /zip URL */
 
   FILE *fDebug;           /* Write debug information here, if the file exists */
 
   /* Storage for the aux() and/or option() SQL function arguments */
   int nAux;                    /* Number of distinct aux() or option() values */

Modified src/my_page.c from [52bd9eb48f] to [98c9467776].

@@ -27,12 +27,12 @@
 */
 #include <assert.h>
 #include "config.h"
 #include "my_page.h"
 
-/**
-Renders a logout button.
+/*
+** Renders a logout button.
 */
 static void mypage_logout_button()
 {
   if( g.zLogin ){
     @ <br clear="both"/><hr/>
@@ -43,12 +43,12 @@
     @ <input type="submit" name="out" value="Logout"/></p>
     @ </form>
   }
 }
 
-/**
-Renders a password changer.
+/*
+** Renders a password changer.
 */
 static void mypage_password_changer()
 {
   if( g.okPassword ){
     @ <br clear="both"/><hr/>
@@ -71,12 +71,12 @@
     @ </form>
   }
 
 }
 
-/**
-Default page rendered for /my.
+/*
+** Default page rendered for /my.
 */
 static void mypage_page_default()
 {
   int uid = g.userUid;
   char * sql = mprintf( "SELECT login,cap,info FROM user WHERE uid=%d",

Modified src/name.c from [cf54ee1717] to [f2384ef316].

@@ -110,20 +110,21 @@
 **      1   A single UUID was found
 **      2   More than one UUID was found, so this is presumably a
 **          propagating tag. The return UUID is the most recent,
 **          which is most likely to be the one wanted.
 */
-int sym_tag_to_uuid(const char *pName, Blob *pUuid){
+int tag_to_uuid(const char *pName, Blob *pUuid,const char *pPrefix){
   Stmt q;
   int count = 0;
   db_prepare(&q,
     "SELECT (SELECT uuid FROM blob WHERE rid=objid)"
     "  FROM tagxref JOIN event ON rid=objid"
-    " WHERE tagid=(SELECT tagid FROM tag WHERE tagname='sym-'||%Q)"
+    " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q||%Q)"
     "   AND tagtype>0"
     "   AND value IS NULL"
     " ORDER BY event.mtime DESC",
+	pPrefix,
     pName
   );
   blob_zero(pUuid);
   while( db_step(&q)==SQLITE_ROW ){
     count++;
@@ -132,10 +133,13 @@
     }
     db_column_blob(&q, 0, pUuid);
   }
   db_finalize(&q);
   return count;
+}
+int sym_tag_to_uuid(const char *pName, Blob *pUuid){
+	return tag_to_uuid(pName,pUuid,"sym-");
 }
 
 /*
 ** COMMAND:  test-name-to-uuid
 **

Modified src/schema.c from [d06483adac] to [73d36a924e].

@@ -296,10 +296,13 @@
 # define TAG_COMMENT    2     /* The check-in comment */
 # define TAG_USER       3     /* User who made a checking */
 # define TAG_HIDDEN     4     /* Do not display or sync */
 # define TAG_PRIVATE    5     /* Display but do not sync */
 # define TAG_CLUSTER    6     /* A cluster */
+#endif
+#if EXPORT_INTERFACE
+# define MAX_INT_TAG    6     /* The largest pre-assigned tag id */
 #endif
 
 /*
 ** The schema for the locate FOSSIL database file found at the root
 ** of very check-out.  This database contains the complete state of

Modified src/setup.c from [7115d9c001] to [9a76b5ae3a].

@@ -134,52 +134,70 @@
   @ </table></td></tr></table>
   @ <td valign="top">
   @ <b>Notes:</b>
   @ <ol>
   @ <li><p>The permission flags are as follows:</p>
-  @ <ol type="a">
-  @ <li value="1"><b>Admin</b>: Create and delete users</li>
-  @ <li value="3"><b>Append-Tkt</b>: Append to tickets</li>
-  @ <li value="4"><b>Delete</b>: Delete wiki and tickets</li>
-  @ <li value="5"><b>Email</b>: View sensitive data such as EMail addresses</li>
-  @ <li value="6"><b>New-Wiki</b>: Create new wiki pages</li>
-  @ <li value="7"><b>Clone</b>: Clone the repository</li>
-  @ <li value="8"><b>History</b>: View detail repository history</li>
-  @ <li value="9"><b>Check-In</b>: Commit new versions in the repository</li>
-  @ <li value="10"><b>Read-Wiki</b>: View wiki pages</li>
-  @ <li value="11"><b>Write-Wiki</b>: Edit wiki pages</li>
-  @ <li value="13"><b>Append-Wiki</b>: Append to wiki pages</li>
-  @ <li value="14"><b>New-Tkt</b>: Create new tickets</li>
-  @ <li value="15"><b>Check-Out</b>: Check out versions</li>
-  @ <li value="16"><b>Password</b>: Change your own password</li>
-  @ <li value="18"><b>Read-Tkt</b>: View tickets</li>
-  @ <li value="19"><b>Setup:</b> Setup and configure this website</li>
-  @ <li value="20"><b>Tkt-Report:</b> Create new bug summary reports</li>
-  @ <li value="23"><b>Write-Tkt</b>: Edit tickets</li>
-  @ </ol>
-  @ </p></li>
-  @
-  @ <li><p>
-  @ Every user, logged in or not, has the privileges of <b>nobody</b>.
+  @ <table>
+     @ <tr><td><b>a</b></td><td><i>Admin:</i> Create and delete users</td></tr>
+     @ <tr><td><b>c</b></td><td><i>Append-Tkt:</i> Append to tickets</td></tr>
+     @ <tr><td><b>d</b></td><td><i>Delete:</i> Delete wiki and tickets</td></tr>
+     @ <tr><td><b>e</b></td><td><i>Email:</i> View sensitive data such as EMail addresses</td></tr>
+     @ <tr><td><b>f</b></td><td><i>New-Wiki:</i> Create new wiki pages</td></tr>
+     @ <tr><td><b>g</b></td><td><i>Clone:</i> Clone the repository</td></tr>
+     @ <tr><td><b>h</b></td><td><i>History:</i> View detail repository history</td></tr>
+     @ <tr><td><b>i</b></td><td><i>Check-In:</i> Commit new versions in the repository</td></tr>
+     @ <tr><td><b>j</b></td><td><i>Read-Wiki:</i> View wiki pages</td></tr>
+     @ <tr><td><b>k</b></td><td><i>Write-Wiki:</i> Edit wiki pages</td></tr>
+     @ <tr><td><b>m</b></td><td><i>Append-Wiki:</i> Append to wiki pages</td></tr>
+     @ <tr><td><b>n</b></td><td><i>New-Tkt:</i> Create new tickets</td></tr>
+     @ <tr><td><b>o</b></td><td><i>Check-Out:</i> Check out versions</td></tr>
+     @ <tr><td><b>p</b></td><td><i>Password:</i> Change your own password</td></tr>
+     @ <tr><td><b>r</b></td><td><i>Read-Tkt:</i> View tickets</td></tr>
+     @ <tr><td><b>s</b></td><td><i>Setup:</i> Setup and configure this website</td></tr>
+     @ <tr><td><b>t</b></td><td><i>Tkt-Report:</i> Create new bug summary reports</td></tr>
+     @ <tr><td><b>v</b></td><td><i>Developer:</i> Inherit privileges of user <tt>developer</tt></td></tr>
+     @ <tr><td><b>w</b></td><td><i>Write-Tkt:</i> Edit tickets</td></tr>
+     @ <tr><td><b>z</b></td><td><i>Zip download:</i> Download a baseline via the
+     @ <tt>/zip</tt> URL even without check<b>o</b>ut and <b>h</b>istory permissions</td></tr>
+  @ </table>
+  @ </li>
+  @
+  @ <li><p>
+  @ Every user, logged in or not, inherits the privileges of <b>nobody</b>.
   @ Any human can login as <b>anonymous</b> since the password is
   @ clearly displayed on the login page for them to type.  The purpose
   @ of requiring anonymous to log in is to prevent access by spiders.
+  @ Every logged-in user inherits the privileges of <b>anonymous</b>.
   @ </p></li>
   @
   @ </ol>
   @ </td></tr></table>
   style_footer();
 }
 
 /*
+** Return true if zPw is a valid password string.  A valid
+** password string is:
+**
+**  (1)  A zero-length string, or
+**  (2)  a string that contains a character other than '*'.
+*/
+static int isValidPwString(const char *zPw){
+  if( zPw==0 ) return 0;
+  if( zPw[0]==0 ) return 1;
+  while( zPw[0]=='*' ){ zPw++; }
+  return zPw[0]!=0;
+}
+
+/*
 ** WEBPAGE: /setup_uedit
 */
 void user_edit(void){
-  const char *zId, *zLogin, *zInfo, *zCap;
+  const char *zId, *zLogin, *zInfo, *zCap, *zPw;
   char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
   char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
-  char *oat;
+  char *oat, *oav, *oaz;
   int doWrite;
   int uid;
   int higherUser = 0;  /* True if user being edited is SETUP and the */
                        /* user doing the editing is ADMIN.  Disallow editing */
 
@@ -208,12 +226,10 @@
   ** modified user record.  After writing the user record, redirect
   ** to the page that displays a list of users.
   */
   doWrite = cgi_all("login","info","pw") && !higherUser;
   if( doWrite ){
-    const char *zPw;
-    const char *zLogin;
     char zCap[50];
     int i = 0;
     int aa = P("aa")!=0;
     int ad = P("ad")!=0;
     int ae = P("ae")!=0;
@@ -230,10 +246,12 @@
     int af = P("af")!=0;
     int am = P("am")!=0;
     int ah = P("ah")!=0;
     int ag = P("ag")!=0;
     int at = P("at")!=0;
+    int av = P("av")!=0;
+    int az = P("az")!=0;
     if( aa ){ zCap[i++] = 'a'; }
     if( ac ){ zCap[i++] = 'c'; }
     if( ad ){ zCap[i++] = 'd'; }
     if( ae ){ zCap[i++] = 'e'; }
     if( af ){ zCap[i++] = 'f'; }
@@ -247,15 +265,17 @@
     if( ao ){ zCap[i++] = 'o'; }
     if( ap ){ zCap[i++] = 'p'; }
     if( ar ){ zCap[i++] = 'r'; }
     if( as ){ zCap[i++] = 's'; }
     if( at ){ zCap[i++] = 't'; }
+    if( av ){ zCap[i++] = 'v'; }
     if( aw ){ zCap[i++] = 'w'; }
+    if( az ){ zCap[i++] = 'z'; }
 
     zCap[i] = 0;
     zPw = P("pw");
-    if( zPw==0 || zPw[0]==0 ){
+    if( !isValidPwString(zPw) ){
       zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
     }
     zLogin = P("login");
     if( uid>0 &&
         db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
@@ -280,16 +300,18 @@
   /* Load the existing information about the user, if any
   */
   zLogin = "";
   zInfo = "";
   zCap = "";
+  zPw = "";
   oaa = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
-        oan = oao = oap = oar = oas = oat = oaw = "";
+        oan = oao = oap = oar = oas = oat = oav = oaw = oaz = "";
   if( uid ){
     zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
     zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
     zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
+    zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
     if( strchr(zCap, 'a') ) oaa = " checked";
     if( strchr(zCap, 'c') ) oac = " checked";
     if( strchr(zCap, 'd') ) oad = " checked";
     if( strchr(zCap, 'e') ) oae = " checked";
     if( strchr(zCap, 'f') ) oaf = " checked";
@@ -303,11 +325,13 @@
     if( strchr(zCap, 'o') ) oao = " checked";
     if( strchr(zCap, 'p') ) oap = " checked";
     if( strchr(zCap, 'r') ) oar = " checked";
     if( strchr(zCap, 's') ) oas = " checked";
     if( strchr(zCap, 't') ) oat = " checked";
+    if( strchr(zCap, 'v') ) oav = " checked";
     if( strchr(zCap, 'w') ) oaw = " checked";
+    if( strchr(zCap, 'z') ) oaz = " checked";
   }
 
   /* Begin generating the page
   */
   style_submenu_element("Cancel", "Cancel", "setup_ulist");
@@ -346,35 +370,46 @@
   @     <input type="checkbox" name="ae"%s(oad)>Email</input><br>
   @     <input type="checkbox" name="ap"%s(oap)>Password</input><br>
   @     <input type="checkbox" name="ai"%s(oai)>Check-In</input><br>
   @     <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br>
   @     <input type="checkbox" name="ah"%s(oah)>History</input><br>
+  @     <input type="checkbox" name="av"%s(oav)>Developer</input><br>
   @     <input type="checkbox" name="ag"%s(oag)>Clone</input><br>
   @     <input type="checkbox" name="aj"%s(oaj)>Read Wiki</input><br>
   @     <input type="checkbox" name="af"%s(oaf)>New Wiki</input><br>
   @     <input type="checkbox" name="am"%s(oam)>Append Wiki</input><br>
   @     <input type="checkbox" name="ak"%s(oak)>Write Wiki</input><br>
   @     <input type="checkbox" name="ar"%s(oar)>Read Tkt</input><br>
   @     <input type="checkbox" name="an"%s(oan)>New Tkt</input><br>
   @     <input type="checkbox" name="ac"%s(oac)>Append Tkt</input><br>
   @     <input type="checkbox" name="aw"%s(oaw)>Write Tkt</input><br>
-  @     <input type="checkbox" name="at"%s(oat)>Tkt Report</input>
+  @     <input type="checkbox" name="at"%s(oat)>Tkt Report</input><br>
+  @     <input type="checkbox" name="az"%s(oaz)>Download Zip</input>
   @   </td>
   @ </tr>
   @ <tr>
   @   <td align="right">Password:</td>
-  @   <td><input type="password" name="pw" value=""></td>
+  if( strcmp(zLogin, "anonymous")==0 ){
+    /* User the password for "anonymous" as cleartext */
+    @   <td><input type="text" name="pw" value="%h(zPw)"></td>
+  }else if( zPw[0] ){
+    /* Obscure the password for all other users */
+    @   <td><input type="password" name="pw" value="**********"></td>
+  }else{
+    /* Show an empty password as an empty input field */
+    @   <td><input type="password" name="pw" value=""></td>
+  }
   @ </tr>
   if( !higherUser ){
     @ <tr>
     @   <td>&nbsp</td>
     @   <td><input type="submit" name="submit" value="Apply Changes">
     @ </tr>
   }
   @ </table></td></tr></table>
-  @ <p><b>Notes:</b></p>
-  @ <ol>
+  @ <h2>Privileges And Capabilities:</h2>
+  @ <ul>
   if( higherUser ){
     @ <li><p><font color="blue"><b>
     @ User %h(zLogin) has Setup privileges and you only have Admin privileges
     @ so you are not permitted to make changes to %h(zLogin).
     @ </b></font></p></li>
@@ -402,10 +437,24 @@
   @ user "nobody" to avoid problems with spiders trying to walk every
   @ historical version of every baseline and file.
   @ </p></li>
   @
   @ <li><p>
+  @ The <b>Zip</b> privilege allows a user to see the download as zip hyperlink
+  @ as well as permit access to the <tt>/zip</tt> page. It can be allowed for
+  @ user "nobody" to grant him access to download artifacts he know from the
+  @ server without giving him other rights like <b>Read</b> or <b>History</b>.
+  @ So automatic package dowloaders could be able to obtain the sources without
+  @ going thru the login procedure.
+  @ </p></li>
+  @
+  @ <li><p>
+  @ The <b>Developer</b> privilege causes all privileges of the user
+  @ named "developer" to be inherited by this user.
+  @ </p></li>
+  @
+  @ <li><p>
   @ The <b>Check-in</b> privilege allows remote users to "push".
   @ The <b>Check-out</b> privilege allows remote users to "pull".
   @ The <b>Clone</b> privilege allows remote users to "clone".
   @ </li><p>
   @
@@ -418,39 +467,58 @@
   @ ticket report formats.
   @ </p></li>
   @
   @ <li><p>
   @ Users with the <b>Password</b> privilege are allowed to change their
-  @ own password.  Recommended ON for most users but OFF for "anonynmous"
-  @ and "nobody".
+  @ own password.  Recommended ON for most users but OFF for special
+  @ users "developer, "anonynmous", and "nobody".
   @ </p></li>
   @
   @ <li><p>
   @ The <b>EMail</b> privilege allows the display of sensitive information
   @ such as the email address of users and contact information on tickets.
   @ Recommended OFF for "anonymous" and for "nobody".
   @ </p></li>
   @
   @ <li><p>
+  @ Login is prohibited if the password is an empty string.
+  @ </p></li>
+  @ </ul>
+  @
+  @ <h2>Special Logins</h2>
+  @
+  @ <ul>
+  @ <li><p>
   @ No login is required for user "<b>nobody</b>".  The capabilities
-  @ of this user are available to anyone without supplying a username or
-  @ password.  To disable nobody access, make sure there is no user
-  @ with an ID of <b>nobody</b> or that the nobody user has no
-  @ capabilities enabled.  The password for nobody is ignore.  To
-  @ avoid problems with spiders overloading the server, it is suggested
-  @ that the 'h' (History) capability be turned off for user nobody.
+  @ of the <b>nobody</b> user are inherited by all users, regardless of
+  @ whether or not they are logged in.  To disable universal access
+  @ to the repository, make sure no user named "<b>nobody</b>" exists or
+  @ that the <b>nobody</b> user has no capabilities enabled.
+  @ The password for <b>nobody</b> is ignore.  To avoid problems with
+  @ spiders overloading the server, it is recommended
+  @ that the 'h' (History) capability be turned off for the <b>nobody</b>
+  @ user.
   @ </p></li>
   @
   @ <li><p>
   @ Login is required for user "<b>anonymous</b>" but the password
   @ is displayed on the login screen beside the password entry box
   @ so anybody who can read should be able to login as anonymous.
   @ On the other hand, spiders and web-crawlers will typically not
   @ be able to login.  Set the capabilities of the anonymous user
   @ to things that you want any human to be able to do, but not any
-  @ spider.
+  @ spider.  Every other logged-in user inherits the privileges of
+  @ <b>anonymous</b>.
   @ </p></li>
+  @
+  @ <li><p>
+  @ The "<b>developer</b>" user is intended as a template for trusted users
+  @ with check-in privileges.  When adding new trusted users, simply
+  @ select the <b>Developer</b> privilege to cause the new user to inherit
+  @ all privileges of the "developer" user.
+  @ </li></p>
+  @ </ul>
   @ </form>
   style_footer();
 }
 
 

Modified src/style.c from [934dddd1ca] to [89fb0defc5].


Modified src/tag.c from [ec276e4852] to [842caf6a79].

@@ -247,18 +247,73 @@
   tag_insert(zTag, tagtype, zValue, -1, 0.0, rid);
   db_end_transaction(0);
 }
 
 /*
+** Prepare an artifact that describes a fork from a certain UUID.
+** Furthermore a propagating symbolic tag will be inserted and
+** all other propagating symbolic tags will be cancelled.
+**
+** The changes are appended at the Blob pCtrl. However the manifest
+** is not complete at that stage.
+*/
+static void tag_prepare_fork(
+  Blob *pCtrl,
+  const char *zTagname,
+  int rid
+){
+  Stmt q;
+  Manifest origin;
+  Blob originContent;
+  char *zDate;
+  int i;
+
+  blob_appendf(pCtrl, "C Create\\snamed\\sfork\\s%s\n", zTagname+4);
+  content_get(rid, &originContent);
+  manifest_parse(&origin, &originContent);
+  zDate = db_text(0, "SELECT datetime('now')");
+  zDate[10] = 'T';
+  blob_appendf(pCtrl, "D %s\n", zDate);
+  for(i=0; i<origin.nFile; ++i){
+    blob_appendf(pCtrl, "F %s %s %s\n",
+                 origin.aFile[i].zName,
+                 origin.aFile[i].zUuid,
+                 origin.aFile[i].zPerm);
+  }
+  if( origin.nParent>0 ){
+    blob_appendf(pCtrl, "P %s\n", origin.azParent[0]);
+  }
+  blob_appendf(pCtrl, "R %s\n", origin.zRepoCksum);
+  blob_appendf(pCtrl, "T *%F *", zTagname);
+
+  /* Cancel any sym- tags that propagate */
+  db_prepare(&q,
+      "SELECT tagname FROM tagxref, tag"
+      " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
+      "   AND tagtype>0 AND tagname LIKE 'sym-%%'"
+      " ORDER BY tagname",
+      rid);
+  while( db_step(&q)==SQLITE_ROW ){
+    const char *zTag = db_column_text(&q, 0);
+    blob_appendf(pCtrl, "\nT -%s *", zTag);
+  }
+  db_finalize(&q);
+
+  /* Cleanup */
+  manifest_clear(&origin);
+}
+
+/*
 ** Add a control record to the repository that either creates
 ** or cancels a tag.
 */
 static void tag_add_artifact(
   const char *zTagname,       /* The tag to add or cancel */
   const char *zObjName,       /* Name of object attached to */
   const char *zValue,         /* Value for the tag.  Might be NULL */
-  int tagtype                 /* 0:cancel 1:singleton 2:propagated */
+  int tagtype,                /* 0:cancel 1:singleton 2:propagated */
+  int fork                    /* Should a fork created from zObjName? */
 ){
   int rid;
   int nrid;
   char *zDate;
   Blob uuid;
@@ -278,14 +333,18 @@
 
   if( validate16(zTagname, strlen(zTagname)) ){
     fossil_fatal("invalid tag name \"%s\" - might be confused with a UUID",
                  zTagname);
   }
-  zDate = db_text(0, "SELECT datetime('now')");
-  zDate[10] = 'T';
-  blob_appendf(&ctrl, "D %s\n", zDate);
-  blob_appendf(&ctrl, "T %c%F %b", zTagtype[tagtype], zTagname, &uuid);
+  if( fork ){
+    tag_prepare_fork(&ctrl, zTagname, rid);
+  }else{
+    zDate = db_text(0, "SELECT datetime('now')");
+    zDate[10] = 'T';
+    blob_appendf(&ctrl, "D %s\n", zDate);
+    blob_appendf(&ctrl, "T %c%F %b", zTagtype[tagtype], zTagname, &uuid);
+  }
   if( tagtype && zValue && zValue[0] ){
     blob_appendf(&ctrl, " %F\n", zValue);
   }else{
     blob_appendf(&ctrl, "\n");
   }
@@ -309,20 +368,31 @@
 **
 **     %fossil tag add ?--raw? TAGNAME UUID ?VALUE?
 **
 **         Add a new tag or property to UUID. The tag will
 **         be usable instead of a UUID in commands like
-**         update and the like.
+**         update and such.
 **
 **     %fossil tag branch ?--raw? TAGNAME UUID ?VALUE?
 **
-**         Add a new tag or property to UUID and make that
-**         tag propagate to all direct children.
-**
-**     %fossil tag delete ?--raw? TAGNAME UUID
+**         A fork of UUID will be created. Then the new tag
+**         or property will be added to the fork that
+**         propagate to all direct children.
+**
+**         Additionally all symbolic tags of that fork
+**         inherited from UUID will be cancelled.
+**
+**         However, if the option '--raw' was given, no
+**         fork will be created but the tag/property will be
+**         added to UUID directly and no tag will be
+**         canceled.
+**
+**         Please see the description of '--raw' below too.
+**
+**     %fossil tag cancel ?--raw? TAGNAME UUID
 **
-**         Delete the tag TAGNAME from UUID
+**         Cancel the tag TAGNAME from UUID
 **
 **     %fossil tag find ?--raw? TAGNAME
 **
 **         List all baselines that use TAGNAME
 **
@@ -369,39 +439,43 @@
   blob_set(&tagname, prefix);
 
   if( strncmp(g.argv[2],"add",n)==0 ){
     char *zValue;
     if( g.argc!=5 && g.argc!=6 ){
-      usage("add TAGNAME UUID ?VALUE?");
+      usage("add ?--raw? TAGNAME UUID ?VALUE?");
     }
     blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
     zValue = g.argc==6 ? g.argv[5] : 0;
-    tag_add_artifact(blob_str(&tagname), g.argv[4], zValue, 1);
+    tag_add_artifact(blob_str(&tagname), g.argv[4], zValue, 1, 0);
   }else
 
   if( strncmp(g.argv[2],"branch",n)==0 ){
     char *zValue;
     if( g.argc!=5 && g.argc!=6 ){
-      usage("branch TAGNAME UUID ?VALUE?");
+      usage("branch ?--raw? TAGNAME UUID ?VALUE?");
     }
     blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
     zValue = g.argc==6 ? g.argv[5] : 0;
-    tag_add_artifact(blob_str(&tagname), g.argv[4], zValue, 2);
+    tag_add_artifact(blob_str(&tagname), g.argv[4], zValue, 2, raw==0);
+    if( !raw ){
+      const char *zUuid = db_text(0, "SELECT uuid, MAX(rowid) FROM blob");
+      printf("New_Fork \"%s\": %s\n", g.argv[3], zUuid);
+    }
   }else
 
-  if( strncmp(g.argv[2],"delete",n)==0 ){
+  if( strncmp(g.argv[2],"cancel",n)==0 ){
     if( g.argc!=5 ){
-      usage("delete TAGNAME UUID");
+      usage("cancel ?--raw? TAGNAME UUID");
     }
     blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
-    tag_add_artifact(blob_str(&tagname), g.argv[4], 0, 0);
+    tag_add_artifact(blob_str(&tagname), g.argv[4], 0, 0, 0);
   }else
 
   if( strncmp(g.argv[2],"find",n)==0 ){
     Stmt q;
     if( g.argc!=4 ){
-      usage("find TAGNAME");
+      usage("find ?--raw? TAGNAME");
     }
     blob_append(&tagname, g.argv[3], strlen(g.argv[3]));
     db_prepare(&q,
       "SELECT blob.uuid FROM tagxref, blob"
       " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%B)"
@@ -415,12 +489,11 @@
 
   if( strncmp(g.argv[2],"list",n)==0 ){
     Stmt q;
     if( g.argc==3 ){
       db_prepare(&q,
-        "SELECT tagname"
-        "  FROM tag"
+        "SELECT tagname FROM tag"
         " WHERE EXISTS(SELECT 1 FROM tagxref"
         "               WHERE tagid=tag.tagid"
         "                 AND tagtype>0)"
         " ORDER BY tagname"
       );
@@ -432,12 +505,11 @@
       }
       db_finalize(&q);
     }else if( g.argc==4 ){
       int rid = name_to_rid(g.argv[3]);
       db_prepare(&q,
-        "SELECT tagname, value"
-        "  FROM tagxref, tag"
+        "SELECT tagname, value FROM tagxref, tag"
         " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
         "   AND tagtype>0"
         " ORDER BY tagname",
         rid
       );
@@ -466,7 +538,7 @@
   /* Cleanup */
   blob_reset(&tagname);
   return;
 
 tag_cmd_usage:
-  usage("add|branch|delete|find|list ...");
+  usage("add|branch|cancel|find|list ...");
 }

Modified src/tagview.c from [b4a9071e7b] to [49b12135cb].

@@ -135,36 +135,10 @@
   db_generic_query_view(zSql, 1);
   free(zSql);
 }
 
 /*
-** Get the UUIDs for a tag
-*/
-char *tag_query_for_www(const char *pName){
-  static const char zBaseSql[] =
-  @ SELECT
-  @   blob.rid,
-  @   uuid,
-  @   datetime(event.mtime,'localtime') AS timestamp,
-  @   coalesce(ecomment, comment),
-  @   coalesce(euser, user),
-  @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim=1),
-  @   (SELECT count(*) FROM plink WHERE cid=blob.rid),
-  @   NOT EXISTS (SELECT 1 FROM plink WHERE pid=blob.rid),
-  @   coalesce(bgcolor, brbgcolor),
-  @   event.type
-  @  FROM event JOIN blob JOIN tagxref
-  @ WHERE blob.rid=event.objid
-  @ AND tagxref.rid = event.objid
-  @ AND tagxref.tagid = (SELECT tagid FROM tag
-  @       WHERE tagname = 'sym-'||%Q)
-  @ ORDER BY 3 desc
-  ;
-  return mprintf(zBaseSql,pName);
-}
-
-/*
 ** WEBP AGE: /tagview
 */
 void old_tagview_page(void){
   char const * check = 0;
   login_check_credentials();
@@ -202,11 +176,31 @@
     char *zSql;
     Stmt q;
     if( sym_tag_to_uuid(zName, &uuid) > 0){
       style_header("Tagged Baselines");
       @ <h2>%s(zName):</h2>
-      zSql = tag_query_for_www(zName);
+      zSql = mprintf("%s AND EXISTS (SELECT 1"
+             " FROM tagxref"
+             "  WHERE tagxref.rid = event.objid"
+             "  AND tagxref.tagid = (SELECT tagid FROM tag"
+             "      WHERE tagname = 'sym-'||%Q))"
+             " ORDER BY 3 desc",
+             timeline_query_for_www(), zName);
+      db_prepare(&q, zSql);
+      free(zSql);
+      www_print_timeline(&q);
+      db_finalize(&q);
+    }else if( tag_to_uuid(zName, &uuid, "") > 0){
+      style_header("Tagged Baselines");
+      @ <h2>%s(zName):</h2>
+      zSql = mprintf("%s AND EXISTS (SELECT 1"
+             " FROM tagxref"
+             "  WHERE tagxref.rid = event.objid"
+             "  AND tagxref.tagid = (SELECT tagid FROM tag"
+             "      WHERE tagname = %Q))"
+             " ORDER BY 3 desc",
+             timeline_query_for_www(), zName);
       db_prepare(&q, zSql);
       free(zSql);
       www_print_timeline(&q);
       db_finalize(&q);
     }else{
@@ -223,22 +217,29 @@
       "SELECT tagname"
       "  FROM tag"
       " WHERE EXISTS(SELECT 1 FROM tagxref"
       "               WHERE tagid=tag.tagid"
       "                 AND tagtype>0)"
-      " ORDER BY tagname"
+      " AND tagid > %d"
+      " AND tagname NOT GLOB 'wiki-*'"
+      " AND tagname NOT GLOB 'tkt-*'"
+      " ORDER BY tagname",
+      MAX_INT_TAG
     );
     @ <ul>
     while( db_step(&q)==SQLITE_ROW ){
       const char *name = db_column_text(&q, 0);
       if( strncmp(name, prefix, preflen)==0 ){
         @ <li><a href=%s(g.zBaseURL)/tagview?name=%s(name+preflen)>
-        @ %s(name+preflen)</a></li>
+        @ <strong>%s(name+preflen)</strong></a></li>
+      }else{
+        @ <li><a href=%s(g.zBaseURL)/tagview?name=%s(name)>
+        @ %s(name)</a></li>
       }
     }
     @ </ul>
     db_finalize(&q);
   }
   style_footer();
 }
 
 #undef TAGVIEW_DEFAULT_FILTER

Modified src/zip.c from [8367a41493] to [c82f467b68].

@@ -351,31 +351,34 @@
 ** Generate a ZIP archive for the baseline.
 ** Return that ZIP archive as the HTTP reply content.
 */
 void baseline_zip_page(void){
   int rid;
-  char *zName;
-  int nName;
+  char *zName, *zRid;
+  int nName, nRid;
   Blob zip;
 
   login_check_credentials();
-  if( !g.okRead || !g.okHistory ){ login_needed(); return; }
+  if( !g.okZip && (!g.okRead || !g.okHistory) ){ login_needed(); return; }
   zName = mprintf("%s", PD("name",""));
   nName = strlen(zName);
+  zRid = mprintf("%s", PD("uuid",""));
+  nRid = strlen(zRid);
   for(nName=strlen(zName)-1; nName>5; nName--){
     if( zName[nName]=='.' ){
       zName[nName] = 0;
       break;
     }
   }
-  rid = name_to_rid(zName);
+  rid = name_to_rid(nRid?zRid:zName);
   if( rid==0 ){
     @ Not found
     return;
   }
-  if( nName>10 ) zName[10] = 0;
+  if( nRid==0 && nName>10 ) zName[10] = 0;
   zip_of_baseline(rid, &zip, zName);
   free( zName );
+  free( zRid );
   cgi_set_content(&zip);
   cgi_set_content_type("application/zip");
   cgi_reply();
 }