@@ -23,9 +23,8 @@
**
** This file contains code used render and control ticket entry
** and display pages.
*/
-#if 0
#include "config.h"
#include "tkt.h"
#include <assert.h>
@@ -39,15 +38,15 @@
/*
** 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
@@ -84,8 +83,72 @@
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
@@ -94,31 +157,45 @@
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);
}
/*
@@ -126,24 +203,24 @@
*/
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);
}
@@ -153,28 +230,41 @@
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;
@@ -183,6 +273,109 @@
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();
+}