Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | The entire header, including the menu bar, is now generated by TH script. This allows the menu bar to be customized by editing the header script. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA1: |
ffe92f1a2f103efe35fa61d108a5ef06 |
| User & Date: | drh 2008-02-13 19:50:27.000 |
Context
|
2008-02-13
| ||
| 22:31 | Cut over all code to use TH1 instead of subscript. Completely remove the subscript interpreter from the source tree. check-in: fde1d82372 user: drh tags: trunk | |
| 19:50 | The entire header, including the menu bar, is now generated by TH script. This allows the menu bar to be customized by editing the header script. check-in: ffe92f1a2f user: drh tags: trunk | |
| 18:18 | TH1 script now used to render the header and footer of each page. check-in: 3ad9a5e210 user: drh tags: trunk | |
Changes
Changes to src/style.c.
| ︙ | ︙ | |||
70 71 72 73 74 75 76 |
return strcmp(A->zLabel, B->zLabel);
}
/*
** Draw the header.
*/
void style_header(const char *zTitle){
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
return strcmp(A->zLabel, B->zLabel);
}
/*
** Draw the header.
*/
void style_header(const char *zTitle){
const char *zHeader = db_get("header", (char*)zDefaultHeader);
login_check_credentials();
cgi_destination(CGI_HEADER);
/* Generate the header up through the main menu */
Th_InitVar("project_name", db_get("project-name","Unnamed Fossil Project"));
Th_InitVar("title", zTitle);
Th_InitVar("baseurl", g.zBaseURL);
Th_InitVar("manifest_version", MANIFEST_VERSION);
Th_InitVar("manifest_date", MANIFEST_DATE);
if( g.zLogin ){
Th_InitVar("login", g.zLogin);
}
Th_Render(zHeader);
cgi_destination(CGI_BODY);
g.cgiPanic = 1;
}
/*
** Draw the footer at the bottom of the page.
*/
|
| ︙ | ︙ | |||
160 161 162 163 164 165 166 | /* @-comment: // */ /* ** The default page header. */ const char zDefaultHeader[] = @ <html> @ <head> | | | | > > > > > > > > > > > > > > > > > > > > > > | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
/* @-comment: // */
/*
** The default page header.
*/
const char zDefaultHeader[] =
@ <html>
@ <head>
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@ href="$baseurl/timeline.rss">
@ <link rel="stylesheet" href="$baseurl/style.css" type="text/css"
@ media="screen">
@ </head>
@ <body>
@ <div class="header">
@ <div class="logo">
@ <!-- <img src="logo.gif" alt="logo"><br></br> -->
@ <nobr>$<project_name></nobr>
@ </div>
@ <div class="title">$<title></div>
@ <div class="status"><nobr><th1>
@ if {[info exists login]} {
@ html "Logged in as <a href='$baseurl/my'>$login</a>"
@ } else {
@ puts "Not logged in"
@ }
@ </th1></nobr></div>
@ </div>
@ <div class="mainmenu"><th1>
@ html "<a href='$baseurl/home'>Home</a>"
@ if {[hascap h]} {
@ html "<a href='$baseurl/dir'>Files</a>"
@ }
@ if {[hascap i]} {
@ html "<a href='$baseurl/leaves'>Leaves</a>"
@ html "<a href='$baseurl/timeline'>Timeline</a>"
@ html "<a href='$baseurl/tagview'>Tags</a>"
@ }
@ if {[hascap j]} {
@ html "<a href='$baseurl/wiki'>Wiki</a>"
@ }
@ if {[hascap s]} {
@ html "<a href='$baseurl/setup'>Setup</a>"
@ }
@ if {[info exists login]} {
@ html "<a href='$baseurl/login'>Login</a>"
@ } else {
@ html "<a href='$baseurl/login'>Logout</a>"
@ }
@ </th1></div>
;
/*
** The default page footer
*/
const char zDefaultFooter[] =
@ <div class="footer">
|
| ︙ | ︙ |
Changes to src/th_main.c.
| ︙ | ︙ | |||
82 83 84 85 86 87 88 | return Th_ToInt(interp, argv[1], argl[1], &enableOutput); } /* ** Send text to the appropriate output: Either to the console ** or to the CGI reply buffer. */ | | > > > > > < < < < | < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > | | 82 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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
return Th_ToInt(interp, argv[1], argl[1], &enableOutput);
}
/*
** Send text to the appropriate output: Either to the console
** or to the CGI reply buffer.
*/
static void sendText(const char *z, int n, int encode){
if( enableOutput && n ){
if( n<0 ) n = strlen(z);
if( encode ){
z = htmlize(z, n);
n = strlen(z);
}
if( g.cgiPanic ){
cgi_append_content(z, n);
}else{
fwrite(z, 1, n, stdout);
}
if( encode ) free((char*)z);
}
}
/*
** TH command: puts STRING
** TH command: html STRING
**
** Output STRING as HTML (html) or unchanged (puts).
*/
static int putsCmd(
Th_Interp *interp,
void *pConvert,
int argc,
const unsigned char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "puts STRING");
}
sendText((char*)argv[1], argl[1], pConvert!=0);
return TH_OK;
}
/*
** TH command: wiki STRING
**
** Render the input string as wiki.
*/
static int wikiCmd(
Th_Interp *interp,
void *p,
int argc,
const unsigned char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "wiki STRING");
}
if( enableOutput ){
Blob src;
blob_init(&src, (char*)argv[1], argl[1]);
wiki_convert(&src, 0, WIKI_INLINE);
blob_reset(&src);
}
return TH_OK;
}
/*
** TH command: hascap STRING
**
** Return true if the user has all of the capabilities listed in STRING.
*/
static int hascapCmd(
Th_Interp *interp,
void *p,
int argc,
const unsigned char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "hascap STRING");
}
Th_SetResultInt(interp, login_has_capability((char*)argv[1],argl[1]));
return TH_OK;
}
/*
** Make sure the interpreter has been initialized.
*/
static void initializeInterp(void){
static struct _Command {
const char *zName;
Th_CommandProc xProc;
void *pContext;
} aCommand[] = {
{"enable_output", enableOutputCmd, 0},
{"hascap", hascapCmd, 0},
{"html", putsCmd, 0},
{"puts", putsCmd, (void*)1},
{"wiki", wikiCmd, 0},
};
if( interp==0 ){
int i;
interp = Th_CreateInterp(&vtab);
th_register_language(interp); /* Basic scripting commands. */
for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc,
aCommand[i].pContext, 0);
}
}
}
/*
** Initialize a variable in the interpreter.
*/
void Th_InitVar(const char *zName, const char *zValue){
initializeInterp();
Th_SetVar(interp, (uchar*)zName, -1, (uchar*)zValue, strlen(zValue));
}
/*
** Return true if the string begins with the TH1 begin-script
** tag: <th1>.
*/
static int isBeginScriptTag(const char *z){
|
| ︙ | ︙ | |||
215 216 217 218 219 220 221 222 223 |
/*
** If string z[0...] contains a valid variable name, return
** the number of characters in that name. Otherwise, return 0.
*/
static int validVarName(const char *z){
int i = 0;
if( z[0]==':' && z[1]==':' && isalpha(z[2]) ){
z += 3;
| > > > > > | | > > > > | > > > > | | > > | > > > > > > > > > | | | | | | | | | | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
/*
** If string z[0...] contains a valid variable name, return
** the number of characters in that name. Otherwise, return 0.
*/
static int validVarName(const char *z){
int i = 0;
int inBracket = 0;
if( z[0]=='<' ){
inBracket = 1;
z++;
}
if( z[0]==':' && z[1]==':' && isalpha(z[2]) ){
z += 3;
i += 3;
}else if( isalpha(z[0]) ){
z ++;
i += 1;
}else{
return 0;
}
while( isalnum(z[0]) || z[0]=='_' ){
z++;
i++;
}
if( inBracket ){
if( z[0]!='>' ) return 0;
i += 2;
}
return i;
}
/*
** The z[] input contains text mixed with TH1 scripts.
** The TH1 scripts are contained within <th1>...</th1>.
** TH1 variables are $aaa or $<aaa>. The first form of
** variable is literal. The second is run through htmlize
** before being inserted.
**
** This routine processes the template and writes the results
** on either stdout or into CGI.
*/
int Th_Render(const char *z){
int i = 0;
int n;
int rc = TH_OK;
uchar *zResult;
initializeInterp();
while( z[i] ){
if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
const char *zVar;
int nVar;
sendText(z, i, 0);
if( z[i+1]=='<' ){
/* Variables of the form $<aaa> */
zVar = &z[i+2];
nVar = n-2;
}else{
/* Variables of the form $aaa */
zVar = &z[i+1];
nVar = n;
}
rc = Th_GetVar(interp, (uchar*)zVar, nVar);
z += i+1+n;
i = 0;
zResult = (uchar*)Th_GetResult(interp, &n);
sendText((char*)zResult, n, n>nVar);
}else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
sendText(z, i, 0);
z += i+5;
for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
rc = Th_Eval(interp, 0, (const uchar*)z, i);
if( rc!=SBS_OK ) break;
z += i;
if( z[0] ){ z += 6; }
i = 0;
}else{
i++;
}
}
if( rc==TH_ERROR ){
sendText("<hr><p><font color=\"red\"><b>ERROR: ", -1, 0);
zResult = (uchar*)Th_GetResult(interp, &n);
sendText((char*)zResult, n, 1);
sendText("</b></font></p>", -1, 0);
}else{
sendText(z, i, 0);
}
return rc;
}
/*
** COMMAND: test-th-render
*/
|
| ︙ | ︙ |
Changes to src/tktconfig.c.
| ︙ | ︙ | |||
31 32 33 34 35 36 37 | ** This is a sample "ticket configuration" file for fossil. ** ** There is considerable flexibility in how tickets are defined ** in fossil. Each repository can define its own ticket setup ** differently. Each repository has an instance of a file, like ** this one that defines how that repository deals with tickets. ** | | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | < | > | | | | > > | | | | | | | | > > > > > > > > | < | | | | | | | | | > | | | | | | | | < | < | | | | | | | | | | | | | | 31 32 33 34 35 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 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 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
** This is a sample "ticket configuration" file for fossil.
**
** There is considerable flexibility in how tickets are defined
** in fossil. Each repository can define its own ticket setup
** differently. Each repository has an instance of a file, like
** this one that defines how that repository deals with tickets.
**
** This file is in the form of a script in TH1 - a minimalist
** TCL clone.
*/
/* @-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".
@ #
@ set ticket_sql {
@ CREATE TABLE ticket(
@ -- Do not change any column that begins with tkt_
@ tkt_id INTEGER PRIMARY KEY,
@ tkt_uuid TEXT,
@ tkt_mtime DATE,
@ -- Add as many field as required below this line
@ type TEXT,
@ status TEXT,
@ subsystem TEXT,
@ priority TEXT,
@ severity TEXT,
@ foundin TEXT,
@ contact TEXT,
@ title TEXT,
@ comment TEXT,
@ -- Do not alter this UNIQUE clause:
@ UNIQUE(tkt_uuid, tkt_mtime)
@ );
@ -- Add indices as desired
@ }
@
@ ############################################################################
@ # You can define additional variables here. These variables will be
@ # accessible to the page templates when they run.
@ #
@ set type_choices {
@ Code_Defect
@ Build_Problem
@ Documentation
@ Feature_Request
@ Incident
@ }
@ set priority_choices {Immediate High Medium Low Zero}
@ set severity_choices {Critical Severe Important Minor Cosmetic}
@ set resolution_choices {
@ Open
@ Fixed
@ Rejected
@ Unable_To_Reproduce
@ Works_As_Designed
@ External_Bug
@ Not_A_Bug
@ Duplicate
@ Overcome_By_Events
@ Drive_By_Patch
@ }
@ set status_choices {
@ Open
@ Verified
@ In_Process
@ Deferred
@ Fixed
@ Tested
@ Closed
@ }
@ set subsystem_choices {one two three}
@
@ ##########################################################################
@ # The "tktnew_template" variable is set to text which is a template for
@ # the HTML of the "New Ticket" page. Within this template, text contained
@ # within [...] is subscript. That subscript runs when the page is
@ # rendered.
@ #
@ set tktnew_template {
@ <!-- load database field names not found in CGI with an empty string -->
@ <!-- start a form -->
@ <th1>
@ if {[info exists submit]} {
@ set status Open
@ submit_ticket
@ }
@ </th1>
@ <table cellpadding="5">
@ <tr>
@ <td colspan="2">
@ Enter a one-line summary of the problem:<br>
@ <input type="text" name="title" size="60" value="$<title>">
@ </td>
@ </tr>
@
@ <tr>
@ <td align="right">Type:
@ <th1>combobox type $type_choices 1</th1>
@ </td>
@ <td>What type of ticket is this?</td>
@ </tr>
@
@ <tr>
@ <td align="right">Version:
@ <input type="text" name="foundin" size="20" value="$<foundin>">
@ </td>
@ <td>In what version or build number do you observer the problem?</td>
@ </tr>
@
@ <tr>
@ <td align="right">Severity:
@ <th1>comboboxy severity severity_choices 1</th1>
@ </td>
@ <td>How debilitating is the problem? How badly does the problem
@ effect the operation of the product?</td>
@ </tr>
@
@ <tr>
@ <td align="right">EMail:
@ <input type="text" name="contact" value="$<contact>" size="30">
@ </td>
@ <td>Not publically visible. Used by developers to contact you with
@ questions.</td>
@ </tr>
@
@ <tr>
@ <td colspan="2">
@ Enter a detailed description of the problem.
@ For code defects, be sure to provide details on exactly how
@ the problem can be reproduced. Provide as much detail as
@ possible.
@ <br>
@ <th1>
@ if {![info exists comment]} {
@ set nline 10
@ } else {
@ set nline [linecount $comment]
@ if {$nline>50} {set nline 50}
@ }
@ </th1>
@ <textarea name="comment" cols="80" rows="$nline"
@ wrap="virtual" class="wikiedit">$<comment></textarea><br>
@ <input type="submit" name="preview" value="Preview">
@ </tr>
@
@ <th1>enable_output [info exists preview]</th1>
@ <tr><td colspan="2">
@ Description Preview:<br><hr>
@ <th1>wiki $comment</th1>
@ <hr>
@ </td></tr>
@ <th1>enable_output 1</th1>
@
@ <tr>
@ <td align="right">
@ <input type="submit" name="submit" value="Submit">
@ </td>
@ <td>After filling in the information above, press this button to create
@ the new ticket</td>
@ </tr>
@ </table>
@ <!-- end of form -->
@ }
@
@ ##########################################################################
@ # The template for the "edit ticket" page
@ #
@ # Then generated text is inserted inside a form which feeds back to itself.
@ # All CGI parameters are loaded into variables. All database files are
@ # loaded into variables if they have not previously been loaded by
@ # CGI parameters.
@ set tktedit_template {
@ <th1>
@ if {![info exists username]} {set username $login}
@ if {[info exists submit]} {
@ if {[info exists $cmappnd] && [string length $cmappnd]>0} {
@ set ctxt "\n\n<hr><i>"
@ if {$username==$login} {
@ set usr "$ctxt[htmlize $login]"
@ } else {
@ set usr "[htmlize $login claimingn to be [htmlize $username]"
@ }
@ append_field comment \
@ "\n\n<hr><i>$usr added on [date]:</i><br>\n$comment"
@ }
@ submit_ticket
@ }
@ </th1>
@ <table cellpadding="5">
@ <tr><td align="right">Title:</td><td>
@ <input type="text" name="title" value="$<title>" size="60">
@ </td></tr>
@ <tr><td align="right">Status:</td><td>
@ <th1>combobox status $status_choices 1</th1>
@ </td></tr>
@ <tr><td align="right">Type:</td><td>
@ <th1>combobox type $type_choices 1</th1>
@ </td></tr>
@ <tr><td align="right">Severity:</td><td>
@ <th1>combobox severity $severity_choices 1</th1>
@ </td></tr>
@ <tr><td align="right">Priority:</td><td>
@ <th1>combobox priority $priority_choices 1</th1>
@ </td></tr>
@ <tr><td align="right">Resolution:</td><td>
@ <th1>combobox resolution $resolution_choices 1</th1>
@ </td></tr>
@ <tr><td align="right">Subsystem:</td><td>
@ <th1>combobox subsystem $subsystem_choices 1</th1>
@ </td></tr>
@ <th1>enable_output [hascap e]</th1>
@ <tr><td align="right">Contact:</td><td>
@ <input type="text" name="contact" size="40" value="$<contact>">
@ </td></tr>
@ <th1>enable_output 1</th1>
@ <tr><td align="right">Version Found In:</td><td>
@ <input type="text" name="foundin" size="50" value="$<foundin>">
@ </td></tr>
@ <tr><td colspan="2">
@
@ [
@ 0 /eall get /eall set # eall means "edit all". default==no
@ /aonlybtn exists not /eall set # Edit all if no aonlybtn CGI param
@ /eallbtn exists /eall set # Edit all if eallbtn CGI param
|
| ︙ | ︙ | |||
324 325 326 327 328 329 330 | @ <input type="submit" name="submit" value="Submit Changes"> @ </td></tr> @ </table> @ } /tktedit_template set @ @ ########################################################################## @ # The template for the "view ticket" page | | | | | | | | | | | | | | | | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 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 328 329 |
@ <input type="submit" name="submit" value="Submit Changes">
@ </td></tr>
@ </table>
@ } /tktedit_template set
@
@ ##########################################################################
@ # The template for the "view ticket" page
@ set tktview_template {
@ <!-- load database fields automatically loaded into variables -->
@ <table cellpadding="5">
@ <tr><td align="right">Title:</td><td>
@ $<title>
@ </td></tr>
@ <tr><td align="right">Status:</td><td>
@ $<status>
@ </td></tr>
@ <tr><td align="right">Type:</td><td>
@ $<type>
@ </td></tr>
@ <tr><td align="right">Severity:</td><td>
@ $<severity>
@ </td></tr>
@ <tr><td align="right">Priority:</td><td>
@ $<priority>
@ </td></tr>
@ <tr><td align="right">Resolution:</td><td>
@ $<resolution>
@ </td></tr>
@ <tr><td align="right">Subsystem:</td><td>
@ $<subsystem>
@ </td></tr>
@ <th1>enable_output [hascap e]</th1>
@ <tr><td align="right">Contact:</td><td>
@ $<contact>
@ </td></tr>
@ <th1>enable_output 1</th1>
@ <tr><td align="right">Version Found In:</td><td>
@ $<foundin>
@ </td></tr>
@ <tr><td colspan="2">
@ Description And Comments:<br>
@ <th1>wiki $comment</th1>
@ </td></tr>
@ </table>
@ }
;
|