Fossil

Check-in [5f3ddcc1b8]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add ticket configuration editing capability.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 5f3ddcc1b80bc8e20a09be8ff131522caa5d9359
User & Date: drh 2007-11-25 21:11:33.000
Context
2007-11-26
01:33
Begin porting the CVSTrac ticket reporting code over to fossil. The new code is not yet connected into the system. The port is incomplete. ... (check-in: 62f37c9722 user: drh tags: trunk)
2007-11-25
21:11
Add ticket configuration editing capability. ... (check-in: 5f3ddcc1b8 user: drh tags: trunk)
17:13
Changes to the diff algorithm to put bounds on run-time for very large files with many differences. (This came up on the previous check-in when you try to diff the two versions of sqlite3.c.) ... (check-in: 4c22ae52fd user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/setup.c.
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
** the menu entry has no hyperlink - it is disabled.
*/
void setup_menu_entry(
  const char *zTitle,
  const char *zLink,
  const char *zDesc
){
  @ <dt>
  if( zLink && zLink[0] ){
    @ <a href="%s(zLink)">%h(zTitle)</a>
  }else{
    @ %h(zTitle)
  }
  @ </dt>
  @ <dd>%h(zDesc)</dd>
}

/*
** WEBPAGE: /setup
*/
void setup_page(void){
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }

  style_header("Setup");
  @ <dl id="setup">
  setup_menu_entry("Users", "setup_ulist",
    "Grant privileges to individual users.");
  setup_menu_entry("Access", "setup_access",
    "Control access settings.");
  setup_menu_entry("Configuration", "setup_config",
    "Configure the WWW components of the repository");
  setup_menu_entry("Tickets", "tktsetup",
    "Configure the trouble-ticketing system for this repository");
  setup_menu_entry("CSS", "setup_editcss",
    "Edit the Cascading Style Sheet used by all pages of this repository");
  setup_menu_entry("Header", "setup_header",
    "Edit HTML text inserted at the top of every page");
  setup_menu_entry("Footer", "setup_footer",
    "Edit HTML text inserted at the bottom of every page");
  @ </dl>

  style_footer();
}

/*
** WEBPAGE: setup_ulist
**







|





<
|












|






|







|







36
37
38
39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
** the menu entry has no hyperlink - it is disabled.
*/
void setup_menu_entry(
  const char *zTitle,
  const char *zLink,
  const char *zDesc
){
  @ <tr><td valign="top" align="right">
  if( zLink && zLink[0] ){
    @ <a href="%s(zLink)">%h(zTitle)</a>
  }else{
    @ %h(zTitle)
  }

  @ </td><td valign="top">%h(zDesc)</td></tr>
}

/*
** WEBPAGE: /setup
*/
void setup_page(void){
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }

  style_header("Setup");
  @ <table border="0" cellspacing="20">
  setup_menu_entry("Users", "setup_ulist",
    "Grant privileges to individual users.");
  setup_menu_entry("Access", "setup_access",
    "Control access settings.");
  setup_menu_entry("Configuration", "setup_config",
    "Configure the WWW components of the repository");
  setup_menu_entry("Tickets", "setup_ticket",
    "Configure the trouble-ticketing system for this repository");
  setup_menu_entry("CSS", "setup_editcss",
    "Edit the Cascading Style Sheet used by all pages of this repository");
  setup_menu_entry("Header", "setup_header",
    "Edit HTML text inserted at the top of every page");
  setup_menu_entry("Footer", "setup_footer",
    "Edit HTML text inserted at the bottom of every page");
  @ </table>

  style_footer();
}

