Check-in [8c96ed62f5]
Not logged in
Overview

SHA1 Hash:8c96ed62f5ba69a67713c2c9c1494013f1c971bb
Date: 2007-11-17 17:48:46
User: drh
Comment:Continuing work on the infrastructure for tickets.
Timelines: ancestors | descendants | both | trunk
Other Links: files | ZIP archive | manifest

Tags And Properties
Changes
[hide diffs]

Modified src/sample-config1.txt from [54c5c82c83] to [870c66c21e].

@@ -1,7 +1,93 @@
-{Default Ticket Configuration} /config_title set
-{type status subsystem priority severity contact title comment} setfields
+# This is a sample "ticket configuration" file for fossil.
+#
+# 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.
+#
+############################################################################
+#
+# Every ticket configuration file should have a title.  When you
+# come up with a new ticket configuration, please change the title
+# to something descriptive.
+#
+{Default Ticket Configuration} /ticket_config_title set
+
+############################################################################
+# Every ticket configuration *must* define a set of columns for the
+# "ticket" table of the database.  These column names will also be
+# used as CGI parameter names, so to avoid problems it is best that
+# the names be all lower-case alphabetic characters.  The names must
+# be unique and must not begin with "tkt_".
+#
+{
+   type
+   status
+   subsystem
+   priority
+   severity
+   contact
+   title
+   comment
+} /ticket_fields
+
+#############################################################################
+# You can define additional variables here.  These variables will be
+# accessible to the page templates when they run.
+#
 {Code Build_Problem Documentation Feature_Request Incident} /type_choices set
 {High Medium Low} /priority_choices set
 {Critical Severe Important Minor Cosmetic} /severity_choices set
 {
   Open
@@ -22,17 +108,26 @@
   Deferred
   Fixed
   Tested
   Closed
 } /status_choices set
