Check-in [ffe92f1a2f]
Not logged in
Overview

SHA1 Hash:ffe92f1a2f103efe35fa61d108a5ef060a50861e
Date: 2008-02-13 19:50:27
User: drh
Comment:The entire header, including the menu bar, is now generated by TH script. This allows the menu bar to be customized by editing the header script.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/style.c from [efdb67a174] to [da3e8aee87].

@@ -72,11 +72,10 @@
 
 /*
 ** Draw the header.
 */
 void style_header(const char *zTitle){
-  const char *zLogInOut = "Login";
   const char *zHeader = db_get("header", (char*)zDefaultHeader);
   login_check_credentials();
 
   cgi_destination(CGI_HEADER);
 
@@ -86,40 +85,12 @@
   Th_InitVar("baseurl", g.zBaseURL);
   Th_InitVar("manifest_version", MANIFEST_VERSION);
   Th_InitVar("manifest_date", MANIFEST_DATE);
   if( g.zLogin ){
     Th_InitVar("login", g.zLogin);
-    zLogInOut = "Logout";
   }
   Th_Render(zHeader);
-
-  /* Generate the main menu */
-  @ <div class="mainmenu">
-  @ <a href="%s(g.zBaseURL)/home">Home</a>
-  if( g.okHistory ){
-    @ <a href="%s(g.zBaseURL)/dir">Files</a>
-  }
-  if( g.okRead ){
-    @ <a href="%s(g.zBaseURL)/leaves">Leaves</a>
-    @ <a href="%s(g.zBaseURL)/timeline">Timeline</a>
-    @ <a href="%s(g.zBaseURL)/tagview">Tags</a>
-  }
-  if( g.okRdWiki ){
-    @ <a href="%s(g.zBaseURL)/wiki">Wiki</a>
-  }
-#if 0
-  @ <font color="#888888">Search</font>
-  @ <font color="#888888">Ticket</font>
-  @ <font color="#888888">Reports</font>
-#endif
-  if( g.okSetup ){
-    @ <a href="%s(g.zBaseURL)/setup">Setup</a>
-  }
-  if( !g.noPswd ){
-    @ <a href="%s(g.zBaseURL)/login">%s(zLogInOut)</a>
-  }
-  @ </div>
   cgi_destination(CGI_BODY);
   g.cgiPanic = 1;
 }
 
 /*
@@ -162,31 +133,53 @@
 ** The default page header.
 */
 const char zDefaultHeader[] =
 @ <html>
 @ <head>
-@ <title><th1>puts "$project_name: $title"</th1></title>
+@ <title>$<project_name>: $<title></title>
 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
 @       href="$baseurl/timeline.rss">
 @ <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
 @       media="screen">
 @ </head>
 @ <body>
 @ <div class="header">
 @   <div class="logo">
 @     <!-- <img src="logo.gif" alt="logo"><br></br> -->
-@     <nobr><th1>puts $project_name</th1></nobr>
+@     <nobr>$<project_name></nobr>
 @   </div>
-@   <div class="title"><th1>puts $title</th1></div>
+@   <div class="title">$<title></div>
 @   <div class="status"><nobr><th1>
 @      if {[info exists login]} {
 @        html "Logged in as <a href='$baseurl/my'>$login</a>"
 @      } else {
 @        puts "Not logged in"
 @      }
 @   </th1></nobr></div>
 @ </div>
+@ <div class="mainmenu"><th1>
+@ html "<a href='$baseurl/home'>Home</a>"
+@ if {[hascap h]} {
+@   html "<a href='$baseurl/dir'>Files</a>"
+@ }
+@ if {[hascap i]} {
+@   html "<a href='$baseurl/leaves'>Leaves</a>"
+@   html "<a href='$baseurl/timeline'>Timeline</a>"
+@   html "<a href='$baseurl/tagview'>Tags</a>"
+@ }
+@ if {[hascap j]} {
+@   html "<a href='$baseurl/wiki'>Wiki</a>"
+@ }
+@ if {[hascap s]} {
+@   html "<a href='$baseurl/setup'>Setup</a>"
+@ }
+@ if {[info exists login]} {
+@   html "<a href='$baseurl/login'>Login</a>"
+@ } else {
+@   html "<a href='$baseurl/login'>Logout</a>"
+@ }
+@ </th1></div>
 ;
 
 /*
 ** The default page footer
 */