/*
** WEBPAGE: setup_ulist
**
652
653
654
655
656
657
658



















































  @ <hr>
  @ Here is the default page footer:
  @ <blockquote><pre>
  @ %h(zDefaultFooter)
  @ </pre></blockquote>
  db_end_transaction(0);
}


























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
  @ <hr>
  @ Here is the default page footer:
  @ <blockquote><pre>
  @ %h(zDefaultFooter)
  @ </pre></blockquote>
  db_end_transaction(0);
}

/*
** WEBPAGE: setup_ticket
*/
void setup_ticket(void){
  const char *zConfig;
  int isSubmit;
  
  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }
  isSubmit = P("submit")!=0;
  db_begin_transaction();
  zConfig = P("cfg");
  if( zConfig==0 ){
    zConfig = db_text((char*)zDefaultTicketConfig,
               "SELECT value FROM config WHERE name='ticket-configuration'");
  }
  style_header("Edit Ticket Configuration");
  if( P("clear")!=0 ){
    db_multi_exec("DELETE FROM config WHERE name='ticket-configuration'");
    zConfig = zDefaultTicketConfig;
  }else if( isSubmit ){
    char *zErr = ticket_config_check(zConfig);
    if( zErr==0 ){
      db_multi_exec(
         "REPLACE INTO config(name,value) VALUES('ticket-configuration',"
         "%Q)", zConfig
      );
    }else{
      @ <p><font color="red"><b>
      @ SCRIPT ERROR: %h(zErr)
      @ </b></font></p>
    }
  }
  @ <form action="%s(g.zBaseURL)/setup_ticket" method="POST">
  @ <p>Edit the "subscript" script that defines the ticketing
  @ system setup for this server.</p>
  @ <textarea name="cfg" rows="40" cols="80">%h(zConfig)</textarea>
  @ <br />
  @ <input type="submit" name="submit" value="Apply Changes">
  @ <input type="submit" name="clear" value="Revert To Default">
  @ </form>
  @ <hr>
  @ Here is the default page header:
  @ <blockquote><pre>
  @ %h(zDefaultTicketConfig)
  @ </pre></blockquote>
  db_end_transaction(0);
}
Changes to src/style.c.
44
45
46
47
48
49
50
51

52

53
54
55

56

57
58
59
60
61
62
63

/*
** Add a new element to the submenu
*/
void style_submenu_element(
  const char *zLabel,
  const char *zTitle,
  const char *zLink

){

  assert( nSubmenu < sizeof(aSubmenu)/sizeof(aSubmenu[0]) );
  aSubmenu[nSubmenu].zLabel = zLabel;
  aSubmenu[nSubmenu].zTitle = zTitle;

  aSubmenu[nSubmenu].zLink = zLink;

  nSubmenu++;
}

/*
** Compare two submenu items for sorting purposes
*/
static int submenuCompare(const void *a, const void *b){







|
>

>



>
|
>







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

/*
** Add a new element to the submenu
*/
void style_submenu_element(
  const char *zLabel,
  const char *zTitle,
  const char *zLink,
  ...
){
  va_list ap;
  assert( nSubmenu < sizeof(aSubmenu)/sizeof(aSubmenu[0]) );
  aSubmenu[nSubmenu].zLabel = zLabel;
  aSubmenu[nSubmenu].zTitle = zTitle;
  va_start(ap, zLink);
  aSubmenu[nSubmenu].zLink = vmprintf(zLink, ap);
  va_end(ap);
  nSubmenu++;
}

/*
** Compare two submenu items for sorting purposes
*/
static int submenuCompare(const void *a, const void *b){
Changes to src/tkt.c.
302
303
304
305
306
307
308


309
310
311
312
313
314




315
316
317
318
319
320
321
  }
  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();







>
>






>
>
>
>







302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
  }
  db_finalize(&q);
  db_end_transaction(0);
}

/*
** WEBPAGE: tktview
**
** View a ticket.
*/
void tktview_page(void){
  char *zScript;
  int nScript;
  login_check_credentials();
  if( !g.okRdTkt ){ login_needed(); return; }
  if( g.okWrTkt ){
    style_submenu_element("Edit", "Edit The Ticket", "%s/tktedit?name=%T",
        g.zTop, PD("name",""));
  }
  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();
419
420
421
422
423
424
425








426
427
428
429
430
431
432
  return SBS_RETURN;
}


/*
** WEBPAGE: tktnew
** WEBPAGE: debug_tktnew








*/
void tktnew_page(void){
  char *zScript;
  int nScript;
  char *zNewUuid = 0;

  login_check_credentials();







>
>
>
>
>
>
>
>







425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
  return SBS_RETURN;
}