-######################################################################
+
+############################################################################
+# 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.
+#
 {
+  <!-- load database field names not found in CGI with an empty string -->
+  <!-- start a form -->
+  [{Open} /status set /submit submit_new_ticket]
   <table cellpadding="5">
   <tr>
   <td cellpadding="2">
   Enter a one-line summary of the problem:<br>
-  [/title 60 textedit]
+  <input type="text" name="title" size="60" value="[title html]">
   </td>
   </tr>
 
   <tr>
   <td align="right">Type:
@@ -41,11 +136,11 @@
   <td>What type of ticket is this?</td>
   </tr>
 
   <tr>
   <td align="right">Version:
-  [/foundin 20 textedit]
+  <input type="text" name="foundin" size="20" value="[foundin html]">
   </td>
   <td>In what version or build number do you observer the problem?</td>
   </tr>
 
   <tr>
@@ -61,177 +156,135 @@
   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>
-  [/cmpv 0 cgi_parameter enable_output]
-  [/comment wikiview]
-  <hr>
-  [1 enable_output]
-  [/comment 70 /comment linecount 15 max multilineedit]
+  <textarea name="comment" cols="80" rows="[comment linecount 50 max 10 min]"
+   wrap="virtual" class="wikiedit">[comment html]</textarea><br>
   <br>
-  [/cmpv {Preview} auxbutton]
+  <input type="submit" name="preview" value="Preview">
   </tr>
+
+  [0 /preview get enable_output]
+  <tr><td colspan="2">
+  Description Preview:<br><hr>
+  [/comment html]
+  <hr>
+  </td></tr>
+  [1 enable_output]
 
   <tr>
   <td align="right">
-  [submitbutton]
+  <input type="submit" name="submit" value="Submit">
   </td>
   <td>After filling in the information above, press this button to create
   the new ticket</td>
   </tr>
   </table>
-  [/status /Open default_value]
-} /new setpage
-######################################################################
-{
-  [
-     # Extract the current information from the ticket table
-     {SELECT type, status, subsystem, priority,
-             severity, contact, title, comment
-        FROM ticket
-       WHERE tktid=:name} db_prepare
-     /name {} cgi_parameter {:name} db_bind
-     db_exec
-     if /title exists not {
-        {<i>No such ticket: } puts
-        /name {} cgi_parameter htmlize puts
-        {</i>} puts
-        return
-     } endif
-     /title title cgiparam /vtitle store
-     /status status cgiparam /vstatus store
-     /type type cgiparam /vtype store
+  <!-- end of form -->
+} /tktnew_template set
 
-     if /submit cgiexists {
-       /name {} cgi_parameter login ticketchng_begin
-       if /apndcom cgiexists {
-         {+comment}
-         {<hr><i>Added by }
-         login htmlize concat
-         { on } concat
-         datetime concat
-         {:</i><br} concat
-         /apndcom {} cgi_parameter htmlize concat
-         ticketchng_field
-       } else {
-         if vcomment comment eq not {/comment vcomment ticketchng_field} endif
-       } endif
-       if vtitle title eq not {/title vtitle ticketchng_field} endif
-       ticketchng_submit
-       baseurl /tktview?name= concat /name {} cgi_parameter concat redirect
-     } endif
-  ]
-  <form method="POST" action="[baseurl]/tktedit">
+######################################################################
+# The template for the "edit ticket" page
+{
+  <!-- database field names not found as CGI parameters are loaded
+       from the database automatically -->
+  <!-- start a form -->
+  [{<hr><i>%USER% added on %DATE%:</i><br>}
+    /cmappnd /comment append_remark]
+  [/submit submit_ticket_change]
   <table cellpadding="5">
   <tr><td align="right">Title:</td><td>
-  <input type="text" name="title" value="[vtitle htmlize puts] size=60">
+  <input type="text" name="title" value="[title html] size=60">
+  </td></tr>
+  <tr><td align="right">Status:</td><td>
+  [/status status_choices 20 combobox]
+  </td></tr>
+  <tr><td align="right">Type:</td><td>
+  [/type type_choices 20 combobox]
+  </td></tr>
+  <tr><td align="right">Severity:</td><td>
+  [/severity {High Medium Low} 10 combobox]
+  </td></tr>
+  <tr><td align="right">Priority:</td><td>
+  [/priority {High Medium Low} 10 combobox]
+  </td></tr>
+  <tr><td align="right">Resolution:</td><td>
+  [/resolution resolution_choices 20 combobox]
+  </td></tr>
+  <tr><td align="right">Subsystem:</td><td>
+  [/subsystem subsystem_choices 30 combobox]
+  </td></tr>
+  [/e hascap enable_output]
+    <tr><td align="right">Contact:</td><td>
+    <input type="text" name="contact" size="40" value="[contact html]">
+    </td></tr>
+  [1 enable_output]
+  <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
+  <input type="text" name="foundin" size="50" value="[foundin html]">
   </td></tr>
-  <tr><td align="right">Status:</td><td>
-  [vstatus /status status_choices 20 combobox]
-  </td></tr>
-  <tr><td align="right">Type:</td><td>
-  [vtype /type type_choices 20 combobox]
-  </td></tr>
-  <tr><td align="right">Severity:</td><td>
-  [vseverity /severity {High Medium Low} 10 combobox]
-  </td></tr>
-  <tr><td align="right">Priority:</td><td>
-  [vpriority /priority {High Medium Low} 10 combobox]
-  </td></tr>
-  <tr><td align="right">Resolution:</td><td>
-  [vresolution /resolution resolution_choices 20 combobox]
-  </td></tr>
-  <tr><td align="right">Subsystem:</td><td>
-  [vsubsystem /subsystem subsystem_choices 30 combobox]
-  </td></tr>
-  [{e} hascap enable_output]
-    <tr><td align="right">Contact:</td><td>
-    <input type="text" name="contact" size="40" value="[vcontact htmlize puts]">
-    </td></tr>
-  [1 enable_output]
-  <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
-  <input type="text" name="foundin" size="50" value="[foundin htmlize puts]">
-  </td></tr>
   <tr><td colspan="2">
-  [w hascap /eall 0 paramget and /eall store]
+  [0 /eall 0 get /eall set]
+  [/aonlybtn exists not /eall set]
+  [/eallbtn exists /eall set]
+  [/w hascap eall and /eall set]
   [eall enable_output]
     Description And Comments:<br>
     <textarea name="comment" cols="80" rows="[comment linecount 15 max 10 min]"
-     wrap="virtual" class="wikiedit">[comment htmlize puts]</textarea><br>
-    <input type="submit" name="aonly" value="Append Remark">
+     wrap="virtual" class="wikiedit">[comment html]</textarea><br>
+    <input type="hidden" name="eall" value="1">
+    <input type="submit" name="aonlybtn" value="Append Remark">
   [eall not enable_output]
     Append Remark:<br>
-    [/comment /cmappnd 70 /cmappnd linecount 10 max multilineappend]<br>
-    [ok_wrtkt enable_output /eall {Edit All} auxbutton]
+    <textarea name="cmappnd" cols="80" rows="15"
+     wrap="virtual" class="wikiedit">[cmappnd html]</textarea><br>
+    [ok_wrtkt enable_output]
+    <input type="submit" name="eallbtn" value="Edit All">
   [1 enable_output]
   </td></tr>
   <tr><td align="right"></td><td>
-  [{Submit Changes} submitbutton]
+  <input type="submit" name="submit" value="Submit Changes">
   </td></tr>
   </table>
-} /edit setpage
+  <!-- end-form inserted automatically -->
+} /tktedit_template set
+
 ######################################################################