Modified src/th_main.c from [7f63a6f888] to [9966e4d470].

@@ -84,18 +84,23 @@
 
 /*
 ** Send text to the appropriate output:  Either to the console
 ** or to the CGI reply buffer.
 */
-static void sendText(const char *z, int n){
+static void sendText(const char *z, int n, int encode){
   if( enableOutput && n ){
     if( n<0 ) n = strlen(z);
+    if( encode ){
+      z = htmlize(z, n);
+      n = strlen(z);
+    }
     if( g.cgiPanic ){
       cgi_append_content(z, n);
     }else{
       fwrite(z, 1, n, stdout);
     }
+    if( encode ) free((char*)z);
   }
 }
 
 /*
 ** TH command:     puts STRING
@@ -111,25 +116,11 @@
   int *argl
 ){
   if( argc!=2 ){
     return Th_WrongNumArgs(interp, "puts STRING");
   }
-  if( enableOutput ){
-    int size;
-    char *zOut;
-    if( pConvert ){
-      zOut = htmlize((char*)argv[1], argl[1]);
-      size = strlen(zOut);
-    }else{
-      zOut = (char*)argv[1];
-      size = argl[1];
-    }
-    sendText(zOut, size);
-    if( pConvert ){
-      free(zOut);
-    }
-  }
+  sendText((char*)argv[1], argl[1], pConvert!=0);
   return TH_OK;
 }
 
 /*
 ** TH command:      wiki STRING
@@ -142,18 +133,37 @@
   int argc,
   const unsigned char **argv,
   int *argl
 ){
   if( argc!=2 ){
-    return Th_WrongNumArgs(interp, "puts STRING");
+    return Th_WrongNumArgs(interp, "wiki STRING");
   }
   if( enableOutput ){
     Blob src;
     blob_init(&src, (char*)argv[1], argl[1]);
     wiki_convert(&src, 0, WIKI_INLINE);
     blob_reset(&src);
   }
+  return TH_OK;
+}
+
+/*
+** TH command:     hascap STRING
+**
+** Return true if the user has all of the capabilities listed in STRING.
+*/
+static int hascapCmd(
+  Th_Interp *interp,
+  void *p,
+  int argc,
+  const unsigned char **argv,
+  int *argl
+){
+  if( argc!=2 ){
+    return Th_WrongNumArgs(interp, "hascap STRING");
+  }
+  Th_SetResultInt(interp, login_has_capability((char*)argv[1],argl[1]));
   return TH_OK;
 }
 
 /*
 ** Make sure the interpreter has been initialized.
@@ -163,10 +173,11 @@
     const char *zName;
     Th_CommandProc xProc;
     void *pContext;
   } aCommand[] = {
     {"enable_output", enableOutputCmd,      0},
+    {"hascap",        hascapCmd,            0},
     {"html",          putsCmd,              0},
     {"puts",          putsCmd,       (void*)1},
     {"wiki",          wikiCmd,              0},
   };
   if( interp==0 ){
@@ -183,11 +194,11 @@
 /*
 ** Initialize a variable in the interpreter.
 */
 void Th_InitVar(const char *zName, const char *zValue){
   initializeInterp();
-  Th_SetVar(interp, zName, -1, zValue, strlen(zValue));
+  Th_SetVar(interp, (uchar*)zName, -1, (uchar*)zValue, strlen(zValue));
 }
 
 /*
 ** Return true if the string begins with the TH1 begin-script
 ** tag:  <th1>.
@@ -217,48 +228,72 @@
 ** If string z[0...] contains a valid variable name, return
 ** the number of characters in that name.  Otherwise, return 0.
 */
 static int validVarName(const char *z){
   int i = 0;
+  int inBracket = 0;
+  if( z[0]=='<' ){
+    inBracket = 1;
+    z++;
+  }
   if( z[0]==':' && z[1]==':' && isalpha(z[2]) ){
     z += 3;
-    i = 3;
+    i += 3;
   }else if( isalpha(z[0]) ){
     z ++;
-    i = 1;
+    i += 1;
   }else{
     return 0;
   }
   while( isalnum(z[0]) || z[0]=='_' ){
     z++;
     i++;
   }
+  if( inBracket ){
+    if( z[0]!='>' ) return 0;
+    i += 2;
+  }
   return i;
 }
 
 /*
 ** The z[] input contains text mixed with TH1 scripts.
-** The TH1 scripts are contained within <th1>...</th1>.  This routine
-** processes the template and writes the results on either
-** stdout or into CGI.
+** The TH1 scripts are contained within <th1>...</th1>.
+** TH1 variables are $aaa or $<aaa>.  The first form of
+** variable is literal.  The second is run through htmlize
+** before being inserted.
+**
+** This routine processes the template and writes the results
+** on either stdout or into CGI.
 */
 int Th_Render(const char *z){
   int i = 0;
   int n;
   int rc = TH_OK;
   uchar *zResult;
   initializeInterp();
   while( z[i] ){
     if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
-      sendText(z, i);
-      rc = Th_GetVar(interp, &z[i+1], n);
+      const char *zVar;
+      int nVar;
+      sendText(z, i, 0);
+      if( z[i+1]=='<' ){
+        /* Variables of the form $<aaa> */
+        zVar = &z[i+2];
+        nVar = n-2;
+      }else{
+        /* Variables of the form $aaa */
+        zVar = &z[i+1];
+        nVar = n;
+      }
+      rc = Th_GetVar(interp, (uchar*)zVar, nVar);
       z += i+1+n;
       i = 0;
-      zResult = Th_GetResult(interp, &n);
-      sendText(zResult, n);
+      zResult = (uchar*)Th_GetResult(interp, &n);
+      sendText((char*)zResult, n, n>nVar);
     }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
-      sendText(z, i);
+      sendText(z, i, 0);
       z += i+5;
       for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
       rc = Th_Eval(interp, 0, (const uchar*)z, i);
       if( rc!=SBS_OK ) break;
       z += i;
@@ -267,16 +302,16 @@
     }else{
       i++;
     }
   }
   if( rc==TH_ERROR ){
-    sendText("<hr><p><font color=\"red\"><b>ERROR: ", -1);
-    zResult = Th_GetResult(interp, &n);
-    sendText(zResult, n);
-    sendText("</b></font></p>", -1);
+    sendText("<hr><p><font color=\"red\"><b>ERROR: ", -1, 0);
+    zResult = (uchar*)Th_GetResult(interp, &n);
+    sendText((char*)zResult, n, 1);
+    sendText("</b></font></p>", -1, 0);
   }else{
-    sendText(z, i);
+    sendText(z, i, 0);
   }
   return rc;
 }
 
 /*

Modified src/tktconfig.c from [c567cf64e8] to [60a45e0840].

@@ -33,60 +33,12 @@
 ** There is considerable flexibility in how tickets are defined
 ** in fossil.  Each repository can define its own ticket setup
 ** differently.  Each repository has an instance of a file, like
 ** this one that defines how that repository deals with tickets.
 **
-** This file is in the form of a script in an exceedingly
-** minimalist scripting language called "subscript".  Here are
-** the rules:
-**
-** SYNTAX
-**
-**     *  The script consists of a sequence of whitespace
-**        separated tokens.  Whitespace is ignored, except
-**        as its role as a token separator.
-**
-**     *  Lines that begin with '#' are considered to be whitespace.
-**
-**     *  Text within matching {...} is consider to be a single "string"
-**        token, even if the text spans multiple lines and includes
-**        embedded whitespace.  The outermost {...} are not part of
-**        the text of the token.
-**
-**     *  A token that begins with "/" is a string token.
-**
-**     *  A token that looks like a number is a string token.
-**
-**     *  Tokens that do not fall under the previous three rules
-**        are "verb" tokens.
-**
-**  PROCESSING:
-**
-**     *  When a script is processed, the engine reads tokens
-**        one by one.
-**
-**     *  String tokens are pushed onto the stack.
-**
-**     *  Verb tokens which correspond to the names of variables
-**        cause the corresponding variable to be pushed onto the
-**        stack.
-**
-**     *  Verb tokens which correspond to procedures cause the
-**        procedures to run.  The procedures might push or pull
-**        values from the stack.
-**
-** There are just a handful of verbs.  For the purposes of this
-** configuration script, there is only a single verb: "set".  The
-** "set" verb pops two arguments from the stack.  The topmost is
-** the name of a variable.  The second element is the value.  The
-** "set" verb sets the value of the named variable.
-**
-**      VALUE NAME set
-**
-** This configuration file just sets the values of various variables.
-** Some of the variables have special meanings.  The content of some
-** of the variables are additional subscript scripts.
+** This file is in the form of a script in TH1 - a minimalist
+** TCL clone.
 */
 
 /* @-comment: ** */
 const char zDefaultTicketConfig[] =
 @ ############################################################################
