Overview
SHA1 Hash: | fb358ca492904507ebfc504c40b0a6e4f957537a |
---|---|
Date: | 2007-11-24 19:33:46 |
User: | drh |
Comment: | Progress toward getting ticketing working. We can enter a new ticket and display it. Cannot yet edit a ticket. |
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/cgi.c from [144475b0ed] to [1c05503871].
@@ -725,10 +725,22 @@ return zValue; } } CGIDEBUG(("no-match [%s]\n", zName)); return zDefault; +} + +/* +** Return the name of the i-th CGI parameter. Return NULL if there +** are fewer than i registered CGI parmaeters. +*/ +const char *cgi_parameter_name(int i){ + if( i>=0 && i<nUsedQP ){ + return aParamQP[i].zName; + }else{ + return 0; + } } /* ** Print CGI debugging messages. */
Modified src/db.c from [96be69e00f] to [48224de87a].
@@ -319,10 +319,16 @@ double db_column_double(Stmt *pStmt, int N){ return sqlite3_column_double(pStmt->pStmt, N); } const char *db_column_text(Stmt *pStmt, int N){ return (char*)sqlite3_column_text(pStmt->pStmt, N); +} +const char *db_column_name(Stmt *pStmt, int N){ + return (char*)sqlite3_column_name(pStmt->pStmt, N); +} +int db_column_count(Stmt *pStmt){ + return (char*)sqlite3_column_count(pStmt->pStmt); } char *db_column_malloc(Stmt *pStmt, int N){ return mprintf("%s", db_column_text(pStmt, N)); } void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){
Modified src/manifest.c from [716c309625] to [9573b5d563].
@@ -857,12 +857,28 @@ m.rDate, rid, m.zUser, zComment, TAG_BGCOLOR, rid, TAG_BGCOLOR, rid, TAG_USER, rid, TAG_COMMENT, rid + ); + free(zComment); + } + if( m.type==CFTYPE_TICKET ){ + char *zTag; + char *zComment; + + ticket_insert(&m, 1, 1); + zTag = mprintf("tkt-%s", m.zTicketUuid); + tag_insert(zTag, 1, 0, rid, m.rDate, rid); + free(zTag); + zComment = mprintf("Changes to ticket [%.10s]", m.zTicketUuid); + db_multi_exec( + "INSERT INTO event(type,mtime,objid,user,comment)" + "VALUES('t',%.17g,%d,%Q,%Q)", + m.rDate, rid, m.zUser, zComment ); free(zComment); } db_end_transaction(0); manifest_clear(&m); return 1; }
Modified src/rebuild.c from [45d87035e5] to [d7213a8b18].
@@ -57,10 +57,11 @@ if( zTable==0 ) break; db_multi_exec("DROP TABLE %Q", zTable); free(zTable); } db_multi_exec(zRepositorySchema2); + ticket_create_table(0); db_multi_exec("INSERT INTO unclustered SELECT rid FROM blob"); db_multi_exec( "DELETE FROM unclustered" " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))"
Modified src/subscript.c from [fb8956976f] to [d503240921].
@@ -60,10 +60,12 @@ #if INTERFACE typedef struct Subscript Subscript; #define SBS_OK 0 #define SBS_ERROR 1 +#define SBS_RETURN 2 +#define SBS_BREAK 3 #endif /* ** Configuration constants */ @@ -343,17 +345,25 @@ /* ** Set the error message for an interpreter. Verb implementations ** use this routine when they encounter an error. */ -void SbS_SetErrorMessage(struct Subscript *p, const char *zErr){ - int nErr = strlen(zErr); +void SbS_SetErrorMessage(struct Subscript *p, const char *zErr, ...){ + int nErr; + char *zMsg; + va_list ap; + + va_start(ap, zErr); + zMsg = vmprintf(zErr, ap); + va_end(ap); + nErr = strlen(zMsg); if( nErr>sizeof(p->zErrMsg)-1 ){ nErr = sizeof(p->zErrMsg)-1; } - memcpy(p->zErrMsg, zErr, nErr); + memcpy(p->zErrMsg, zMsg, nErr); p->zErrMsg[nErr] = 0; + free(zMsg); } /* ** Return a pointer to the current error message for the ** interpreter. @@ -528,15 +538,16 @@ ** the interpreter. Make a copy if you need it to persist. */ const char *SbS_Fetch( struct Subscript *p, /* The interpreter we are interrogating */ const char *zKey, /* Name of the variable. Case sensitive */ + int nKey, /* Length of the key */ int *pLength /* Write the length here */ ){ const SbSValue *pVal; - pVal = sbs_fetch(&p->symTab, zKey, -1); + pVal = sbs_fetch(&p->symTab, zKey, nKey); if( pVal==0 || (pVal->flags & SBSVAL_STR)==0 ){ *pLength = 0; return 0; }else{ *pLength = pVal->u.str.size; @@ -709,10 +720,25 @@ SbS_Pop(p, 1); return 0; } /* +** Send text to the appropriate output: Either to the console +** or to the CGI reply buffer. +*/ +static void sendText(const char *z, int n){ + if( enableOutput && n ){ + if( n<0 ) n = strlen(z); + if( g.cgiPanic ){ + cgi_append_content(z, n); + }else{ + fwrite(z, 1, n, stdout); + } + } +} + +/* ** Subscript command: STRING puts ** Subscript command: STRING html ** ** Output STRING as HTML (html) or unchanged (puts). ** Pop it from the stack. @@ -728,20 +754,83 @@ zOut = htmlize(z, size); size = strlen(zOut); }else{ zOut = (char*)z; } - if( g.cgiPanic ){ - cgi_append_content(zOut, size); - }else{ - printf("%.*s", size, zOut); - } + sendText(zOut, size); if( pConvert ){ free(zOut); } } SbS_Pop(p, 1); + return 0; +} + +/* +** Subscript command: STRING wiki +** +** Render the input string as wiki. +*/ +static int wikiCmd(struct Subscript *p, void *pConvert){ + int size; + const char *z; + if( SbS_RequireStack(p, 1, "wiki") ) return 1; + if( enableOutput ){ + Blob src; + z = SbS_StackValue(p, 0, &size); + blob_init(&src, z, size); + wiki_convert(&src, 0, WIKI_INLINE); + blob_reset(&src); + } + SbS_Pop(p, 1); + return 0; +} + +/* +** Subscript command: NAME TEXT-LIST NUMLINES combobox +** +** Generate an HTML combobox. NAME is both the name of the +** CGI parameter and the name of a variable that contains the +** currently selected value. TEXT-LIST is a list of possible +** values for the combobox. NUMLINES is 1 for a true combobox. +** If NUMLINES is greater than one then the display is a listbox +** with the number of lines given. +*/ +static int comboboxCmd(struct Subscript *p, void *NotUsed){ + if( SbS_RequireStack(p, 3, "combobox") ) return 1; + if( enableOutput ){ + int height; + Blob list, elem; + char *zName, *zList; + int nName, nList, nValue; + const char *zValue; + char *z, *zH; + + height = SbS_StackValueInt(p, 0); + zList = (char*)SbS_StackValue(p, 1, &nList); + blob_init(&list, zList, nList); + zName = (char*)SbS_StackValue(p, 2, &nName); + zValue = SbS_Fetch(p, zName, nName, &nValue); + z = mprintf("<select name=\"%.*h\" size=\"%d\">", nName, zName, height); + sendText(z, -1); + free(z); + while( blob_token(&list, &elem) ){ + zH = mprintf("%.*h", blob_size(&elem), blob_buffer(&elem)); + if( zValue && blob_size(&elem)==nValue + && memcmp(zValue, blob_buffer(&elem), nValue)==0 ){ + z = mprintf("<option value=\"%s\" selected>%s</option>", zH, zH); + }else{ + z = mprintf("<option value=\"%s\">%s</option>", zH, zH); + } + free(zH); + sendText(z, -1); + free(z); + } + sendText("</select>", -1); + blob_reset(&list); + } + SbS_Pop(p, 3); return 0; } /* @@ -752,10 +841,11 @@ int (*xCmd)(Subscript*,void*); void *pArg; } aBuiltin[] = { { "add", bopCmd, (void*)SBSOP_AND }, { "and", bopCmd, (void*)SBSOP_AND }, + { "combobox", comboboxCmd, 0, }, { "div", bopCmd, (void*)SBSOP_DIV }, { "enable_output", enableOutputCmd, 0 }, { "exists", existsCmd, 0, }, { "get", getCmd, 0, }, { "hascap", hascapCmd, 0 }, @@ -767,10 +857,11 @@ { "not", notCmd, 0 }, { "or", bopCmd, (void*)SBSOP_OR }, { "puts", putsCmd, 0 }, { "set", setCmd, 0 }, { "sub", bopCmd, (void*)SBSOP_SUB }, + { "wiki", wikiCmd, 0, }, }; /* ** Compare a zero-terminated string zPattern against @@ -860,10 +951,13 @@ upr = i-1; }else{ lwr = i+1; } } + if( upr<lwr ){ + SbS_SetErrorMessage(p, "unknown verb: %.*s", n, zScript); + } }else if( pVal->flags & SBSVAL_VERB ){ rc = pVal->u.verb.xVerb(p, pVal->u.verb.pArg); }else if( pVal->flags & SBSVAL_EXEC ){ rc = SbS_Eval(p, pVal->u.str.z, pVal->u.str.size); }else{ @@ -887,17 +981,11 @@ int SbS_Render(struct Subscript *p, const char *z){ int i = 0; int rc = SBS_OK; while( z[i] ){ if( z[i]=='[' ){ - if( enableOutput ){ - if( g.cgiPanic ){ - cgi_append_content(z, i); - }else{ - fwrite(z, 1, i, stdout); - } - } + sendText(z, i); z += i+1; for(i=0; z[i] && z[i]!=']'; i++){} rc = SbS_Eval(p, z, i); if( rc!=SBS_OK ) break; if( z[i] ) i++; @@ -905,16 +993,16 @@ i = 0; }else{ i++; } } - if( i>0 && enableOutput ){ - if( g.cgiPanic ){ - cgi_append_content(z, i); - }else{ - fwrite(z, 1, i, stdout); - } + if( rc==SBS_ERROR ){ + sendText("<hr><p><font color=\"red\"><b>ERROR: ", -1); + sendText(SbS_GetErrorMessage(p), -1); + sendText("</b></font></p>", -1); + }else{ + sendText(z, i); } return rc; } /*
Modified src/tkt.c from [e32e47c61c] to [7e0e9ce39d].
@@ -22,11 +22,10 @@ ******************************************************************************* ** ** This file contains code used render and control ticket entry ** and display pages. */ -#if 0 #include "config.h" #include "tkt.h" #include <assert.h> /* @@ -38,17 +37,17 @@ static char **azField = 0; /* ** A subscript interpreter used for processing Tickets. */ -struct Subscript *pInterp = 0; +static struct Subscript *pInterp = 0; /* ** Compare two entries in azField for sorting purposes */ -static int nameCmpr(void *a, void *b){ - return strcmp((char*)a, (char*)b); +static int nameCmpr(const void *a, const void *b){ + return strcmp(*(char**)a, *(char**)b); } /* ** Obtain a list of all fields of the TICKET table. Put them ** in sorted order. @@ -83,10 +82,74 @@ } return 0; } /* +** Query the database for all TICKET fields for the specific +** ticket whose name is given by the "name" CGI parameter. +** Load the values for all fields into the interpreter. +*/ +static void initializeVariablesFromDb(void){ + const char *zName; + Stmt q; + int i, n; + + zName = PD("name",""); + db_prepare(&q, "SELECT * FROM ticket WHERE tkt_uuid GLOB '%q*'", zName); + if( db_step(&q)==SQLITE_ROW ){ + n = db_column_count(&q); + for(i=0; i<n; i++){ + const char *zVal = db_column_text(&q, i); + if( zVal==0 ) zVal = ""; + SbS_Store(pInterp, db_column_name(&q,i), zVal, 1); + } + }else{ + db_finalize(&q); + db_prepare(&q, "PRAGMA table_info(ticket)"); + while( db_step(&q)==SQLITE_ROW ){ + const char *zField = db_column_text(&q, 1); + SbS_Store(pInterp, zField, "", 0); + } + } + db_finalize(&q); +} + +/* +** Transfer all CGI parameters to variables in the interpreter. +*/ +static void initializeVariablesFromCGI(void){ + int i; + const char *z; + + for(i=0; (z = cgi_parameter_name(i))!=0; i++){ + SbS_Store(pInterp, z, P(z), 0); + } +} + +/* +** Rebuild all tickets named in the _pending_ticket table. +** +** This routine is called just prior to commit after new +** out-of-sequence ticket changes have been added. +*/ +static int ticket_rebuild_at_commit(void){ + Stmt q; + db_multi_exec( + "DELETE FROM ticket WHERE tkt_uuid IN _pending_ticket" + ); + db_prepare(&q, "SELECT uuid FROM _pending_ticket"); + while( db_step(&q)==SQLITE_ROW ){ + const char *zUuid = db_column_text(&q, 0); + ticket_rebuild_entry(zUuid); + } + db_multi_exec( + "DELETE FROM _pending_ticket" + ); + return 0; +} + +/* ** Update an entry of the TICKET table according to the information ** in the control file given in p. Attempt to create the appropriate ** TICKET table entry if createFlag is true. If createFlag is false, ** that means we already know the entry exists and so we can save the ** work of trying to create it. @@ -93,58 +156,72 @@ */ void ticket_insert(Manifest *p, int createFlag, int checkTime){ Blob sql; Stmt q; int i; - + const char *zSep; + + getAllTicketFields(); if( createFlag ){ - db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid) " - "VALUES(%Q)", p->zTicketUuid); + db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) " + "VALUES(%Q, 0)", p->zTicketUuid); } blob_zero(&sql); blob_appendf(&sql, "UPDATE ticket SET tkt_mtime=:mtime"); zSep = "SET"; for(i=0; i<p->nField; i++){ const char *zName = p->aField[i].zName; if( zName[0]=='+' ){ + zName++; if( !isTicketField(zName) ) continue; blob_appendf(&sql,", %s=%s || %Q", zName, zName, p->aField[i].zValue); }else{ if( !isTicketField(zName) ) continue; blob_appendf(&sql,", %s=%Q", zName, p->aField[i].zValue); } } - blob_appendf(&sql, " WHERE tkt_uuid='%s'", p->zTicketUuid); + blob_appendf(&sql, " WHERE tkt_uuid='%s' AND tkt_mtime<:mtime", + p->zTicketUuid); db_prepare(&q, "%s", blob_str(&sql)); db_bind_double(&q, ":mtime", p->rDate); db_step(&q); db_finalize(&q); + if( checkTime && db_changes()==0 ){ + static int isInit = 0; + if( !isInit ){ + db_multi_exec("CREATE TEMP TABLE _pending_ticket(uuid TEXT UNIQUE)"); + db_commit_hook(ticket_rebuild_at_commit, 1); + isInit = 1; + } + db_multi_exec("INSERT OR IGNORE INTO _pending_ticket" + "VALUES(%Q)", p->zTicketUuid); + } blob_reset(&sql); } /* ** Rebuild an entire entry in the TICKET table */ void ticket_rebuild_entry(const char *zTktUuid){ char *zTag = mprintf("tkt-%s", zTktUuid); int tagid = tag_findid(zTag, 1); - Stmt *q; + Stmt q; Manifest manifest; Blob content; - int clearFlag = 1; + int createFlag = 1; db_multi_exec( "DELETE FROM ticket WHERE tkt_uuid=%Q", zTktUuid ); db_prepare(&q, "SELECT rid FROM tagxref WHERE tagid=%d ORDER BY mtime",tagid); while( db_step(&q)==SQLITE_ROW ){ int rid = db_column_int(&q, 0); - content_get(rid, &entry); - manifest_parse(&manifest, &entry); - ticket_insert(&manifest, clearFlag); + content_get(rid, &content); + manifest_parse(&manifest, &content); + ticket_insert(&manifest, createFlag, 0); manifest_clear(&manifest); - clearFlag = 0; + createFlag = 0; } db_finalize(&q); } /* @@ -152,30 +229,43 @@ */ void ticket_init(void){ char *zConfig; if( pInterp ) return; pInterp = SbS_Create(); - zConfig = db_text(zDefaultTicketConfig, + zConfig = db_text((char*)zDefaultTicketConfig, "SELECT value FROM config WHERE name='ticket-configuration'"); - SbS_Eval(pInterp, zConfig); + SbS_Eval(pInterp, zConfig, -1); } /* -** Rebuild the entire TICKET table. +** Recreate the ticket table. */ -void ticket_rebuild(void){ +void ticket_create_table(int separateConnection){ char *zSql; int nSql; + db_multi_exec("DROP TABLE IF EXISTS ticket;"); ticket_init(); - zSql = SbS_Fetch(pInterp, "ticket_sql", &nSql); + zSql = (char*)SbS_Fetch(pInterp, "ticket_sql", -1, &nSql); if( zSql==0 ){ - fossil_error("no ticket_sql defined by ticket configuration"); + fossil_panic("no ticket_sql defined by ticket configuration"); + } + if( separateConnection ){ + zSql = mprintf("%.*s", nSql, zSql); + db_init_database(g.zRepositoryName, zSql, 0); + free(zSql); + }else{ + db_multi_exec("%.*s", nSql, zSql); } - zSql = mprintf("%.*s", nSql, zSql); - db_init_database(g.zRepositoryName, zSql, 0); - free(zSql); +} + +/* +** Repopulate the ticket table +*/ +void ticket_rebuild(void){ + Stmt q; + db_begin_transaction(); db_prepare(&q,"SELECT tagname FROM tag WHERE tagname GLOB 'tkt-*'"); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); int len; zName += 4; @@ -182,7 +272,110 @@ len = strlen(zName); if( len<20 || !validate16(zName, len) ) continue; ticket_rebuild_entry(zName); } db_finalize(&q); + db_end_transaction(0); +} + +/* +** WEBPAGE: tktview +*/ +void tktview_page(void){ + char *zScript; + int nScript; + login_check_credentials(); + if( !g.okRdTkt ){ login_needed(); return; } + style_header("View Ticket"); + ticket_init(); + initializeVariablesFromDb(); + zScript = (char*)SbS_Fetch(pInterp, "tktview_template", -1, &nScript); + zScript = mprintf("%.*s", nScript, zScript); + SbS_Render(pInterp, zScript); + style_footer(); } + +/* +** Subscript command: LABEL submit_new_ticket +** +** If the variable named LABEL exists, then submit a new ticket +** based on the values of other defined variables. +*/ +static int submitNewCmd(struct Subscript *p, void *pNotify){ + const char *zLabel; + int nLabel, size; + if( SbS_RequireStack(p, 1, "submit_new_ticket") ) return 1; + zLabel = SbS_StackValue(p, 0, &nLabel); + if( SbS_Fetch(p, zLabel, nLabel, &size)!=0 ){ + char *zDate, *zUuid; + int i; + int rid; + Blob tktchng, cksum; + + (*(int*)pNotify) = 1; + blob_zero(&tktchng); + zDate = db_text(0, "SELECT datetime('now')"); + zDate[10] = 'T'; + blob_appendf(&tktchng, "D %s\n", zDate); + free(zDate); + for(i=0; i<nField; i++){ + const char *zValue; + int nValue; + zValue = SbS_Fetch(p, azField[i], -1, &nValue); + if( zValue ){ + while( nValue>0 && isspace(zValue[nValue-1]) ){ nValue--; } + blob_appendf(&tktchng, "J %s %z\n", + azField[i], fossilize(zValue,nValue)); + } + } + zUuid = db_text(0, "SELECT lower(hex(randomblob(20)))"); + blob_appendf(&tktchng, "K %s\n", zUuid); + (*(char**)pNotify) = zUuid; + blob_appendf(&tktchng, "U %F\n", g.zLogin ? g.zLogin : ""); + md5sum_blob(&tktchng, &cksum); + blob_appendf(&tktchng, "Z %b\n", &cksum); + +#if 0 + @ <hr><pre> + @ %h(blob_str(&tktchng)) + @ </pre><hr> + blob_zero(&tktchng) + SbS_Pop(p, 1); + return SBS_OK; #endif + + rid = content_put(&tktchng, 0, 0); + if( rid==0 ){ + fossil_panic("trouble committing ticket: %s", g.zErrMsg); + } + manifest_crosslink(rid, &tktchng); + return SBS_RETURN; + } + SbS_Pop(p, 1); + return SBS_OK; +} + +/* +** WEBPAGE: tktnew +*/ +void tktnew_page(void){ + char *zScript; + int nScript; + char *zNewUuid = 0; + + login_check_credentials(); + if( !g.okNewTkt ){ login_needed(); return; } + style_header("New Ticket"); + ticket_init(); + getAllTicketFields(); + initializeVariablesFromCGI(); + @ <form method="POST" action="%s(g.zBaseURL)/tktnew"> + zScript = (char*)SbS_Fetch(pInterp, "tktnew_template", -1, &nScript); + zScript = mprintf("%.*s", nScript, zScript); + SbS_AddVerb(pInterp, "submit_new_ticket", submitNewCmd, (void*)&zNewUuid); + if( SbS_Render(pInterp, zScript)==SBS_RETURN && zNewUuid ){ + cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zNewUuid)); + return; + } + @ </form> + style_footer(); +}
Deleted src/tktconf.c version [d769ab0345]
Modified src/tktconfig.c from [a3312ee193] to [9a0ee09a71].
@@ -103,44 +103,43 @@ @ # 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 -@ { @ CREATE TABLE ticket( @ -- Do not change any column that begins with tkt_ @ tkt_id INTEGER PRIMARY KEY, -@ tkt_uuid TEXT UNIQUE, -@ tkt_mtime DATE, -@ tkt_valid BOOLEAN, +@ tkt_uuid TEXT, +@ tkt_mtime DATE, @ -- Add as many field as required below this line @ type TEXT, @ status TEXT, @ subsystem TEXT, @ priority TEXT, @ severity TEXT, +@ foundin TEXT, +@ contact TEXT, @ title TEXT, -@ comment TEXT +@ 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. @ # -@ {Code Build_Problem Documentation Feature_Request Incident} /type_choices set -@ {High Medium Low} /priority_choices set +@ { +@ 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 @ { @ Open @ Fixed @ Rejected @@ -159,10 +158,11 @@ @ Deferred @ Fixed @ Tested @ Closed @ } /status_choices set +@ {} /subsystem_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 @@ -172,55 +172,63 @@ @ <!-- 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"> +@ <td colspan="2"> @ Enter a one-line summary of the problem:<br> -@ <input type="text" name="title" size="60" value="[title html]"> +@ <input type="text" name="title" size="60" value="[{} /title get html]"> @ </td> @ </tr> @ @ <tr> @ <td align="right">Type: -@ [/type typechoices 20 combobox] +@ [/type type_choices 1 combobox] @ </td> @ <td>What type of ticket is this?</td> @ </tr> @ @ <tr> @ <td align="right">Version: -@ <input type="text" name="foundin" size="20" value="[foundin html]"> +@ <input type="text" name="foundin" size="20" value="[{} /foundin get html]"> @ </td> @ <td>In what version or build number do you observer the problem?</td> @ </tr> @ @ <tr> @ <td align="right">Severity: -@ [/severity {High Medium Low} 8 combobox] +@ [/severity severity_choices 1 combobox] @ </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: +@ [/severity severity_choices 1 combobox] +@ </td> +@ <td>Not publically visible. Used by developers to contact you with +@ questions.</td> @ </tr> @ @ <tr> @ <td colspan="2"> @ 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 linecount 50 max 10 min]" -@ wrap="virtual" class="wikiedit">[comment html]</textarea><br> -@ <br> +@ <textarea name="comment" cols="80" +@ rows="[{} /comment linecount 50 max 10 min]" +@ wrap="virtual" class="wikiedit">[{} /comment get html]</textarea><br> @ <input type="submit" name="preview" value="Preview"> @ </tr> @ -@ [0 /preview get enable_output] +@ [/preview exists enable_output] @ <tr><td colspan="2"> @ Description Preview:<br><hr> -@ [/comment html] +@ [{} /comment get wiki] @ <hr> @ </td></tr> @ [1 enable_output] @ @ <tr> @@ -246,26 +254,26 @@ @ <table cellpadding="5"> @ <tr><td align="right">Title:</td><td> @ <input type="text" name="title" value="[title html] size=60"> @ </td></tr> @ <tr><td align="right">Status:</td><td> -@ [/status status_choices 20 combobox] +@ [/status status_choices 1 combobox] @ </td></tr> @ <tr><td align="right">Type:</td><td> -@ [/type type_choices 20 combobox] +@ [/type type_choices 1 combobox] @ </td></tr> @ <tr><td align="right">Severity:</td><td> -@ [/severity {High Medium Low} 10 combobox] +@ [/severity severity_choices 1 combobox] @ </td></tr> @ <tr><td align="right">Priority:</td><td> -@ [/priority {High Medium Low} 10 combobox] +@ [/priority priority_choices 1 combobox] @ </td></tr> @ <tr><td align="right">Resolution:</td><td> -@ [/resolution resolution_choices 20 combobox] +@ [/resolution resolution_choices 1 combobox] @ </td></tr> @ <tr><td align="right">Subsystem:</td><td> -@ [/subsystem subsystem_choices 30 combobox] +@ [/subsystem subsystem_choices 1 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> @@ -303,40 +311,40 @@ @ # 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 html] +@ [title html] @ </td></tr> @ <tr><td align="right">Status:</td><td> -@ [/status html] +@ [status html] @ </td></tr> @ <tr><td align="right">Type:</td><td> -@ [/type html] +@ [type html] @ </td></tr> @ <tr><td align="right">Severity:</td><td> -@ [/severity html] +@ [severity html] @ </td></tr> @ <tr><td align="right">Priority:</td><td> -@ [/priority html] +@ [priority html] @ </td></tr> @ <tr><td align="right">Resolution:</td><td> -@ [/priority html] +@ [priority html] @ </td></tr> @ <tr><td align="right">Subsystem:</td><td> -@ [/subsystem html] +@ [subsystem html] @ </td></tr> @ [{e} hascap enable_output] @ <tr><td align="right">Contact:</td><td> -@ [/contact html] +@ [contact html] @ </td></tr> @ [1 enable_output] @ <tr><td align="right">Version Found In:</td><td> -@ [/foundin html] +@ [foundin html] @ </td></tr> @ <tr><td colspan="2"> @ Description And Comments:<br> -@ [/comment html] +@ [comment wiki] @ </td></tr> @ </table> @ } /tktview_template set ;