File Annotation
Not logged in
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** Copyright (c) 2007 D. Richard Hipp
62f37c9722 2007-11-26       drh: **
62f37c9722 2007-11-26       drh: ** This program is free software; you can redistribute it and/or
62f37c9722 2007-11-26       drh: ** modify it under the terms of the GNU General Public
62f37c9722 2007-11-26       drh: ** License as published by the Free Software Foundation; either
62f37c9722 2007-11-26       drh: ** version 2 of the License, or (at your option) any later version.
62f37c9722 2007-11-26       drh: **
62f37c9722 2007-11-26       drh: ** This program is distributed in the hope that it will be useful,
62f37c9722 2007-11-26       drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of
62f37c9722 2007-11-26       drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
62f37c9722 2007-11-26       drh: ** General Public License for more details.
62f37c9722 2007-11-26       drh: **
62f37c9722 2007-11-26       drh: ** You should have received a copy of the GNU General Public
62f37c9722 2007-11-26       drh: ** License along with this library; if not, write to the
62f37c9722 2007-11-26       drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
62f37c9722 2007-11-26       drh: ** Boston, MA  02111-1307, USA.
62f37c9722 2007-11-26       drh: **
62f37c9722 2007-11-26       drh: ** Author contact information:
62f37c9722 2007-11-26       drh: **   drh@hwaci.com
62f37c9722 2007-11-26       drh: **   http://www.hwaci.com/drh/
62f37c9722 2007-11-26       drh: **
62f37c9722 2007-11-26       drh: *******************************************************************************
62f37c9722 2007-11-26       drh: **
62f37c9722 2007-11-26       drh: ** Code to generate the bug report listings
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: #include "config.h"
62f37c9722 2007-11-26       drh: #include "report.h"
62f37c9722 2007-11-26       drh: #include <assert.h>
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /* Forward references to static routines */
62f37c9722 2007-11-26       drh: static void report_format_hints(void);
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** WEBPAGE: /reportlist
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: void view_list(void){
62f37c9722 2007-11-26       drh:   Stmt q;
86ed68ba34 2008-05-17       drh:   int rn = 0;
d3e711fd2f 2008-07-15       drh:   int cnt = 0;
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   login_check_credentials();
86ed68ba34 2008-05-17       drh:   if( !g.okRdTkt && !g.okNewTkt ){ login_needed(); return; }
86ed68ba34 2008-05-17       drh:   style_header("Bug Report Main Menu");
86ed68ba34 2008-05-17       drh:   if( g.okNewTkt ){
86ed68ba34 2008-05-17       drh:     @ <p>Enter a new bug report:</p>
49380d500a 2008-05-28       drh:     @ <ol><li value="1"><a href="tktnew">New bug report</a></li></ol>
86ed68ba34 2008-05-17       drh:     @
d3e711fd2f 2008-07-15       drh:     cnt++;
86ed68ba34 2008-05-17       drh:   }
86ed68ba34 2008-05-17       drh:   if( !g.okRdTkt ){
86ed68ba34 2008-05-17       drh:     @ <p>You are not authorized to view existing bug reports.</p>
86ed68ba34 2008-05-17       drh:   }else{
86ed68ba34 2008-05-17       drh:     db_prepare(&q, "SELECT rn, title, owner FROM reportfmt ORDER BY title");
86ed68ba34 2008-05-17       drh:     @ <p>Choose a report format from the following list:</p>
86ed68ba34 2008-05-17       drh:     @ <ol>
86ed68ba34 2008-05-17       drh:     while( db_step(&q)==SQLITE_ROW ){
86ed68ba34 2008-05-17       drh:       const char *zTitle = db_column_text(&q, 1);
86ed68ba34 2008-05-17       drh:       const char *zOwner = db_column_text(&q, 2);
9e2d2676a4 2008-06-08       drh:       rn = db_column_int(&q, 0);
49380d500a 2008-05-28       drh:       cnt++;
49380d500a 2008-05-28       drh:       @ <li value="%d(cnt)"><a href="rptview?rn=%d(rn)"
86ed68ba34 2008-05-17       drh:       @        rel="nofollow">%h(zTitle)</a>&nbsp;&nbsp;&nbsp;
86ed68ba34 2008-05-17       drh:       if( g.okWrite && zOwner && zOwner[0] ){
86ed68ba34 2008-05-17       drh:         @ (by <i>%h(zOwner)</i>)
86ed68ba34 2008-05-17       drh:       }
49380d500a 2008-05-28       drh:       if( g.okTktFmt ){
86ed68ba34 2008-05-17       drh:         @ [<a href="rptedit?rn=%d(rn)&amp;copy=1" rel="nofollow">copy</a>]
86ed68ba34 2008-05-17       drh:       }
86ed68ba34 2008-05-17       drh:       if( g.okAdmin || (g.okWrTkt && zOwner && strcmp(g.zLogin,zOwner)==0) ){
86ed68ba34 2008-05-17       drh:         @ [<a href="rptedit?rn=%d(rn)" rel="nofollow">edit</a>]
86ed68ba34 2008-05-17       drh:       }
d3e711fd2f 2008-07-15       drh:       if( g.okTktFmt ){
d3e711fd2f 2008-07-15       drh:         @ [<a href="rptsql?rn=%d(rn)" rel="nofollow">sql</a>]
d3e711fd2f 2008-07-15       drh:       }
86ed68ba34 2008-05-17       drh:       @ </li>
86ed68ba34 2008-05-17       drh:     }
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   @ </ol>
49380d500a 2008-05-28       drh:   if( g.okTktFmt ){
86ed68ba34 2008-05-17       drh:     @ <p>Create a new bug report display format:</p>
86ed68ba34 2008-05-17       drh:     @ <ol>
49380d500a 2008-05-28       drh:     @ <li value="%d(cnt+1)"><a href="rptnew">New report format</a></li>
86ed68ba34 2008-05-17       drh:     @ </ol>
86ed68ba34 2008-05-17       drh:   }
55342eb9fb 2008-05-17       drh:   style_footer();
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** Remove whitespace from both ends of a string.
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: char *trim_string(const char *zOrig){
62f37c9722 2007-11-26       drh:   int i;
62f37c9722 2007-11-26       drh:   while( isspace(*zOrig) ){ zOrig++; }
62f37c9722 2007-11-26       drh:   i = strlen(zOrig);
62f37c9722 2007-11-26       drh:   while( i>0 && isspace(zOrig[i-1]) ){ i--; }
62f37c9722 2007-11-26       drh:   return mprintf("%.*s", i, zOrig);
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** Extract a numeric (integer) value from a string.
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: char *extract_integer(const char *zOrig){
62f37c9722 2007-11-26       drh:   if( zOrig == NULL || zOrig[0] == 0 ) return "";
62f37c9722 2007-11-26       drh:   while( *zOrig && !isdigit(*zOrig) ){ zOrig++; }
62f37c9722 2007-11-26       drh:   if( *zOrig ){
62f37c9722 2007-11-26       drh:     /* we have a digit. atoi() will get as much of the number as it
62f37c9722 2007-11-26       drh:     ** can. We'll run it through mprintf() to get a string. Not
62f37c9722 2007-11-26       drh:     ** an efficient way to do it, but effective.
62f37c9722 2007-11-26       drh:     */
62f37c9722 2007-11-26       drh:     return mprintf("%d", atoi(zOrig));
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   return "";
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** Remove blank lines from the beginning of a string and
62f37c9722 2007-11-26       drh: ** all whitespace from the end. Removes whitespace preceeding a NL,
62f37c9722 2007-11-26       drh: ** which also converts any CRNL sequence into a single NL.
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: char *remove_blank_lines(const char *zOrig){
62f37c9722 2007-11-26       drh:   int i, j, n;
62f37c9722 2007-11-26       drh:   char *z;
62f37c9722 2007-11-26       drh:   for(i=j=0; isspace(zOrig[i]); i++){ if( zOrig[i]=='\n' ) j = i+1; }
62f37c9722 2007-11-26       drh:   n = strlen(&zOrig[j]);
62f37c9722 2007-11-26       drh:   while( n>0 && isspace(zOrig[j+n-1]) ){ n--; }
62f37c9722 2007-11-26       drh:   z = mprintf("%.*s", n, &zOrig[j]);
62f37c9722 2007-11-26       drh:   for(i=j=0; z[i]; i++){
62f37c9722 2007-11-26       drh:     if( z[i+1]=='\n' && z[i]!='\n' && isspace(z[i]) ){
62f37c9722 2007-11-26       drh:       z[j] = z[i];
62f37c9722 2007-11-26       drh:       while(isspace(z[j]) && z[j] != '\n' ){ j--; }
62f37c9722 2007-11-26       drh:       j++;
62f37c9722 2007-11-26       drh:       continue;
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:     z[j++] = z[i];
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   z[j] = 0;
62f37c9722 2007-11-26       drh:   return z;
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*********************************************************************/
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** This is the SQLite authorizer callback used to make sure that the
62f37c9722 2007-11-26       drh: ** SQL statements entered by users do not try to do anything untoward.
62f37c9722 2007-11-26       drh: ** If anything suspicious is tried, set *(char**)pError to an error
62f37c9722 2007-11-26       drh: ** message obtained from malloc.
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: static int report_query_authorizer(
62f37c9722 2007-11-26       drh:   void *pError,
62f37c9722 2007-11-26       drh:   int code,
62f37c9722 2007-11-26       drh:   const char *zArg1,
62f37c9722 2007-11-26       drh:   const char *zArg2,
62f37c9722 2007-11-26       drh:   const char *zArg3,
62f37c9722 2007-11-26       drh:   const char *zArg4
62f37c9722 2007-11-26       drh: ){
21326fb6f7 2008-07-19       drh:   int rc = SQLITE_OK;
21326fb6f7 2008-07-19       drh:   if( *(char**)pError ){
62f37c9722 2007-11-26       drh:     /* We've already seen an error.  No need to continue. */
62f37c9722 2007-11-26       drh:     return SQLITE_OK;
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   switch( code ){
62f37c9722 2007-11-26       drh:     case SQLITE_SELECT:
62f37c9722 2007-11-26       drh:     case SQLITE_FUNCTION: {
62f37c9722 2007-11-26       drh:       break;
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:     case SQLITE_READ: {
62f37c9722 2007-11-26       drh:       static const char *azAllowed[] = {
62f37c9722 2007-11-26       drh:          "ticket",
62f37c9722 2007-11-26       drh:          "blob",
62f37c9722 2007-11-26       drh:          "filename",
62f37c9722 2007-11-26       drh:          "mlink",
62f37c9722 2007-11-26       drh:          "plink",
62f37c9722 2007-11-26       drh:          "event",
62f37c9722 2007-11-26       drh:          "tag",
62f37c9722 2007-11-26       drh:          "tagxref",
55342eb9fb 2008-05-17       drh:       };
62f37c9722 2007-11-26       drh:       int i;
62f37c9722 2007-11-26       drh:       for(i=0; i<sizeof(azAllowed)/sizeof(azAllowed[0]); i++){
62f37c9722 2007-11-26       drh:         if( strcasecmp(zArg1, azAllowed[i])==0 ) break;
62f37c9722 2007-11-26       drh:       }
62f37c9722 2007-11-26       drh:       if( i>=sizeof(azAllowed)/sizeof(azAllowed[0]) ){
21326fb6f7 2008-07-19       drh:         *(char**)pError = mprintf("access to table \"%s\" is restricted",zArg1);
21326fb6f7 2008-07-19       drh:         rc = SQLITE_DENY;
21326fb6f7 2008-07-19       drh:       }else if( !g.okRdAddr && strncmp(zArg2, "private_", 8)==0 ){
21326fb6f7 2008-07-19       drh:         rc = SQLITE_IGNORE;
62f37c9722 2007-11-26       drh:       }
62f37c9722 2007-11-26       drh:       break;
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:     default: {
21326fb6f7 2008-07-19       drh:       *(char**)pError = mprintf("only SELECT statements are allowed");
21326fb6f7 2008-07-19       drh:       rc = SQLITE_DENY;
62f37c9722 2007-11-26       drh:       break;
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:   }
21326fb6f7 2008-07-19       drh:   return rc;
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** Check the given SQL to see if is a valid query that does not
62f37c9722 2007-11-26       drh: ** attempt to do anything dangerous.  Return 0 on success and a
62f37c9722 2007-11-26       drh: ** pointer to an error message string (obtained from malloc) if
62f37c9722 2007-11-26       drh: ** there is a problem.
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: char *verify_sql_statement(char *zSql){
62f37c9722 2007-11-26       drh:   int i;
55342eb9fb 2008-05-17       drh:   char *zErr = 0;
55342eb9fb 2008-05-17       drh:   const char *zTail;
55342eb9fb 2008-05-17       drh:   sqlite3_stmt *pStmt;
55342eb9fb 2008-05-17       drh:   int rc;
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   /* First make sure the SQL is a single query command by verifying that
62f37c9722 2007-11-26       drh:   ** the first token is "SELECT" and that there are no unquoted semicolons.
62f37c9722 2007-11-26       drh:   */
62f37c9722 2007-11-26       drh:   for(i=0; isspace(zSql[i]); i++){}
62f37c9722 2007-11-26       drh:   if( strncasecmp(&zSql[i],"select",6)!=0 ){
62f37c9722 2007-11-26       drh:     return mprintf("The SQL must be a SELECT statement");
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   for(i=0; zSql[i]; i++){
62f37c9722 2007-11-26       drh:     if( zSql[i]==';' ){
62f37c9722 2007-11-26       drh:       int bad;
62f37c9722 2007-11-26       drh:       int c = zSql[i+1];
62f37c9722 2007-11-26       drh:       zSql[i+1] = 0;
62f37c9722 2007-11-26       drh:       bad = sqlite3_complete(zSql);
62f37c9722 2007-11-26       drh:       zSql[i+1] = c;
62f37c9722 2007-11-26       drh:       if( bad ){
62f37c9722 2007-11-26       drh:         /* A complete statement basically means that an unquoted semi-colon
62f37c9722 2007-11-26       drh:         ** was found. We don't actually check what's after that.
62f37c9722 2007-11-26       drh:         */
62f37c9722 2007-11-26       drh:         return mprintf("Semi-colon detected! "
62f37c9722 2007-11-26       drh:                        "Only a single SQL statement is allowed");
62f37c9722 2007-11-26       drh:       }
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   /* Compile the statement and check for illegal accesses or syntax errors. */
62f37c9722 2007-11-26       drh:   sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr);
62f37c9722 2007-11-26       drh:   rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, &zTail);
62f37c9722 2007-11-26       drh:   if( rc!=SQLITE_OK ){
62f37c9722 2007-11-26       drh:     zErr = mprintf("Syntax error: %s", sqlite3_errmsg(g.db));
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   if( pStmt ){
62f37c9722 2007-11-26       drh:     sqlite3_finalize(pStmt);
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   sqlite3_set_authorizer(g.db, 0, 0);
62f37c9722 2007-11-26       drh:   return zErr;
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** WEBPAGE: /rptsql
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: void view_see_sql(void){
55342eb9fb 2008-05-17       drh:   int rn;
55342eb9fb 2008-05-17       drh:   const char *zTitle;
55342eb9fb 2008-05-17       drh:   const char *zSQL;
55342eb9fb 2008-05-17       drh:   const char *zOwner;
55342eb9fb 2008-05-17       drh:   const char *zClrKey;
62f37c9722 2007-11-26       drh:   Stmt q;
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   login_check_credentials();
d3e711fd2f 2008-07-15       drh:   if( !g.okTktFmt ){
62f37c9722 2007-11-26       drh:     login_needed();
62f37c9722 2007-11-26       drh:     return;
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   rn = atoi(PD("rn","0"));
62f37c9722 2007-11-26       drh:   db_prepare(&q, "SELECT title, sqlcode, owner, cols "
62f37c9722 2007-11-26       drh:                    "FROM reportfmt WHERE rn=%d",rn);
62f37c9722 2007-11-26       drh:   style_header("SQL For Report Format Number %d", rn);
62f37c9722 2007-11-26       drh:   if( db_step(&q)!=SQLITE_ROW ){
62f37c9722 2007-11-26       drh:     @ <p>Unknown report number: %d(rn)</p>
62f37c9722 2007-11-26       drh:     style_footer();
62f37c9722 2007-11-26       drh:     return;
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   zTitle = db_column_text(&q, 0);
62f37c9722 2007-11-26       drh:   zSQL = db_column_text(&q, 1);
62f37c9722 2007-11-26       drh:   zOwner = db_column_text(&q, 2);
62f37c9722 2007-11-26       drh:   zClrKey = db_column_text(&q, 3);
62f37c9722 2007-11-26       drh:   @ <table cellpadding=0 cellspacing=0 border=0>
62f37c9722 2007-11-26       drh:   @ <tr><td valign="top" align="right">Title:</td><td width=15></td>
62f37c9722 2007-11-26       drh:   @ <td colspan=3>%h(zTitle)</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr><td valign="top" align="right">Owner:</td><td></td>
62f37c9722 2007-11-26       drh:   @ <td colspan=3>%h(zOwner)</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr><td valign="top" align="right">SQL:</td><td></td>
62f37c9722 2007-11-26       drh:   @ <td valign="top"><pre>
62f37c9722 2007-11-26       drh:   @ %h(zSQL)
62f37c9722 2007-11-26       drh:   @ </pre></td>
62f37c9722 2007-11-26       drh:   @ <td width=15></td><td valign="top">
62f37c9722 2007-11-26       drh:   output_color_key(zClrKey, 0, "border=0 cellspacing=0 cellpadding=3");
62f37c9722 2007-11-26       drh:   @ </td>
62f37c9722 2007-11-26       drh:   @ </tr></table>
62f37c9722 2007-11-26       drh:   report_format_hints();
62f37c9722 2007-11-26       drh:   style_footer();
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** WEBPAGE: /rptnew
62f37c9722 2007-11-26       drh: ** WEBPAGE: /rptedit
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: void view_edit(void){
62f37c9722 2007-11-26       drh:   int rn;
62f37c9722 2007-11-26       drh:   const char *zTitle;
62f37c9722 2007-11-26       drh:   const char *z;
62f37c9722 2007-11-26       drh:   const char *zOwner;
49380d500a 2008-05-28       drh:   const char *zClrKey;
62f37c9722 2007-11-26       drh:   char *zSQL;
62f37c9722 2007-11-26       drh:   char *zErr = 0;
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   login_check_credentials();
d3e711fd2f 2008-07-15       drh:   if( !g.okTktFmt ){
62f37c9722 2007-11-26       drh:     login_needed();
62f37c9722 2007-11-26       drh:     return;
62f37c9722 2007-11-26       drh:   }
55342eb9fb 2008-05-17       drh:   /*view_add_functions(0);*/
62f37c9722 2007-11-26       drh:   rn = atoi(PD("rn","0"));
62f37c9722 2007-11-26       drh:   zTitle = P("t");
62f37c9722 2007-11-26       drh:   zOwner = PD("w",g.zLogin);
62f37c9722 2007-11-26       drh:   z = P("s");
62f37c9722 2007-11-26       drh:   zSQL = z ? trim_string(z) : 0;
62f37c9722 2007-11-26       drh:   zClrKey = trim_string(PD("k",""));
62f37c9722 2007-11-26       drh:   if( rn>0 && P("del2") ){
0be54823ba 2008-10-18       drh:     login_verify_csrf_secret();
62f37c9722 2007-11-26       drh:     db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn);
62f37c9722 2007-11-26       drh:     cgi_redirect("reportlist");
62f37c9722 2007-11-26       drh:     return;
62f37c9722 2007-11-26       drh:   }else if( rn>0 && P("del1") ){
62f37c9722 2007-11-26       drh:     zTitle = db_text(0, "SELECT title FROM reportfmt "
62f37c9722 2007-11-26       drh:                          "WHERE rn=%d", rn);
62f37c9722 2007-11-26       drh:     if( zTitle==0 ) cgi_redirect("reportlist");
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:     style_header("Are You Sure?");
62f37c9722 2007-11-26       drh:     @ <form action="rptedit" method="POST">
62f37c9722 2007-11-26       drh:     @ <p>You are about to delete all traces of the report
62f37c9722 2007-11-26       drh:     @ <strong>%h(zTitle)</strong> from
62f37c9722 2007-11-26       drh:     @ the database.  This is an irreversible operation.  All records
62f37c9722 2007-11-26       drh:     @ related to this report will be removed and cannot be recovered.</p>
62f37c9722 2007-11-26       drh:     @
62f37c9722 2007-11-26       drh:     @ <input type="hidden" name="rn" value="%d(rn)">
0be54823ba 2008-10-18       drh:     login_insert_csrf_secret();
62f37c9722 2007-11-26       drh:     @ <input type="submit" name="del2" value="Delete The Report">
62f37c9722 2007-11-26       drh:     @ <input type="submit" name="can" value="Cancel">
62f37c9722 2007-11-26       drh:     @ </form>
62f37c9722 2007-11-26       drh:     style_footer();
62f37c9722 2007-11-26       drh:     return;
62f37c9722 2007-11-26       drh:   }else if( P("can") ){
62f37c9722 2007-11-26       drh:     /* user cancelled */
62f37c9722 2007-11-26       drh:     cgi_redirect("reportlist");
62f37c9722 2007-11-26       drh:     return;
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   if( zTitle && zSQL ){
62f37c9722 2007-11-26       drh:     if( zSQL[0]==0 ){
62f37c9722 2007-11-26       drh:       zErr = "Please supply an SQL query statement";
62f37c9722 2007-11-26       drh:     }else if( (zTitle = trim_string(zTitle))[0]==0 ){
62f37c9722 2007-11-26       drh:       zErr = "Please supply a title";
62f37c9722 2007-11-26       drh:     }else{
62f37c9722 2007-11-26       drh:       zErr = verify_sql_statement(zSQL);
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:     if( zErr==0 ){
0be54823ba 2008-10-18       drh:       login_verify_csrf_secret();
62f37c9722 2007-11-26       drh:       if( rn>0 ){
62f37c9722 2007-11-26       drh:         db_multi_exec("UPDATE reportfmt SET title=%Q, sqlcode=%Q,"
62f37c9722 2007-11-26       drh:                       " owner=%Q, cols=%Q WHERE rn=%d",
62f37c9722 2007-11-26       drh:            zTitle, zSQL, zOwner, zClrKey, rn);
62f37c9722 2007-11-26       drh:       }else{
62f37c9722 2007-11-26       drh:         db_multi_exec("INSERT INTO reportfmt(title,sqlcode,owner,cols) "
62f37c9722 2007-11-26       drh:            "VALUES(%Q,%Q,%Q,%Q)",
62f37c9722 2007-11-26       drh:            zTitle, zSQL, zOwner, zClrKey);
62f37c9722 2007-11-26       drh:         rn = db_last_insert_rowid();
62f37c9722 2007-11-26       drh:       }
62f37c9722 2007-11-26       drh:       cgi_redirect(mprintf("rptview?rn=%d", rn));
62f37c9722 2007-11-26       drh:       return;
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:   }else if( rn==0 ){
62f37c9722 2007-11-26       drh:     zTitle = "";
49380d500a 2008-05-28       drh:     zSQL = ticket_report_template();
49380d500a 2008-05-28       drh:     zClrKey = ticket_key_template();
62f37c9722 2007-11-26       drh:   }else{
55342eb9fb 2008-05-17       drh:     Stmt q;
62f37c9722 2007-11-26       drh:     db_prepare(&q, "SELECT title, sqlcode, owner, cols "
62f37c9722 2007-11-26       drh:                      "FROM reportfmt WHERE rn=%d",rn);
62f37c9722 2007-11-26       drh:     if( db_step(&q)==SQLITE_ROW ){
62f37c9722 2007-11-26       drh:       zTitle = db_column_malloc(&q, 0);
62f37c9722 2007-11-26       drh:       zSQL = db_column_malloc(&q, 1);
62f37c9722 2007-11-26       drh:       zOwner = db_column_malloc(&q, 2);
62f37c9722 2007-11-26       drh:       zClrKey = db_column_malloc(&q, 3);
62f37c9722 2007-11-26       drh:     }
55342eb9fb 2008-05-17       drh:     db_finalize(&q);
62f37c9722 2007-11-26       drh:     if( P("copy") ){
62f37c9722 2007-11-26       drh:       rn = 0;
62f37c9722 2007-11-26       drh:       zTitle = mprintf("Copy Of %s", zTitle);
62f37c9722 2007-11-26       drh:       zOwner = g.zLogin;
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   if( zOwner==0 ) zOwner = g.zLogin;
62f37c9722 2007-11-26       drh:   style_submenu_element("Cancel", "Cancel", "reportlist");
62f37c9722 2007-11-26       drh:   if( rn>0 ){
62f37c9722 2007-11-26       drh:     style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn);
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
62f37c9722 2007-11-26       drh:   if( zErr ){
62f37c9722 2007-11-26       drh:     @ <blockquote><font color="#ff0000"><b>%h(zErr)</b></font></blockquote>
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   @ <form action="rptedit" method="POST">
62f37c9722 2007-11-26       drh:   @ <input type="hidden" name="rn" value="%d(rn)">
62f37c9722 2007-11-26       drh:   @ <p>Report Title:<br>
62f37c9722 2007-11-26       drh:   @ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
62f37c9722 2007-11-26       drh:   @ <p>Enter a complete SQL query statement against the "TICKET" table:<br>
62f37c9722 2007-11-26       drh:   @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
62f37c9722 2007-11-26       drh:   @ </p>
0be54823ba 2008-10-18       drh:   login_insert_csrf_secret();
62f37c9722 2007-11-26       drh:   if( g.okAdmin ){
62f37c9722 2007-11-26       drh:     @ <p>Report owner:
62f37c9722 2007-11-26       drh:     @ <input type="text" name="w" size="20" value="%h(zOwner)">
62f37c9722 2007-11-26       drh:     @ </p>
62f37c9722 2007-11-26       drh:   } else {
62f37c9722 2007-11-26       drh:     @ <input type="hidden" name="w" value="%h(zOwner)">
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   @ <p>Enter an optional color key in the following box.  (If blank, no
62f37c9722 2007-11-26       drh:   @ color key is displayed.)  Each line contains the text for a single
62f37c9722 2007-11-26       drh:   @ entry in the key.  The first token of each line is the background
62f37c9722 2007-11-26       drh:   @ color for that line.<br>
62f37c9722 2007-11-26       drh:   @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
62f37c9722 2007-11-26       drh:   @ </p>
62f37c9722 2007-11-26       drh:   if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){
62f37c9722 2007-11-26       drh:     @ <p>This report format is owned by %h(zOwner).  You are not allowed
62f37c9722 2007-11-26       drh:     @ to change it.</p>
62f37c9722 2007-11-26       drh:     @ </form>
62f37c9722 2007-11-26       drh:     report_format_hints();
62f37c9722 2007-11-26       drh:     style_footer();
62f37c9722 2007-11-26       drh:     return;
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   @ <input type="submit" value="Apply Changes">
62f37c9722 2007-11-26       drh:   if( rn>0 ){
62f37c9722 2007-11-26       drh:     @ <input type="submit" value="Delete This Report" name="del1">
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   @ </form>
62f37c9722 2007-11-26       drh:   report_format_hints();
62f37c9722 2007-11-26       drh:   style_footer();
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** Output a bunch of text that provides information about report
62f37c9722 2007-11-26       drh: ** formats
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: static void report_format_hints(void){
62f37c9722 2007-11-26       drh:   char *zSchema;
62f37c9722 2007-11-26       drh:   zSchema = db_text(0,"SELECT sql FROM sqlite_master WHERE name='ticket'");
d3e711fd2f 2008-07-15       drh:   if( zSchema==0 ){
d3e711fd2f 2008-07-15       drh:     zSchema = db_text(0,"SELECT sql FROM repository.sqlite_master"
d3e711fd2f 2008-07-15       drh:                         " WHERE name='ticket'");
d3e711fd2f 2008-07-15       drh:   }
62f37c9722 2007-11-26       drh:   @ <hr><h3>TICKET Schema</h3>
62f37c9722 2007-11-26       drh:   @ <blockquote><pre>
62f37c9722 2007-11-26       drh:   @ %h(zSchema)
62f37c9722 2007-11-26       drh:   @ </pre></blockquote>
62f37c9722 2007-11-26       drh:   @ <h3>Notes</h3>
62f37c9722 2007-11-26       drh:   @ <ul>
62f37c9722 2007-11-26       drh:   @ <li><p>The SQL must consist of a single SELECT statement</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>If a column of the result set is named "#" then that column
62f37c9722 2007-11-26       drh:   @ is assumed to hold a ticket number.  A hyperlink will be created from
62f37c9722 2007-11-26       drh:   @ that column to a detailed view of the ticket.</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>If a column of the result set is named "bgcolor" then the content
62f37c9722 2007-11-26       drh:   @ of that column determines the background color of the row.</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>The <b>user()</b> SQL function returns a string
62f37c9722 2007-11-26       drh:   @ which is the login of the current user.</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>The first column whose name begins with underscore ("_") and all
62f37c9722 2007-11-26       drh:   @ subsequent columns are shown on their own rows in the table.  This might
62f37c9722 2007-11-26       drh:   @ be useful for displaying the description of tickets.
62f37c9722 2007-11-26       drh:   @ </p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>The <b>aux()</b> SQL function takes a parameter name as an argument
62f37c9722 2007-11-26       drh:   @ and returns the value that the user enters in the resulting HTML form. A
62f37c9722 2007-11-26       drh:   @ second optional parameter provides a default value for the field.</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>The <b>option()</b> SQL function takes a parameter name
62f37c9722 2007-11-26       drh:   @ and a quoted SELECT statement as parameters. The query results are
62f37c9722 2007-11-26       drh:   @ presented as an HTML dropdown menu and the function returns
62f37c9722 2007-11-26       drh:   @ the currently selected value. Results may be a single value column or
62f37c9722 2007-11-26       drh:   @ two <b>value,description</b> columns. The first row is the default.</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>The <b>cgi()</b> SQL function takes a parameter name as an argument
62f37c9722 2007-11-26       drh:   @ and returns the value of a corresponding CGI query value. If the CGI
62f37c9722 2007-11-26       drh:   @ parameter doesn't exist, an optional second argument will be returned
62f37c9722 2007-11-26       drh:   @ instead.</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>If a column is wrapped by the <b>wiki()</b> SQL function, it will
62f37c9722 2007-11-26       drh:   @ be rendered as wiki formatted content.</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>If a column is wrapped by the <b>tkt()</b> SQL function, it will
62f37c9722 2007-11-26       drh:   @ be shown as a ticket id with a link to the appropriate page</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>If a column is wrapped by the <b>chng()</b> SQL function, it will
62f37c9722 2007-11-26       drh:   @ be shown as a baseline id with a link to the appropriate page.</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>The <b>search()</b> SQL function takes a keyword pattern and
62f37c9722 2007-11-26       drh:   @ a search text. The function returns an integer score which is
62f37c9722 2007-11-26       drh:   @ higher depending on how well the search went.</p></li>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <li><p>The query can join other tables in the database besides TICKET.
62f37c9722 2007-11-26       drh:   @ </p></li>
62f37c9722 2007-11-26       drh:   @ </ul>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <h3>Examples</h3>
62f37c9722 2007-11-26       drh:   @ <p>In this example, the first column in the result set is named
62f37c9722 2007-11-26       drh:   @ "bgcolor".  The value of this column is not displayed.  Instead, it
62f37c9722 2007-11-26       drh:   @ selects the background color of each row based on the TICKET.STATUS
62f37c9722 2007-11-26       drh:   @ field of the database.  The color key at the right shows the various
62f37c9722 2007-11-26       drh:   @ color codes.</p>
62f37c9722 2007-11-26       drh:   @ <table align="right" style="margin: 0 5px;" border=1 cellspacing=0 width=125>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#f2dcdc"><td align="center">new or active</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#e8e8bd"><td align="center">review</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#cfe8bd"><td align="center">fixed</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#bde5d6"><td align="center">tested</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#cacae5"><td align="center">defer</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#c8c8c8"><td align="center">closed</td></tr>
62f37c9722 2007-11-26       drh:   @ </table>
62f37c9722 2007-11-26       drh:   @ <blockquote><pre>
62f37c9722 2007-11-26       drh:   @ SELECT
62f37c9722 2007-11-26       drh:   @   CASE WHEN status IN ('new','active') THEN '#f2dcdc'
62f37c9722 2007-11-26       drh:   @        WHEN status='review' THEN '#e8e8bd'
62f37c9722 2007-11-26       drh:   @        WHEN status='fixed' THEN '#cfe8bd'
62f37c9722 2007-11-26       drh:   @        WHEN status='tested' THEN '#bde5d6'
62f37c9722 2007-11-26       drh:   @        WHEN status='defer' THEN '#cacae5'
62f37c9722 2007-11-26       drh:   @        ELSE '#c8c8c8' END as 'bgcolor',
62f37c9722 2007-11-26       drh:   @   tn AS '#',
62f37c9722 2007-11-26       drh:   @   type AS 'Type',
62f37c9722 2007-11-26       drh:   @   status AS 'Status',
62f37c9722 2007-11-26       drh:   @   sdate(origtime) AS 'Created',
62f37c9722 2007-11-26       drh:   @   owner AS 'By',
62f37c9722 2007-11-26       drh:   @   subsystem AS 'Subsys',
62f37c9722 2007-11-26       drh:   @   sdate(changetime) AS 'Changed',
62f37c9722 2007-11-26       drh:   @   assignedto AS 'Assigned',
62f37c9722 2007-11-26       drh:   @   severity AS 'Svr',
62f37c9722 2007-11-26       drh:   @   priority AS 'Pri',
62f37c9722 2007-11-26       drh:   @   title AS 'Title'
62f37c9722 2007-11-26       drh:   @ FROM ticket
62f37c9722 2007-11-26       drh:   @ </pre></blockquote>
62f37c9722 2007-11-26       drh:   @ <p>To base the background color on the TICKET.PRIORITY or
62f37c9722 2007-11-26       drh:   @ TICKET.SEVERITY fields, substitute the following code for the
62f37c9722 2007-11-26       drh:   @ first column of the query:</p>
62f37c9722 2007-11-26       drh:   @ <table align="right" style="margin: 0 5px;" border=1 cellspacing=0 width=125>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#f2dcdc"><td align="center">1</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#e8e8bd"><td align="center">2</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#cfe8bd"><td align="center">3</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#cacae5"><td align="center">4</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="#c8c8c8"><td align="center">5</td></tr>
62f37c9722 2007-11-26       drh:   @ </table>
62f37c9722 2007-11-26       drh:   @ <blockquote><pre>
62f37c9722 2007-11-26       drh:   @ SELECT
62f37c9722 2007-11-26       drh:   @   CASE priority WHEN 1 THEN '#f2dcdc'
62f37c9722 2007-11-26       drh:   @        WHEN 2 THEN '#e8e8bd'
62f37c9722 2007-11-26       drh:   @        WHEN 3 THEN '#cfe8bd'
62f37c9722 2007-11-26       drh:   @        WHEN 4 THEN '#cacae5'
62f37c9722 2007-11-26       drh:   @        ELSE '#c8c8c8' END as 'bgcolor',
62f37c9722 2007-11-26       drh:   @ ...
62f37c9722 2007-11-26       drh:   @ FROM ticket
62f37c9722 2007-11-26       drh:   @ </pre></blockquote>
62f37c9722 2007-11-26       drh: #if 0
62f37c9722 2007-11-26       drh:   @ <p>You can, of course, substitute different colors if you choose.
62f37c9722 2007-11-26       drh:   @ Here is a palette of suggested background colors:</p>
62f37c9722 2007-11-26       drh:   @ <blockquote>
62f37c9722 2007-11-26       drh:   @ <table border=1 cellspacing=0 width=300>
62f37c9722 2007-11-26       drh:   @ <tr><td align="center" bgcolor="#ffbdbd">#ffbdbd</td>
62f37c9722 2007-11-26       drh:   @     <td align="center" bgcolor="#f2dcdc">#f2dcdc</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr><td align="center" bgcolor="#ffffbd">#ffffbd</td>
62f37c9722 2007-11-26       drh:   @     <td align="center" bgcolor="#e8e8bd">#e8e8bd</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr><td align="center" bgcolor="#c0ebc0">#c0ebc0</td>
62f37c9722 2007-11-26       drh:   @     <td align="center" bgcolor="#cfe8bd">#cfe8bd</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr><td align="center" bgcolor="#c0c0f4">#c0c0f4</td>
62f37c9722 2007-11-26       drh:   @     <td align="center" bgcolor="#d6d6e8">#d6d6e8</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr><td align="center" bgcolor="#d0b1ff">#d0b1ff</td>
62f37c9722 2007-11-26       drh:   @     <td align="center" bgcolor="#d2c0db">#d2c0db</td></tr>
62f37c9722 2007-11-26       drh:   @ <tr><td align="center" bgcolor="#bbbbbb">#bbbbbb</td>
62f37c9722 2007-11-26       drh:   @     <td align="center" bgcolor="#d0d0d0">#d0d0d0</td></tr>
62f37c9722 2007-11-26       drh:   @ </table>
62f37c9722 2007-11-26       drh:   @ </blockquote>
62f37c9722 2007-11-26       drh: #endif
62f37c9722 2007-11-26       drh:   @ <p>To see the TICKET.DESCRIPTION and TICKET.REMARKS fields, include
62f37c9722 2007-11-26       drh:   @ them as the last two columns of the result set and given them names
62f37c9722 2007-11-26       drh:   @ that begin with an underscore.  Like this:</p>
62f37c9722 2007-11-26       drh:   @ <blockquote><pre>
62f37c9722 2007-11-26       drh:   @  SELECT
62f37c9722 2007-11-26       drh:   @    tn AS '#',
62f37c9722 2007-11-26       drh:   @    type AS 'Type',
62f37c9722 2007-11-26       drh:   @    status AS 'Status',
62f37c9722 2007-11-26       drh:   @    sdate(origtime) AS 'Created',
62f37c9722 2007-11-26       drh:   @    owner AS 'By',
62f37c9722 2007-11-26       drh:   @    subsystem AS 'Subsys',
62f37c9722 2007-11-26       drh:   @    sdate(changetime) AS 'Changed',
62f37c9722 2007-11-26       drh:   @    assignedto AS 'Assigned',
62f37c9722 2007-11-26       drh:   @    severity AS 'Svr',
62f37c9722 2007-11-26       drh:   @    priority AS 'Pri',
62f37c9722 2007-11-26       drh:   @    title AS 'Title',
62f37c9722 2007-11-26       drh:   @    description AS '_Description',   -- When the column name begins with '_'
62f37c9722 2007-11-26       drh:   @    remarks AS '_Remarks'            -- the data is shown on a separate row.
62f37c9722 2007-11-26       drh:   @  FROM ticket
62f37c9722 2007-11-26       drh:   @ </pre></blockquote>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh:   @ <p>Or, to see part of the description on the same row, use the
62f37c9722 2007-11-26       drh:   @ <b>wiki()</b> function with some string manipulation. Using the
62f37c9722 2007-11-26       drh:   @ <b>tkt()</b> function on the ticket number will also generate a linked
62f37c9722 2007-11-26       drh:   @ field, but without the extra <i>edit</i> column:
62f37c9722 2007-11-26       drh:   @ </p>
62f37c9722 2007-11-26       drh:   @ <blockquote><pre>
62f37c9722 2007-11-26       drh:   @  SELECT
62f37c9722 2007-11-26       drh:   @    tkt(tn) AS '',
62f37c9722 2007-11-26       drh:   @    title AS 'Title',
62f37c9722 2007-11-26       drh:   @    wiki(substr(description,0,80)) AS 'Description'
62f37c9722 2007-11-26       drh:   @  FROM ticket
62f37c9722 2007-11-26       drh:   @ </pre></blockquote>
62f37c9722 2007-11-26       drh:   @
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: static void column_header(int rn,const char *zCol, int nCol, int nSorted,
62f37c9722 2007-11-26       drh:     const char *zDirection, const char *zExtra
62f37c9722 2007-11-26       drh: ){
62f37c9722 2007-11-26       drh:   int set = (nCol==nSorted);
62f37c9722 2007-11-26       drh:   int desc = !strcmp(zDirection,"DESC");
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   /*
62f37c9722 2007-11-26       drh:   ** Clicking same column header 3 times in a row resets any sorting.
62f37c9722 2007-11-26       drh:   ** Note that we link to rptview, which means embedded reports will get
62f37c9722 2007-11-26       drh:   ** sent to the actual report view page as soon as a user tries to do
62f37c9722 2007-11-26       drh:   ** any sorting. I don't see that as a Bad Thing.
62f37c9722 2007-11-26       drh:   */
62f37c9722 2007-11-26       drh:   if(set && desc){
62f37c9722 2007-11-26       drh:     @ <th bgcolor="%s(BG1)" class="bkgnd1">
62f37c9722 2007-11-26       drh:     @   <a href="rptview?rn=%d(rn)%s(zExtra)">%h(zCol)</a></th>
62f37c9722 2007-11-26       drh:   }else{
62f37c9722 2007-11-26       drh:     if(set){
62f37c9722 2007-11-26       drh:       @ <th bgcolor="%s(BG1)" class="bkgnd1"><a
62f37c9722 2007-11-26       drh:     }else{
62f37c9722 2007-11-26       drh:       @ <th><a
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:     @ href="rptview?rn=%d(rn)&amp;order_by=%d(nCol)&amp;\
62f37c9722 2007-11-26       drh:     @ order_dir=%s(desc?"ASC":"DESC")\
62f37c9722 2007-11-26       drh:     @ %s(zExtra)">%h(zCol)</a></th>
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
55342eb9fb 2008-05-17       drh: /*
55342eb9fb 2008-05-17       drh: ** The state of the report generation.
55342eb9fb 2008-05-17       drh: */
62f37c9722 2007-11-26       drh: struct GenerateHTML {
86ed68ba34 2008-05-17       drh:   int rn;          /* Report number */
86ed68ba34 2008-05-17       drh:   int nCount;      /* Row number */
86ed68ba34 2008-05-17       drh:   int nCol;        /* Number of columns */
86ed68ba34 2008-05-17       drh:   int isMultirow;  /* True if multiple table rows per query result row */
86ed68ba34 2008-05-17       drh:   int iNewRow;     /* Index of first column that goes on separate row */
86ed68ba34 2008-05-17       drh:   int iBg;         /* Index of column that defines background color */
62f37c9722 2007-11-26       drh: };
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** The callback function for db_query
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: static int generate_html(
55342eb9fb 2008-05-17       drh:   void *pUser,     /* Pointer to output state */
62f37c9722 2007-11-26       drh:   int nArg,        /* Number of columns in this result row */
62f37c9722 2007-11-26       drh:   char **azArg,    /* Text of data in all columns */
62f37c9722 2007-11-26       drh:   char **azName    /* Names of the columns */
62f37c9722 2007-11-26       drh: ){
55342eb9fb 2008-05-17       drh:   struct GenerateHTML *pState = (struct GenerateHTML*)pUser;
62f37c9722 2007-11-26       drh:   int i;
ce277cadda 2008-05-25       drh:   const char *zTid;  /* Ticket UUID.  (value of column named '#') */
86ed68ba34 2008-05-17       drh:   int rn;            /* Report number */
62f37c9722 2007-11-26       drh:   char *zBg = 0;     /* Use this background color */
62f37c9722 2007-11-26       drh:   char zPage[30];    /* Text version of the ticket number */
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   /* Get the report number
62f37c9722 2007-11-26       drh:   */
62f37c9722 2007-11-26       drh:   rn = pState->rn;
62f37c9722 2007-11-26       drh: 
86ed68ba34 2008-05-17       drh:   /* Do initialization
62f37c9722 2007-11-26       drh:   */
86ed68ba34 2008-05-17       drh:   if( pState->nCount==0 ){
58f15e92ae 2009-01-08       drh:     /* Turn off the authorizer.  It is no longer doing anything since the
58f15e92ae 2009-01-08       drh:     ** query has already been prepared.
58f15e92ae 2009-01-08       drh:     */
58f15e92ae 2009-01-08       drh:     sqlite3_set_authorizer(g.db, 0, 0);
58f15e92ae 2009-01-08       drh: 
86ed68ba34 2008-05-17       drh:     /* Figure out the number of columns, the column that determines background
86ed68ba34 2008-05-17       drh:     ** color, and whether or not this row of data is represented by multiple
86ed68ba34 2008-05-17       drh:     ** rows in the table.
86ed68ba34 2008-05-17       drh:     */
86ed68ba34 2008-05-17       drh:     pState->nCol = 0;
86ed68ba34 2008-05-17       drh:     pState->isMultirow = 0;
86ed68ba34 2008-05-17       drh:     pState->iNewRow = -1;
86ed68ba34 2008-05-17       drh:     pState->iBg = -1;
86ed68ba34 2008-05-17       drh:     for(i=0; i<nArg; i++){
86ed68ba34 2008-05-17       drh:       if( azName[i][0]=='b' && strcmp(azName[i],"bgcolor")==0 ){
86ed68ba34 2008-05-17       drh:         pState->iBg = i;
86ed68ba34 2008-05-17       drh:         continue;
86ed68ba34 2008-05-17       drh:       }
86ed68ba34 2008-05-17       drh:       if( g.okWrite && azName[i][0]=='#' ){
86ed68ba34 2008-05-17       drh:         pState->nCol++;
86ed68ba34 2008-05-17       drh:       }
86ed68ba34 2008-05-17       drh:       if( !pState->isMultirow ){
86ed68ba34 2008-05-17       drh:         if( azName[i][0]=='_' ){
86ed68ba34 2008-05-17       drh:           pState->isMultirow = 1;
86ed68ba34 2008-05-17       drh:           pState->iNewRow = i;
86ed68ba34 2008-05-17       drh:         }else{
86ed68ba34 2008-05-17       drh:           pState->nCol++;
86ed68ba34 2008-05-17       drh:         }
62f37c9722 2007-11-26       drh:       }
62f37c9722 2007-11-26       drh:     }
86ed68ba34 2008-05-17       drh: 
86ed68ba34 2008-05-17       drh:     /* The first time this routine is called, output a table header
86ed68ba34 2008-05-17       drh:     */
55342eb9fb 2008-05-17       drh:     @ <tr>
ce277cadda 2008-05-25       drh:     zTid = 0;
55342eb9fb 2008-05-17       drh:     for(i=0; i<nArg; i++){
55342eb9fb 2008-05-17       drh:       char *zName = azName[i];
86ed68ba34 2008-05-17       drh:       if( i==pState->iBg ) continue;
86ed68ba34 2008-05-17       drh:       if( pState->iNewRow>=0 && i>=pState->iNewRow ){
ce277cadda 2008-05-25       drh:         if( g.okWrite && zTid ){
55342eb9fb 2008-05-17       drh:           @ <th>&nbsp;</th>
ce277cadda 2008-05-25       drh:           zTid = 0;
55342eb9fb 2008-05-17       drh:         }
55342eb9fb 2008-05-17       drh:         if( zName[0]=='_' ) zName++;
86ed68ba34 2008-05-17       drh:         @ </tr><tr><th colspan=%d(pState->nCol)>%h(zName)</th>
55342eb9fb 2008-05-17       drh:       }else{
55342eb9fb 2008-05-17       drh:         if( zName[0]=='#' ){
ce277cadda 2008-05-25       drh:           zTid = zName;
62f37c9722 2007-11-26       drh:         }
ce277cadda 2008-05-25       drh:         @ <th>%h(zName)</th>
55342eb9fb 2008-05-17       drh:       }
62f37c9722 2007-11-26       drh:     }
ce277cadda 2008-05-25       drh:     if( g.okWrite && zTid ){
62f37c9722 2007-11-26       drh:       @ <th>&nbsp;</th>
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:     @ </tr>
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   if( azArg==0 ){
86ed68ba34 2008-05-17       drh:     @ <tr><td colspan="%d(pState->nCol)">
62f37c9722 2007-11-26       drh:     @ <i>No records match the report criteria</i>
62f37c9722 2007-11-26       drh:     @ </td></tr>
62f37c9722 2007-11-26       drh:     return 0;
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   ++pState->nCount;
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   /* Output the separator above each entry in a table which has multiple lines
62f37c9722 2007-11-26       drh:   ** per database entry.
62f37c9722 2007-11-26       drh:   */
86ed68ba34 2008-05-17       drh:   if( pState->iNewRow>=0 ){
86ed68ba34 2008-05-17       drh:     @ <tr><td colspan=%d(pState->nCol)><font size=1>&nbsp;</font></td></tr>
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   /* Output the data for this entry from the database
62f37c9722 2007-11-26       drh:   */
86ed68ba34 2008-05-17       drh:   zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0;
62f37c9722 2007-11-26       drh:   if( zBg==0 ) zBg = "white";
62f37c9722 2007-11-26       drh:   @ <tr bgcolor="%h(zBg)">
ce277cadda 2008-05-25       drh:   zTid = 0;
62f37c9722 2007-11-26       drh:   zPage[0] = 0;
62f37c9722 2007-11-26       drh:   for(i=0; i<nArg; i++){
62f37c9722 2007-11-26       drh:     char *zData;
86ed68ba34 2008-05-17       drh:     if( i==pState->iBg ) continue;
62f37c9722 2007-11-26       drh:     zData = azArg[i];
62f37c9722 2007-11-26       drh:     if( zData==0 ) zData = "";
86ed68ba34 2008-05-17       drh:     if( pState->iNewRow>=0 && i>=pState->iNewRow ){
ce277cadda 2008-05-25       drh:       if( zTid && g.okWrite ){
ce277cadda 2008-05-25       drh:         @ <td valign="top"><a href="tktedit/%h(zTid)">edit</a></td>
ce277cadda 2008-05-25       drh:         zTid = 0;
62f37c9722 2007-11-26       drh:       }
62f37c9722 2007-11-26       drh:       if( zData[0] ){
ce277cadda 2008-05-25       drh:         Blob content;
86ed68ba34 2008-05-17       drh:         @ </tr><tr bgcolor="%h(zBg)"><td colspan=%d(pState->nCol)>
ce277cadda 2008-05-25       drh:         blob_init(&content, zData, -1);
ce277cadda 2008-05-25       drh:         wiki_convert(&content, 0, 0);
ce277cadda 2008-05-25       drh:         blob_reset(&content);
62f37c9722 2007-11-26       drh:       }
62f37c9722 2007-11-26       drh:     }else if( azName[i][0]=='#' ){
ce277cadda 2008-05-25       drh:       zTid = zData;
ce277cadda 2008-05-25       drh:       @ <td valign="top"><a href="tktview?name=%h(zData)">%h(zData)</a></td>
62f37c9722 2007-11-26       drh:     }else if( zData[0]==0 ){
62f37c9722 2007-11-26       drh:       @ <td valign="top">&nbsp;</td>
62f37c9722 2007-11-26       drh:     }else{
62f37c9722 2007-11-26       drh:       @ <td valign="top">
55342eb9fb 2008-05-17       drh:       @ %h(zData)
62f37c9722 2007-11-26       drh:       @ </td>
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:   }
ce277cadda 2008-05-25       drh:   if( zTid && g.okWrite ){
ce277cadda 2008-05-25       drh:     @ <td valign="top"><a href="tktedit/%h(zTid)">edit</a></td>
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   @ </tr>
62f37c9722 2007-11-26       drh:   return 0;
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** Output the text given in the argument.  Convert tabs and newlines into
62f37c9722 2007-11-26       drh: ** spaces.
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: static void output_no_tabs(const char *z){
62f37c9722 2007-11-26       drh:   while( z && z[0] ){
62f37c9722 2007-11-26       drh:     int i, j;
62f37c9722 2007-11-26       drh:     for(i=0; z[i] && (!isspace(z[i]) || z[i]==' '); i++){}
62f37c9722 2007-11-26       drh:     if( i>0 ){
62f37c9722 2007-11-26       drh:       cgi_printf("%.*s", i, z);
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:     for(j=i; isspace(z[j]); j++){}
62f37c9722 2007-11-26       drh:     if( j>i ){
62f37c9722 2007-11-26       drh:       cgi_printf("%*s", j-i, "");
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:     z += j;
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** Output a row as a tab-separated line of text.
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: static int output_tab_separated(
62f37c9722 2007-11-26       drh:   void *pUser,     /* Pointer to row-count integer */
62f37c9722 2007-11-26       drh:   int nArg,        /* Number of columns in this result row */
62f37c9722 2007-11-26       drh:   char **azArg,    /* Text of data in all columns */
62f37c9722 2007-11-26       drh:   char **azName    /* Names of the columns */
62f37c9722 2007-11-26       drh: ){
62f37c9722 2007-11-26       drh:   int *pCount = (int*)pUser;
62f37c9722 2007-11-26       drh:   int i;
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   if( *pCount==0 ){
62f37c9722 2007-11-26       drh:     for(i=0; i<nArg; i++){
62f37c9722 2007-11-26       drh:       output_no_tabs(azName[i]);
62f37c9722 2007-11-26       drh:       cgi_printf("%c", i<nArg-1 ? '\t' : '\n');
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   ++*pCount;
62f37c9722 2007-11-26       drh:   for(i=0; i<nArg; i++){
62f37c9722 2007-11-26       drh:     output_no_tabs(azArg[i]);
62f37c9722 2007-11-26       drh:     cgi_printf("%c", i<nArg-1 ? '\t' : '\n');
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   return 0;
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** Generate HTML that describes a color key.
62f37c9722 2007-11-26       drh: */
62f37c9722 2007-11-26       drh: void output_color_key(const char *zClrKey, int horiz, char *zTabArgs){
62f37c9722 2007-11-26       drh:   int i, j, k;
62f37c9722 2007-11-26       drh:   char *zSafeKey, *zToFree;
62f37c9722 2007-11-26       drh:   while( isspace(*zClrKey) ) zClrKey++;
62f37c9722 2007-11-26       drh:   if( zClrKey[0]==0 ) return;
62f37c9722 2007-11-26       drh:   @ <table %s(zTabArgs)>
62f37c9722 2007-11-26       drh:   if( horiz ){
62f37c9722 2007-11-26       drh:     @ <tr>
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   zToFree = zSafeKey = mprintf("%h", zClrKey);
62f37c9722 2007-11-26       drh:   while( zSafeKey[0] ){
62f37c9722 2007-11-26       drh:     while( isspace(*zSafeKey) ) zSafeKey++;
62f37c9722 2007-11-26       drh:     for(i=0; zSafeKey[i] && !isspace(zSafeKey[i]); i++){}
62f37c9722 2007-11-26       drh:     for(j=i; isspace(zSafeKey[j]); j++){}
62f37c9722 2007-11-26       drh:     for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){}
62f37c9722 2007-11-26       drh:     if( !horiz ){
62f37c9722 2007-11-26       drh:       cgi_printf("<tr bgcolor=\"%.*s\"><td>%.*s</td></tr>\n",
62f37c9722 2007-11-26       drh:         i, zSafeKey, k-j, &zSafeKey[j]);
62f37c9722 2007-11-26       drh:     }else{
62f37c9722 2007-11-26       drh:       cgi_printf("<td bgcolor=\"%.*s\">%.*s</td>\n",
62f37c9722 2007-11-26       drh:         i, zSafeKey, k-j, &zSafeKey[j]);
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:     zSafeKey += k;
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   free(zToFree);
62f37c9722 2007-11-26       drh:   if( horiz ){
62f37c9722 2007-11-26       drh:     @ </tr>
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   @ </table>
62f37c9722 2007-11-26       drh: }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh: /*
62f37c9722 2007-11-26       drh: ** WEBPAGE: /rptview
62f37c9722 2007-11-26       drh: **
62f37c9722 2007-11-26       drh: ** Generate a report.  The rn query parameter is the report number
62f37c9722 2007-11-26       drh: ** corresponding to REPORTFMT.RN.  If the tablist query parameter exists,
62f37c9722 2007-11-26       drh: ** then the output consists of lines of tab-separated fields instead of
62f37c9722 2007-11-26       drh: ** an HTML table.
62f37c9722 2007-11-26       drh: */
dff17229ff 2007-12-02       drh: void rptview_page(void){
62f37c9722 2007-11-26       drh:   int count = 0;
62f37c9722 2007-11-26       drh:   int rn;
62f37c9722 2007-11-26       drh:   char *zSql;
62f37c9722 2007-11-26       drh:   char *zTitle;
62f37c9722 2007-11-26       drh:   char *zOwner;
62f37c9722 2007-11-26       drh:   char *zClrKey;
62f37c9722 2007-11-26       drh:   int tabs;
62f37c9722 2007-11-26       drh:   Stmt q;
21326fb6f7 2008-07-19       drh:   char *zErr1 = 0;
21326fb6f7 2008-07-19       drh:   char *zErr2 = 0;
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   login_check_credentials();
62f37c9722 2007-11-26       drh:   if( !g.okRead ){ login_needed(); return; }
62f37c9722 2007-11-26       drh:   rn = atoi(PD("rn","0"));
62f37c9722 2007-11-26       drh:   if( rn==0 ){
62f37c9722 2007-11-26       drh:     cgi_redirect("reportlist");
62f37c9722 2007-11-26       drh:     return;
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   tabs = P("tablist")!=0;
55342eb9fb 2008-05-17       drh:   /* view_add_functions(tabs); */
62f37c9722 2007-11-26       drh:   db_prepare(&q,
62f37c9722 2007-11-26       drh:     "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn);
62f37c9722 2007-11-26       drh:   if( db_step(&q)!=SQLITE_ROW ){
62f37c9722 2007-11-26       drh:     cgi_redirect("reportlist");
62f37c9722 2007-11-26       drh:     return;
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh:   zTitle = db_column_malloc(&q, 0);
62f37c9722 2007-11-26       drh:   zSql = db_column_malloc(&q, 1);
62f37c9722 2007-11-26       drh:   zOwner = db_column_malloc(&q, 2);
62f37c9722 2007-11-26       drh:   zClrKey = db_column_malloc(&q, 3);
62f37c9722 2007-11-26       drh:   db_finalize(&q);
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   if( P("order_by") ){
62f37c9722 2007-11-26       drh:     /*
62f37c9722 2007-11-26       drh:     ** If the user wants to do a column sort, wrap the query into a sub
62f37c9722 2007-11-26       drh:     ** query and then sort the results. This is a whole lot easier than
62f37c9722 2007-11-26       drh:     ** trying to insert an ORDER BY into the query itself, especially
62f37c9722 2007-11-26       drh:     ** if the query is already ordered.
62f37c9722 2007-11-26       drh:     */
62f37c9722 2007-11-26       drh:     int nField = atoi(P("order_by"));
62f37c9722 2007-11-26       drh:     if( nField > 0 ){
62f37c9722 2007-11-26       drh:       const char* zDir = PD("order_dir","");
62f37c9722 2007-11-26       drh:       zDir = !strcmp("ASC",zDir) ? "ASC" : "DESC";
62f37c9722 2007-11-26       drh:       zSql = mprintf("SELECT * FROM (%s) ORDER BY %d %s", zSql, nField, zDir);
62f37c9722 2007-11-26       drh:     }
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh: 
62f37c9722 2007-11-26       drh:   count = 0;
62f37c9722 2007-11-26       drh:   if( !tabs ){
62f37c9722 2007-11-26       drh:     struct GenerateHTML sState;
62f37c9722 2007-11-26       drh: 
55342eb9fb 2008-05-17       drh:     db_multi_exec("PRAGMA empty_result_callbacks=ON");
62f37c9722 2007-11-26       drh:     style_submenu_element("Raw", "Raw",
55342eb9fb 2008-05-17       drh:       "rptview?tablist=1&%s", PD("QUERY_STRING",""));
62f37c9722 2007-11-26       drh:     if( g.okAdmin
d3e711fd2f 2008-07-15       drh:        || (g.okTktFmt && g.zLogin && zOwner && strcmp(g.zLogin,zOwner)==0) ){
55342eb9fb 2008-05-17       drh:       style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);
62f37c9722 2007-11-26       drh:     }
d3e711fd2f 2008-07-15       drh:     if( g.okTktFmt ){
d3e711fd2f 2008-07-15       drh:       style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn);
d3e711fd2f 2008-07-15       drh:     }
62f37c9722 2007-11-26       drh:     style_header(zTitle);
62f37c9722 2007-11-26       drh:     output_color_key(zClrKey, 1,
62f37c9722 2007-11-26       drh:         "border=0 cellpadding=3 cellspacing=0 class=\"report\"");
62f37c9722 2007-11-26       drh:     @ <table border=1 cellpadding=2 cellspacing=0 class="report">
62f37c9722 2007-11-26       drh:     sState.rn = rn;
62f37c9722 2007-11-26       drh:     sState.nCount = 0;
21326fb6f7 2008-07-19       drh:     sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
21326fb6f7 2008-07-19       drh:     sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2);
21326fb6f7 2008-07-19       drh:     sqlite3_set_authorizer(g.db, 0, 0);
62f37c9722 2007-11-26       drh:     @ </table>
21326fb6f7 2008-07-19       drh:     if( zErr1 ){
21326fb6f7 2008-07-19       drh:       @ <p><font color="red"><b>Error: %h(zErr1)</b></font></p>
21326fb6f7 2008-07-19       drh:     }else if( zErr2 ){
21326fb6f7 2008-07-19       drh:       @ <p><font color="red"><b>Error: %h(zErr2)</b></font></p>
21326fb6f7 2008-07-19       drh:     }
62f37c9722 2007-11-26       drh:     style_footer();
62f37c9722 2007-11-26       drh:   }else{
21326fb6f7 2008-07-19       drh:     sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
21326fb6f7 2008-07-19       drh:     sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2);
21326fb6f7 2008-07-19       drh:     sqlite3_set_authorizer(g.db, 0, 0);
62f37c9722 2007-11-26       drh:     cgi_set_content_type("text/plain");
62f37c9722 2007-11-26       drh:   }
62f37c9722 2007-11-26       drh: }