@@ -95,11 +47,11 @@
 @ # tkt_id, tkt_uuid, and tkt_mtime.  tkt_id must be the integer primary
 @ # key and tkt_uuid and tkt_mtime must be unique.  A configuration should
 @ # define addition columns as necessary.  All columns should be in all
 @ # lower-case letters and should not begin with "tkt".
 @ #
-@ {
+@ set ticket_sql {
 @    CREATE TABLE ticket(
 @      -- Do not change any column that begins with tkt_
 @      tkt_id INTEGER PRIMARY KEY,
 @      tkt_uuid TEXT,
 @      tkt_mtime DATE,
@@ -115,26 +67,26 @@
 @      comment TEXT,
 @      -- Do not alter this UNIQUE clause:
 @      UNIQUE(tkt_uuid, tkt_mtime)
 @    );
 @    -- Add indices as desired
-@ } /ticket_sql set
+@ }
 @
 @ ############################################################################
 @ # You can define additional variables here.  These variables will be
 @ # accessible to the page templates when they run.
 @ #
-@ {
+@ set type_choices {
 @    Code_Defect
 @    Build_Problem
 @    Documentation
 @    Feature_Request
 @    Incident
-@ } /type_choices set
-@ {Immediate High Medium Low Zero} /priority_choices set
-@ {Critical Severe Important Minor Cosmetic} /severity_choices set
-@ {
+@ }
+@ set priority_choices {Immediate High Medium Low Zero}
+@ set severity_choices {Critical Severe Important Minor Cosmetic}
+@ set resolution_choices {
 @   Open
 @   Fixed
 @   Rejected
 @   Unable_To_Reproduce
 @   Works_As_Designed
@@ -141,68 +93,70 @@
 @   External_Bug
 @   Not_A_Bug
 @   Duplicate
 @   Overcome_By_Events
 @   Drive_By_Patch
-@ } /resolution_choices set
-@ {
+@ }
+@ set status_choices {
 @   Open
 @   Verified
 @   In_Process
 @   Deferred
 @   Fixed
 @   Tested
 @   Closed
-@ } /status_choices set
-@ {one two three} /subsystem_choices set
+@ }
+@ set subsystem_choices {one two three}
 @
 @ ##########################################################################
 @ # The "tktnew_template" variable is set to text which is a template for
 @ # the HTML of the "New Ticket" page.  Within this template, text contained
 @ # within [...] is subscript.  That subscript runs when the page is
 @ # rendered.
 @ #