+# The template for the "view ticket" page
 {
+  <!-- load database fields automatically loaded into variables -->
   <table cellpadding="5">
   <tr><td align="right">Title:</td><td>
-  [/title htmlize]
+  [/title html]
   </td></tr>
   <tr><td align="right">Status:</td><td>
-  [/status textview]
+  [/status html]
   </td></tr>
   <tr><td align="right">Type:</td><td>
-  [/type textview]
+  [/type html]
   </td></tr>
   <tr><td align="right">Severity:</td><td>
-  [/severity textview]
+  [/severity html]
   </td></tr>
   <tr><td align="right">Priority:</td><td>
-  [/priority textview]
+  [/priority html]
   </td></tr>
   <tr><td align="right">Resolution:</td><td>
-  [/priority textview]
+  [/priority html]
   </td></tr>
   <tr><td align="right">Subsystem:</td><td>
-  [/subsystem textview]
+  [/subsystem html]
   </td></tr>
   [{e} hascap enable_output]
     <tr><td align="right">Contact:</td><td>
-    [/contact textview]
+    [/contact html]
     </td></tr>
   [1 enable_output]
   <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
-  [/foundin textview]
+  [/foundin html]
   </td></tr>
   <tr><td colspan="2">
   Description And Comments:<br>
-  [/comment wikiview]
+  [/comment html]
   </td></tr>
   </table>
-} /view setpage
-
-##############
-# Verb list:
-#
-# CNEV
-# ****  VALUE NAME set
-# *---  LIST setfields
-#
-# -*--  FIELD STRING default_value
-# -**-  FIELD WIDTH textedit
-# -**-  FIELD CHOICELIST WIDTH combobox
-# -**-  FIELD WIDTH HEIGHT multilineedit
-# --*-  FIELD CGIFIELD WIDTH HEIGHT multilineappend
-# -**-  CGIFIELD LABEL auxbutton
-# -**-  LABEL submitbutton
-#
-# -***  FIELD wikiview
-# -***  FIELD textview
-# -***  FIELD linecount INT
-# -**-  CGIFIELD DEFAULT paramget VALUE
-# -***  BOOLEAN enable_output
-# -***  ok_wrtkt BOOLEAN
-# -***  is_anon BOOLEAN
-#
-# -***  INT INT max INT
-# -***  BOOLEAN not BOOLEAN
-# -***  BOOLEAN BOOLEAN and BOOLEAN
+} /tktview_template set

Modified src/subscript.c from [3df34dd953] to [10b9ab48f7].

