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
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
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 Found 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 Found 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 Found 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; } }