/*
** WEBPAGE: tktnew
** WEBPAGE: debug_tktnew
**
** Enter a new ticket.  the tktnew_template script in the ticket
** configuration is used.  The /tktnew page is the official ticket
** entry page.  The /debug_tktnew page is used for debugging the
** tktnew_template in the ticket configuration.  /debug_tktnew works
** just like /tktnew except that it does not really save the new ticket
** when you press submit - it just prints the ticket artifact at the
** top of the screen.
*/
void tktnew_page(void){
  char *zScript;
  int nScript;
  char *zNewUuid = 0;

  login_check_credentials();
445
446
447
448
449
450
451
452
453
454
455
456







457
458
459
460
461
462
463
    cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zNewUuid));
    return;
  }
  @ </form>
  style_footer();
}



/*
** WEBPAGE: tktedit
** WEBPAGE: debug_tktedit







*/
void tktedit_page(void){
  char *zScript;
  int nScript;
  int nName;
  const char *zName;
  int nRec;







<
<



>
>
>
>
>
>
>







459
460
461
462
463
464
465


466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
    cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zNewUuid));
    return;
  }
  @ </form>
  style_footer();
}



/*
** WEBPAGE: tktedit
** WEBPAGE: debug_tktedit
**
** Edit a ticket.  The ticket is identified by the name CGI parameter.
** /tktedit is the official page.  The /debug_tktedit page does the same
** thing except that it does not save the ticket change record when you
** press submit - it instead prints the ticket change record at the top
** of the page.  The /debug_tktedit page is intended to be used when
** debugging ticket configurations.
*/
void tktedit_page(void){
  char *zScript;
  int nScript;
  int nName;
  const char *zName;
  int nRec;
499
500
501
502
503
504
505

























































  if( SbS_Render(pInterp, zScript)==SBS_RETURN && zName ){
    cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zName));
    return;
  }
  @ </form>
  style_footer();
}
































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
  if( SbS_Render(pInterp, zScript)==SBS_RETURN && zName ){
    cgi_redirect(mprintf("%s/tktview/%s", g.zBaseURL, zName));
    return;
  }
  @ </form>
  style_footer();
}

/*
** Check the ticket configuration in zConfig to see if it appears to
** be well-formed.  If everything is OK, return NULL.  If something is
** amiss, then return a pointer to a string (obtained from malloc) that
** describes the problem.
*/
char *ticket_config_check(const char *zConfig){
  struct Subscript *p;
  char *zErr = 0;
  const char *z;
  int n;
  int i;
  int rc;
  sqlite3 *db;
  static const char *azRequired[] = {
     "tktnew_template",
     "tktview_template",
     "tktedit_template",
  };
  
  p = SbS_Create();
  rc = SbS_Eval(p, zConfig, strlen(zConfig));
  if( rc!=SBS_OK ){
    zErr = mprintf("%s", SbS_GetErrorMessage(p));
    SbS_Destroy(p);
    return zErr;
  }
  for(i=0; i<sizeof(azRequired)/sizeof(azRequired[0]); i++){
    z = SbS_Fetch(p, azRequired[i], -1, &n);
    if( z==0 ){
      zErr = mprintf("missing definition: %s", azRequired[i]);
      SbS_Destroy(p);
      return zErr;
    }
  }
  z = SbS_Fetch(p, "ticket_sql", -1, &n);
  if( z==0 ){
    zErr = mprintf("missing definition: ticket_sql");
    SbS_Destroy(p);
    return zErr;
  }
  rc = sqlite3_open(":memory:", &db);
  if( rc==SQLITE_OK ){
    char *zSql = mprintf("%.*s", n, z);
    rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
    if( rc!=SQLITE_OK ){
      sqlite3_close(db);
      SbS_Destroy(p);
      return zErr;
    }
    /* TODO: verify that the TICKET table exists and has required fields */
    sqlite3_close(db);
  }
  SbS_Destroy(p);
  return 0;
}
Changes to src/tktconfig.c.
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104