-@ {
+@ set tktnew_template {
 @   <!-- load database field names not found in CGI with an empty string -->
 @   <!-- start a form -->
-@   [{
-@      {Open} /status set
-@       submit_ticket
-@   } /submit exists if]
+@   <th1>
+@     if {[info exists submit]} {
+@        set status Open
+@        submit_ticket
+@     }
+@   </th1>
 @   <table cellpadding="5">
 @   <tr>
 @   <td colspan="2">
 @   Enter a one-line summary of the problem:<br>
-@   <input type="text" name="title" size="60" value="[{} /title get html]">
+@   <input type="text" name="title" size="60" value="$<title>">
 @   </td>
 @   </tr>
 @
 @   <tr>
 @   <td align="right">Type:
-@   [/type type_choices 1 combobox]
+@   <th1>combobox type $type_choices 1</th1>
 @   </td>
 @   <td>What type of ticket is this?</td>
 @   </tr>
 @
 @   <tr>
 @   <td align="right">Version:
-@   <input type="text" name="foundin" size="20" value="[{} /foundin get html]">
+@   <input type="text" name="foundin" size="20" value="$<foundin>">
 @   </td>
 @   <td>In what version or build number do you observer the problem?</td>
 @   </tr>
 @
 @   <tr>
 @   <td align="right">Severity:
-@   [/severity severity_choices 1 combobox]
+@   <th1>comboboxy severity severity_choices 1</th1>
 @   </td>
 @   <td>How debilitating is the problem?  How badly does the problem
 @   effect the operation of the product?</td>
 @   </tr>
 @
 @   <tr>
 @   <td align="right">EMail:
-@   <input type="text" name="contact" value="[{} /contact get html]" size="30">
+@   <input type="text" name="contact" value="$<contact>" size="30">
 @   </td>
 @   <td>Not publically visible. Used by developers to contact you with
 @   questions.</td>
 @   </tr>
 @
@@ -211,23 +165,30 @@
 @   Enter a detailed description of the problem.
 @   For code defects, be sure to provide details on exactly how
 @   the problem can be reproduced.  Provide as much detail as
 @   possible.
 @   <br>
-@   <textarea name="comment" cols="80"
-@    rows="[{} /comment get linecount 50 max 10 min html]"
-@    wrap="virtual" class="wikiedit">[{} /comment get html]</textarea><br>
+@   <th1>
+@      if {![info exists comment]} {
+@        set nline 10
+@      } else {
+@        set nline [linecount $comment]
+@        if {$nline>50} {set nline 50}
+@      }
+@   </th1>
+@   <textarea name="comment" cols="80" rows="$nline"
+@    wrap="virtual" class="wikiedit">$<comment></textarea><br>
 @   <input type="submit" name="preview" value="Preview">
 @   </tr>
 @
-@   [/preview exists enable_output]
+@   <th1>enable_output [info exists preview]</th1>
 @   <tr><td colspan="2">
 @   Description Preview:<br><hr>
-@   [{} /comment get wiki]
+@   <th1>wiki $comment</th1>
 @   <hr>
 @   </td></tr>
-@   [1 enable_output]
+@   <th1>enable_output 1</th1>
 @
 @   <tr>
 @   <td align="right">
 @   <input type="submit" name="submit" value="Submit">
 @   </td>
@@ -234,66 +195,65 @@
 @   <td>After filling in the information above, press this button to create
 @   the new ticket</td>
 @   </tr>
 @   </table>
 @   <!-- end of form -->
-@ } /tktnew_template set
+@ }
 @
 @ ##########################################################################
 @ # The template for the "edit ticket" page
 @ #
 @ # Then generated text is inserted inside a form which feeds back to itself.
 @ # All CGI parameters are loaded into variables.  All database files are
 @ # loaded into variables if they have not previously been loaded by
 @ # CGI parameters.