@@ -127,13 +127,13 @@
 
 /*
 ** An instance of the Subscript interpreter
 */
 struct Subscript {
-  int nStack;                      /* Number of entries on stack */
-  SbsHashTab symTab;                /* The symbol table */
-  char zErrMsg[SBSCONFIG_ERRSIZE];  /* Space to write an error message */
+  int nStack;                        /* Number of entries on stack */
+  SbsHashTab symTab;                 /* The symbol table */
+  char zErrMsg[SBSCONFIG_ERRSIZE];   /* Space to write an error message */
   SbSValue aStack[SBSCONFIG_NSTACK]; /* The stack */
 };
 
 
 /*
@@ -599,32 +599,124 @@
 **
 ** Return true if the user has all of the capabilities listed.
 */
 static int hascapCmd(struct Subscript *p, void *pNotUsed){
   const char *z;
-  int i, n, a;
+  int n, a;
   if( SbS_RequireStack(p, 1, "hascap") ) return 1;
   z = SbS_StackValue(p, 0, &n);
   a = login_has_capability(z, n);
   SbS_Pop(p, 1);
   SbS_PushInt(p, a);
+  return 0;
+}
+
+/*
+** Subscript command:     STRING linecount INTEGER
+**
+** Return one more than the number of \n characters in STRING.
+*/
+static int linecntCmd(struct Subscript *p, void *pNotUsed){
+  const char *z;
+  int size, n, i;
+  if( SbS_RequireStack(p, 1, "linecount") ) return 1;
+  z = SbS_StackValue(p, 0, &size);
+  for(n=1, i=0; i<size; i++){
+    if( z[i]=='\n' ) n++;
+  }
+  SbS_Pop(p, 1);
+  SbS_PushInt(p, n);
+  return 0;
 }
 
 /*
-** Subscript command:      STRING puts
+** Subscript command:     NAME exists INTEGER
+**
+** Return TRUE if the variable NAME exists.
 */