105
106
107
108
109
110
111
112
**      VALUE NAME set
**
** This configuration file just sets the values of various variables.
** Some of the variables have special meanings.  The content of some
** of the variables are additional subscript scripts.
*/

/* @-comment: # */
const char zDefaultTicketConfig[] = 
@ ##########################################################################
@ #
@ # Every ticket configuration file should have a title.  When you
@ # come up with a new ticket configuration, please change the title
@ # to something descriptive.
@ #
@ {Default Ticket Configuration} /ticket_config_title set
@ 
@ ############################################################################
@ # Every ticket configuration *must* define a set of columns for the
@ # "ticket" table of the database.  These column names will also be
@ # used as CGI parameter names, so to avoid problems it is best that
@ # the names be all lower-case alphabetic characters.  The names must



@ # be unique and must not begin with "tkt_".
@ #
@ {
@    CREATE TABLE ticket(
@      -- Do not change any column that begins with tkt_
@      tkt_id INTEGER PRIMARY KEY,
@      tkt_uuid TEXT,
@      tkt_mtime DATE,







|

<
<
<
<
<
<
<
<

|
<
<
|
>
>
>
|







83
84
85
86
87
88
89
90
91








92
93


94
95
96
97
98
99
100
101
102
103
104
105
**      VALUE NAME set
**
** This configuration file just sets the values of various variables.
** Some of the variables have special meanings.  The content of some
** of the variables are additional subscript scripts.
*/

/* @-comment: ** */
const char zDefaultTicketConfig[] = 








@ ############################################################################
@ # Every ticket configuration *must* define an SQL statement that creates


@ # the TICKET table.  This table must have three columns named
@ # tkt_id, tkt_uuid, and tkt_mtime.  tkt_id must be the integer primary
@ # key and tkt_uuid and tkt_mtime must be unique.  A configuration should
@ # define addition columns as necessary.  All columns should be in all
@ # lower-case letters and should not begin with "tkt".
@ #
@ {
@    CREATE TABLE ticket(
@      -- Do not change any column that begins with tkt_
@      tkt_id INTEGER PRIMARY KEY,
@      tkt_uuid TEXT,
@      tkt_mtime DATE,
Changes to src/wiki.c.
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
      manifest_parse(&m, &content);
      if( m.type==CFTYPE_WIKI ){
        zBody = m.zWiki;
      }
    }
  }
  if( isSandbox || (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
    style_submenu_element("Edit", "Edit Wiki Page", 
       mprintf("%s/wikiedit?name=%T", g.zTop, zPageName));
  }
  if( isSandbox || (rid && g.okApndWiki) ){
    style_submenu_element("Append", "Add A Comment", 
       mprintf("%s/wikiappend?name=%T", g.zTop, zPageName));
  }
  if( !isSandbox && g.okHistory ){
    style_submenu_element("History", "History", 
         mprintf("%s/whistory?name=%T", g.zTop, zPageName));
  }
  zHtmlPageName = mprintf("%h", zPageName);
  style_header(zHtmlPageName);
  blob_init(&wiki, zBody, -1);
  wiki_convert(&wiki, 0, 0);
  blob_reset(&wiki);
  if( !isSandbox ){







|
|


|
|


|
|







159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
      manifest_parse(&m, &content);
      if( m.type==CFTYPE_WIKI ){
        zBody = m.zWiki;
      }
    }
  }
  if( isSandbox || (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
    style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
         g.zTop, zPageName);
  }
  if( isSandbox || (rid && g.okApndWiki) ){
    style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
         g.zTop, zPageName);
  }
  if( !isSandbox && g.okHistory ){
    style_submenu_element("History", "History", "%s/whistory?name=%T",
         g.zTop, zPageName);
  }
  zHtmlPageName = mprintf("%h", zPageName);
  style_header(zHtmlPageName);
  blob_init(&wiki, zBody, -1);
  wiki_convert(&wiki, 0, 0);
  blob_reset(&wiki);
  if( !isSandbox ){