Diff
Not logged in

Differences From:

File src/tkt.c part of check-in [2a707334c9] - Fix some compiler warnings. Comment changes on non-functioning code in tkt.c. by drh on 2007-11-22 01:53:24. [view]

To:

File src/tkt.c part of check-in [fb358ca492] - Progress toward getting ticketing working. We can enter a new ticket and display it. Cannot yet edit a ticket. by drh on 2007-11-24 19:33:46. [view]

@@ -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();
+}