-static int putsCmd(struct Subscript *p, void *pNotUsed){
-  int size;
+static int existsCmd(struct Subscript *p, void *pNotUsed){
   const char *z;
-  if( SbS_RequireStack(p, 1, "puts") ) return 1;
+  int size, x;
+  if( SbS_RequireStack(p, 1, "exists") ) return 1;
   z = SbS_StackValue(p, 0, &size);
-  if( g.cgiPanic ){
-    char *zCopy = mprintf("%.*s", size, z);
-    cgi_printf("%h", zCopy);
-    free(zCopy);
+  x = sbs_fetch(&p->symTab, (char*)z, size)!=0;
+  SbS_Pop(p, 1);
+  SbS_PushInt(p, x);
+  return 0;
+}
+
+/*
+** Subscript command:       VALUE NAME get VALUE
+**
+** Push the value of varible NAME onto the stack if it exists.
+** If NAME does not exist, puts VALUE onto the stack instead.
+*/
+static int getCmd(Subscript *p, void *pNotUsed){
+  const char *zName;
+  int nName;
+  const SbSValue *pVal;
+  int rc = 0;
+
+  if( SbS_RequireStack(p, 2, "get") ) return SBS_ERROR;
+  zName = SbS_StackValue(p, 0, &nName);
+  pVal = sbs_fetch(&p->symTab, (char*)zName, nName);
+  if( pVal!=0 && (pVal->flags & SBSVAL_STR)!=0 ){
+    SbS_Pop(p, 2);
+    rc = SbS_Push(p, pVal->u.str.z, pVal->u.str.size, 0);
   }else{
-    printf("%.*s\n", size, z);
+    SbS_Pop(p, 1);
+  }
+  return rc;
+}
+
+
+
+/*
+** True if output is enabled.  False if disabled.
+*/
+static int enableOutput = 1;
+
+/*
+** Subscript command:      BOOLEAN enable_output
+**
+** Enable or disable the puts and hputs commands.
+*/
+static int enableOutputCmd(struct Subscript *p, void *pNotUsed){
+  if( SbS_RequireStack(p, 1, "enable_output") ) return 1;
+  enableOutput = SbS_StackValueInt(p, 0)!=0;
+  SbS_Pop(p, 1);
+  return 0;
+}
+
+/*
+** Subscript command:      STRING puts
+** Subscript command:      STRING html
+**
+** Output STRING as HTML (html) or unchanged (puts).
+** Pop it from the stack.
+*/
+static int putsCmd(struct Subscript *p, void *pConvert){
+  int size;
+  const char *z;
+  char *zOut;
+  if( SbS_RequireStack(p, 1, "puts") ) return 1;
+  if( enableOutput ){
+    z = SbS_StackValue(p, 0, &size);
+    if( pConvert ){
+      zOut = htmlize(z, size);
+      size = strlen(zOut);
+    }else{
+      zOut = (char*)z;
+    }
+    if( g.cgiPanic ){
+      cgi_append_content(zOut, size);
+    }else{
+      printf("%.*s\n", size, zOut);
+    }
+    if( pConvert ){
+      free(zOut);
+    }
   }
   SbS_Pop(p, 1);
   return 0;
 }
 
@@ -635,33 +727,41 @@
 static const struct {
   const char *zCmd;
   int (*xCmd)(Subscript*,void*);
   void *pArg;
 } aBuiltin[] = {
-  { "add",    bopCmd,    (void*)SBSOP_AND    },
-  { "and",    bopCmd,    (void*)SBSOP_AND    },
-  { "div",    bopCmd,    (void*)SBSOP_DIV    },
-  { "hascap", hascapCmd, 0                },
-  { "max",    bopCmd,    (void*)SBSOP_MAX    },
-  { "min",    bopCmd,    (void*)SBSOP_MIN    },
-  { "mul",    bopCmd,    (void*)SBSOP_MUL    },
-  { "not",    notCmd,    0                   },
-  { "or",     bopCmd,    (void*)SBSOP_OR     },
-  { "puts",   putsCmd,   0                   },
-  { "set",    setCmd,    0                   },
-  { "sub",    bopCmd,    (void*)SBSOP_SUB    },
+  { "add",             bopCmd,               (void*)SBSOP_AND    },
+  { "and",             bopCmd,               (void*)SBSOP_AND    },
+  { "div",             bopCmd,               (void*)SBSOP_DIV    },
+  { "enable_output",   enableOutputCmd,      0                   },
+  { "exists",          existsCmd,            0,                  },
+  { "get",             getCmd,               0,                  },
+  { "hascap",          hascapCmd,            0                   },
+  { "html",            putsCmd,              (void*)1            },
+  { "linecount",       linecntCmd,           0                   },
+  { "max",             bopCmd,               (void*)SBSOP_MAX    },
+  { "min",             bopCmd,               (void*)SBSOP_MIN    },
+  { "mul",             bopCmd,               (void*)SBSOP_MUL    },
+  { "not",             notCmd,               0                   },
+  { "or",              bopCmd,               (void*)SBSOP_OR     },
+  { "puts",            putsCmd,              0                   },
+  { "set",             setCmd,               0                   },
+  { "sub",             bopCmd,               (void*)SBSOP_SUB    },
 };
 
 
 /*
 ** Compare a zero-terminated string zPattern against
 ** an unterminated string zStr of length nStr.
+**
+** Return less than, equal to, or greater than zero if
+** zPattern is less than, equal to, or greater than zStr.
 */
 static int compare_cmd(const char *zPattern, const char *zStr, int nStr){
   int c = strncmp(zPattern, zStr, nStr);
   if( c==0 && zPattern[nStr]!=0 ){
-    c = -1;
+    c = 1;
   }
   return c;
 }
 
 /*
@@ -673,10 +773,27 @@
   if( nScript<0 ) nScript = strlen(zScript);
   while( nScript>0 && rc==SBS_OK ){
     int n;
     int ttype;
     n = sbs_next_token(zScript, nScript, &ttype);
+
+#if 0
+    {
+      int i, nElem;
+      const char *zElem;
+      printf("TOKEN(%d): [%.*s]\n", ttype, n, zScript);
+      if( p->nStack>0 ){
+        printf("STACK:");
+        for(i=0; i<p->nStack; i++){
+          zElem = SbS_StackValue(p, i, &nElem);
+          printf(" [%.*s]", nElem, zElem);
+        }
+        printf("\n");
+      }
+    }
+#endif
+
     switch( ttype ){
       case SBSTT_WHITESPACE: {
         break;
       }
       case SBSTT_EOF: {
@@ -714,11 +831,11 @@
             int i = (upr+lwr)/2;
             int c = compare_cmd(aBuiltin[i].zCmd, zScript, n);
             if( c==0 ){
               rc = aBuiltin[i].xCmd(p, aBuiltin[i].pArg);
               break;
-            }else if( c<0 ){
+            }else if( c>0 ){
               upr = i-1;
             }else{
               lwr = i+1;
             }
           }