-@ {
-@   [
-@     login /username get /username set
-@     {
-@       {
-@         username login eq /samename set
-@         {
-@            "\n\n<hr><i>" login " added on " date ":</i><br>\n"
-@            cmappnd 6 concat /comment append_field
-@         } samename if
-@         {
-@            "\n\n<hr><i>" login " claiming to be " username " added on " date
-@            "</i><br>\n" cmappnd 8 concat /comment append_field
-@         } samename not if
-@       } 0 {} /cmappnd get length lt if
+@ set tktedit_template {
+@   <th1>
+@     if {![info exists username]} {set username $login}
+@     if {[info exists submit]} {
+@       if {[info exists $cmappnd] && [string length $cmappnd]>0} {
+@         set ctxt "\n\n<hr><i>"
+@         if {$username==$login} {
+@           set usr "$ctxt[htmlize $login]"
+@         } else {
+@           set usr "[htmlize $login claimingn to be [htmlize $username]"
+@         }
+@         append_field comment \
+@            "\n\n<hr><i>$usr added on [date]:</i><br>\n$comment"
+@       }
 @       submit_ticket
-@     } /submit exists if
-@   ]
+@     }
+@   </th1>
 @   <table cellpadding="5">
 @   <tr><td align="right">Title:</td><td>
-@   <input type="text" name="title" value="[title html]" size="60">
+@   <input type="text" name="title" value="$<title>" size="60">
 @   </td></tr>
 @   <tr><td align="right">Status:</td><td>
-@   [/status status_choices 1 combobox]
+@   <th1>combobox status $status_choices 1</th1>
 @   </td></tr>
 @   <tr><td align="right">Type:</td><td>
-@   [/type type_choices 1 combobox]
+@   <th1>combobox type $type_choices 1</th1>
 @   </td></tr>
 @   <tr><td align="right">Severity:</td><td>
-@   [/severity severity_choices 1 combobox]
+@   <th1>combobox severity $severity_choices 1</th1>
 @   </td></tr>
 @   <tr><td align="right">Priority:</td><td>
-@   [/priority priority_choices 1 combobox]
+@   <th1>combobox priority $priority_choices 1</th1>
 @   </td></tr>
 @   <tr><td align="right">Resolution:</td><td>
-@   [/resolution resolution_choices 1 combobox]
+@   <th1>combobox resolution $resolution_choices 1</th1>
 @   </td></tr>
 @   <tr><td align="right">Subsystem:</td><td>
-@   [/subsystem subsystem_choices 1 combobox]
-@   </td></tr>
-@   [/e hascap enable_output]
+@   <th1>combobox subsystem $subsystem_choices 1</th1>
+@   </td></tr>
+@   <th1>enable_output [hascap e]</th1>
 @     <tr><td align="right">Contact:</td><td>
-@     <input type="text" name="contact" size="40" value="[contact html]">
+@     <input type="text" name="contact" size="40" value="$<contact>">
 @     </td></tr>
-@   [1 enable_output]
+@   <th1>enable_output 1</th1>
 @   <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
-@   <input type="text" name="foundin" size="50" value="[foundin html]">
+@   <input type="text" name="foundin" size="50" value="$<foundin>">
 @   </td></tr>
 @   <tr><td colspan="2">
 @
 @   [
 @      0 /eall get /eall set           # eall means "edit all".  default==no
@@ -326,44 +286,44 @@
 @   </table>
 @ } /tktedit_template set
 @
 @ ##########################################################################
 @ # The template for the "view ticket" page
-@ {
+@ set tktview_template {
 @   <!-- load database fields automatically loaded into variables -->
 @   <table cellpadding="5">
 @   <tr><td align="right">Title:</td><td>
-@   [title html]
+@   $<title>
 @   </td></tr>
 @   <tr><td align="right">Status:</td><td>
-@   [status html]
+@   $<status>
 @   </td></tr>
 @   <tr><td align="right">Type:</td><td>
-@   [type html]
+@   $<type>
 @   </td></tr>
 @   <tr><td align="right">Severity:</td><td>
-@   [severity html]
+@   $<severity>
 @   </td></tr>
 @   <tr><td align="right">Priority:</td><td>
-@   [priority html]
+@   $<priority>
 @   </td></tr>
 @   <tr><td align="right">Resolution:</td><td>
-@   [priority html]
+@   $<resolution>
 @   </td></tr>
 @   <tr><td align="right">Subsystem:</td><td>
-@   [subsystem html]
+@   $<subsystem>
 @   </td></tr>
-@   [{e} hascap enable_output]
+@   <th1>enable_output [hascap e]</th1>
 @     <tr><td align="right">Contact:</td><td>
-@     [contact html]
+@     $<contact>
 @     </td></tr>
-@   [1 enable_output]
+@   <th1>enable_output 1</th1>
 @   <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
-@   [foundin html]
+@   $<foundin>
 @   </td></tr>
 @   <tr><td colspan="2">
 @   Description And Comments:<br>
-@   [comment wiki]
+@   <th1>wiki $comment</th1>
 @   </td></tr>
 @   </table>
-@ } /tktview_template set
+@ }
 ;