Fossil

Changes On Branch wolfgangFormat2CSS
Login

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

Changes In Branch wolfgangFormat2CSS Excluding Merge-Ins

This is equivalent to a diff from 08aa440a3d to 7d2fe13c82

2010-09-13
23:40
Merge the CSS and XHTML fixes from the wolfgangFormat2CSS branch into the trunk. check-in: 3243e63bba user: drh tags: trunk
23:37
Fix some rendering issues with the new wiki formatter. Closed-Leaf check-in: 7d2fe13c82 user: drh tags: wolfgangFormat2CSS
22:15
Fix a compiler warning of an unused variable. check-in: d35608836e user: drh tags: wolfgangFormat2CSS
2010-09-12
15:39
merged win dmc make build changes from [08aa440a3d],.. check-in: 642f4dcfa2 user: wolfgang tags: wolfgangFormat2CSS
15:23
(more type's) solves ticket [5e9f1b1cd8] check-in: 08aa440a3d user: renez tags: trunk
15:18
(delete 1 character{*} to much in dmc) solves ticket [5e9f1b1cd8] check-in: 2862ed5979 user: renez tags: trunk

Changes to src/attach.c.
73
74
75
76
77
78
79
80

81
82

83
84
85
86

87
88
89

90
91
92
93
94
95
96
73
74
75
76
77
78
79

80
81

82
83
84
85

86
87
88

89
90
91
92
93
94
95
96







-
+

-
+



-
+


-
+







    for(i=0; zFilename[i]; i++){
      if( zFilename[i]=='/' && zFilename[i+1]!=0 ){ 
        zFilename = &zFilename[i+1];
        i = -1;
      }
    }
    if( strlen(zTarget)==UUID_SIZE && validate16(zTarget,UUID_SIZE) ){
      zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
      zUrlTail = mprintf("tkt=%s&file=%t", zTarget, zFilename);
    }else{
      zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
      zUrlTail = mprintf("page=%t&file=%t", zTarget, zFilename);
    }
    @
    @ <p><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a>
    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br>
    @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br />
    if( zComment ) while( isspace(zComment[0]) ) zComment++;
    if( zComment && zComment[0] ){
      @ %w(zComment)<br>
      @ %w(zComment)<br />
    }
    if( zPage==0 && zTkt==0 ){
      if( zSrc==0 || zSrc[0]==0 ){
        zSrc = "Deleted from";
      }else {
        zSrc = "Added to";
      }
271
272
273
274
275
276
277
278
279
280



281
282
283
284
285
286
287
271
272
273
274
275
276
277



278
279
280
281
282
283
284
285
286
287







-
-
-
+
+
+







    cgi_redirect(zFrom);
  }
  style_header("Add Attachment");
  @ <h2>Add Attachment To %s(zTargetType)</h2>
  @ <form action="%s(g.zBaseURL)/attachadd" method="POST"
  @  enctype="multipart/form-data">
  @ File to Attach:
  @ <input type="file" name="f" size="60"><br>
  @ Description:<br>
  @ <textarea name="comment" cols=80 rows=5 wrap="virtual"></textarea><br>
  @ <input type="file" name="f" size="60"><br />
  @ Description:<br />
  @ <textarea name="comment" cols=80 rows=5 wrap="virtual"></textarea><br />
  if( zTkt ){
    @ <input type="hidden" name="tkt" value="%h(zTkt)">
  }else{
    @ <input type="hidden" name="page" value="%h(zPage)">
  }
  @ <input type="hidden" name="from" value="%h(zFrom)">
  @ <input type="submit" name="ok" value="Add Attachment">
349
350
351
352
353
354
355
356

357
358
359
360
361
362
363
364
365
366
367
368
369
349
350
351
352
353
354
355

356
357
358
359
360
361
362
363
364
365
366
367
368
369







-
+













    manifest_crosslink(rid, &manifest);
    db_end_transaction(0);
    cgi_redirect(zFrom);
  }    
  style_header("Delete Attachment");
  @ <form action="%s(g.zBaseURL)/attachdelete" method="POST">
  @ <p>Confirm that you want to delete the attachment named
  @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br>
  @ "%h(zFile)" on %s(zTkt?"ticket":"wiki page") %h(zTarget):<br />
  if( zTkt ){
    @ <input type="hidden" name="tkt" value="%h(zTkt)">
  }else{
    @ <input type="hidden" name="page" value="%h(zPage)">
  }
  @ <input type="hidden" name="file" value="%h(zFile)">
  @ <input type="hidden" name="from" value="%h(zFrom)">
  @ <input type="submit" name="confirm" value="Delete">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>
  style_footer();

}
Changes to src/branch.c.
229
230
231
232
233
234
235

236

237
238
239
240
241




242
243
244
245
246
247
248
229
230
231
232
233
234
235
236

237
238
239
240


241
242
243
244
245
246
247
248
249
250
251







+
-
+



-
-
+
+
+
+







  }else{
    style_submenu_element("Closed","Closed","brlist?closed");
  }
  login_anonymous_available();
  compute_leaves(0, 1);
  style_sidebox_begin("Nomenclature:", "33%");
  @ <ol>
  @ <li> An <div class="sideboxDescribed"><a href="brlist">
  @ <li> An <a href="brlist">open branch</a> is a branch that has one or
  @ open branch</a></div> is a branch that has one or
  @ more <a href="leaves">open leaves.</a>
  @ The presence of open leaves presumably means
  @ that the branch is still being extended with new check-ins.</li>
  @ <li> A <a href="brlist?closed">closed branch</a> is a branch with only
  @ <a href="leaves?closed">closed leaves</a>.
  @ <li> A <div class="sideboxDescribed"><a href="brlist?closed">
  @ closed branch</a></div> is a branch with only
  @ <div class="sideboxDescribed"><a href="leaves?closed">
  @ closed leaves</a></div>.
  @ Closed branches are fixed and do not change (unless they are first
  @ reopened)</li>
  @ </ol>
  style_sidebox_end();

  cnt = 0;
  if( !showClosed ){
282
283
284
285
286
287
288
289
290
291

292
293
294
295
296
297
298
285
286
287
288
289
290
291



292
293
294
295
296
297
298
299







-
-
-
+







      @ <li><b>%h(zBr)</b></li>
    }
  }
  if( cnt ){
    @ </ul>
  }
  db_finalize(&q);
  @ </ul>
  @ <br clear="both">
  @ <script>
  @ <script  type="text/JavaScript">
  @ function xin(id){
  @ }
  @ function xout(id){
  @ }
  @ </script>
  style_footer();
}
339
340
341
342
343
344
345
346
347

348
349
350
351
352
353
354
340
341
342
343
344
345
346


347
348
349
350
351
352
353
354







-
-
+







    "%s AND blob.rid IN (SELECT rid FROM tagxref"
    "                     WHERE tagtype>0 AND tagid=%d AND srcid!=0)"
    " ORDER BY event.mtime DESC",
    timeline_query_for_www(), TAG_BRANCH
  );
  www_print_timeline(&q, 0, brtimeline_extra);
  db_finalize(&q);
  @ <br clear="both">
  @ <script>
  @ <script  type="text/JavaScript">
  @ function xin(id){
  @ }
  @ function xout(id){
  @ }
  @ </script>
  style_footer();
}
Changes to src/browse.c.
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
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







-
+












-
+

-
+







  if( zCI ){
    char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
    char zShort[20];
    memcpy(zShort, zUuid, 10);
    zShort[10] = 0;
    @ <h2>Files of check-in [<a href="vinfo?name=%T(zUuid)">%s(zShort)</a>]
    @ %s(blob_str(&dirname))</h2>
    zSubdirLink = mprintf("%s/dir?ci=%S&name=%T", g.zTop, zUuid, zPrefix);
    zSubdirLink = mprintf("%s/dir?ci=%S&amp;name=%T", g.zTop, zUuid, zPrefix);
    if( zD ){
      style_submenu_element("Top", "Top", "%s/dir?ci=%S", g.zTop, zUuid);
      style_submenu_element("All", "All", "%s/dir?name=%t", g.zTop, zD);
    }else{
      style_submenu_element("All", "All", "%s/dir", g.zBaseURL);
    }
  }else{
    @ <h2>The union of all files from all check-ins
    @ %s(blob_str(&dirname))</h2>
    zSubdirLink = mprintf("%s/dir?name=%T", g.zBaseURL, zPrefix);
    if( zD ){
      style_submenu_element("Top", "Top", "%s/dir", g.zBaseURL);
      style_submenu_element("Tip", "Tip", "%s/dir?name=%t&ci=tip",
      style_submenu_element("Tip", "Tip", "%s/dir?name=%t&amp;ci=tip",
                            g.zBaseURL, zD);
      style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&ci=trunk",
      style_submenu_element("Trunk", "Trunk", "%s/dir?name=%t&amp;ci=trunk",
                             g.zBaseURL,zD);
    }else{
      style_submenu_element("Tip", "Tip", "%s/dir?ci=tip", g.zBaseURL);
      style_submenu_element("Trunk", "Trunk", "%s/dir?ci=trunk", g.zBaseURL);
    }
  }

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
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







-
+




-
+









-
+

-
+
+



-
+


  ** directory.
  */
  mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
  cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/");
  nCol = 4;
  nRow = (cnt+nCol-1)/nCol;
  db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
  @ <table border="0" width="100%%"><tr><td valign="top" width="25%%">
  @ <table class="browser"><tr><td class="browser"><ul class="browser">
  i = 0;
  while( db_step(&q)==SQLITE_ROW ){
    const char *zFN;
    if( i==nRow ){
      @ </td><td valign="top" width="25%%">
      @ </ul></td><td class="browser"><ul class="browser">
      i = 0;
    }
    i++;
    zFN = db_column_text(&q, 0);
    if( zFN[0]=='/' ){
      zFN++;
      @ <li><a href="%s(zSubdirLink)%T(zFN)">%h(zFN)/</a></li>
    }else if( zCI ){
      const char *zUuid = db_column_text(&q, 1);
      @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a>
      @ <li><a href="%s(g.zBaseURL)/artifact?name=%s(zUuid)">%h(zFN)</a></li>
    }else{
      @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)</a>
      @ <li><a href="%s(g.zBaseURL)/finfo?name=%T(zPrefix)%T(zFN)">%h(zFN)
      @     </a></li>
    }
  }
  db_finalize(&q);
  @ </td></tr></table>
  @ </ul></td></tr></table>
  style_footer();
}
Changes to src/db.c.
662
663
664
665
666
667
668
669

670
671
672
673
674
675
676
662
663
664
665
666
667
668

669
670
671
672
673
674
675
676







-
+







    fossil_fatal("cannot locate home directory - "
                 "please set the HOME environment variable");
  }
#endif
  if( file_isdir(zHome)!=1 ){
    fossil_fatal("invalid home directory: %s", zHome);
  }
#ifndef __MINGW32__
#ifndef _WIN32
  if( access(zHome, W_OK) ){
    fossil_fatal("home directory %s must be writeable", zHome);
  }
#endif
  g.zHome = mprintf("%/", zHome);
#if defined(_WIN32)
  /* . filenames give some window systems problems and many apps problems */
Changes to src/descendants.c.
316
317
318
319
320
321
322

323
324



325

326

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347


348
349
350
351
352
353
354
316
317
318
319
320
321
322
323


324
325
326
327
328

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348


349
350
351
352
353
354
355
356
357







+
-
-
+
+
+

+
-
+



















-
-
+
+







    style_submenu_element("Open", "Open", "leaves");
  }
  style_header("Leaves");
  login_anonymous_available();
  compute_leaves(0, showAll ? 0 : showClosed ? 2 : 1);
  style_sidebox_begin("Nomenclature:", "33%");
  @ <ol>
  @ <li> A <div class="sideboxDescribed">leaf</div>
  @ <li> A <b>leaf</b> is a check-in with no descendants.</li>
  @ <li> An <b>open leaf</b> is a leaf that does not have a "closed" tag
  @ is a check-in with no descendants.</li>
  @ <li> An <div class="sideboxDescribed">open leaf</div>
  @ is a leaf that does not have a "closed" tag
  @ and is thus assumed to still be in use.</li>
  @ <li> A <div class="sideboxDescribed">closed leaf</div>
  @ <li> A <b>closed leaf</b> has a "closed" tag and is thus assumed to
  @ has a "closed" tag and is thus assumed to
  @ be historical and no longer in active use.</li>
  @ </ol>
  style_sidebox_end();

  if( showAll ){
    @ <h1>All leaves, both open and closed:</h1>
  }else if( showClosed ){
    @ <h1>Closed leaves:</h1>
  }else{
    @ <h1>Open leaves:</h1>
  }
  db_prepare(&q,
    "%s"
    "   AND blob.rid IN leaves"
    " ORDER BY event.mtime DESC",
    timeline_query_for_www()
  );
  www_print_timeline(&q, TIMELINE_LEAFONLY, leaves_extra);
  db_finalize(&q);
  @ <br clear="both">
  @ <script>
  @ <br />
  @ <script  type="text/JavaScript">
  @ function xin(id){
  @ }
  @ function xout(id){
  @ }
  @ </script>
  style_footer();
}
Changes to src/finfo.c.
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
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







-
+




















-
+




-
+

-
+

-
+

-
+


















+



-
+


-
+








-
+
+






  blob_zero(&title);
  blob_appendf(&title, "History of ");
  hyperlinked_path(zFilename, &title);
  @ <h2>%b(&title)</h2>
  blob_reset(&title);
  pGraph = graph_init();
  @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
  @ <table cellspacing=0 border=0 cellpadding=0>
  @ <table class="timelineTable">
  while( db_step(&q)==SQLITE_ROW ){
    const char *zDate = db_column_text(&q, 0);
    const char *zCom = db_column_text(&q, 1);
    const char *zUser = db_column_text(&q, 2);
    int fpid = db_column_int(&q, 3);
    int frid = db_column_int(&q, 4);
    const char *zPUuid = db_column_text(&q, 5);
    const char *zUuid = db_column_text(&q, 6);
    const char *zCkin = db_column_text(&q,7);
    const char *zBgClr = db_column_text(&q, 8);
    const char *zBr = db_column_text(&q, 9);
    int gidx;
    char zTime[10];
    char zShort[20];
    char zShortCkin[20];
    if( zBr==0 ) zBr = "trunk";
    gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr);
    if( memcmp(zDate, zPrevDate, 10) ){
      sprintf(zPrevDate, "%.10s", zDate);
      @ <tr><td>
      @   <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
      @   <div class="divider">%s(zPrevDate)</div>
      @ </td></tr>
    }
    memcpy(zTime, &zDate[11], 5);
    zTime[5] = 0;
    @ <tr><td valign="top" align="right">
    @ <tr><td class="timelineTime">
    @ <a href="%s(g.zTop)/timeline?c=%t(zDate)">%s(zTime)</a></td>
    @ <td width="20" align="left" valign="top"><div id="m%d(gidx)"></div></td>
    @ <td class="timelineGraph"><div id="m%d(gidx)"></div></td>
    if( zBgClr && zBgClr[0] ){
      @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
      @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
    }else{
      @ <td valign="top" align="left">
      @ <td class="timelineTableCell">
    }
    sqlite3_snprintf(sizeof(zShort), zShort, "%.10s", zUuid);
    sqlite3_snprintf(sizeof(zShortCkin), zShortCkin, "%.10s", zCkin);
    if( zUuid ){
      if( g.okHistory ){
        @ <a href="%s(g.zTop)/artifact/%s(zUuid)">[%S(zUuid)]</a>
      }else{
        @ [%S(zUuid)]
      }
      @ part of check-in
    }else{
      @ <b>Deleted</b> by check-in
    }
    hyperlink_to_uuid(zShortCkin);
    @ %h(zCom) (user: 
    hyperlink_to_user(zUser, zDate, "");
    @ branch: %h(zBr))
    if( g.okHistory && zUuid ){
      const char *z = zFilename;
      if( fpid ){
        @ <a href="%s(g.zTop)/fdiff?v1=%s(zPUuid)&amp;v2=%s(zUuid)">[diff]</a>
      }
      @ <a href="%s(g.zTop)/annotate?checkin=%S(zCkin)&amp;filename=%h(zFilename)">
      @ <a href="%s(g.zTop)/annotate?checkin=%S(zCkin)&amp;filename=%h(z)">
      @ [annotate]</a>
    }
    @ </td>
    @ </td></tr>
  }
  db_finalize(&q);
  if( pGraph ){
    graph_finish(pGraph, 1);
    if( pGraph->nErr ){
      graph_free(pGraph);
      pGraph = 0;
    }else{
      @ <tr><td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
      @ <tr><td></td><td><div style="width:%d(pGraph->mxRail*20+30)px;"></div>
      @     </td></tr>
    }
  }
  @ </table>
  timeline_output_graph_javascript(pGraph);
  style_footer();
}
Changes to src/info.c.
192
193
194
195
196
197
198
199

200
201

202
203

204
205
206
207
208
209
210
192
193
194
195
196
197
198

199
200

201
202

203
204
205
206
207
208
209
210







-
+

-
+

-
+







    cnt++;
    if( cnt==1 ){
      @ <div class="section">Tags And Properties</div>
      @ <ul>
    }
    @ <li>
    if( tagtype==0 ){
      @ <b><s>%h(zTagname)</s></b> cancelled
      @ <span class="infoTagCancelled">%h(zTagname)</span> cancelled
    }else if( zValue ){
      @ <b>%h(zTagname)=%h(zValue)</b>
      @ <span class="infoTag">%h(zTagname)=%h(zValue)</span>
    }else {
      @ <b>%h(zTagname)</b>
      @ <span class="infoTag">%h(zTagname)</span>
    }
    if( tagtype==2 ){
      if( zOrigUuid && zOrigUuid[0] ){
        @ inherited from
        hyperlink_to_uuid(zOrigUuid);
      }else{
        @ propagates to descendants
220
221
222
223
224
225
226

227
228
229
230
231
232
233
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234







+







      }else{
        @ added by
      }
      hyperlink_to_uuid(zSrcUuid);
      @ on
      hyperlink_to_date(zDate,0);
    }
    @ </li>
  }
  db_finalize(&q);
  if( cnt ){
    @ </ul>
  }
}

274
275
276
277
278
279
280
281

282
283
284
285
286
287
288

289
290
291
292
293
294
295
275
276
277
278
279
280
281

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297







-
+







+







    }
  }else if( zOld && zNew ){
    @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
    @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a>
    @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a>
    if( !showDiff ){
      @ &nbsp;&nbsp;
      @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a>
      @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;v2=%S(zNew)">[diff]</a>
    }else{
      int rid1 = uuid_to_rid(zOld, 0);
      int rid2 = uuid_to_rid(zNew, 0);
      @ <blockquote><pre>
      append_diff(rid1, rid2);
      @ </pre></blockquote>
    }
    @ </p>
  }else if( zOld ){
    @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
    @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a></p>
  }else{
    @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
    @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a></p>
  }
352
353
354
355
356
357
358
359

360
361
362
363
364
365
366
354
355
356
357
358
359
360

361
362
363
364
365
366
367
368







-
+







    zEComment = db_text(0, 
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_COMMENT, rid);
    zUser = db_column_text(&q, 2);
    zComment = db_column_text(&q, 3);
    zDate = db_column_text(&q,1);
    @ <div class="section">Overview</div>
    @ <p><table class="label-value">
    @ <table class="label-value">
    @ <tr><th>SHA1&nbsp;Hash:</th><td>%s(zUuid)
    if( g.okSetup ){
      @ (Record ID: %d(rid))
    }
    @ </td></tr>
    @ <tr><th>Date:</th><td>
    hyperlink_to_date(zDate, "</td></tr>");
395
396
397
398
399
400
401
402
403
404



405
406
407
408
409
410
411
397
398
399
400
401
402
403



404
405
406
407
408
409
410
411
412
413







-
-
-
+
+
+







        @ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
      }
      db_finalize(&q);
    }
    if( g.okHistory ){
      const char *zProjName = db_get("project-name", "unnamed");
      @ <tr><th>Timelines:</th><td>
      @    <a href="%s(g.zBaseURL)/timeline?p=%S(zUuid)">ancestors</a>
      @    | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)">descendants</a>
      @    | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)&p=%S(zUuid)">both</a>
      @   <a href="%s(g.zBaseURL)/timeline?p=%S(zUuid)">ancestors</a>
      @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)">descendants</a>
      @ | <a href="%s(g.zBaseURL)/timeline?d=%S(zUuid)&amp;p=%S(zUuid)">both</a>
      db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
                     " WHERE rid=%d AND tagtype>0 "
                     "   AND tag.tagid=tagxref.tagid "
                     "   AND +tag.tagname GLOB 'sym-*'", rid);
      while( db_step(&q)==SQLITE_ROW ){
        const char *zTagName = db_column_text(&q, 0);
        @  | <a href="%s(g.zTop)/timeline?r=%T(zTagName)">%h(zTagName)</a>
422
423
424
425
426
427
428
429

430
431
432
433
434
435
436
424
425
426
427
428
429
430

431
432
433
434
435
436
437
438







-
+







      @   | <a href="%s(g.zTop)/artifact/%S(zUuid)">manifest</a>
      if( g.okWrite ){
        @   | <a href="%s(g.zTop)/ci_edit?r=%S(zUuid)">edit</a>
      }
      @   </td>
      @ </tr>
    }
    @ </table></p>
    @ </table>
  }else{
    style_header("Check-in Information");
    login_anonymous_available();
  }
  db_finalize(&q);
  showTags(rid, "");
  @ <div class="section">Changes</div>
614
615
616
617
618
619
620
621

622
623
624
625
626
627
628
616
617
618
619
620
621
622

623
624
625
626
627
628
629
630







-
+







  }
  db_finalize(&q);
}


/*
** WEBPAGE: vdiff
** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN
** URL: /vdiff?from=UUID&amp;to=UUID&amp;detail=BOOLEAN
**
** Show all differences between two checkins.  
*/
void vdiff_page(void){
  int ridFrom, ridTo;
  int showDetail = 0;
  int iFrom, iTo;
636
637
638
639
640
641
642
643

644
645
646
647
648
649
650
638
639
640
641
642
643
644

645
646
647
648
649
650
651
652







-
+







  if( vdiff_parse_manifest("to", &ridTo, &mTo) ) return;
  showDetail = atoi(PD("detail","0"));
  style_header("Check-in Differences");
  @ <h2>Difference From:</h2><blockquote>
  checkin_description(ridFrom);
  @ </blockquote><h2>To:</h2><blockquote>
  checkin_description(ridTo);
  @ </blockquote><hr><p>
  @ </blockquote><hr /><p>

  iFrom = iTo = 0;
  while( iFrom<mFrom.nFile && iTo<mTo.nFile ){
    int cmp;
    if( iFrom>=mFrom.nFile ){
      cmp = +1;
    }else if( iTo>=mTo.nFile ){
878
879
880
881
882
883
884
885

886
887

888
889

890
891
892


893
894
895
896
897
898
899
880
881
882
883
884
885
886

887
888

889
890

891
892


893
894
895
896
897
898
899
900
901







-
+

-
+

-
+

-
-
+
+







  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }
  v1 = name_to_rid_www("v1");
  v2 = name_to_rid_www("v2");
  if( v1==0 || v2==0 ) fossil_redirect_home();
  style_header("Diff");
  @ <h2>Differences From:</h2>
  @ <blockquote>
  @ <blockquote><p>
  object_description(v1, 1, 0);
  @ </blockquote>
  @ </p></blockquote>
  @ <h2>To:</h2>
  @ <blockquote>
  @ <blockquote><p>
  object_description(v2, 1, 0);
  @ </blockquote>
  @ <hr>
  @ </p></blockquote>
  @ <hr />
  @ <blockquote><pre>
  content_get(v1, &c1);
  content_get(v2, &c2);
  blob_zero(&diff);
  text_diff(&c1, &c2, &diff, 4, 1);
  blob_reset(&c1);
  blob_reset(&c2);
992
993
994
995
996
997
998
999

1000
1001
1002
1003
1004
1005
1006
1007
1008
1009

1010
1011
1012
1013
1014
1015


1016
1017
1018
1019
1020
1021
1022
994
995
996
997
998
999
1000

1001
1002
1003
1004
1005
1006
1007
1008
1009
1010

1011
1012
1013
1014
1015


1016
1017
1018
1019
1020
1021
1022
1023
1024







-
+









-
+




-
-
+
+







  rid = name_to_rid_www("name");
  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }
  if( rid==0 ) fossil_redirect_home();
  if( g.okAdmin ){
    const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
    if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&amp;sub=1",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
  style_header("Hex Artifact Content");
  zUuid = db_text("?","SELECT uuid FROM blob WHERE rid=%d", rid);
  @ <h2>Artifact %s(zUuid):</h2>
  @ <blockquote>
  @ <blockquote><p>
  blob_zero(&downloadName);
  object_description(rid, 0, &downloadName);
  style_submenu_element("Download", "Download", 
        "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
  @ </blockquote>
  @ <hr>
  @ </p></blockquote>
  @ <hr />
  content_get(rid, &content);
  @ <blockquote><pre>
  hexdump(&content);
  @ </pre></blockquote>
  style_footer();
}

1074
1075
1076
1077
1078
1079
1080
1081

1082
1083
1084
1085
1086
1087
1088
1089
1090
1091

1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105

1106
1107
1108
1109
1110
1111
1112
1113
1114

1115
1116
1117
1118
1119


1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136

1137
1138
1139
1140
1141
1142
1143
1076
1077
1078
1079
1080
1081
1082

1083
1084
1085
1086
1087
1088
1089
1090
1091
1092

1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106

1107
1108
1109
1110
1111
1112
1113
1114
1115

1116
1117
1118
1119


1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137

1138
1139
1140
1141
1142
1143
1144
1145







-
+









-
+













-
+








-
+



-
-
+
+
















-
+








  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }
  if( rid==0 ) fossil_redirect_home();
  if( g.okAdmin ){
    const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
    if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&amp;sub=1",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
  style_header("Artifact Content");
  zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
  @ <h2>Artifact %s(zUuid)</h2>
  @ <blockquote>
  @ <blockquote><p>
  blob_zero(&downloadName);
  object_description(rid, 0, &downloadName);
  style_submenu_element("Download", "Download", 
          "%s/raw/%T?name=%s", g.zTop, blob_str(&downloadName), zUuid);
  zMime = mimetype_from_name(blob_str(&downloadName));
  if( zMime ){
    if( strcmp(zMime, "text/html")==0 ){
      if( P("txt") ){
        style_submenu_element("Html", "Html",
                              "%s/artifact?name=%s", g.zTop, zUuid);
      }else{
        renderAsHtml = 1;
        style_submenu_element("Text", "Text",
                              "%s/artifact?name=%s&txt=1", g.zTop, zUuid);
                              "%s/artifact?name=%s&amp;txt=1", g.zTop, zUuid);
      }
    }else if( strcmp(zMime, "application/x-fossil-wiki")==0 ){
      if( P("txt") ){
        style_submenu_element("Wiki", "Wiki",
                              "%s/artifact?name=%s", g.zTop, zUuid);
      }else{
        renderAsWiki = 1;
        style_submenu_element("Text", "Text",
                              "%s/artifact?name=%s&txt=1", g.zTop, zUuid);
                              "%s/artifact?name=%s&amp;txt=1", g.zTop, zUuid);
      }
    }
  }
  @ </blockquote>
  @ <hr>
  @ </p></blockquote>
  @ <hr />
  content_get(rid, &content);
  if( renderAsWiki ){
    wiki_convert(&content, 0, 0);
  }else if( renderAsHtml ){
    @ <div>
    cgi_append_content(blob_buffer(&content), blob_size(&content));
    @ </div>
  }else{
    zMime = mimetype_from_content(&content);
    @ <blockquote>
    if( zMime==0 ){
      @ <pre>
      @ %h(blob_str(&content))
      @ </pre>
      style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
    }else if( strncmp(zMime, "image/", 6)==0 ){
      @ <img src="%s(g.zBaseURL)/raw?name=%s(zUuid)&m=%s(zMime)"></img>
      @ <img src="%s(g.zBaseURL)/raw?name=%s(zUuid)&amp;m=%s(zMime)"></img>
      style_submenu_element("Hex","Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
    }else{
      @ <pre>
      hexdump(&content);
      @ </pre>
    }
    @ </blockquote>
1162
1163
1164
1165
1166
1167
1168
1169

1170
1171
1172
1173
1174
1175
1176
1164
1165
1166
1167
1168
1169
1170

1171
1172
1173
1174
1175
1176
1177
1178







-
+







  login_check_credentials();
  if( !g.okRdTkt ){ login_needed(); return; }
  rid = name_to_rid_www("name");
  if( rid==0 ){ fossil_redirect_home(); }
  zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
  if( g.okAdmin ){
    if( db_exists("SELECT 1 FROM shun WHERE uuid='%s'", zUuid) ){
      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&sub=1",
      style_submenu_element("Unshun","Unshun", "%s/shun?uuid=%s&amp;sub=1",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun","Shun", "%s/shun?shun=%s#addshun",
            g.zTop, zUuid);
    }
  }
  content_get(rid, &content);
1440
1441
1442
1443
1444
1445
1446
1447

1448
1449
1450
1451
1452
1453
1454
1442
1443
1444
1445
1446
1447
1448

1449
1450
1451
1452
1453
1454
1455
1456







-
+







  if( P("preview") ){
    Blob suffix;
    int nTag = 0;
    @ <b>Preview:</b>
    @ <blockquote>
    @ <table border=0>
    if( zNewColor && zNewColor[0] ){
      @ <tr><td bgcolor="%h(zNewColor)">
      @ <tr><td style="background-color: %h(zNewColor);">
    }else{
      @ <tr><td>
    }
    wiki_convert(&comment, 0, WIKI_INLINE);
    blob_zero(&suffix);
    blob_appendf(&suffix, "(user: %h", zNewUser);
    db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
1465
1466
1467
1468
1469
1470
1471
1472

1473
1474
1475
1476
1477
1478
1479
1467
1468
1469
1470
1471
1472
1473

1474
1475
1476
1477
1478
1479
1480
1481







-
+







      nTag++;
    }
    db_finalize(&q);
    blob_appendf(&suffix, ")");
    @ %s(blob_str(&suffix))
    @ </td></tr></table>
    @ </blockquote>
    @ <hr>
    @ <hr />
    blob_reset(&suffix);
  }
  @ <p>Make changes to attributes of check-in
  @ [<a href="ci?name=%s(zUuid)">%s(zUuid)</a>]:</p>
  @ <form action="%s(g.zBaseURL)/ci_edit" method="POST">
  login_insert_csrf_secret();
  @ <input type="hidden" name="r" value="%S(zUuid)">
1503
1504
1505
1506
1507
1508
1509
1510

1511
1512
1513
1514
1515
1516
1517
1505
1506
1507
1508
1509
1510
1511

1512
1513
1514
1515
1516
1517
1518
1519







-
+







  }else{
    @ <input type="checkbox" name="pclr">
  }
  @ Propagate color to descendants</input></td></tr>
  @ <tr>
  for(i=0; i<nColor; i++){
    if( aColor[i].zColor[0] ){
      @ <td bgcolor="%h(aColor[i].zColor)">
      @ <td style="background-color: %h(aColor[i].zColor);">
    }else{
      @ <td>
    }
    if( strcmp(zNewColor, aColor[i].zColor)==0 ){
      @ <input type="radio" name="clr" value="%h(aColor[i].zColor)" checked>
    }else{
      @ <input type="radio" name="clr" value="%h(aColor[i].zColor)">
1539
1540
1541
1542
1543
1544
1545
1546

1547
1548

1549
1550
1551
1552
1553
1554
1555
1541
1542
1543
1544
1545
1546
1547

1548
1549

1550
1551
1552
1553
1554
1555
1556
1557







-
+

-
+







  );
  while( db_step(&q)==SQLITE_ROW ){
    int tagid = db_column_int(&q, 0);
    const char *zTagName = db_column_text(&q, 1);
    char zLabel[30];
    sprintf(zLabel, "c%d", tagid);
    if( P(zLabel) ){
      @ <br><input type="checkbox" name="c%d(tagid)" checked>
      @ <br /><input type="checkbox" name="c%d(tagid)" checked>
    }else{
      @ <br><input type="checkbox" name="c%d(tagid)">
      @ <br /><input type="checkbox" name="c%d(tagid)">
    }
    if( strncmp(zTagName, "sym-", 4)==0 ){
      @ Cancel tag <b>%h(&zTagName[4])</b>
    }else{
      @ Cancel special tag <b>%h(zTagName)</b>
    }
  }
Changes to src/login.c.
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
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







-
+


-
+



-
+


-
+







  if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
    zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin);
    if( db_int(1, "SELECT 0 FROM user"
                  " WHERE uid=%d AND (pw=%Q OR pw=%Q)", 
                  g.userUid, zPasswd, zSha1Pw) ){
      sleep(1);
      zErrMsg = 
         @ <p><font color="red">
         @ <p><span class="loginError">
         @ You entered an incorrect old password while attempting to change
         @ your password.  Your password is unchanged.
         @ </font></p>
         @ </span></p>
      ;
    }else if( strcmp(zNew1,zNew2)!=0 ){
      zErrMsg = 
         @ <p><font color="red">
         @ <p><span class="loginError">
         @ The two copies of your new passwords do not match.
         @ Your password is unchanged.
         @ </font></p>
         @ </span></p>
      ;
    }else{
      char *zNewPw = sha1_shared_secret(zNew1, g.zLogin);
      db_multi_exec(
         "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
      );
      redirect_to_g();
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

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
320
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
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
320







-
+

-
+




















-
+

-
+

-
+

-
+

-
+

-
+



-
-
+
+








-
+


-
+
















-
-
+
+

-
+





-
+

-
+



-
+

-
-
+
+



-
+



-
+

-
-
-
-
-
-
+
+
+
+
+
+

-
+







        "   AND login NOT IN ('anonymous','nobody','developer','reader')"
        "   AND (pw=%Q OR pw=%Q)",
        zUsername, zPasswd, zSha1Pw
    );
    if( uid<=0 ){
      sleep(1);
      zErrMsg = 
         @ <p><font color="red">
         @ <p><span class="loginError">
         @ You entered an unknown user or an incorrect password.
         @ </font></p>
         @ </span></p>
      ;
    }else{
      char *zCookie;
      const char *zCookieName = login_cookie_name();
      const char *zExpire = db_get("cookie-expire","8766");
      int expires = atoi(zExpire)*3600;
      const char *zIpAddr = PD("REMOTE_ADDR","nil");
 
      zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
      cgi_set_cookie(zCookieName, zCookie, 0, expires);
      db_multi_exec(
        "UPDATE user SET cookie=%Q, ipaddr=%Q, "
        "  cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
        zCookie, zIpAddr, expires, uid
      );
      redirect_to_g();
    }
  }
  style_header("Login/Logout");
  @ %s(zErrMsg)
  @ <form action="login" method="POST">
  @ <form action="login" method="post">
  if( P("g") ){
    @ <input type="hidden" name="g" value="%h(P("g"))">
    @ <input type="hidden" name="g" value="%h(P("g"))" />
  }
  @ <table align="left" hspace="10">
  @ <table class="login_out">
  @ <tr>
  @   <td align="right">User ID:</td>
  @   <td class="login_out_label">User ID:</td>
  if( anonFlag ){
    @   <td><input type="text" id="u" name="u" value="anonymous" size=30></td>
    @ <td><input type="text" id="u" name="u" value="anonymous" size="30" /></td>
  }else{
    @   <td><input type="text" id="u" name="u" value="" size=30></td>
    @ <td><input type="text" id="u" name="u" value="" size="30" /></td>
  }
  @ </tr>
  @ <tr>
  @  <td align="right">Password:</td>
  @   <td><input type="password" id="p" name="p" value="" size=30></td>
  @  <td class="login_out_label">Password:</td>
  @   <td><input type="password" id="p" name="p" value="" size="30" /></td>
  @ </tr>
  if( g.zLogin==0 ){
    zAnonPw = db_text(0, "SELECT pw FROM user"
                         " WHERE login='anonymous'"
                         "   AND cap!=''");
  }
  @ <tr>
  @   <td></td>
  @   <td><input type="submit" name="in" value="Login"></td>
  @   <td><input type="submit" name="in" value="Login" /></td>
  @ </tr>
  @ </table>
  @ <script>document.getElementById('u').focus()</script>
  @ <script type="text/JavaScript">document.getElementById('u').focus()</script>
  if( g.zLogin==0 ){
    @ <p>Enter
  }else{
    @ <p>You are currently logged in as <b>%h(g.zLogin)</b></p>
    @ <p>To change your login to a different user, enter
  }
  @ your user-id and password at the left and press the
  @ "Login" button.  Your user name will be stored in a browser cookie.
  @ You must configure your web browser to accept cookies in order for
  @ the login to take.</p>
  if( zAnonPw ){
    unsigned int uSeed = captcha_seed();
    char const *zDecoded = captcha_decode(uSeed);
    int bAutoCaptcha = db_get_boolean("auto-captcha", 1);
    char *zCaptcha = captcha_render(zDecoded);

    @ <input type="hidden" name="cs" value="%u(uSeed)"/>
    @ <p>Visitors may enter <b>anonymous</b> as the user-ID with
    @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
    @ Visitors may enter <b>anonymous</b> as the user-ID with
    @ the 8-character hexadecimal password shown below:</p>
    @ <center><table border="1" cellpadding="10"><tr><td><pre>
    @ <div class="captcha"><table class="captcha"><tr><td><pre>
    @ %s(zCaptcha)
    @ </pre></td></tr></table>
    if( bAutoCaptcha ) {
        @ <input type="button" value="Fill out captcha"
        @  onclick="document.getElementById('u').value='anonymous';
        @           document.getElementById('p').value='%s(zDecoded)';"/>
        @           document.getElementById('p').value='%s(zDecoded)';" />
    }
    @ </center>
    @ </div>
    free(zCaptcha);
  }
  if( g.zLogin ){
    @ <br clear="both"><hr>
    @ <hr />
    @ <p>To log off the system (and delete your login cookie)
    @  press the following button:<br>
    @ <input type="submit" name="out" value="Logout"></p>
    @  press the following button:<br />
    @ <input type="submit" name="out" value="Logout" /></p>
  }
  @ </form>
  if( g.okPassword ){
    @ <br clear="both"><hr>
    @ <hr />
    @ <p>To change your password, enter your old password and your
    @ new password twice below then press the "Change Password"
    @ button.</p>
    @ <form action="login" method="POST">
    @ <form action="login" method="post">
    @ <table>
    @ <tr><td align="right">Old Password:</td>
    @ <td><input type="password" name="p" size=30></td></tr>
    @ <tr><td align="right">New Password:</td>
    @ <td><input type="password" name="n1" size=30></td></tr>
    @ <tr><td align="right">Repeat New Password:</td>
    @ <td><input type="password" name="n2" size=30></td></tr>
    @ <tr><td class="login_out_label">Old Password:</td>
    @ <td><input type="password" name="p" size="30" /></td></tr>
    @ <tr><td class="login_out_label">New Password:</td>
    @ <td><input type="password" name="n1" size="30" /></td></tr>
    @ <tr><td class="login_out_label">Repeat New Password:</td>
    @ <td><input type="password" name="n2" size="30" /></td></tr>
    @ <tr><td></td>
    @ <td><input type="submit" value="Change Password"></td></tr>
    @ <td><input type="submit" value="Change Password" /></td></tr>
    @ </table>
    @ </form>
  }
  style_footer();
}


594
595
596
597
598
599
600
601
602


603
604
605
606
607
608
609
610
611
612

613
614
615
616
617
618
619
594
595
596
597
598
599
600


601
602
603
604
605
606
607
608
609
610
611

612
613
614
615
616
617
618
619







-
-
+
+









-
+







*/
void login_anonymous_available(void){
  if( !g.okHistory &&
      db_exists("SELECT 1 FROM user"
                " WHERE login='anonymous'"
                "   AND cap LIKE '%%h%%'") ){
    const char *zUrl = PD("REQUEST_URI", "index");
    @ <p>Many <font color="red">hyperlinks are disabled.</font><br />
    @ Use <a href="%s(g.zTop)/login?anon=1&g=%T(zUrl)">anonymous login</a>
    @ <p>Many <span class="disabled">hyperlinks are disabled.</span><br />
    @ Use <a href="%s(g.zTop)/login?anon=1&amp;g=%T(zUrl)">anonymous login</a>
    @ to enable hyperlinks.</p>
  }
}

/*
** While rendering a form, call this routine to add the Anti-CSRF token
** as a hidden element of the form.
*/
void login_insert_csrf_secret(void){
  @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
  @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)" />
}

/*
** Before using the results of a form, first call this routine to verify
** that ths Anti-CSRF token is present and is valid.  If the Anti-CSRF token
** is missing or is incorrect, that indicates a cross-site scripting attach
** so emits an error message and abort.
Changes to src/report.c.
275
276
277
278
279
280
281
282

283
284

285
286
287
288
289
290
291
275
276
277
278
279
280
281

282
283

284
285
286
287
288
289
290
291







-
+

-
+







  }
  zTitle = db_column_text(&q, 0);
  zSQL = db_column_text(&q, 1);
  zOwner = db_column_text(&q, 2);
  zClrKey = db_column_text(&q, 3);
  @ <table cellpadding=0 cellspacing=0 border=0>
  @ <tr><td valign="top" align="right">Title:</td><td width=15></td>
  @ <td colspan=3>%h(zTitle)</td></tr>
  @ <td colspan="3">%h(zTitle)</td></tr>
  @ <tr><td valign="top" align="right">Owner:</td><td></td>
  @ <td colspan=3>%h(zOwner)</td></tr>
  @ <td colspan="3">%h(zOwner)</td></tr>
  @ <tr><td valign="top" align="right">SQL:</td><td></td>
  @ <td valign="top"><pre>
  @ %h(zSQL)
  @ </pre></td>
  @ <td width=15></td><td valign="top">
  output_color_key(zClrKey, 0, "border=0 cellspacing=0 cellpadding=3");
  @ </td>
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
406

407
408

409
410
411
412
413
414
415
416
417
418
419
420
421
422

423
424
425
426
427
428
429
391
392
393
394
395
396
397

398
399
400
401
402
403
404
405

406
407

408
409
410
411
412
413
414
415
416
417
418
419
420
421

422
423
424
425
426
427
428
429







-
+







-
+

-
+













-
+







      zTitle = mprintf("Copy Of %s", zTitle);
      zOwner = g.zLogin;
    }
  }
  if( zOwner==0 ) zOwner = g.zLogin;
  style_submenu_element("Cancel", "Cancel", "reportlist");
  if( rn>0 ){
    style_submenu_element("Delete", "Delete", "rptedit?rn=%d&del1=1", rn);
    style_submenu_element("Delete", "Delete", "rptedit?rn=%d&amp;del1=1", rn);
  }
  style_header(rn>0 ? "Edit Report Format":"Create New Report Format");
  if( zErr ){
    @ <blockquote><font color="#ff0000"><b>%h(zErr)</b></font></blockquote>
  }
  @ <form action="rptedit" method="POST">
  @ <input type="hidden" name="rn" value="%d(rn)">
  @ <p>Report Title:<br>
  @ <p>Report Title:<br />
  @ <input type="text" name="t" value="%h(zTitle)" size="60"></p>
  @ <p>Enter a complete SQL query statement against the "TICKET" table:<br>
  @ <p>Enter a complete SQL query statement against the "TICKET" table:<br />
  @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea>
  @ </p>
  login_insert_csrf_secret();
  if( g.okAdmin ){
    @ <p>Report owner:
    @ <input type="text" name="w" size="20" value="%h(zOwner)">
    @ </p>
  } else {
    @ <input type="hidden" name="w" value="%h(zOwner)">
  }
  @ <p>Enter an optional color key in the following box.  (If blank, no
  @ color key is displayed.)  Each line contains the text for a single
  @ entry in the key.  The first token of each line is the background
  @ color for that line.<br>
  @ color for that line.<br />
  @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
  @ </p>
  if( !g.okAdmin && strcmp(zOwner,g.zLogin)!=0 ){
    @ <p>This report format is owned by %h(zOwner).  You are not allowed
    @ to change it.</p>
    @ </form>
    report_format_hints();
446
447
448
449
450
451
452
453

454
455
456
457
458
459
460
446
447
448
449
450
451
452

453
454
455
456
457
458
459
460







-
+







static void report_format_hints(void){
  char *zSchema;
  zSchema = db_text(0,"SELECT sql FROM sqlite_master WHERE name='ticket'");
  if( zSchema==0 ){
    zSchema = db_text(0,"SELECT sql FROM repository.sqlite_master"
                        " WHERE name='ticket'");
  }
  @ <hr><h3>TICKET Schema</h3>
  @ <hr /><h3>TICKET Schema</h3>
  @ <blockquote><pre>
  @ %h(zSchema)
  @ </pre></blockquote>
  @ <h3>Notes</h3>
  @ <ul>
  @ <li><p>The SQL must consist of a single SELECT statement</p></li>
  @
827
828
829
830
831
832
833
834

835
836
837

838
839
840
841
842
843
844
827
828
829
830
831
832
833

834
835
836

837
838
839
840
841
842
843
844







-
+


-
+







  zToFree = zSafeKey = mprintf("%h", zClrKey);
  while( zSafeKey[0] ){
    while( isspace(*zSafeKey) ) zSafeKey++;
    for(i=0; zSafeKey[i] && !isspace(zSafeKey[i]); i++){}
    for(j=i; isspace(zSafeKey[j]); j++){}
    for(k=j; zSafeKey[k] && zSafeKey[k]!='\n' && zSafeKey[k]!='\r'; k++){}
    if( !horiz ){
      cgi_printf("<tr bgcolor=\"%.*s\"><td>%.*s</td></tr>\n",
      cgi_printf("<tr style=\"background-color: %.*s;\"><td>%.*s</td></tr>\n",
        i, zSafeKey, k-j, &zSafeKey[j]);
    }else{
      cgi_printf("<td bgcolor=\"%.*s\">%.*s</td>\n",
      cgi_printf("<td style=\"background-color: %.*s;\">%.*s</td>\n",
        i, zSafeKey, k-j, &zSafeKey[j]);
    }
    zSafeKey += k;
  }
  free(zToFree);
  if( horiz ){
    @ </tr>
905
906
907
908
909
910
911
912

913
914
915
916
917
918
919
920
921
922
923
924
925
926
927


928
929
930
931
932
933
934
905
906
907
908
909
910
911

912
913
914
915
916
917
918
919
920
921
922
923
924
925


926
927
928
929
930
931
932
933
934







-
+













-
-
+
+








  count = 0;
  if( !tabs ){
    struct GenerateHTML sState;

    db_multi_exec("PRAGMA empty_result_callbacks=ON");
    style_submenu_element("Raw", "Raw", 
      "rptview?tablist=1&%s", PD("QUERY_STRING",""));
      "rptview?tablist=1&amp;%s", PD("QUERY_STRING",""));
    if( g.okAdmin 
       || (g.okTktFmt && g.zLogin && zOwner && strcmp(g.zLogin,zOwner)==0) ){
      style_submenu_element("Edit", "Edit", "rptedit?rn=%d", rn);
    }
    if( g.okTktFmt ){
      style_submenu_element("SQL", "SQL", "rptsql?rn=%d",rn);
    }
    if( g.okNewTkt ){
      style_submenu_element("New Ticket", "Create a new ticket",
        "%s/tktnew", g.zTop);
    }
    style_header(zTitle);
    output_color_key(zClrKey, 1, 
        "border=0 cellpadding=3 cellspacing=0 class=\"report\"");
    @ <table border=1 cellpadding=2 cellspacing=0 class="report">
        "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\"");
    @ <table border="1" cellpadding="2" cellspacing="0" class="report">
    sState.rn = rn;
    sState.nCount = 0;
    sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1);
    sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2);
    sqlite3_set_authorizer(g.db, 0, 0);
    @ </table>
    if( zErr1 ){
Changes to src/setup.c.
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
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







-
-
-
+
+
+
-
-
+

-
-
-
+
+
+






-
+



-
+



-
-
+
+
-
-
+


-
-
-
+
+
+







  if( !g.okAdmin ){
    login_needed();
    return;
  }

  style_submenu_element("Add", "Add User", "setup_uedit");
  style_header("User List");
  @ <table border="0" cellpadding="0" cellspacing="25">
  @ <tr><td valign="top">
  @ <b>Users:</b>
  @ <table class="usetupLayoutTable">
  @ <tr><td class="usetupColumnLayout">
  @ <span class="note">Users:</span>
  @ <table border="1" cellpadding="10"><tr><td>
  @ <table cellspacing=0 cellpadding=0 border=0>
  @ <table class="usetupUserList">
  @ <tr>
  @   <th align="right">User&nbsp;ID</th><td width="20">&nbsp;</td>
  @   <th>Capabilities</th><td width="15">&nbsp;</td>
  @   <th>Contact&nbsp;Info</th>
  @   <th class="usetupListUser" style="text-align: right;padding-right: 20px;">User&nbsp;ID</th>
  @   <th class="usetupListCap" style="text-align: center;padding-right: 15px;">Capabilities</th>
  @   <th class="usetupListCon"  style="text-align: left;">Contact&nbsp;Info</th>
  @ </tr>
  db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login");
  while( db_step(&s)==SQLITE_ROW ){
    const char *zCap = db_column_text(&s, 2);
    if( strstr(zCap, "s") ) zCap = "s";
    @ <tr>
    @ <td align="right">
    @ <td class="usetupListUser" style="text-align: right;padding-right: 20px;white-space:nowrap;">
    if( g.okAdmin && (zCap[0]!='s' || g.okSetup) ){
      @ <a href="setup_uedit?id=%d(db_column_int(&s,0))">
    }
    @ <nobr>%h(db_column_text(&s,1))</nobr>
    @ %h(db_column_text(&s,1))
    if( g.okAdmin ){
      @ </a>
    }
    @ </td><td>&nbsp;&nbsp;&nbsp;</td>
    @ <td align="center">%s(zCap)</td>
    @ </td>
    @ <td class="usetupListCap" style="text-align: center;padding-right: 15px;">%s(zCap)</td>
    @ <td>&nbsp;&nbsp;&nbsp;</td>
    @ <td align="left">%s(db_column_text(&s,3))</td>
    @ <td  class="usetupListCon"  style="text-align: left;">%s(db_column_text(&s,3))</td>
    @ </tr>
  }
  @ </table></td></tr></table>
  @ <td valign="top">
  @ <b>Notes:</b>
  @ </table>
  @ </td><td class="usetupColumnLayout">
  @ <span class="note">Notes:</span>
  @ <ol>
  @ <li><p>The permission flags are as follows:</p>
  @ <table>
     @ <tr><td valign="top"><b>a</b></td>
     @   <td><i>Admin:</i> Create and delete users</td></tr>
     @ <tr><td valign="top"><b>b</b></td>
     @   <td><i>Attach:</i> Add attachments to wiki or tickets</td></tr>
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
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







-
-
+
+
+




-
+
+



-
-
-
+
+
+

-
-
+
+



-
-
+
+
+
+







     @ <tr><td valign="top"><b>v</b></td>
     @   <td><i>Developer:</i> Inherit privileges of
     @   user <tt>developer</tt></td></tr>
     @ <tr><td valign="top"><b>w</b></td>
     @   <td><i>Write-Tkt:</i> Edit tickets</td></tr>
     @ <tr><td valign="top"><b>z</b></td>
     @   <td><i>Zip download:</i> Download a baseline via the
     @   <tt>/zip</tt> URL even without check<b>o</b>ut
     @    and <b>h</b>istory permissions</td></tr>
     @   <tt>/zip</tt> URL even without 
     @    check<span class="capability">o</span>ut
     @    and <span class="capability">h</span>istory permissions</td></tr>
  @ </table>
  @ </li>
  @
  @ <li><p>
  @ Every user, logged in or not, inherits the privileges of <b>nobody</b>.
  @ Every user, logged in or not, inherits the privileges of
  @ <span class="usertype">nobody</span>.
  @ </p></li>
  @
  @ <li><p>
  @ Any human can login as <b>anonymous</b> since the password is
  @ clearly displayed on the login page for them to type.  The purpose
  @ of requiring anonymous to log in is to prevent access by spiders.
  @ Any human can login as <span class="usertype">anonymous</span> since the
  @ password is clearly displayed on the login page for them to type. The
  @ purpose of requiring anonymous to log in is to prevent access by spiders.
  @ Every logged-in user inherits the combined privileges of
  @ <b>anonymous</b> and
  @ <b>nobody</b>.
  @ <span class="usertype">anonymous</span> and
  @ <span class="usertype">nobody</span>.
  @ </p></li>
  @
  @ <li><p>
  @ Users with privilege <b>v</b> inherit the combined privileges of
  @ <b>developer</b>, <b>anonymous</b>, and <b>nobody</b>.
  @ Users with privilege <span class="capability">v</span> inherit the combined
  @ privileges of <span class="usertype">developer</span>,
  @ <span class="usertype">anonymous</span>, and
  @ <span class="usertype">nobody</span>.
  @ </p></li>
  @
  @ </ol>
  @ </td></tr></table>
  style_footer();
}

321
322
323
324
325
326
327
328
329


330
331
332
333
334
335
336
323
324
325
326
327
328
329


330
331
332
333
334
335
336
337
338







-
-
+
+







    }else{
      zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
    }
    if( uid>0 &&
        db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
    ){
      style_header("User Creation Error");
      @ <font color="red">Login "%h(zLogin)" is already used by a different
      @ user.</font>
      @ <span class="loginError">Login "%h(zLogin)" is already used by
      @ a different user.</span>
      @
      @ <p><a href="setup_uedit?id=%d(uid)">[Bummer]</a></p>
      style_footer();
      return;
    }
    login_verify_csrf_secret();
    db_multi_exec(
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379






















380
381
382
383
384
385
386
387
388


389
390
391
392
393
394
395
396


397
398
399
400
401
402
403
404


405
406
407
408
409
410
411
412


413
414
415
416
417
418
419
420
421
422
423
424
425
426


427
428
429
430

431
432

433
434

435
436
437
438
439


440
441
442
443


444
445
446

447
448
449
450

451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472





















473
474
475
476
477
478
479

480
481
482

483
484
485
486
487
488


489
490
491



492
493
494
495
496
497
498
499
500
501

502
503


504
505
506
507
508
509
510
511



512
513
514
515
516



517
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
582
583
584
585
586




587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605










606
607
608
609
610


611
612
613
614



615
616

617
618
619
620
621
622
623
624
625









626
627
628
629
630
631
632
633
634
635
353
354
355
356
357
358
359






















360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

390
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
406
407

408
409
410
411
412
413
414
415
416

417
418
419
420
421
422
423
424
425
426
427
428
429
430


431
432
433
434
435

436
437

438
439

440
441
442
443


444
445
446
447


448
449
450
451

452
453
454
455

456
457





















458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484

485
486
487

488
489
490
491
492


493
494
495
496

497
498
499
500
501
502
503
504
505
506
507
508
509
510


511
512
513
514
515
516
517



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
582
583
584




585
586
587
588
589
590
591
592



593
594
595
596
597
598
599
600




601
602
603
604
605
606
607
608
609



610
611
612
613
614
615
616
617
618
619
620
621
622
623









624
625
626
627
628
629
630
631
632
633
634
635
636


637
638
639
640


641
642
643
644

645
646
647
648






649
650
651
652
653
654
655
656
657
658
659

660
661
662
663
664
665
666







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
+
+







-
+
+







-
+
+







-
+
+












-
-
+
+



-
+

-
+

-
+



-
-
+
+


-
-
+
+


-
+



-
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
+


-
+




-
-
+
+


-
+
+
+










+
-
-
+
+





-
-
-
+
+
+



-
-
+
+
+



-
-
-
+
+
+
+



-
-
-
+
+
+
+



-
-
-
-
-
+
+
+
+
+
+



-
-
-
-
+
+
+
+



+
-
+


+
-
-
-
+
+
+
+



-
-
-
-
+
+
+
+
+


+
+
-
+

+
+
-
-
-
-
+
+
+
+
+



-
-
-
+
+
+
+
+



-
-
-
-
+
+
+
+
+
+



-
-
-
+
+
+
+










-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+



-
-
+
+


-
-
+
+
+

-
+



-
-
-
-
-
-
+
+
+
+
+
+
+
+
+


-







  oaa = oab = oac = oad = oae = oaf = oag = oah = oai = oaj = oak = oam =
        oan = oao = oap = oar = oas = oat = oau = oav = oaw = oaz = "";
  if( uid ){
    zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
    zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
    zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
    zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
    if( strchr(zCap, 'a') ) oaa = " checked";
    if( strchr(zCap, 'b') ) oab = " checked";
    if( strchr(zCap, 'c') ) oac = " checked";
    if( strchr(zCap, 'd') ) oad = " checked";
    if( strchr(zCap, 'e') ) oae = " checked";
    if( strchr(zCap, 'f') ) oaf = " checked";
    if( strchr(zCap, 'g') ) oag = " checked";
    if( strchr(zCap, 'h') ) oah = " checked";
    if( strchr(zCap, 'i') ) oai = " checked";
    if( strchr(zCap, 'j') ) oaj = " checked";
    if( strchr(zCap, 'k') ) oak = " checked";
    if( strchr(zCap, 'm') ) oam = " checked";
    if( strchr(zCap, 'n') ) oan = " checked";
    if( strchr(zCap, 'o') ) oao = " checked";
    if( strchr(zCap, 'p') ) oap = " checked";
    if( strchr(zCap, 'r') ) oar = " checked";
    if( strchr(zCap, 's') ) oas = " checked";
    if( strchr(zCap, 't') ) oat = " checked";
    if( strchr(zCap, 'u') ) oau = " checked";
    if( strchr(zCap, 'v') ) oav = " checked";
    if( strchr(zCap, 'w') ) oaw = " checked";
    if( strchr(zCap, 'z') ) oaz = " checked";
    if( strchr(zCap, 'a') ) oaa = " checked=\"checked\"";
    if( strchr(zCap, 'b') ) oab = " checked=\"checked\"";
    if( strchr(zCap, 'c') ) oac = " checked=\"checked\"";
    if( strchr(zCap, 'd') ) oad = " checked=\"checked\"";
    if( strchr(zCap, 'e') ) oae = " checked=\"checked\"";
    if( strchr(zCap, 'f') ) oaf = " checked=\"checked\"";
    if( strchr(zCap, 'g') ) oag = " checked=\"checked\"";
    if( strchr(zCap, 'h') ) oah = " checked=\"checked\"";
    if( strchr(zCap, 'i') ) oai = " checked=\"checked\"";
    if( strchr(zCap, 'j') ) oaj = " checked=\"checked\"";
    if( strchr(zCap, 'k') ) oak = " checked=\"checked\"";
    if( strchr(zCap, 'm') ) oam = " checked=\"checked\"";
    if( strchr(zCap, 'n') ) oan = " checked=\"checked\"";
    if( strchr(zCap, 'o') ) oao = " checked=\"checked\"";
    if( strchr(zCap, 'p') ) oap = " checked=\"checked\"";
    if( strchr(zCap, 'r') ) oar = " checked=\"checked\"";
    if( strchr(zCap, 's') ) oas = " checked=\"checked\"";
    if( strchr(zCap, 't') ) oat = " checked=\"checked\"";
    if( strchr(zCap, 'u') ) oau = " checked=\"checked\"";
    if( strchr(zCap, 'v') ) oav = " checked=\"checked\"";
    if( strchr(zCap, 'w') ) oaw = " checked=\"checked\"";
    if( strchr(zCap, 'z') ) oaz = " checked=\"checked\"";
  }

  /* figure out inherited permissions */
  memset(inherit, 0, sizeof(inherit));
  if( strcmp(zLogin, "developer") ){
    char *z1, *z2;
    z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='developer'");
    while( z1 && *z1 ){
      inherit[0x7f & *(z1++)] = "<font color=\"red\">&bull;</font>";
      inherit[0x7f & *(z1++)] =
         "<span class=\"ueditInheritDeveloper\">&bull;</span>";
    }
    free(z2);
  }
  if( strcmp(zLogin, "reader") ){
    char *z1, *z2;
    z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='reader'");
    while( z1 && *z1 ){
      inherit[0x7f & *(z1++)] = "<font color=\"black\">&bull;</font>";
      inherit[0x7f & *(z1++)] =
          "<span class=\"ueditInheritReader\">&bull;</span>";
    }
    free(z2);
  }
  if( strcmp(zLogin, "anonymous") ){
    char *z1, *z2;
    z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='anonymous'");
    while( z1 && *z1 ){
      inherit[0x7f & *(z1++)] = "<font color=\"blue\">&bull;</font>";
      inherit[0x7f & *(z1++)] =
           "<span class=\"ueditInheritAnonymous\">&bull;</span>";
    }
    free(z2);
  }
  if( strcmp(zLogin, "nobody") ){
    char *z1, *z2;
    z1 = z2 = db_text(0,"SELECT cap FROM user WHERE login='nobody'");
    while( z1 && *z1 ){
      inherit[0x7f & *(z1++)] = "<font color=\"green\">&bull;</font>";
      inherit[0x7f & *(z1++)] =
           "<span class=\"ueditInheritNobody\">&bull;</span>";
    }
    free(z2);
  }

  /* Begin generating the page
  */
  style_submenu_element("Cancel", "Cancel", "setup_ulist");
  if( uid ){
    style_header(mprintf("Edit User %h", zLogin));
  }else{
    style_header("Add A New User");
  }
  @ <table align="left" hspace="20" vspace="10"><tr><td>
  @ <form action="%s(g.zPath)" method="POST">
  @ <div class="ueditCapBox">
  @ <form action="%s(g.zPath)" method="post"><div>
  login_insert_csrf_secret();
  @ <table>
  @ <tr>
  @   <td align="right"><nobr>User ID:</nobr></td>
  @   <td class="usetupEditLabel">User ID:</td>
  if( uid ){
    @   <td>%d(uid) <input type="hidden" name="id" value="%d(uid)"></td>
    @   <td>%d(uid) <input type="hidden" name="id" value="%d(uid)" /></td>
  }else{
    @   <td>(new user)<input type="hidden" name="id" value=0></td>
    @   <td>(new user)<input type="hidden" name="id" value="0" /></td>
  }
  @ </tr>
  @ <tr>
  @   <td align="right"><nobr>Login:</nobr></td>
  @   <td><input type="text" name="login" value="%h(zLogin)"></td>
  @   <td class="usetupEditLabel">Login:</td>
  @   <td><input type="text" name="login" value="%h(zLogin)" /></td>
  @ </tr>
  @ <tr>
  @   <td align="right"><nobr>Contact&nbsp;Info:</nobr></td>
  @   <td><input type="text" name="info" size=40 value="%h(zInfo)"></td>
  @   <td class="usetupEditLabel">Contact&nbsp;Info:</td>
  @   <td><input type="text" name="info" size="40" value="%h(zInfo)" /></td>
  @ </tr>
  @ <tr>
  @   <td align="right" valign="top">Capabilities:</td>
  @   <td class="usetupEditLabel">Capabilities:</td>
  @   <td>
#define B(x) inherit[x]
  if( g.okSetup ){
    @    <input type="checkbox" name="as"%s(oas)/>%s(B('s'))Setup<br>
    @    <input type="checkbox" name="as"%s(oas) />%s(B('s'))Setup<br />
  }
  @    <input type="checkbox" name="aa"%s(oaa)/>%s(B('a'))Admin<br>
  @    <input type="checkbox" name="ad"%s(oad)/>%s(B('d'))Delete<br>
  @    <input type="checkbox" name="ae"%s(oae)/>%s(B('e'))Email<br>
  @    <input type="checkbox" name="ap"%s(oap)/>%s(B('p'))Password<br>
  @    <input type="checkbox" name="ai"%s(oai)/>%s(B('i'))Check-In<br>
  @    <input type="checkbox" name="ao"%s(oao)/>%s(B('o'))Check-Out<br>
  @    <input type="checkbox" name="ah"%s(oah)/>%s(B('h'))History<br>
  @    <input type="checkbox" name="au"%s(oau)/>%s(B('u'))Reader<br>
  @    <input type="checkbox" name="av"%s(oav)/>%s(B('v'))Developer<br>
  @    <input type="checkbox" name="ag"%s(oag)/>%s(B('g'))Clone<br>
  @    <input type="checkbox" name="aj"%s(oaj)/>%s(B('j'))Read Wiki<br>
  @    <input type="checkbox" name="af"%s(oaf)/>%s(B('f'))New Wiki<br>
  @    <input type="checkbox" name="am"%s(oam)/>%s(B('m'))Append Wiki<br>
  @    <input type="checkbox" name="ak"%s(oak)/>%s(B('k'))Write Wiki<br>
  @    <input type="checkbox" name="ab"%s(oab)/>%s(B('b'))Attachments<br>
  @    <input type="checkbox" name="ar"%s(oar)/>%s(B('r'))Read Ticket<br>
  @    <input type="checkbox" name="an"%s(oan)/>%s(B('n'))New Ticket<br>
  @    <input type="checkbox" name="ac"%s(oac)/>%s(B('c'))Append Ticket<br>
  @    <input type="checkbox" name="aw"%s(oaw)/>%s(B('w'))Write Ticket<br>
  @    <input type="checkbox" name="at"%s(oat)/>%s(B('t'))Ticket Report<br>
  @    <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
  @    <input type="checkbox" name="aa"%s(oaa) />%s(B('a'))Admin<br />
  @    <input type="checkbox" name="ad"%s(oad) />%s(B('d'))Delete<br />
  @    <input type="checkbox" name="ae"%s(oae) />%s(B('e'))Email<br />
  @    <input type="checkbox" name="ap"%s(oap) />%s(B('p'))Password<br />
  @    <input type="checkbox" name="ai"%s(oai) />%s(B('i'))Check-In<br />
  @    <input type="checkbox" name="ao"%s(oao) />%s(B('o'))Check-Out<br />
  @    <input type="checkbox" name="ah"%s(oah) />%s(B('h'))History<br />
  @    <input type="checkbox" name="au"%s(oau) />%s(B('u'))Reader<br />
  @    <input type="checkbox" name="av"%s(oav) />%s(B('v'))Developer<br />
  @    <input type="checkbox" name="ag"%s(oag) />%s(B('g'))Clone<br />
  @    <input type="checkbox" name="aj"%s(oaj) />%s(B('j'))Read Wiki<br />
  @    <input type="checkbox" name="af"%s(oaf) />%s(B('f'))New Wiki<br />
  @    <input type="checkbox" name="am"%s(oam) />%s(B('m'))Append Wiki<br />
  @    <input type="checkbox" name="ak"%s(oak) />%s(B('k'))Write Wiki<br />
  @    <input type="checkbox" name="ab"%s(oab) />%s(B('b'))Attachments<br />
  @    <input type="checkbox" name="ar"%s(oar) />%s(B('r'))Read Ticket<br />
  @    <input type="checkbox" name="an"%s(oan) />%s(B('n'))New Ticket<br />
  @    <input type="checkbox" name="ac"%s(oac) />%s(B('c'))Append Ticket<br />
  @    <input type="checkbox" name="aw"%s(oaw) />%s(B('w'))Write Ticket<br />
  @    <input type="checkbox" name="at"%s(oat) />%s(B('t'))Ticket Report<br />
  @    <input type="checkbox" name="az"%s(oaz) />%s(B('z'))Download Zip
  @   </td>
  @ </tr>
  @ <tr>
  @   <td align="right">Password:</td>
  if( zPw[0] ){
    /* Obscure the password for all users */
    @   <td><input type="password" name="pw" value="**********"></td>
    @   <td><input type="password" name="pw" value="**********" /></td>
  }else{
    /* Show an empty password as an empty input field */
    @   <td><input type="password" name="pw" value=""></td>
    @   <td><input type="password" name="pw" value="" /></td>
  }
  @ </tr>
  if( !higherUser ){
    @ <tr>
    @   <td>&nbsp</td>
    @   <td><input type="submit" name="submit" value="Apply Changes">
    @   <td>&nbsp;</td>
    @   <td><input type="submit" name="submit" value="Apply Changes" /></td>
    @ </tr>
  }
  @ </table></td></tr></table>
  @ </table>
  @ </div></form>
  @ </div>
  @ <h2>Privileges And Capabilities:</h2>
  @ <ul>
  if( higherUser ){
    @ <li><p><font color="blue"><b>
    @ User %h(zLogin) has Setup privileges and you only have Admin privileges
    @ so you are not permitted to make changes to %h(zLogin).
    @ </b></font></p></li>
    @
  }
  @ <li><p>
  @ The <span class="capability">Setup</span> user can make arbitrary
  @ The <b>Setup</b> user can make arbitrary configuration changes.
  @ An <b>Admin</b> user can add other users and change user privileges
  @ configuration changes. An <span class="usertype">Admin</span> user
  @ can add other users and change user privileges
  @ and reset user passwords.  Both automatically get all other privileges
  @ listed below.  Use these two settings with discretion.
  @ </p></li>
  @
  @ <li><p>
  @ The "<font color="green"><big>&bull;</big></font>" mark indicates
  @ the privileges of "nobody" that are available to all users
  @ regardless of whether or not they are logged in.
  @ The "<span class="ueditInheritNobody"><big>&bull;</big></span>" mark
  @ indicates the privileges of <span class="usertype">nobody</span> that
  @ are available to all users regardless of whether or not they are logged in.
  @ </p></li>
  @
  @ <li><p>
  @ The "<font color="blue"><big>&bull;</big></font>" mark indicates
  @ the privileges of "anonymous" that are inherited by all logged-in users.
  @ The "<span class="ueditInheritAnonymous"><big>&bull;</big></span>" mark
  @ indicates the privileges of <span class="usertype">anonymous</span> that
  @ are inherited by all logged-in users.
  @ </p></li>
  @
  @ <li><p>
  @ The "<font color="red"><big>&bull;</big></font>" mark indicates
  @ the privileges of "developer" that are inherited by all users with
  @ the <b>Developer</b> privilege.
  @ The "<span class="ueditInheritDeveloper"><big>&bull;</big></span>" mark
  @ indicates the privileges of <span class="usertype">developer</span> that
  @ are inherited by all users with the
  @ <span class="capability">Developer</span> privilege.
  @ </p></li>
  @
  @ <li><p>
  @ The "<font color="black"><big>&bull;</big></font>" mark indicates
  @ the privileges of "reader" that are inherited by all users with
  @ the <b>Reader</b> privilege.
  @ The "<span class="ueditInheritReader"><big>&bull;</big></span>" mark
  @ indicates the privileges of <span class="usertype">reader</span> that
  @ are inherited by all users with the <span class="capability">Reader</span>
  @ privilege.
  @ </p></li>
  @
  @ <li><p>
  @ The <b>Delete</b> privilege give the user the ability to erase
  @ wiki, tickets, and attachments that have been added by anonymous
  @ users.  This capability is intended for deletion of spam.  The
  @ delete capability is only in effect for 24 hours after the item
  @ is first posted.  The Setup user can delete anything at any time.
  @ The <span class="capability">Delete</span> privilege give the user the
  @ ability to erase wiki, tickets, and attachments that have been added
  @ by anonymous users.  This capability is intended for deletion of spam. 
  @ The delete capability is only in effect for 24 hours after the item
  @ is first posted.  The <span class="usertype">Setup</span> user can
  @ delete anything at any time.
  @ </p></li>
  @
  @ <li><p>
  @ The <b>History</b> privilege allows a user to see most hyperlinks.
  @ This is recommended ON for most logged-in users but OFF for
  @ user "nobody" to avoid problems with spiders trying to walk every
  @ historical version of every baseline and file.
  @ The <span class="capability">History</span> privilege allows a user
  @ to see most hyperlinks. This is recommended ON for most logged-in users
  @ but OFF for user "nobody" to avoid problems with spiders trying to walk
  @ every historical version of every baseline and file.
  @ </p></li>
  @
  @ <li><p>
  @ The <span class="capability">Zip</span> privilege allows a user to
  @ The <b>Zip</b> privilege allows a user to see the "download as ZIP"
  @ see the "download as ZIP"
  @ hyperlink and permits access to the <tt>/zip</tt> page.  This allows
  @ users to download ZIP archives without granting other rights like
  @ <span class="capability">Read</span> or
  @ <b>Read</b> or <b>History</b>.  This privilege is recommended for
  @ user <b>nobody</b> so that automatic package downloaders can obtain
  @ the sources without going through the login procedure.
  @ <span class="capability">History</span>.  This privilege is recommended for
  @ user <span class="usertype">nobody</span> so that automatic package
  @ downloaders can obtain the sources without going through the login
  @ procedure.
  @ </p></li>
  @
  @ <li><p>
  @ The <b>Check-in</b> privilege allows remote users to "push".
  @ The <b>Check-out</b> privilege allows remote users to "pull".
  @ The <b>Clone</b> privilege allows remote users to "clone".
  @ </li><p>
  @ The <span class="capability">Check-in</span> privilege allows remote
  @ users to "push". The <span class="capability">Check-out</span> privilege
  @ allows remote users to "pull". The <span class="capability">Clone</span>
  @ privilege allows remote users to "clone".
  @ </p></li>
  @
  @ <li><p>
  @ The <span class="capability">Read Wiki</span>,
  @ <span class="capability">New Wiki</span>,
  @ The <b>Read Wiki</b>, <b>New Wiki</b>, <b>Append Wiki</b>, and
  @ <span class="capability">Append Wiki</span>, and
  @ <b>Write Wiki</b> privileges control access to wiki pages.  The
  @ <span class="capability">Read Ticket</span>,
  @ <span class="capability">New Ticket</span>,
  @ <b>Read Ticket</b>, <b>New Ticket</b>, <b>Append Ticket</b>, and
  @ <b>Write Ticket</b> privileges control access to trouble tickets.
  @ The <b>Ticket Report</b> privilege allows the user to create or edit
  @ ticket report formats.
  @ <span class="capability">Append Ticket</span>, and
  @ <span class="capability">Write Ticket</span> privileges control access
  @ to trouble tickets.
  @ The <span class="capability">Ticket Report</span> privilege allows
  @ the user to create or edit ticket report formats.
  @ </p></li>
  @
  @ <li><p>
  @ Users with the <b>Password</b> privilege are allowed to change their
  @ own password.  Recommended ON for most users but OFF for special
  @ users "developer", "anonymous", and "nobody".
  @ Users with the <span class="capability">Password</span> privilege
  @ are allowed to change their own password.  Recommended ON for most
  @ users but OFF for special users <span class="usertype">developer</span>,
  @ <span class="usertype">anonymous</span>,
  @ and <span class="usertype">nobody</span>.
  @ </p></li>
  @
  @ <li><p>
  @ The <b>EMail</b> privilege allows the display of sensitive information
  @ such as the email address of users and contact information on tickets.
  @ Recommended OFF for "anonymous" and for "nobody" but ON for
  @ "developer".
  @ The <span class="capability">EMail</span> privilege allows the display of
  @ sensitive information such as the email address of users and contact
  @ information on tickets. Recommended OFF for 
  @ <span class="usertype">anonymousy</span> and for
  @ <span class="usertype">nobody</span> but ON for
  @ <span class="usertype">developer</span>.
  @ </p></li>
  @
  @ <li><p>
  @ The <b>Attachment</b> privilege is needed in order to add attachments
  @ to tickets or wiki.  Write privilege on the ticket or wiki is also
  @ required.</p></li>
  @ The <span class="capability">Attachment</span> privilege is needed in
  @ order to add attachments to tickets or wiki.  Write privilege on the
  @ ticket or wiki is also required.
  @ </p></li>
  @
  @ <li><p>
  @ Login is prohibited if the password is an empty string.
  @ </p></li>
  @ </ul>
  @
  @ <h2>Special Logins</h2>
  @
  @ <ul>
  @ <li><p>
  @ No login is required for user "<b>nobody</b>".  The capabilities
  @ of the <b>nobody</b> user are inherited by all users, regardless of
  @ whether or not they are logged in.  To disable universal access
  @ to the repository, make sure no user named "<b>nobody</b>" exists or
  @ that the <b>nobody</b> user has no capabilities enabled.
  @ The password for <b>nobody</b> is ignore.  To avoid problems with
  @ spiders overloading the server, it is recommended
  @ that the 'h' (History) capability be turned off for the <b>nobody</b>
  @ user.
  @ No login is required for user <span class="usertype">nobody</span>. The
  @ capabilities of the <span class="usertype">nobody</span> user are
  @ inherited by all users, regardless of whether or not they are logged in.
  @ To disable universal access to the repository, make sure no user named 
  @ <span class="usertype">nobody</span> exists or that the
  @ <span class="usertype">nobody</span> user has no capabilities
  @ enabled. The password for <span class="usertype">nobody</span> is ignore.
  @ To avoid problems with spiders overloading the server, it is recommended
  @ that the <span class="capability">h</span> (History) capability be turned 
  @ off for the <span class="usertype">nobody</span> user.
  @ </p></li>
  @
  @ <li><p>
  @ Login is required for user "<b>anonymous</b>" but the password
  @ is displayed on the login screen beside the password entry box
  @ Login is required for user <span class="usertype">anonymous</span> but the
  @ password is displayed on the login screen beside the password entry box
  @ so anybody who can read should be able to login as anonymous.
  @ On the other hand, spiders and web-crawlers will typically not
  @ be able to login.  Set the capabilities of the anonymous user
  @ to things that you want any human to be able to do, but not any
  @ be able to login.  Set the capabilities of the
  @ <span class="usertype">anonymous</span>
  @ user to things that you want any human to be able to do, but not any
  @ spider.  Every other logged-in user inherits the privileges of
  @ <b>anonymous</b>.
  @ <span class="usertype">anonymous</span>.
  @ </p></li>
  @
  @ <li><p>
  @ The "<b>developer</b>" user is intended as a template for trusted users
  @ with check-in privileges.  When adding new trusted users, simply
  @ select the <b>Developer</b> privilege to cause the new user to inherit
  @ all privileges of the "developer" user.  Similarly, the "<b>reader</b>"
  @ user is a template for users who are allowed more access than anonymous,
  @ but less than a developer.
  @ The <span class="usertype">developer</span> user is intended as a template
  @ for trusted users with check-in privileges. When adding new trusted users,
  @ simply select the <span class="capability">developer</span> privilege to
  @ cause the new user to inherit all privileges of the 
  @ <span class="usertype">developer</span>
  @ user.  Similarly, the <span class="usertype">reader</span> user is a 
  @ template for users who are allowed more access than
  @ <span class="usertype">anonymous</span>,
  @ but less than a <span class="usertype">developer</span>.
  @ </p></li>
  @ </ul>
  @ </form>
  style_footer();
}


/*
** Generate a checkbox for an attribute.
*/
649
650
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
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
709
710

711
712
713
714
715
716
717
718







-
+
+

-
+




















-
+







    if( iQ!=iVal ){
      login_verify_csrf_secret();
      db_set(zVar, iQ ? "1" : "0", 0);
      iVal = iQ;
    }
  }
  if( iVal ){
    @ <input type="checkbox" name="%s(zQParm)" checked><b>%s(zLabel)</b></input>
    @ <input type="checkbox" name="%s(zQParm)" checked="checked" />
    @ <b>%s(zLabel)</b>
  }else{
    @ <input type="checkbox" name="%s(zQParm)"><b>%s(zLabel)</b></input>
    @ <input type="checkbox" name="%s(zQParm)" /><b>%s(zLabel)</b>
  }
}

/*
** Generate an entry box for an attribute.
*/
void entry_attribute(
  const char *zLabel,   /* The text label on the entry box */
  int width,            /* Width of the entry box */
  const char *zVar,     /* The corresponding row in the VAR table */
  const char *zQParm,   /* The query parameter */
  char *zDflt     /* Default value if VAR table entry does not exist */
){
  const char *zVal = db_get(zVar, zDflt);
  const char *zQ = P(zQParm);
  if( zQ && strcmp(zQ,zVal)!=0 ){
    login_verify_csrf_secret();
    db_set(zVar, zQ, 0);
    zVal = zQ;
  }
  @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)">
  @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" />
  @ <b>%s(zLabel)</b>
}

/*
** Generate a text box for an attribute.
*/
static void textarea_attribute(
696
697
698
699
700
701
702

703

704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719

720
721

722
723
724
725
726
727
728
729

730
731

732
733
734
735
736
737

738
739

740
741
742
743
744
745

746
747
748
749
750
751
752
753
754

755
756
757
758
759
760
761
762
763
764
765



766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781

782
783
784

785
786
787
788
789
790

791
792
793
794
795
796

797
798
799
800
801
802
803
804

805
806
807
808
809
810
811
812



813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828

829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845

846
847
848
849
850

851
852
853
854
855
856
857
858
859
860




861
862
863
864
865
866
867
868
869
870


871
872
873
874
875
876
877
728
729
730
731
732
733
734
735

736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751

752
753

754
755
756
757
758
759
760
761

762
763

764
765
766
767
768
769

770
771

772
773
774
775
776
777

778
779
780
781
782
783
784
785
786

787
788
789
790
791
792
793
794
795



796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813

814
815
816

817
818
819
820
821
822

823
824
825
826
827
828

829
830
831
832
833
834
835
836

837
838
839
840
841
842



843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860

861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877

878
879
880
881
882

883
884
885
886
887
888
889
890



891
892
893
894
895
896
897
898
899
900
901
902


903
904
905
906
907
908
909
910
911







+
-
+















-
+

-
+







-
+

-
+





-
+

-
+





-
+








-
+








-
-
-
+
+
+















-
+


-
+





-
+





-
+







-
+





-
-
-
+
+
+















-
+
















-
+




-
+







-
-
-
+
+
+
+








-
-
+
+







  if( zQ && strcmp(zQ,z)!=0 ){
    login_verify_csrf_secret();
    db_set(zVar, zQ, 0);
    z = zQ;
  }
  if( rows>0 && cols>0 ){
    @ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea>
    if (zLabel && *zLabel)
    @ <b>%s(zLabel)</b>
      @ <span class="textareaLabel">%s(zLabel)</span>
  }
}


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

  style_header("Access Control Settings");
  db_begin_transaction();
  @ <form action="%s(g.zBaseURL)/setup_access" method="POST">
  @ <form action="%s(g.zBaseURL)/setup_access" method="post"><div>
  login_insert_csrf_secret();
  @ <hr>
  @ <hr />
  onoff_attribute("Require password for local access",
     "localauth", "localauth", 0);
  @ <p>When enabled, the password sign-in is required for
  @ web access coming from 127.0.0.1.  When disabled, web access
  @ from 127.0.0.1 is allows without any login - the user id is selected
  @ from the ~/.fossil database. Password login is always required
  @ for incoming web connections on internet addresses other than
  @ 127.0.0.1.</p></li>
  @ 127.0.0.1.</p>

  @ <hr>
  @ <hr />
  onoff_attribute("Allow REMOTE_USER authentication",
     "remote_user_ok", "remote_user_ok", 0);
  @ <p>When enabled, if the REMOTE_USER environment variable is set to the
  @ login name of a valid user and no other login credentials are available,
  @ then the REMOTE_USER is accepted as an authenticated user.
  @ </p></li>
  @ </p>

  @ <hr>
  @ <hr />
  entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
  @ <p>The number of hours for which a login is valid.  This must be a
  @ positive number.  The default is 8760 hours which is approximately equal
  @ to a year.</p>

  @ <hr>
  @ <hr />
  entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
                  "5000000");
  @ <p>Fossil tries to limit out-bound sync, clone, and pull packets
  @ to this many bytes, uncompressed.  If the client requires more data
  @ than this, then the client will issue multiple HTTP requests.
  @ Values below 1 million are not recommended.  5 million is a
  @ reasonable number.</p>

  @ <hr>
  @ <hr />
  onoff_attribute("Show javascript button to fill in CAPTCHA",
                  "auto-captcha", "autocaptcha", 0);
  @ <p>When enabled, a button appears on the login screen for user
  @ "anonymous" that will automatically fill in the CAPTCHA password.
  @ This is less secure that forcing the user to do it manually, but is
  @ probably secure enough and it is certainly more convenient for
  @ anonymous users.</p>

  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </form>
  @ <hr />
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ </div></form>
  db_end_transaction(0);
  style_footer();
}

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

  style_header("Timeline Display Preferences");
  db_begin_transaction();
  @ <form action="%s(g.zBaseURL)/setup_timeline" method="POST">
  @ <form action="%s(g.zBaseURL)/setup_timeline" method="post"><div>
  login_insert_csrf_secret();

  @ <hr>
  @ <hr />
  onoff_attribute("Allow block-markup in timeline",
                  "timeline-block-markup", "tbm", 0);
  @ <p>In timeline displays, check-in comments can be displayed with or
  @ without block markup (paragraphs, tables, etc.)</p>

  @ <hr>
  @ <hr />
  onoff_attribute("Use Universal Coordinated Time (UTC)",
                  "timeline-utc", "utc", 1);
  @ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
  @ Zulu) instead of in local time.</p>

  @ <hr>
  @ <hr />
  onoff_attribute("Show version differences by default",
                  "show-version-diffs", "vdiff", 0);
  @ <p>On the version-information pages linked from the timeline can either
  @ show complete diffs of all file changes, or can just list the names of
  @ the files that have changed.  Users can get to either page by
  @ clicking.  This setting selects the default.</p>

  @ <hr>
  @ <hr />
  entry_attribute("Max timeline comment length", 6,
                  "timeline-max-comment", "tmc", "0");
  @ <p>The maximum length of a comment to be displayed in a timeline.
  @ "0" there is no length limit.</p>

  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </form>
  @ <hr />
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ </div></form>
  db_end_transaction(0);
  style_footer();
}

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

  style_header("WWW Configuration");
  db_begin_transaction();
  @ <form action="%s(g.zBaseURL)/setup_config" method="POST">
  @ <form action="%s(g.zBaseURL)/setup_config" method="post"><div>
  login_insert_csrf_secret();
  @ <hr />
  entry_attribute("Project Name", 60, "project-name", "pn", "");
  @ <p>Give your project a name so visitors know what this site is about.
  @ The project name will also be used as the RSS feed title.</p>
  @ <hr />
  textarea_attribute("Project Description", 5, 60,
                     "project-description", "pd", "");
  @ <p>Describe your project. This will be used in page headers for search
  @ engines as well as a short RSS description.</p>
  @ <hr />
  entry_attribute("Index Page", 60, "index-page", "idxpg", "/home");
  @ <p>Enter the pathname of the page to display when the "Home" menu
  @ option is selected and when no pathname is
  @ specified in the URL.  For example, if you visit the url:</p>
  @
  @ <blockquote>%h(g.zBaseURL)</blockquote>
  @ <blockquote><p>%h(g.zBaseURL)</p></blockquote>
  @
  @ <p>And you have specified an index page of "/home" the above will
  @ automatically redirect to:</p>
  @
  @ <blockquote>%h(g.zBaseURL)/home</blockquote>
  @ <blockquote><p>%h(g.zBaseURL)/home</p></blockquote>
  @
  @ <p>The default "/home" page displays a Wiki page with the same name
  @ as the Project Name specified above.  Some sites prefer to redirect
  @ to a documentation page (ex: "/doc/tip/index.wiki") or to "/timeline".</p>
  @ <hr />
  onoff_attribute("Use HTML as wiki markup language",
    "wiki-use-html", "wiki-use-html", 0);
  @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed but
  @ all other wiki formatting will be ignored. This option is helpful if you have
  @ chosen to use a rich HTML editor for wiki markup such as TinyMCE.</p>
  @ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
  @ but all other wiki formatting will be ignored. This option is helpful
  @ if you have chosen to use a rich HTML editor for wiki markup such as
  @ TinyMCE.</p>
  @ <p><strong>CAUTION:</strong> when
  @ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki.
  @ No sanitization is done. This means that it is very possible for malicious
  @ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p>
  @ <p>This should <strong>only</strong> be enabled when wiki editing is limited
  @ to trusted users. It should <strong>not</strong> be used on a publically
  @ editable wiki.</p>
  @ <hr />
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </form>
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ </div></form>
  db_end_transaction(0);
  style_footer();
}

/*
** WEBPAGE: setup_editcss
*/
890
891
892
893
894
895
896
897

898
899
900
901
902
903
904
905
906
907






908
909
910
911
912
913

914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930

931
932
933
934
935






936
937
938
939
940
941
942
943
944
945
946

947



948
949
950
951
952
953
954







-
+




-
-
-
-
-
-
+
+
+
+
+
+





-
+
-
-
-







    textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS);
  }
  if( P("submit")!=0 ){
    db_end_transaction(0);
    cgi_redirect("setup_editcss");
  }
  style_header("Edit CSS");
  @ <form action="%s(g.zBaseURL)/setup_editcss" method="POST">
  @ <form action="%s(g.zBaseURL)/setup_editcss" method="post"><div>
  login_insert_csrf_secret();
  @ Edit the CSS below:<br />
  textarea_attribute("", 40, 80, "css", "css", zDefaultCSS);
  @ <br />
  @ <input type="submit" name="submit" value="Apply Changes">
  @ <input type="submit" name="clear" value="Revert To Default">
  @ </form>
  @ <p><b>Note:</b> Press your browser Reload button after modifying the
  @ CSS in order to pull in the modified CSS file.</p>
  @ <hr>
  @ <input type="submit" name="submit" value="Apply Changes" />
  @ <input type="submit" name="clear" value="Revert To Default" />
  @ </div></form>
  @ <p><span class="note">Note:</span> Press your browser Reload button after
  @ modifying the CSS in order to pull in the modified CSS file.</p>
  @ <hr />
  @ The default CSS is shown below for reference.  Other examples
  @ of CSS files can be seen on the <a href="setup_skin">skins page</a>.
  @ See also the <a href="setup_header">header</a> and
  @ <a href="setup_footer">footer</a> editing screens.
  @ <blockquote><pre>
  @ %h(zDefaultCSS)
  cgi_append_default_css();
  @ %h(zTableLabelValueCSS)
  @ %h(zDivSidebox)
  @ %h(zDivSideboxTitle)
  @ </pre></blockquote>
  style_footer();
  db_end_transaction(0);
}

/*
** WEBPAGE: setup_header
931
932
933
934
935
936
937
938

939
940
941
942
943
944
945
946
947
948




949
950
951
952
953
954
955
962
963
964
965
966
967
968

969
970
971
972
973
974
975




976
977
978
979
980
981
982
983
984
985
986







-
+






-
-
-
-
+
+
+
+







  if( P("clear")!=0 ){
    db_multi_exec("DELETE FROM config WHERE name='header'");
    cgi_replace_parameter("header", zDefaultHeader);
  }else{
    textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
  }
  style_header("Edit Page Header");
  @ <form action="%s(g.zBaseURL)/setup_header" method="POST">
  @ <form action="%s(g.zBaseURL)/setup_header" method="post"><div>
  login_insert_csrf_secret();
  @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
  @ generate the beginning of every page through start of the main
  @ menu.</p>
  textarea_attribute("", 40, 80, "header", "header", zDefaultHeader);
  @ <br />
  @ <input type="submit" name="submit" value="Apply Changes">
  @ <input type="submit" name="clear" value="Revert To Default">
  @ </form>
  @ <hr>
  @ <input type="submit" name="submit" value="Apply Changes" />
  @ <input type="submit" name="clear" value="Revert To Default" />
  @ </div></form>
  @ <hr />
  @ The default header is shown below for reference.  Other examples
  @ of headers can be seen on the <a href="setup_skin">skins page</a>.
  @ See also the <a href="setup_editcss">CSS</a> and
  @ <a href="setup_footer">footer</a> editing screeens.
  @ <blockquote><pre>
  @ %h(zDefaultHeader)
  @ </pre></blockquote>
969
970
971
972
973
974
975
976

977
978
979
980
981
982
983
984
985




986
987
988
989
990
991
992
1000
1001
1002
1003
1004
1005
1006

1007
1008
1009
1010
1011
1012




1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023







-
+





-
-
-
-
+
+
+
+







  if( P("clear")!=0 ){
    db_multi_exec("DELETE FROM config WHERE name='footer'");
    cgi_replace_parameter("footer", zDefaultFooter);
  }else{
    textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
  }
  style_header("Edit Page Footer");
  @ <form action="%s(g.zBaseURL)/setup_footer" method="POST">
  @ <form action="%s(g.zBaseURL)/setup_footer" method="post"><div>
  login_insert_csrf_secret();
  @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to
  @ generate the end of every page.</p>
  textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter);
  @ <br />
  @ <input type="submit" name="submit" value="Apply Changes">
  @ <input type="submit" name="clear" value="Revert To Default">
  @ </form>
  @ <hr>
  @ <input type="submit" name="submit" value="Apply Changes" />
  @ <input type="submit" name="clear" value="Revert To Default" />
  @ </div></form>
  @ <hr />
  @ The default footer is shown below for reference.  Other examples
  @ of footers can be seen on the <a href="setup_skin">skins page</a>.
  @ See also the <a href="setup_editcss">CSS</a> and
  @ <a href="setup_header">header</a> editing screens.
  @ <blockquote><pre>
  @ %h(zDefaultFooter)
  @ </pre></blockquote>
1032
1033
1034
1035
1036
1037
1038
1039

1040
1041
1042
1043
1044
1045
1046
1047
1048


1049
1050
1051
1052
1053
1054
1055
1056




1057
1058
1059
1060
1061




1062
1063
1064
1063
1064
1065
1066
1067
1068
1069

1070
1071
1072
1073
1074
1075
1076
1077


1078
1079
1080
1081
1082
1083




1084
1085
1086
1087
1088




1089
1090
1091
1092
1093
1094
1095







-
+







-
-
+
+




-
-
-
-
+
+
+
+

-
-
-
-
+
+
+
+



    );
    db_end_transaction(0);
    cgi_redirect("setup_logo");
  }
  style_header("Edit Project Logo");
  @ <p>The current project logo has a MIME-Type of <b>%h(zMime)</b> and looks
  @ like this:</p>
  @ <blockquote><img src="%s(g.zTop)/logo" alt="logo"></blockquote>
  @ <blockquote><p><img src="%s(g.zTop)/logo" alt="logo" /></p></blockquote>
  @
  @ <p>The logo is accessible to all users at this URL:
  @ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>.
  @ The logo may or may not appear on each
  @ page depending on the <a href="setup_editcss">CSS</a> and
  @ <a href="setup_header">header setup</a>.</p>
  @
  @ <form action="%s(g.zBaseURL)/setup_logo" method="POST"
  @  enctype="multipart/form-data">
  @ <form action="%s(g.zBaseURL)/setup_logo" method="post"
  @  enctype="multipart/form-data"><div>
  @ <p>To set a new logo image, select a file to use as the logo using
  @ the entry box below and then press the "Change Logo" button.</p>
  login_insert_csrf_secret();
  @ Logo Image file:
  @ <input type="file" name="im" size="60" accepts="image/*"><br>
  @ <input type="submit" name="set" value="Change Logo">
  @ <input type="submit" name="clr" value="Revert To Default">
  @ </form>
  @ <input type="file" name="im" size="60" accept="image/*" /><br />
  @ <input type="submit" name="set" value="Change Logo" />
  @ <input type="submit" name="clr" value="Revert To Default" />
  @ </div></form>
  @
  @ <p><b>Note:</b>  Your browser has probably cached the logo image, so
  @ you will probably need to press the Reload button on your browser after
  @ changing the logo to provoke your browser to reload the new logo image.
  @ </p>
  @ <p><span class="note">Note:</span>  Your browser has probably cached the
  @ logo image, so you will probably need to press the Reload button on your
  @ browser after changing the logo to provoke your browser to reload the new
  @ logo image. </p>
  style_footer();
  db_end_transaction(0);
}
Changes to src/shun.c.
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
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







-
+

-
-
-
+
+
+









-
+

-
-
-
+
+
+








-
+

-
-
+
+


-
-
+
+








-
+

-
+






-
+







  @ from the repository.  Inappropriate content includes such things as
  @ spam added to Wiki, files that violate copyright or patent agreements,
  @ or artifacts that by design or accident interfere with the processing
  @ of the repository.  Do not shun artifacts merely to remove them from
  @ sight - set the "hidden" tag on such artifacts instead.</p>
  @ 
  @ <blockquote>
  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
  @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
  login_insert_csrf_secret();
  @ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50">
  @ <input type="submit" name="add" value="Shun">
  @ </form>
  @ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50" />
  @ <input type="submit" name="add" value="Shun" />
  @ </div></form>
  @ </blockquote>
  @
  @ <p>Enter the UUID of a previous shunned artifact to cause it to be
  @ accepted again in the repository.  The artifact content is not
  @ restored because the content is unknown.  The only change is that
  @ the formerly shunned artifact will be accepted on subsequent sync
  @ operations.</p>
  @
  @ <blockquote>
  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
  @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
  login_insert_csrf_secret();
  @ <input type="text" name="uuid" size="50">
  @ <input type="submit" name="sub" value="Accept">
  @ </form>
  @ <input type="text" name="uuid" size="50" />
  @ <input type="submit" name="sub" value="Accept" />
  @ </div></form>
  @ </blockquote>
  @
  @ <p>Press the Rebuild button below to rebuild the respository.  The
  @ content of newly shunned artifacts is not purged until the repository
  @ is rebuilt.  On larger repositories, the rebuild may take minute or
  @ two, so be patient after pressing the button.</p>
  @
  @ <blockquote>
  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
  @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><div>
  login_insert_csrf_secret();
  @ <input type="submit" name="rebuild" value="Rebuild">
  @ </form>
  @ <input type="submit" name="rebuild" value="Rebuild" />
  @ </div></form>
  @ </blockquote>
  @ 
  @ <hr><p>Shunned Artifacts:</p>
  @ <blockquote>
  @ <hr /><p>Shunned Artifacts:</p>
  @ <blockquote><p>
  db_prepare(&q, 
     "SELECT uuid, EXISTS(SELECT 1 FROM blob WHERE blob.uuid=shun.uuid)"
     "  FROM shun ORDER BY uuid");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    int stillExists = db_column_int(&q, 1);
    cnt++;
    if( stillExists ){
      @ <b><a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a></b><br>
      @ <b><a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a></b><br />
    }else{
      @ <b>%s(zUuid)</b><br>
      @ <b>%s(zUuid)</b><br />
    }
  }
  if( cnt==0 ){
    @ <i>no artifacts are shunned on this server</i>
  }
  db_finalize(&q);
  @ </blockquote>
  @ </p></blockquote>
  style_footer();
}

/*
** Remove from the BLOB table all artifacts that are in the SHUN table.
*/
void shun_artifacts(void){
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
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







-
-
-
-
+
+
+
+
+












-
-
-
-
+
+
+
+







  @ finding and fixing attempts to inject illicit content into the
  @ repository.</p>
  @
  @ <p>Click on the "rcvid" to show a list of specific artifacts received
  @ by a transaction.  After identifying illicit artifacts, remove them
  @ using the "Shun" feature.</p>
  @
  @ <table cellpadding=0 cellspacing=0 border=0>
  @ <tr><th>rcvid</th><th width=15>
  @     <th>Date</th><th width=15><th>User</th>
  @     <th width=15><th>IP&nbsp;Address</th></tr>
  @ <table cellpadding="0" cellspacing="0" border="0">
  @ <tr><th style="padding-right: 15px;text-align: right;">rcvid</th>
  @     <th style="padding-right: 15px;text-align: left;">Date</th>
  @     <th style="padding-right: 15px;text-align: left;">User</th>
  @     <th style="text-align: left;">IP&nbsp;Address</th></tr>
  cnt = 0;
  while( db_step(&q)==SQLITE_ROW ){
    int rcvid = db_column_int(&q, 0);
    const char *zUser = db_column_text(&q, 1);
    const char *zDate = db_column_text(&q, 2);
    const char *zIpAddr = db_column_text(&q, 3);
    if( cnt==30 ){
      style_submenu_element("Older", "Older",
         "rcvfromlist?ofst=%d", ofst+30);
    }else{
      cnt++;
      @ <tr>
      @ <td><a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td><td>
      @ <td>%s(zDate)</td><td>
      @ <td>%h(zUser)</td><td>
      @ <td>&nbsp;%s(zIpAddr)&nbsp</td>
      @ <td style="padding-right: 15px;text-align: right;"><a href="rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a></td>
      @ <td style="padding-right: 15px;text-align: left;">%s(zDate)</td>
      @ <td style="padding-right: 15px;text-align: left;">%h(zUser)</td>
      @ <td style="text-align: left;">%s(zIpAddr)</td>
      @ </tr>
    }
  }
  db_finalize(&q);
  @ </table>
  style_footer();
}
275
276
277
278
279
280
281
282

283
284
285
286
287
288
289
276
277
278
279
280
281
282

283
284
285
286
287
288
289
290







-
+







  style_header("Content Source %d", rcvid);
  db_prepare(&q, 
    "SELECT login, datetime(rcvfrom.mtime), rcvfrom.ipaddr"
    "  FROM rcvfrom LEFT JOIN user USING(uid)"
    " WHERE rcvid=%d",
    rcvid
  );
  @ <table cellspacing=15 cellpadding=0 border=0>
  @ <table cellspacing="15" cellpadding="0" border="0">
  @ <tr><td valign="top" align="right"><b>rcvid:</b></td>
  @ <td valign="top">%d(rcvid)</td></tr>
  if( db_step(&q)==SQLITE_ROW ){
    const char *zUser = db_column_text(&q, 0);
    const char *zDate = db_column_text(&q, 1);
    const char *zIpAddr = db_column_text(&q, 2);
    @ <tr><td valign="top" align="right"><b>User:</b></td>
300
301
302
303
304
305
306
307

308
309
310


311
301
302
303
304
305
306
307

308
309
310
311
312
313
314







-
+



+
+

  @ <tr><td valign="top" align="right"><b>Artifacts:</b></td>
  @ <td valign="top">
  while( db_step(&q)==SQLITE_ROW ){
    int rid = db_column_int(&q, 0);
    const char *zUuid = db_column_text(&q, 1);
    int size = db_column_int(&q, 2);
    @ <a href="%s(g.zBaseURL)/info/%s(zUuid)">%s(zUuid)</a>
    @ (rid: %d(rid), size: %d(size))<br>
    @ (rid: %d(rid), size: %d(size))<br />
  }
  @ </td></tr>
  @ </table>
  db_finalize(&q);
  style_footer();
}
Changes to src/skins.c.
163
164
165
166
167
168
169
170

171
172
173
174
175
176
177
163
164
165
166
167
168
169

170
171
172
173
174
175
176
177







-
+







@       media="screen">
@ </head>
@ <body>
@ <div class="header">
@   <div class="logo">
@     <img src="$baseurl/logo" alt="logo">
@   </div>
@   <div class="title"><small>$<project_name></small><br>$<title></div>
@   <div class="title"><small>$<project_name></small><br />$<title></div>
@   <div class="status"><nobr><th1>
@      if {[info exists login]} {
@        puts "Logged in as $login"
@      } else {
@        puts "Not logged in"
@      }
@   </th1></nobr></div>
598
599
600
601
602
603
604
605

606
607
608
609
610
611
612
598
599
600
601
602
603
604

605
606
607
608
609
610
611
612







-
+







@ <link rel="stylesheet" href="$baseurl/style.css?black2" type="text/css"
@       media="screen">
@ </head>
@ <body>
@ <div class="header">
@   <div class="logo">
@     <!-- <img src="$baseurl/logo" alt="logo"> -->
@     <br><nobr>$<project_name></nobr>
@     <br /><nobr>$<project_name></nobr>
@   </div>
@   <div class="title">$<title></div>
@   <div class="status"><nobr><th1>
@      if {[info exists login]} {
@        puts "Logged in as $login"
@      } else {
@        puts "Not logged in"
729
730
731
732
733
734
735
736

737
738
739
740
741



742
743

744
745
746
747
748
749
750
729
730
731
732
733
734
735

736
737
738



739
740
741
742

743
744
745
746
747
748
749
750







-
+


-
-
-
+
+
+

-
+







    login_needed();
  }
  db_begin_transaction();

  /* Process requests to delete a user-defined skin */
  if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
    style_header("Confirm Custom Skin Delete");
    @ <form action="%s(g.zBaseURL)/setup_skin" method="POST">
    @ <form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
    @ <p>Deletion of a custom skin is a permanent action that cannot
    @ be undone.  Please confirm that this is what you want to do:</p>
    @ <input type="hidden" name="sn" value="%h(P("sn"))">
    @ <input type="submit" name="del2" value="Confirm - Delete The Skin">
    @ <input type="submit" name="cancel" value="Cancel - Do Not Delete">
    @ <input type="hidden" name="sn" value="%h(P("sn"))" />
    @ <input type="submit" name="del2" value="Confirm - Delete The Skin" />
    @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" />
    login_insert_csrf_secret();
    @ </form>
    @ </div></form>
    style_footer();
    return;
  }
  if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
    db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
  }

810
811
812
813
814
815
816
817

818
819
820
821



822
823
824
825
826
827
828
810
811
812
813
814
815
816

817
818



819
820
821
822
823
824
825
826
827
828







-
+

-
-
-
+
+
+







  @ <h2>Available Skins:</h2>
  @ <ol>
  for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){
    z = aBuiltinSkin[i].zName;
    if( strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){
      @ <li><p>%h(z).&nbsp;&nbsp; <b>Currently In Use</b></p>
    }else{
      @ <li><form action="%s(g.zBaseURL)/setup_skin" method="POST">
      @ <li><form action="%s(g.zBaseURL)/setup_skin" method="post"><div>
      @ %h(z).&nbsp;&nbsp; 
      @ <input type="hidden" name="sn" value="%h(z)">
      @ <input type="submit" name="load" value="Use This Skin">
      @ </form></li>
      @ <input type="hidden" name="sn" value="%h(z)" />
      @ <input type="submit" name="load" value="Use This Skin" />
      @ </div></form></li>
    }
  }
  db_prepare(&q,
     "SELECT substr(name, 6), value FROM config"
     " WHERE name GLOB 'skin:*'"
     " ORDER BY name"
  );
Changes to src/stat.c.
26
27
28
29
30
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
26
27
28
29
30
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







+

+



-
+











+

+
-
+
+
+
+
+
+

-
+







** WEBPAGE: stat
**
** Show statistics and global information about the repository.
*/
void stat_page(void){
  i64 t;
  int n, m, fsize;
  int szMax, szAvg;
  char zBuf[100];

  login_check_credentials();
  if( !g.okRead ){ login_needed(); return; }
  style_header("Repository Statistics");
  @ <p><table class="label-value">
  @ <table class="label-value">
  @ <tr><th>Repository&nbsp;Size:</th><td>
  fsize = file_size(g.zRepositoryName);
  @ %d(fsize) bytes
  @ </td></tr>
  @ <tr><th>Number&nbsp;Of&nbsp;Artifacts:</th><td>
  n = db_int(0, "SELECT count(*) FROM blob");
  m = db_int(0, "SELECT count(*) FROM delta");
  @ %d(n) (stored as %d(n-m) full text and %d(m) delta blobs)
  @ </td></tr>
  if( n>0 ){
    int a, b;
    Stmt q;
    @ <tr><th>Uncompressed&nbsp;Artifact&nbsp;Size:</th><td>
    db_prepare(&q, "SELECT total(size), avg(size), max(size)"
    t = db_int64(0, "SELECT total(size) FROM blob WHERE size>0");
                   " FROM blob WHERE size>0");
    db_step(&q);
    t = db_column_int64(&q, 0);
    szAvg = db_column_int(&q, 1);
    szMax = db_column_int(&q, 2);
    db_finalize(&q);
    sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", t);
    @ %d((int)(((double)t)/(double)n)) bytes average, %s(zBuf) bytes total
    @ %d(szAvg) bytes average, %d(szMax) bytes max, %s(zBuf) bytes total
    @ </td></tr>
    @ <tr><th>Compression&nbsp;Ratio:</th><td>
    if( t/fsize < 5 ){
      b = 10;
      fsize /= 10;
    }else{
      b = 1;
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
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







+
+












-
-
+
+
+









-
+


                " WHERE +tagname GLOB 'tkt-*'");
  @ %d(n)
  @ </td></tr>
  @ <tr><th>Duration&nbsp;Of&nbsp;Project:</th><td>
  n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
                " + 0.99");
  @ %d(n) days
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%.2f", n/365.24);
  @ or approximately %s(zBuf) years
  @ </td></tr>
  @ <tr><th>Project&nbsp;ID:</th><td>
  @ %h(db_get("project-code",""))
  @ </td></tr>
  @ <tr><th>Server&nbsp;ID:</th><td>
  @ %h(db_get("server-code",""))
  @ </td></tr>

  @ <tr><th>Fossil&nbsp;Version:</th><td>
  @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION)
  @ </td></tr>
  @ <tr><th>SQLite&nbsp;Version:</th><td>
  @ %h(db_text(0, "SELECT substr(sqlite_source_id(),1,30)"))
  @ (%h(SQLITE_VERSION))
  sqlite3_snprintf(sizeof(zBuf), zBuf, "%.19s [%.10s] (%s)",
                   SQLITE_SOURCE_ID, &SQLITE_SOURCE_ID[20], SQLITE_VERSION);
  @ %s(zBuf)
  @ </td></tr>
  @ <tr><th>Database&nbsp;Stats:</th><td>
  @ %d(db_int(0, "PRAGMA %s.page_count", g.zRepoDb)) pages,
  @ %d(db_int(0, "PRAGMA %s.page_size", g.zRepoDb)) bytes/page,
  @ %d(db_int(0, "PRAGMA %s.freelist_count", g.zRepoDb)) free pages,
  @ %s(db_text(0, "PRAGMA %s.encoding", g.zRepoDb)),
  @ %s(db_text(0, "PRAGMA %s.journal_mode", g.zRepoDb)) mode
  @ </td></tr>

  @ </table></p>
  @ </table>
  style_footer();
}
Changes to src/style.c.
137
138
139
140
141
142
143


144

145

146
147
148
149
150
151
152
153

154
155
156
157
158
159
160
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







+
+

+
-
+







-
+







    }
    @ </div>
  }
  @ <div class="content">
  cgi_destination(CGI_BODY);

  /* Put the footer at the bottom of the page.
  ** the additional clear/both is needed to extend the content
  ** part to the end of an optional sidebox.
  */
  @ <div style="clear: both;"></div>
  @ </div><br clear="both"/>
  @ </div>
  zFooter = db_get("footer", (char*)zDefaultFooter);
  if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1);
  Th_Render(zFooter);
  if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1);
  
  /* Render trace log if TH1 tracing is enabled. */
  if( g.thTrace ){
    cgi_append_content("<font color=\"red\"><hr>\n", -1);
    cgi_append_content("<font color=\"red\"><hr />\n", -1);
    cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog));
    cgi_append_content("</font>\n", -1);
  }
}

/*
** Begin a side-box on the right-hand side of a page.  The title and
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
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







-
+

-
+






-
-
+
+





-
+







** 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">
@       href="$baseurl/timeline.rss" />
@ <link rel="stylesheet" href="$baseurl/style.css?default" type="text/css"
@       media="screen">
@       media="screen" />
@ </head>
@ <body>
@ <div class="header">
@   <div class="logo">
@     <img src="$baseurl/logo" alt="logo">
@   </div>
@   <div class="title"><small>$<project_name></small><br>$<title></div>
@   <div class="status"><nobr><th1>
@   <div class="title"><small>$<project_name></small><br />$<title></div>
@   <div class="status"><th1>
@      if {[info exists login]} {
@        puts "Logged in as $login"
@      } else {
@        puts "Not logged in"
@      }
@   </th1></nobr></div>
@   </th1></div>
@ </div>
@ <div class="mainmenu"><th1>
@ html "<a href='$baseurl$index_page'>Home</a> "
@ if {[anycap jor]} {
@   html "<a href='$baseurl/timeline'>Timeline</a> "
@ }
@ if {[hascap oh]} {
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
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







-
+











+





-
+







@   display: table-cell;
@   font-size: 2em;
@   font-weight: bold;
@   text-align: center;
@   padding: 0 0 0 1em;
@   color: #558195;
@   vertical-align: bottom;
@   width: 100%;
@   width: 100% ;
@ }
@
@ /* The login status message in the top right-hand corner */
@ div.status {
@   display: table-cell;
@   text-align: right;
@   vertical-align: bottom;
@   color: #558195;
@   font-size: 0.8em;
@   font-weight: bold;
@   min-width: 200px;
@   white-space: nowrap;
@ }
@
@ /* The header across the top of the page */
@ div.header {
@   display: table;
@   width: 100%;
@   width: 100% ;
@ }
@
@ /* The main menu bar that appears at the top of the page beneath
@ ** the header */
@ div.mainmenu {
@   padding: 5px 10px 5px 10px;
@   font-size: 0.9em;
337
338
339
340
341
342
343

344
345
346
347
348
349
350
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355







+







@   margin-bottom: 0px;
@   margin-top: 1em;
@   padding: 1px 1px 1px 1px;
@   font-size: 1.2em;
@   font-weight: bold;
@   background-color: #558195;
@   color: white;
@   white-space: nowrap;
@ }
@
@ /* The "Date" that occurs on the left hand side of timelines */
@ div.divider {
@   background: #a1c4d4;
@   border: 2px #558195 solid;
@   font-size: 1em; font-weight: normal;
366
367
368
369
370
371
372
373

374
375
376
377
378
379
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394
395




















































































396
397
398
399
400
401
402










403
404
405
406












































































































































































407


408

409
410


411
412
413






414
415
416








417
418
419
420
421
422

423
424
425
426
427
428
429
430
431
432









433
434
435
436
437
438
439
440
441
442
443
444

445
446
447


448
449
450
371
372
373
374
375
376
377

378
379
380
381
382
383


384
385
386
387
388
389
390
391








392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475







476
477
478
479
480
481
482
483
484
485




486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
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
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
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
709

710
711


712
713
714
715
716







-
+





-
-






+

-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+

+
-
-
+
+
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+





-
+







-
-
-
+
+
+
+
+
+
+
+
+

-









-
+

-
-
+
+



@
@ /* Hyperlink colors in the footer */
@ div.footer a { color: white; }
@ div.footer a:link { color: white; }
@ div.footer a:visited { color: white; }
@ div.footer a:hover { background-color: white; color: #558195; }
@ 
@ /* <verbatim> blocks */
@ /* verbatim blocks */
@ pre.verbatim {
@    background-color: #f5f5f5;
@    padding: 0.5em;
@}
@
;
const char zTableLabelValueCSS[] = 
@ /* The label/value pairs on (for example) the ci page */
@ table.label-value th {
@   vertical-align: top;
@   text-align: right;
@   padding: 0.2ex 2ex;
@ }
@
;
const char zDivSidebox[] =
@ /* The nomenclature sidebox for branches,.. */
@ div.sidebox {
@   float: right;
@   border-width: medium;
@   border-style: double;
@   margin: 10;
@ }


/* The following table contains bits of default CSS that must
** be included if they are not found in the application-defined
** CSS.
*/
const struct strctCssDefaults {
  char const * const elementClass;  /* Name of element needed */
  char const * const comment;       /* Comment text */
  char const * const value;         /* CSS text */
} cssDefaultList[] = {
  { "",
    "",
    zDefaultCSS
  },
  { "div.sidebox",
    "The nomenclature sidebox for branches,..",
    @   float: right;
    @   background-color: white;
    @   border-width: medium;
    @   border-style: double;
    @   margin: 10;
  },
  { "div.sideboxTitle",
    "The nomenclature title in sideboxes for branches,..",
    @   display: inline;
    @   font-weight: bold;
  },
  { "div.sideboxDescribed",
    "The defined element in sideboxes for branches,..",
    @   display: inline;
    @   font-weight: bold;
  },
  { "span.disabled",
    "The defined element in sideboxes for branches,..",
    @   color: red;
  },
  { "span.timelineDisabled",
    "The suppressed duplicates lines in timeline, ..",
    @   font-style: italic;
    @   font-size: small;
  },
  { "table.timelineTable",
    "the format for the timeline data table",
    @   cellspacing: 0;
    @   border: 0;
    @   cellpadding: 0
  },
  { "td.timelineTableCell",
    "the format for the timeline data cells",
    @   valign: top;
    @   align: left;
  },
  { "span.timelineLeaf",
    "the format for the timeline leaf marks",
    @   font-weight: bold;
  },
  { "a.timelineHistLink",
    "the format for the timeline version links",
    @
  },
  { "span.timelineHistDsp",
    "the format for the timeline version display(no history permission!)",
    @   font-weight: bold;
  },
  { "td.timelineTime",
    "the format for the timeline time display",
    @   vertical-align: top;
    @   text-align: right;
  },
  { "td.timelineGraph",
    "the format for the grap placeholder cells in timelines",
    @ width: 20;
    @ text-align: left;
    @ vertical-align: top;
  },
  { "a.tagLink",
    "the format for the tag links",
    @
  },
  { "span.tagDsp",
    "the format for the tag display(no history permission!)",
    @   font-weight: bold;
  },
;
const char zDivSideboxTitle[] =
@ /* The nomenclature title in sideboxes for branches,.. */
@ div.sideboxTitle {
@   display: inline;
@   font-weight: bold;
@ }
  { "span.wikiError",
    "the format for wiki errors",
    @   font-weight: bold;
    @   color: red;
  },
  { "span.infoTagCancelled",
    "the format for fixed/canceled tags,..",
    @   font-weight: bold;
    @   text-decoration: line-through;
  },
;


/* The following table holds the names of CSS elements and the CSS
  { "span.infoTag",
    "the format for tags,..",
    @   font-weight: bold;
  },
  { "span.wikiTagCancelled",
    "the format for fixed/cancelled tags,.. on wiki pages",
    @   text-decoration: line-through;
  },
  { "table.browser",
    "format for the file display table",
    @ /* the format for wiki errors */
    @   width: 100% ;
    @   border: 0;
  },
  { "td.browser",
    "format for cells in the file browser",
    @   width: 24% ;
    @   vertical-align: top;
  },
  { "ul.browser",
    "format for the list in the file browser",
    @   margin-left: 0.5em;
    @   padding-left: 0.5em;
  },
  { "table.login_out",
    "table format for login/out label/input table",
    @   text-align: left;
    @   margin-right: 10px;
    @   margin-left: 10px;
    @   margin-top: 10px;
  },
  { "div.captcha",
    "captcha display options",
    @   text-align: center;
  },
  { "table.captcha",
    "format for the layout table, used for the captcha display",
    @   margin: auto;
    @   padding: 10px;
    @   outline-width: 1;
    @   outline-style: double;
  },
  { "td.login_out_label",
    "format for the label cells in the login/out table",
    @   text-align: center;
  },
  { "span.loginError",
    "format for login error messages",
    @   color: red;
  },
  { "span.note",
    "format for leading text for notes",
    @   font-weight: bold;
  },
  { "span.textareaLabel",
    "format for textare labels",
    @   font-weight: bold;
  },
  { "table.usetupLayoutTable",
    "format for the user setup layout table",
    @   outline-style: none;
    @   padding: 0;
    @   margin: 25px;
  },
  { "td.usetupColumnLayout",
    "format of the columns on the user setup list page",
    @   vertical-align: top
  },
  { "table.usetupUserList",
    "format for the user list table on the user setup page",
    @   outline-style: double;
    @   outline-width: 1;
    @   padding: 10px;
  },
  { "th.usetupListUser",
    "format for table header user in user list on user setup page",
    @   text-align: right;
    @   padding-right: 20px;
  },
  { "th.usetupListCap",
    "format for table header capabilities in user list on user setup page",
    @   text-align: center;
    @   padding-right: 15px;
  },
  { "th.usetupListCon",
    "format for table header contact info in user list on user setup page",
    @   text-align: left;
  },
  { "td.usetupListUser",
    "format for table cell user in user list on user setup page",
    @   text-align: right;
    @   padding-right: 20px;
    @   white-space:nowrap;
  },
  { "td.usetupListCap",
    "format for table cell capabilities in user list on user setup page",
    @   text-align: center;
    @   padding-right: 15px;
  },
  { "td.usetupListCon",
    "format for table cell contact info in user list on user setup page",
    @   text-align: left
  },
  { "div.ueditCapBox",
    "layout definition for the capabilities box on the user edit detail page",
    @   float: left;
    @   margin-right: 20px;
    @   margin-bottom: 20px;
  },
  { "td.usetupEditLabel",
    "format of the label cells in the detailed user edit page",
    @   text-align: right;
    @   vertical-align: top;
    @   white-space: nowrap;
  },
  { "span.ueditInheritNobody",
    "color for capabilities, inherited by nobody",
    @   color: green;
  },
  { "span.ueditInheritDeveloper",
    "color for capabilities, inherited by developer",
    @   color: red;
  },
  { "span.ueditInheritReader",
    "color for capabilities, inherited by reader",
    @   color: black;
  },
  { "span.ueditInheritAnonymous",
    "color for capabilities, inherited by anonymous",
    @   color: blue;
  },
  { "span.capability",
    "format for capabilites, mentioned on the user edit page",
    @   font-weight: bold;
  },
  { "span.usertype",
    "format for different user types, mentioned on the user edit page",
    @   font-weight: bold;
  },
  { "span.usertype:before",
    "leading text for user types, mentioned on the user edit page",
    @   content:"'";
  },
  { "span.usertype:after",
    "trailing text for user types, mentioned on the user edit page",
    @   content:"'";
  },
  { "span.wikiruleHead",
    "format for leading text in wikirules definitions",
    @   font-weight: bold;
  },
  { "td.tktDspLabel",
    "format for labels on ticket display page",
    @   text-align: right;
  },
  { "td.tktDspValue",
    "format for values on ticket display page",
    @   text-align: left;
    @   vertical-align: top;
    @   background-color: #d0d0d0;
  },
  { "span.tktError",
    "format for ticket error messages",
    @   color: red;
    @   font-weight: bold;
  },
  { 0,
    0,
    0
  }
};

** text that implements those elements.
/*
** Append all of the default CSS to the CGI output.
*/
void cgi_append_default_css(void) {
static const struct {
  const char *zElement;   /* Name of the CSS element */
  int i;

  const char *zText;      /* Text of the element */
} cssElements[] = {
  { "table.label-value", zTableLabelValueCSS },
  for (i=0;cssDefaultList[i].elementClass;i++){
    if (cssDefaultList[i].elementClass[0]){
      cgi_printf("/* %s */\n%s {\n%s\n}\n\n",
		 cssDefaultList[i].comment,
		 cssDefaultList[i].elementClass,
		 cssDefaultList[i].value
  { "div.sidebox",       zDivSidebox         },
  { "div.sideboxTitle",  zDivSideboxTitle    },
};
		);
    }else{
      cgi_printf("%s",
		 cssDefaultList[i].value
		);
    }
  }
}

/*
** WEBPAGE: style.css
*/
void page_style_css(void){
  const char *zCSS;
  const char *zCSS    = 0;
  int i;

  cgi_set_content_type("text/css");
  zCSS = db_get("css",(char*)zDefaultCSS);
  /* append user defined css */
  cgi_append_content(zCSS, -1);
  /* add special missing definitions */
  for(i=0; i<count(cssElements); i++){
    if( strstr(cssElements[i].zElement, zCSS)==0 ){
      cgi_append_content(cssElements[i].zText, -1);
  for (i=1;cssDefaultList[i].elementClass;i++)
    if (!strstr(zCSS,cssDefaultList[i].elementClass)) {
      cgi_append_content("/* ", -1);
      cgi_append_content(cssDefaultList[i].comment, -1);
      cgi_append_content(" */\n", -1);
      cgi_append_content(cssDefaultList[i].elementClass, -1);
      cgi_append_content(" {\n", -1);
      cgi_append_content(cssDefaultList[i].value, -1);
      cgi_append_content("}\n\n", -1);
    }
  }
  g.isConst = 1;
}

/*
** WEBPAGE: test_env
*/
void page_test_env(void){
  style_header("Environment Test");
#if !defined(_WIN32)
  @ uid=%d(getuid()), gid=%d(getgid())<br>
  @ uid=%d(getuid()), gid=%d(getgid())<br />
#endif
  @ g.zBaseURL = %h(g.zBaseURL)<br>
  @ g.zTop = %h(g.zTop)<br>
  @ g.zBaseURL = %h(g.zBaseURL)<br />
  @ g.zTop = %h(g.zTop)<br />
  cgi_print_all();
  style_footer();
}
Changes to src/tag.c.
512
513
514
515
516
517
518
519


520
521

522
523
524
525
526
527
528
512
513
514
515
516
517
518

519
520
521

522
523
524
525
526
527
528
529







-
+
+

-
+







    " AND tagname GLOB 'sym-*'"
    " ORDER BY tagname"
  );
  @ <ul>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    if( g.okHistory ){
      @ <li><a href=%s(g.zBaseURL)/timeline?t=%T(zName)>%h(zName)</a></li>
      @ <li><a class="tagLink" href="%s(g.zBaseURL)/timeline?t=%T(zName)">
      @ %h(zName)</a></li>
    }else{
      @ <li><strong>%h(zName)</strong></li>
      @ <li><span class="tagDsp">%h(zName)</span></li>
    }
  }
  @ </ul>
  db_finalize(&q);
  style_footer();
}

540
541
542
543
544
545
546
547


548
549

550
551
552
553
554
555
556
541
542
543
544
545
546
547

548
549
550

551
552
553
554
555
556
557
558







-
+
+

-
+







    "   AND tagxref.tagtype>0 AND tagxref.srcid>0"
    "   AND tag.tagname GLOB 'sym-*'",
    rid
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zTagName = db_column_text(&q, 0);
    if( g.okHistory ){
      @ <a href="%s(g.zBaseURL)/timeline?t=%T(zTagName)">[%h(zTagName)]</a>
      @ <a class="tagLink" href="%s(g.zBaseURL)/timeline?t=%T(zTagName)">
      @ [%h(zTagName)]</a>
    }else{
      @ <b>[%h(zTagName)]</b>
      @ <span class="tagDsp">[%h(zTagName)]</span>
    }
  }
  db_finalize(&q);
}

/*
** WEBPAGE: /tagtimeline
571
572
573
574
575
576
577
578
579


580
581
582
583
584
585
586
573
574
575
576
577
578
579


580
581
582
583
584
585
586
587
588







-
-
+
+







    "                       AND tagid IN (SELECT tagid FROM tag "
    "                                      WHERE tagname GLOB 'sym-*'))"
    " ORDER BY event.mtime DESC",
    timeline_query_for_www()
  );
  www_print_timeline(&q, 0, tagtimeline_extra);
  db_finalize(&q);
  @ <br clear="both">
  @ <script>
  @ <br />
  @ <script  type="text/JavaScript">
  @ function xin(id){
  @ }
  @ function xout(id){
  @ }
  @ </script>
  style_footer();
}
Changes to src/th_main.c.
276
277
278
279
280
281
282
283


284
285
286
287
288
289
290
276
277
278
279
280
281
282

283
284
285
286
287
288
289
290
291







-
+
+







    sendText(z, -1, 0);
    free(z);
    blob_reset(&name);
    for(i=0; i<nElem; i++){
      zH = htmlize((char*)azElem[i], aszElem[i]);
      if( zValue && aszElem[i]==nValue 
             && memcmp(zValue, azElem[i], nValue)==0 ){
        z = mprintf("<option value=\"%s\" selected>%s</option>", zH, zH);
        z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
                     zH, zH);
      }else{
        z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
      }
      free(zH);
      sendText(z, -1, 0);
      free(z);
    }
Changes to src/timeline.c.
46
47
48
49
50
51
52
53


54
55

56
57
58
59
60
61
62
46
47
48
49
50
51
52

53
54
55

56
57
58
59
60
61
62
63







-
+
+

-
+







/*
** Generate a hyperlink to a version.
*/
void hyperlink_to_uuid(const char *zUuid){
  char zShortUuid[UUID_SIZE+1];
  shorten_uuid(zShortUuid, zUuid);
  if( g.okHistory ){
    @ <a href="%s(g.zBaseURL)/info/%s(zShortUuid)">[%s(zShortUuid)]</a>
    @ <a class="timelineHistLink" href="%s(g.zBaseURL)/info/%s(zShortUuid)">
    @ [%s(zShortUuid)]</a>
  }else{
    @ <b>[%s(zShortUuid)]</b>
    @ <span class="timelineHistDsp">[%s(zShortUuid)]</span>
  }
}

/*
** Generate a hyperlink that invokes javascript to highlight
** a version on mouseover.
*/
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96







-
+







** Generate a hyperlink to a diff between two versions.
*/
void hyperlink_to_diff(const char *zV1, const char *zV2){
  if( g.okHistory ){
    if( zV2==0 ){
      @ <a href="%s(g.zBaseURL)/diff?v2=%s(zV1)">[diff]</a>
    }else{
      @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&v2=%s(zV2)">[diff]</a>
      @ <a href="%s(g.zBaseURL)/diff?v1=%s(zV1)&amp;v2=%s(zV2)">[diff]</a>
    }
  }
}

/*
** Generate a hyperlink to a date & time.
*/
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122







-
+







** events by that user.  If the date+time is specified, then the timeline
** is centered on that date+time.
*/
void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
  if( zSuf==0 ) zSuf = "";
  if( g.okHistory ){
    if( zD && zD[0] ){
      @ <a href="%s(g.zTop)/timeline?c=%T(zD)&u=%T(zU)">%h(zU)</a>%s(zSuf)
      @ <a href="%s(g.zTop)/timeline?c=%T(zD)&amp;u=%T(zU)">%h(zU)</a>%s(zSuf)
    }else{
      @ <a href="%s(g.zTop)/timeline?u=%T(zU)">%h(zU)</a>%s(zSuf)
    }
  }else{
    @ %s(zU)
  }
}
187
188
189
190
191
192
193



194
195
196
197

198
199
200
201
202
203
204
188
189
190
191
192
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208







+
+
+



-
+







  if( db_get_boolean("timeline-block-markup", 0) ){
    wikiFlags = WIKI_INLINE;
  }else{
    wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
  }
  if( tmFlags & TIMELINE_GRAPH ){
    pGraph = graph_init();
    /* style is not moved to css, because this is
    ** a technical div for the timeline graph
    */
    @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div>
  }

  @ <table cellspacing=0 border=0 cellpadding=0>
  @ <table class="timelineTable">
  blob_zero(&comment);
  while( db_step(pQuery)==SQLITE_ROW ){
    int rid = db_column_int(pQuery, 0);
    const char *zUuid = db_column_text(pQuery, 1);
    int isLeaf = db_column_int(pQuery, 5);
    const char *zBgClr = db_column_text(pQuery, 6);
    const char *zDate = db_column_text(pQuery, 2);
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
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







-
-
-
+
+
+



-
+





-
+





-
-
+
+







        }else{
          commentColumn = 10;
        }
      }
    }
    prevTagid = tagid;
    if( suppressCnt ){
      @ <tr><td><td><td>
      @ <small><i>... %d(suppressCnt) similar
      @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr>
      @ <tr><td /><td /><td>
      @ <span class="timelineDisabled">... %d(suppressCnt) similar
      @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
      suppressCnt = 0;
    }
    if( strcmp(zType,"div")==0 ){
      @ <tr><td colspan=3><hr></td></tr>
      @ <tr><td colspan="3"><hr /></td></tr>
      continue;
    }
    if( memcmp(zDate, zPrevDate, 10) ){
      sprintf(zPrevDate, "%.10s", zDate);
      @ <tr><td>
      @   <div class="divider"><nobr>%s(zPrevDate)</nobr></div>
      @   <div class="divider">%s(zPrevDate)</div>
      @ </td></tr>
    }
    memcpy(zTime, &zDate[11], 5);
    zTime[5] = 0;
    @ <tr>
    @ <td valign="top" align="right">%s(zTime)</td>
    @ <td width="20" align="left" valign="top">
    @ <td class="timelineTime">%s(zTime)</td>
    @ <td class="timelineGraph">
    if( pGraph && zType[0]=='c' ){
      int nParent = 0;
      int aParent[32];
      const char *zBr;
      int gidx;
      static Stmt qparent;
      static Stmt qbranch;
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
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







+

-
+

-
+







-
+

-
+







      }else{
        zBr = "trunk";
      }
      gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr);
      db_reset(&qbranch);
      @ <div id="m%d(gidx)"></div>
    }
    @</td>
    if( zBgClr && zBgClr[0] ){
      @ <td valign="top" align="left" bgcolor="%h(zBgClr)">
      @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
    }else{
      @ <td valign="top" align="left">
      @ <td class="timelineTableCell">
    }
    if( zType[0]=='c' ){
      hyperlink_to_uuid(zUuid);
      if( isLeaf ){
        if( db_exists("SELECT 1 FROM tagxref"
                      " WHERE rid=%d AND tagid=%d AND tagtype>0",
                      rid, TAG_CLOSED) ){
          @ <b>Closed-Leaf:</b>
          @ <span class="timelineLeaf">Closed-Leaf:</span>
        }else{
          @ <b>Leaf:</b>
          @ <span class="timelineLeaf">Leaf:</span>
        }
      }
    }else if( (tmFlags & TIMELINE_ARTID)!=0 ){
      hyperlink_to_uuid(zUuid);
    }
    db_column_blob(pQuery, commentColumn, &comment);
    if( mxWikiLen>0 && blob_size(&comment)>mxWikiLen ){
307
308
309
310
311
312
313
314
315
316



317
318
319
320
321
322
323
324



325

326

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342

343
344
345
346
347
348
349
312
313
314
315
316
317
318



319
320
321
322
323
324
325
326
327
328
329
330
331
332

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350

351
352
353
354
355
356
357
358







-
-
-
+
+
+








+
+
+
-
+

+















-
+







    }
    if( xExtra ){
      xExtra(rid);
    }
    @ </td></tr>
  }
  if( suppressCnt ){
    @ <tr><td><td><td>
    @ <small><i>... %d(suppressCnt) similar
    @ event%s(suppressCnt>1?"s":"") omitted.</i></small></tr>
    @ <tr><td /><td /><td>
    @ <span class="timelineDisabled">... %d(suppressCnt) similar
    @ event%s(suppressCnt>1?"s":"") omitted.</span></td></tr>
    suppressCnt = 0;
  }
  if( pGraph ){
    graph_finish(pGraph, (tmFlags & TIMELINE_DISJOINT)!=0);
    if( pGraph->nErr ){
      graph_free(pGraph);
      pGraph = 0;
    }else{
      /* style is not moved to css, because this is
      ** a technical div for the timeline graph
      */
      @ <tr><td><td>
      @ <tr><td /><td>
      @ <div id="grbtm" style="width:%d(pGraph->mxRail*20+30)px;"></div>
      @ </td></tr>
    }
  }
  @ </table>
  timeline_output_graph_javascript(pGraph);
}

/*
** Generate all of the necessary javascript to generate a timeline
** graph.
*/
void timeline_output_graph_javascript(GraphContext *pGraph){
  if( pGraph && pGraph->nErr==0 ){
    GraphRow *pRow;
    int i;
    char cSep;
    @ <script type="text/JavaScript">
    @ <script  type="text/JavaScript">
    cgi_printf("var rowinfo = [\n");
    for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
      cgi_printf("{id:\"m%d\",bg:\"%s\",r:%d,d:%d,mo:%d,mu:%d,u:%d,au:",
        pRow->idx,
        pRow->zBgClr,
        pRow->iRail,
        pRow->bDescender,
881
882
883
884
885
886
887
888

889
890

891
892

893
894
895
896
897
898
899
890
891
892
893
894
895
896

897
898

899
900

901
902
903
904
905
906
907
908







-
+

-
+

-
+







      blob_appendf(&desc, " tagged with \"%h\"", zTagName);
      tmFlags |= TIMELINE_DISJOINT;
    }else if( zBrName ){
      blob_appendf(&desc, " related to \"%h\"", zBrName);
      tmFlags |= TIMELINE_DISJOINT;
    }
    if( zAfter ){
      blob_appendf(&desc, " occurring on or after %h.<br>", zAfter);
      blob_appendf(&desc, " occurring on or after %h.<br />", zAfter);
    }else if( zBefore ){
      blob_appendf(&desc, " occurring on or before %h.<br>", zBefore);
      blob_appendf(&desc, " occurring on or before %h.<br />", zBefore);
    }else if( zCirca ){
      blob_appendf(&desc, " occurring around %h.<br>", zCirca);
      blob_appendf(&desc, " occurring around %h.<br />", zCirca);
    }
    if( zSearch ){
      blob_appendf(&desc, " matching \"%h\"", zSearch);
    }
    if( g.okHistory ){
      if( zAfter || n==nEntry ){
        zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
Changes to src/tkt.c.
311
312
313
314
315
316
317
318

319
320
321
322
323
324
325
311
312
313
314
315
316
317

318
319
320
321
322
323
324
325







-
+







  }
  if( g.okNewTkt ){
    style_submenu_element("New Ticket", "Create a new ticket",
        "%s/tktnew", g.zTop);
  }
  if( g.okApndTkt && g.okAttach ){
    style_submenu_element("Attach", "Add An Attachment",
        "%s/attachadd?tkt=%T&from=%s/tktview/%t",
        "%s/attachadd?tkt=%T&amp;from=%s/tktview/%t",
        g.zTop, zUuid, g.zTop, zUuid);
  }
  style_header("View Ticket");
  if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br />\n", -1);
  ticket_init();
  initializeVariablesFromDb();
  zScript = ticket_viewpage_code();
340
341
342
343
344
345
346
347

348
349
350
351
352

353
354
355
356
357
358
359
360

361
362
363
364
365
366
367
340
341
342
343
344
345
346

347
348
349
350
351

352
353
354
355
356
357
358
359

360
361
362
363
364
365
366
367







-
+




-
+







-
+







       " ORDER BY mtime DESC",
       zFullName);
    while( db_step(&q)==SQLITE_ROW ){
      const char *zDate = db_column_text(&q, 0);
      const char *zFile = db_column_text(&q, 1);
      const char *zUser = db_column_text(&q, 2);
      if( cnt==0 ){
        @ <hr><h2>Attachments:</h2>
        @ <hr /><h2>Attachments:</h2>
        @ <ul>
      }
      cnt++;
      if( g.okRead && g.okHistory ){
        @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&file=%t(zFile)">
        @ <li><a href="%s(g.zTop)/attachview?tkt=%s(zFullName)&amp;file=%t(zFile)">
        @ %h(zFile)</a>
      }else{
        @ %h(zFile)
      }
      @ added by %h(zUser) on
      hyperlink_to_date(zDate, ".");
      if( g.okWrTkt && g.okAttach ){
        @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&file=%t(zFile)&from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
        @ [<a href="%s(g.zTop)/attachdelete?tkt=%s(zFullName)&amp;file=%t(zFile)&amp;from=%s(g.zTop)/tktview%%3fname=%s(zFullName)">delete</a>]
      }
    }
    if( cnt ){
      @ </ul>
    }
    db_finalize(&q);
  }
509
510
511
512
513
514
515
516

517

518
519
520
521
522
523
524
509
510
511
512
513
514
515

516
517
518
519
520
521
522
523
524
525







-
+

+







  }
  style_header("New Ticket");
  if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
  ticket_init();
  getAllTicketFields();
  initializeVariablesFromDb();
  initializeVariablesFromCGI();
  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
  @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><p>
  login_insert_csrf_secret();
  @ </p>
  zScript = ticket_newpage_code();
  Th_Store("login", g.zLogin);
  Th_Store("date", db_text(0, "SELECT datetime('now')"));
  Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
                   (void*)&zNewUuid, 0);
  if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
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


582

583
584
585
586
587
588
589
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


582
583
584
585
586
587
588
589
590
591
592







-
+






-
+




-
+
+








-
-
+
+

+







  zName = P("name");
  if( P("cancel") ){
    cgi_redirectf("tktview?name=%T", zName);
  }
  style_header("Edit Ticket");
  if( zName==0 || (nName = strlen(zName))<4 || nName>UUID_SIZE
          || !validate16(zName,nName) ){
    @ <font color="red"><b>Not a valid ticket id: \"%h(zName)\"</b></font>
    @ <span class="tktError">Not a valid ticket id: \"%h(zName)\"</span>
    style_footer();
    return;
  }
  nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
                zName);
  if( nRec==0 ){
    @ <font color="red"><b>No such ticket: \"%h(zName)\"</b></font>
    @ <span class="tktError">No such ticket: \"%h(zName)\"</span>
    style_footer();
    return;
  }
  if( nRec>1 ){
    @ <font color="red"><b>%d(nRec) tickets begin with: \"%h(zName)\"</b></font>
    @ <span class="tktError"><b>%d(nRec) tickets begin with:
    @ \"%h(zName)\"</span>
    style_footer();
    return;
  }
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  ticket_init();
  getAllTicketFields();
  initializeVariablesFromCGI();
  initializeVariablesFromDb();
  @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)">
  @ <input type="hidden" name="name" value="%s(zName)">
  @ <form method="post" action="%s(g.zBaseURL)/%s(g.zPath)"><p>
  @ <input type="hidden" name="name" value="%s(zName)" />
  login_insert_csrf_secret();
  @ </p>
  zScript = ticket_editpage_code();
  Th_Store("login", g.zLogin);
  Th_Store("date", db_text(0, "SELECT datetime('now')"));
  Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
  Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
622
623
624
625
626
627
628
629

630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649

650
651
652
653
654
655
656
625
626
627
628
629
630
631

632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651

652
653
654
655
656
657
658
659







-
+



















-
+







    }
  }
  return 0;
}

/*
** WEBPAGE: tkttimeline
** URL: /tkttimeline?name=TICKETUUID&y=TYPE
** URL: /tkttimeline?name=TICKETUUID&amp;y=TYPE
**
** Show the change history for a single ticket in timeline format.
*/
void tkttimeline_page(void){
  Stmt q;
  char *zTitle;
  char *zSQL;
  const char *zUuid;
  char *zFullUuid;
  int tagid;
  char zGlobPattern[50];
  const char *zType;

  login_check_credentials();
  if( !g.okHistory || !g.okRdTkt ){ login_needed(); return; }
  zUuid = PD("name","");
  zType = PD("y","a");
  if( zType[0]!='c' ){
    style_submenu_element("Check-ins", "Check-ins",
       "%s/tkttimeline?name=%T&y=ci", g.zTop, zUuid);
       "%s/tkttimeline?name=%T&amp;y=ci", g.zTop, zUuid);
  }else{
    style_submenu_element("Timeline", "Timeline",
       "%s/tkttimeline?name=%T", g.zTop, zUuid);
  }
  style_submenu_element("History", "History",
    "%s/tkthistory/%s", g.zTop, zUuid);
  style_submenu_element("Status", "Status",
774
775
776
777
778
779
780
781
782

783
784
785
786
787
788
789
777
778
779
780
781
782
783

784
785
786
787
788
789
790
791
792







-

+







      if( manifest_parse(&m, &content) && m.type==CFTYPE_TICKET ){
        @
        @ <p>Ticket change
        @ [<a href="%s(g.zTop)/artifact/%T(zChngUuid)">%s(zShort)</a>]
        @ (rid %d(rid)) by
        hyperlink_to_user(m.zUser,zDate," on");
        hyperlink_to_date(zDate, ":");
        ticket_output_change_artifact(&m);
        @ </p>
        ticket_output_change_artifact(&m);
      }
      manifest_clear(&m);
    }
  }
  db_finalize(&q);
  style_footer();
}
Changes to src/tktsetup.c.
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
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







-
+



-
-
-
-
-
-
-
+
+
+
+
+
+
+












-
+

-
+







      @ <p><font color="red"><b>ERROR: %h(zErr)</b></font></p>
    }else{
      db_set(zDbField, z, 0);
      if( xRebuild ) xRebuild();
      cgi_redirect("tktsetup");
    }
  }
  @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="POST">
  @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="post"><div>
  login_insert_csrf_secret();
  @ <p>%s(zDesc)</p>
  @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea>
  @ <blockquote>
  @ <input type="submit" name="submit" value="Apply Changes">
  @ <input type="submit" name="clear" value="Revert To Default">
  @ <input type="submit" name="setup" value="Cancel">
  @ </blockquote>
  @ </form>
  @ <hr>
  @ <blockquote><p>
  @ <input type="submit" name="submit" value="Apply Changes" />
  @ <input type="submit" name="clear" value="Revert To Default" />
  @ <input type="submit" name="setup" value="Cancel" />
  @ </p></blockquote>
  @ </div></form>
  @ <hr />
  @ <h2>Default %s(zTitle)</h2>
  @ <blockquote><pre>
  @ %h(zDfltValue)
  @ </pre></blockquote>
  style_footer();
}

/*
** WEBPAGE: tktsetup_tab
*/
void tktsetup_tab_page(void){
  static const char zDesc[] =
  @ <p>Enter a valid CREATE TABLE statement for the "ticket" table.  The
  @ Enter a valid CREATE TABLE statement for the "ticket" table.  The
  @ table must contain columns named "tkt_id", "tkt_uuid", and "tkt_mtime"
  @ with an unique index on "tkt_uuid" and "tkt_mtime".</p>
  @ with an unique index on "tkt_uuid" and "tkt_mtime".
  ;
  tktsetup_generic(
    "Ticket Table Schema",
    "ticket-table",
    zDefaultTicketTable,
    zDesc,
    ticket_schema_check,
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
320
321
322
323
324


325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344


345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360


361
362
363


364
365
366

367
368
369

370
371
372

373
374
375

376
377
378

379
380
381

382
383
384

385
386
387
388

389
390
391
392
393
394


395
396
397
398
399


400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417

418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437

438
439
440
441

442
443
444
445
446
447
448
449
450


451
452

453
454
455

456
457
458

459
460
461

462
463
464

465
466
467

468
469
470
471

472
473

474
475
476
477


478
479
480
481
482
483
484
485
486
487
488
489

490
491
492
493
494




495
496
497

498
499

500
501

502
503

504
505
506
507
508
509

510
511

512
513
514
515

516
517

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
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
320
321
322


323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342


343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358


359
360



361
362
363
364

365
366
367

368
369
370

371
372
373

374
375
376

377
378
379

380
381
382

383
384
385
386

387
388
389
390
391


392
393
394
395
396


397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415

416

417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434

435
436
437
438

439
440
441
442
443
444
445
446


447
448
449

450
451
452

453
454
455

456
457
458

459
460
461

462
463
464

465
466
467
468

469
470

471
472
473


474
475
476
477
478
479
480
481
482
483
484
485
486

487
488




489
490
491
492
493
494

495
496

497
498

499
500

501
502
503
504
505
506

507
508

509
510
511
512

513
514

515
516
517
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







-
-
+
+



















-
+



-
-
+
+




-
+






-
-
+
+





-
+







-
-
+
+

-
-
+
+








-
+


-
-
+
+




-
+

-
+




-
-
+
+





-
-
+
+


















-
-
+
+














-
-
+
+
-
-
-
+
+


-
+


-
+


-
+


-
+


-
+


-
+


-
+



-
+




-
-
+
+



-
-
+
+

















-
+
-


















-
+



-
+







-
-
+
+

-
+


-
+


-
+


-
+


-
+


-
+



-
+

-
+


-
-
+
+











-
+

-
-
-
-
+
+
+
+


-
+

-
+

-
+

-
+





-
+

-
+



-
+

-
+




-
-
+
+
















-
+
-







}

/*
** WEBPAGE: tktsetup_com
*/
void tktsetup_com_page(void){
  static const char zDesc[] =
  @ <p>Enter TH1 script that initializes variables prior to generating
  @ any of the ticket view, edit, or creation pages.</p>
  @ Enter TH1 script that initializes variables prior to generating
  @ any of the ticket view, edit, or creation pages.
  ;
  tktsetup_generic(
    "Ticket Common Script",
    "ticket-common",
    zDefaultTicketCommon,
    zDesc,
    0,
    0,
    30
  );
}

static const char zDefaultNew[] =
@ <th1>
@   if {[info exists submit]} {
@      set status Open
@      submit_ticket
@   }
@ </th1>
@ <h1 align="center">Enter A New Ticket</h1>
@ <h1 style="text-align: center;">Enter A New Ticket</h1>
@ <table cellpadding="5">
@ <tr>
@ <td colspan="2">
@ Enter a one-line summary of the ticket:<br>
@ <input type="text" name="title" size="60" value="$<title>">
@ Enter a one-line summary of the ticket:<br />
@ <input type="text" name="title" size="60" value="$<title>" />
@ </td>
@ </tr>
@ 
@ <tr>
@ <td align="right">Type:
@ <td style="text-align: center;">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 style="text-align: center;">Version: 
@ <input type="text" name="foundin" size="20" value="$<foundin>" />
@ </td>
@ <td>In what version or build number do you observe the problem?</td>
@ </tr>
@ 
@ <tr>
@ <td align="right">Severity:
@ <td style="text-align: center;">Severity:
@ <th1>combobox severity $severity_choices 1</th1>
@ </td>
@ <td>How debilitating is the problem?  How badly does the problem
@ affect the operation of the product?</td>
@ </tr>
@ 
@ <tr>
@ <td align="right">EMail:
@ <input type="text" name="private_contact" value="$<private_contact>" size="30">
@ <td style="text-align: center;">EMail:
@ <input type="text" name="private_contact" value="$<private_contact>" size="30" />
@ </td>
@ <td><u>Not publicly visible</u>. Used by developers to contact you with
@ questions.</td>
@ <td><span style="text-decoration: underline;">Not publicly visible</span>.
@ 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>
@ <br />
@ <th1>set nline [linecount $comment 50 10]</th1>
@ <textarea name="comment" cols="80" rows="$nline"
@  wrap="virtual" class="wikiedit">$<comment></textarea><br>
@ <input type="submit" name="preview" value="Preview">
@  wrap="virtual" class="wikiedit">$<comment></textarea><br />
@ <input type="submit" name="preview" value="Preview" /></td>
@ </tr>
@
@ <th1>enable_output [info exists preview]</th1>
@ <tr><td colspan="2">
@ Description Preview:<br><hr>
@ Description Preview:<br /><hr />
@ <th1>wiki $comment</th1>
@ <hr>
@ <hr />
@ </td></tr>
@ <th1>enable_output 1</th1>
@ 
@ <tr>
@ <td align="right">
@ <input type="submit" name="submit" value="Submit">
@ <td style="text-align: center;">
@ <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>
@ <tr>
@ <td align="right">
@ <input type="submit" name="cancel" value="Cancel">
@ <td style="text-align: center;">
@ <input type="submit" name="cancel" value="Cancel" />
@ </td>
@ <td>Abandon and forget this ticket</td>
@ </tr>
@ </table>
;

/*
** Return the code used to generate the new ticket page
*/
const char *ticket_newpage_code(void){
  return db_get("ticket-newpage", (char*)zDefaultNew);
}

/*
** WEBPAGE: tktsetup_newpage
*/
void tktsetup_newpage_page(void){
  static const char zDesc[] =
  @ <p>Enter HTML with embedded TH1 script that will render the "new ticket"
  @ page</p>
  @ Enter HTML with embedded TH1 script that will render the "new ticket"
  @ page
  ;
  tktsetup_generic(
    "HTML For New Tickets",
    "ticket-newpage",
    zDefaultNew,
    zDesc,
    0,
    0,
    40
  );
}

static const char zDefaultView[] =
@ <table cellpadding="5">
@ <tr><td align="right">Ticket&nbsp;UUID:</td><td bgcolor="#d0d0d0" colspan="3">
@ $<tkt_uuid>
@ <tr><td class="tktDspLabel">Ticket&nbsp;UUID:</td>
@ <td class="tktDspValue" colspan="3">$<tkt_uuid></td></tr>
@ </td></tr>
@ <tr><td align="right">Title:</td>
@ <td bgcolor="#d0d0d0" colspan="3" valign="top">
@ <tr><td class="tktDspLabel">Title:</td>
@ <td class="tktDspValue" colspan="3">
@ <th1>wiki $title</th1>
@ </td></tr>
@ <tr><td align="right">Status:</td><td bgcolor="#d0d0d0">
@ <tr><td class="tktDspLabel">Status:</td><td class="tktDspValue">
@ $<status>
@ </td>
@ <td align="right">Type:</td><td bgcolor="#d0d0d0">
@ <td class="tktDspLabel">Type:</td><td class="tktDspValue">
@ $<type>
@ </td></tr>
@ <tr><td align="right">Severity:</td><td bgcolor="#d0d0d0">
@ <tr><td class="tktDspLabel">Severity:</td><td class="tktDspValue">
@ $<severity>
@ </td>
@ <td align="right">Priority:</td><td bgcolor="#d0d0d0">
@ <td class="tktDspLabel">Priority:</td><td class="tktDspValue">
@ $<priority>
@ </td></tr>
@ <tr><td align="right">Subsystem:</td><td bgcolor="#d0d0d0">
@ <tr><td class="tktDspLabel">Subsystem:</td><td class="tktDspValue">
@ $<subsystem>
@ </td>
@ <td align="right">Resolution:</td><td bgcolor="#d0d0d0">
@ <td class="tktDspLabel">Resolution:</td><td class="tktDspValue">
@ $<resolution>
@ </td></tr>
@ <tr><td align="right">Last&nbsp;Modified:</td><td bgcolor="#d0d0d0">
@ <tr><td class="tktDspLabel">Last&nbsp;Modified:</td><td class="tktDspValue">
@ $<tkt_datetime>
@ </td>
@ <th1>enable_output [hascap e]</th1>
@   <td align="right">Contact:</td><td bgcolor="#d0d0d0">
@   <td class="tktDspLabel">Contact:</td><td class="tktDspValue">
@   $<private_contact>
@   </td>
@ <th1>enable_output 1</th1>
@ </tr>
@ <tr><td align="right">Version&nbsp;Found&nbsp;In:</td>
@ <td colspan="3" valign="top" bgcolor="#d0d0d0">
@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td>
@ <td colspan="3" valign="top" class="tktDspValue">
@ $<foundin>
@ </td></tr>
@ <tr><td>Description &amp; Comments:</td></tr>
@ <tr><td colspan="4" bgcolor="#d0d0d0">
@ <span  bgcolor="#d0d0d0"><th1>wiki $comment</th1></span>
@ <tr><td colspan="4" class="tktDspValue">
@ <th1>wiki $comment</th1>
@ </td></tr>
@ </table>
;


/*
** Return the code used to generate the view ticket page
*/
const char *ticket_viewpage_code(void){
  return db_get("ticket-viewpage", (char*)zDefaultView);
}

/*
** WEBPAGE: tktsetup_viewpage
*/
void tktsetup_viewpage_page(void){
  static const char zDesc[] =
  @ <p>Enter HTML with embedded TH1 script that will render the "view ticket"
  @ Enter HTML with embedded TH1 script that will render the "view ticket" page
  @ page</p>
  ;
  tktsetup_generic(
    "HTML For Viewing Tickets",
    "ticket-viewpage",
    zDefaultView,
    zDesc,
    0,
    0,
    40
  );
}

static const char zDefaultEdit[] =
@ <th1>
@   if {![info exists username]} {set username $login}
@   if {[info exists submit]} {
@     if {[info exists cmappnd]} {
@       if {[string length $cmappnd]>0} {
@         set ctxt "\n\n<hr><i>[htmlize $login]"
@         set ctxt "\n\n<hr /><i>[htmlize $login]"
@         if {$username ne $login} {
@           set ctxt "$ctxt claiming to be [htmlize $username]"
@         }
@         set ctxt "$ctxt added on [date]:</i><br>\n$cmappnd"
@         set ctxt "$ctxt added on [date]:</i><br />\n$cmappnd"
@         append_field comment $ctxt
@       }
@     }
@     submit_ticket
@   }
@ </th1>
@ <table cellpadding="5">
@ <tr><td align="right">Title:</td><td>
@ <input type="text" name="title" value="$<title>" size="60">
@ <tr><td class="tktDspLabel">Title:</td><td>
@ <input type="text" name="title" value="$<title>" size="60" />
@ </td></tr>
@ <tr><td align="right">Status:</td><td>
@ <tr><td class="tktDspLabel">Status:</td><td>
@ <th1>combobox status $status_choices 1</th1>
@ </td></tr>
@ <tr><td align="right">Type:</td><td>
@ <tr><td class="tktDspLabel">Type:</td><td>
@ <th1>combobox type $type_choices 1</th1>
@ </td></tr>
@ <tr><td align="right">Severity:</td><td>
@ <tr><td class="tktDspLabel">Severity:</td><td>
@ <th1>combobox severity $severity_choices 1</th1>
@ </td></tr>
@ <tr><td align="right">Priority:</td><td>
@ <tr><td class="tktDspLabel">Priority:</td><td>
@ <th1>combobox priority $priority_choices 1</th1>
@ </td></tr>
@ <tr><td align="right">Resolution:</td><td>
@ <tr><td class="tktDspLabel">Resolution:</td><td>
@ <th1>combobox resolution $resolution_choices 1</th1>
@ </td></tr>
@ <tr><td align="right">Subsystem:</td><td>
@ <tr><td class="tktDspLabel">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>
@   <tr><td class="tktDspLabel">Contact:</td><td>
@   <input type="text" name="private_contact" size="40"
@    value="$<private_contact>">
@    value="$<private_contact>" />
@   </td></tr>
@ <th1>enable_output 1</th1>
@ <tr><td align="right">Version&nbsp;Found&nbsp;In:</td><td>
@ <input type="text" name="foundin" size="50" value="$<foundin>">
@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td><td>
@ <input type="text" name="foundin" size="50" value="$<foundin>" />
@ </td></tr>
@ <tr><td colspan="2">
@ <th1>
@   if {![info exists eall]} {set eall 0}
@   if {[info exists aonlybtn]} {set eall 0}
@   if {[info exists eallbtn]} {set eall 1}
@   if {![hascap w]} {set eall 0}
@   if {![info exists cmappnd]} {set cmappnd {}}
@   set nline [linecount $comment 15 10]
@   enable_output $eall
@ </th1>
@   Description And Comments:<br>
@   Description And Comments:<br />
@   <textarea name="comment" cols="80" rows="$nline"
@    wrap="virtual" class="wikiedit">$<comment></textarea><br>
@   <input type="hidden" name="eall" value="1">
@   <input type="submit" name="aonlybtn" value="Append Remark">
@   <input type="submit" name="preview1btn" value="Preview">
@    wrap="virtual" class="wikiedit">$<comment></textarea><br />
@   <input type="hidden" name="eall" value="1" />
@   <input type="submit" name="aonlybtn" value="Append Remark" />
@   <input type="submit" name="preview1btn" value="Preview" />
@ <th1>enable_output [expr {!$eall}]</th1>
@   Append Remark from 
@   <input type="text" name="username" value="$<username>" size="30">:<br>
@   <input type="text" name="username" value="$<username>" size="30" />:<br />
@   <textarea name="cmappnd" cols="80" rows="15"
@    wrap="virtual" class="wikiedit">$<cmappnd></textarea><br>
@    wrap="virtual" class="wikiedit">$<cmappnd></textarea><br />
@ <th1>enable_output [expr {[hascap w] && !$eall}]</th1>
@   <input type="submit" name="eallbtn" value="Edit All">
@   <input type="submit" name="eallbtn" value="Edit All" />
@ <th1>enable_output [expr {!$eall}]</th1>
@   <input type="submit" name="preview2btn" value="Preview">
@   <input type="submit" name="preview2btn" value="Preview" />
@ <th1>enable_output 1</th1>
@ </td></tr>
@
@ <th1>enable_output [info exists preview1btn]</th1>
@ <tr><td colspan="2">
@ Description Preview:<br><hr>
@ Description Preview:<br /><hr />
@ <th1>wiki $comment</th1>
@ <hr>
@ <hr />
@ </td></tr>
@ <th1>enable_output [info exists preview2btn]</th1>
@ <tr><td colspan="2">
@ Description Preview:<br><hr>
@ Description Preview:<br /><hr />
@ <th1>wiki $cmappnd</th1>
@ <hr>
@ <hr />
@ </td></tr>
@ <th1>enable_output 1</th1>
@
@ <tr><td align="right"></td><td>
@ <input type="submit" name="submit" value="Submit Changes">
@ <input type="submit" name="cancel" value="Cancel">
@ <input type="submit" name="submit" value="Submit Changes" />
@ <input type="submit" name="cancel" value="Cancel" />
@ </td></tr>
@ </table>
;

/*
** Return the code used to generate the edit ticket page
*/
const char *ticket_editpage_code(void){
  return db_get("ticket-editpage", (char*)zDefaultEdit);
}

/*
** WEBPAGE: tktsetup_editpage
*/
void tktsetup_editpage_page(void){
  static const char zDesc[] =
  @ <p>Enter HTML with embedded TH1 script that will render the "edit ticket"
  @ Enter HTML with embedded TH1 script that will render the "edit ticket" page
  @ page</p>
  ;
  tktsetup_generic(
    "HTML For Editing Tickets",
    "ticket-editpage",
    zDefaultEdit,
    zDesc,
    0,
583
584
585
586
587
588
589
590

591
592
593
594
595
596
597
598
580
581
582
583
584
585
586

587

588
589
590
591
592
593
594







-
+
-







}

/*
** WEBPAGE: tktsetup_reportlist
*/
void tktsetup_reportlist(void){
  static const char zDesc[] =
  @ <p>Enter HTML with embedded TH1 script that will render the "report list"
  @ Enter HTML with embedded TH1 script that will render the "report list" page
  @ page</p>
  ;
  tktsetup_generic(
    "HTML For Report List",
    "ticket-reportlist",
    zDefaultReportList,
    zDesc,
    0,
631
632
633
634
635
636
637
638

639
640

641
642
643
644
645
646
647
627
628
629
630
631
632
633

634
635

636
637
638
639
640
641
642
643







-
+

-
+







}

/*
** WEBPAGE: tktsetup_rpttplt
*/
void tktsetup_rpttplt_page(void){
  static const char zDesc[] =
  @ <p>Enter the default ticket report format template.  This is the
  @ Enter the default ticket report format template.  This is the
  @ the template report format that initially appears when creating a
  @ new ticket summary report.</p>
  @ new ticket summary report.
  ;
  tktsetup_generic(
    "Default Report Template",
    "ticket-report-template",
    zDefaultReport,
    zDesc,
    0,
672
673
674
675
676
677
678
679

680
681

682
683
684
685
686
687
688
668
669
670
671
672
673
674

675
676

677
678
679
680
681
682
683
684







-
+

-
+







}

/*
** WEBPAGE: tktsetup_keytplt
*/
void tktsetup_keytplt_page(void){
  static const char zDesc[] =
  @ <p>Enter the default ticket report color-key template.  This is the
  @ Enter the default ticket report color-key template.  This is the
  @ the color-key that initially appears when creating a
  @ new ticket summary report.</p>
  @ new ticket summary report.
  ;
  tktsetup_generic(
    "Default Report Color-Key Template",
    "ticket-key-template",
    zDefaultKey,
    zDesc,
    0,
701
702
703
704
705
706
707
708

709
710
711

712
713
714
715
716

717
718
719
720
721

722
723
724
725
726
727

728
729
730


731
732

733
734
735
736
697
698
699
700
701
702
703

704
705
706

707
708
709
710
711

712
713
714
715
716

717
718
719
720
721
722

723
724


725
726
727

728
729
730
731
732







-
+


-
+




-
+




-
+





-
+

-
-
+
+

-
+




  }

  if( P("setup") ){
    cgi_redirect("tktsetup");
  }
  style_header("Ticket Display On Timelines");
  db_begin_transaction();
  @ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="POST">
  @ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="post"><div>
  login_insert_csrf_secret();

  @ <hr>
  @ <hr />
  entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title");
  @ <p>An SQL expression in a query against the TICKET table that will
  @ return the title of the ticket for display purposes.</p>

  @ <hr>
  @ <hr />
  entry_attribute("Ticket Status", 40, "ticket-status-column", "s", "status");
  @ <p>The name of the column in the TICKET table that contains the ticket
  @ status in human-readable form.  Case sensitive.</p>

  @ <hr>
  @ <hr />
  entry_attribute("Ticket Closed", 40, "ticket-closed-expr", "c",
                  "status='Closed'");
  @ <p>An SQL expression that evaluates to true in a TICKET table query if
  @ the ticket is closed.</p>

  @ <hr>
  @ <hr />
  @ <p>
  @ <input type="submit"  name="submit" value="Apply Changes">
  @ <input type="submit" name="setup" value="Cancel">
  @ <input type="submit"  name="submit" value="Apply Changes" />
  @ <input type="submit" name="setup" value="Cancel" />
  @ </p>
  @ </form>
  @ </div></form>
  db_end_transaction(0);
  style_footer();
  
}
Changes to src/translate.c.
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
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







-
-
-
-
-
+
+
+
+
+
+














-
-
+
+
+

+
-
+







  }
}

/*
** Translate the input stream into the output stream
*/
static void trans(FILE *in, FILE *out){
  int i, j, k;        /* Loop counters */
  char c1, c2;        /* Characters used to start a comment */
  int lastWasEq = 0;  /* True if last non-whitespace character was "=" */
  char zLine[2000];   /* A single line of input */
  char zOut[4000];    /* The input line translated into appropriate output */
  int i, j, k;          /* Loop counters */
  char c1, c2;          /* Characters used to start a comment */
  int lastWasEq = 0;    /* True if last non-whitespace character was "=" */
  int lastWasComma = 0; /* True if last non-whitespace character was "," */
  char zLine[2000];     /* A single line of input */
  char zOut[4000];      /* The input line translated into appropriate output */

  c1 = c2 = '-';
  while( fgets(zLine, sizeof(zLine), in) ){
    for(i=0; zLine[i] && isspace(zLine[i]); i++){}
    if( zLine[i]!='@' ){
      if( inPrint || inStr ) end_block(out);
      fprintf(out,"%s",zLine);
                       /* 0123456789 12345 */
      if( strncmp(zLine, "/* @-comment: ", 14)==0 ){
        c1 = zLine[14];
        c2 = zLine[15];
      }
      i += strlen(&zLine[i]);
      while( i>0 && isspace(zLine[i-1]) ){ i--; }
      lastWasEq = i>0 && zLine[i-1]=='=';
    }else if( lastWasEq ){
      lastWasEq    = i>0 && zLine[i-1]=='=';
      lastWasComma = i>0 && zLine[i-1]==',';
    }else if( lastWasEq || lastWasComma){
      /* If the last non-whitespace character before the first @ was
      ** an "="(var init/set) or a ","(const definition in list) then
      ** an "=" then generate a string literal.  But skip comments
      ** generate a string literal.  But skip comments
      ** consisting of all text between c1 and c2 (default "--")
      ** and end of line.
      */
      int indent, omitline;
      i++;
      if( isspace(zLine[i]) ){ i++; }
      indent = i - 2;
Changes to src/update.c.
109
110
111
112
113
114
115

116
117
118
119
120
121
122
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123







+







      fossil_fatal("Multiple descendants");
    }
    tid = db_int(0, "SELECT rid FROM leaves, event"
                    " WHERE event.objid=leaves.rid"
                    " ORDER BY event.mtime DESC"); 
  }

  if( tid==vid ) return;  /* Nothing to update */
  db_begin_transaction();
  vfile_check_signature(vid, 1);
  if( !nochangeFlag ) undo_begin();
  load_vfile_from_rid(tid);

  /*
  ** The record.fn field is used to match files against each other.  The
Changes to src/url.c.
349
350
351
352
353
354
355
356

357
358
359
360
361
362
363
349
350
351
352
353
354
355

356
357
358
359
360
361
362
363







-
+







    }
    if( zName2 && strcmp(zName2,p->azName[i])==0 ){
      zName2 = 0;
      z = zValue2;
      if( z==0 ) continue;
    }
    blob_appendf(&p->url, "%s%s=%T", zSep, p->azName[i], z);
    zSep = "&";
    zSep = "&amp;";
  }
  if( zName1 && zValue1 ){
    blob_appendf(&p->url, "%s%s=%T", zSep, zName1, zValue1);
  }
  if( zName2 && zValue2 ){
    blob_appendf(&p->url, "%s%s=%T", zSep, zName2, zValue2);
  }
Changes to src/wiki.c.
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
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







-
+

-
-
-
+
+
+










-
-
+
+







}

/*
** Output rules for well-formed wiki pages
*/
static void well_formed_wiki_name_rules(void){
  @ <ul>
  @ <li> Must not begin or end with a space.
  @ <li> Must not begin or end with a space.</li>
  @ <li> Must not contain any control characters, including tab or
  @      newline.
  @ <li> Must not have two or more spaces in a row internally.
  @ <li> Must be between 3 and 100 characters in length.
  @      newline.</li>
  @ <li> Must not have two or more spaces in a row internally.</li>
  @ <li> Must be between 3 and 100 characters in length.</li>
  @ </ul>
}

/*
** Check a wiki name.  If it is not well-formed, then issue an error
** and return true.  If it is well-formed, return false.
*/
static int check_name(const char *z){
  if( !wiki_name_is_wellformed((const unsigned char *)z) ){
    style_header("Wiki Page Name Error");
    @ The wiki name "<b>%h(z)</b>" is not well-formed.  Rules for
    @ wiki page names:
    @ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed.
    @ Rules for wiki page names:
    well_formed_wiki_name_rules();
    style_footer();
    return 1;
  }
  return 0;
}

153
154
155
156
157
158
159
160
161
162
163




164
165
166
167
168
169
170
153
154
155
156
157
158
159




160
161
162
163
164
165
166
167
168
169
170







-
-
-
-
+
+
+
+







    @ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a>
    @      to experiment.</li>
    if( g.okNewWiki ){
      @ <li>  Create a <a href="%s(g.zBaseURL)/wikinew">new wiki page</a>.</li>
    }
    @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
    @      available on this server.</li>
	@ <li> <form method="GET" action="%s(g.zBaseURL)/wfind">
	@     Search wiki titles: <input type="text" name="title"/>
        @  &nbsp; <input type="submit" />
	@ </li>
    @ <li> <form method="get" action="%s(g.zBaseURL)/wfind"><div>
    @     Search wiki titles: <input type="text" name="title"/>
    @  &nbsp; <input type="submit" /></div></form>
    @ </li>
    @ </ul>
    style_footer();
    return;
  }
  if( check_name(zPageName) ) return;
  isSandbox = is_sandbox(zPageName);
  if( isSandbox ){
192
193
194
195
196
197
198
199

200
201
202
203
204
205
206
192
193
194
195
196
197
198

199
200
201
202
203
204
205
206







-
+







  if( !g.isHome ){
    if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
      style_submenu_element("Edit", "Edit Wiki Page", "%s/wikiedit?name=%T",
           g.zTop, zPageName);
    }
    if( rid && g.okApndWiki && g.okAttach ){
      style_submenu_element("Attach", "Add An Attachment",
           "%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
           "%s/attachadd?page=%T&amp;from=%s/wiki%%3fname=%T",
           g.zTop, zPageName, g.zTop, zPageName);
    }
    if( rid && g.okApndWiki ){
      style_submenu_element("Append", "Add A Comment", "%s/wikiappend?name=%T",
           g.zTop, zPageName);
    }
    if( g.okHistory ){
225
226
227
228
229
230
231

232

233
234
235
236
237
238
239
240

241
242
243
244
245
246
247
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
240

241
242
243
244
245
246
247
248







+
-
+







-
+







    const char *zUser = db_column_text(&q, 2);
    if( cnt==0 ){
      @ <hr><h2>Attachments:</h2>
      @ <ul>
    }
    cnt++;
    if( g.okHistory && g.okRead ){
      @ <li>
      @ <li><a href="%s(g.zTop)/attachview?page=%s(zPageName)&file=%t(zFile)">
      @ <a href="%s(g.zTop)/attachview?page=%s(zPageName)&amp;file=%t(zFile)">
      @ %h(zFile)</a>
    }else{
      @ <li>%h(zFile)
    }
    @ added by %h(zUser) on
    hyperlink_to_date(zDate, ".");
    if( g.okWrWiki && g.okAttach ){
      @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&file=%t(zFile)&from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
      @ [<a href="%s(g.zTop)/attachdelete?page=%s(zPageName)&amp;file=%t(zFile)&amp;from=%s(g.zTop)/wiki%%3fname=%s(zPageName)">delete</a>]
    }
  }
  if( cnt ){
    @ </ul>
  }
  db_finalize(&q);
 
349
350
351
352
353
354
355
356

357
358

359
360
361
362
363
364
365
366

367
368

369
370
371
372
373
374
375





376
377
378
379
380
381
382
350
351
352
353
354
355
356

357
358

359
360
361
362
363
364
365
366

367
368

369
370
371





372
373
374
375
376
377
378
379
380
381
382
383







-
+

-
+







-
+

-
+


-
-
-
-
-
+
+
+
+
+







    zBody = mprintf("<i>Empty Page</i>");
  }
  zHtmlPageName = mprintf("Edit: %s", zPageName);
  style_header(zHtmlPageName);
  if( P("preview")!=0 ){
    blob_zero(&wiki);
    blob_append(&wiki, zBody, -1);
    @ Preview:<hr>
    @ Preview:<hr />
    wiki_convert(&wiki, 0, 0);
    @ <hr>
    @ <hr />
    blob_reset(&wiki);
  }
  for(n=2, z=zBody; z[0]; z++){
    if( z[0]=='\n' ) n++;
  }
  if( n<20 ) n = 20;
  if( n>40 ) n = 40;
  @ <form method="POST" action="%s(g.zBaseURL)/wikiedit">
  @ <form method="post" action="%s(g.zBaseURL)/wikiedit"><div>
  login_insert_csrf_secret();
  @ <input type="hidden" name="name" value="%h(zPageName)">
  @ <input type="hidden" name="name" value="%h(zPageName)" />
  @ <textarea name="w" class="wikiedit" cols="80" 
  @  rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
  @ <br>
  @ <input type="submit" name="preview" value="Preview Your Changes">
  @ <input type="submit" name="submit" value="Apply These Changes">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>
  @ <br />
  @ <input type="submit" name="preview" value="Preview Your Changes" />
  @ <input type="submit" name="submit" value="Apply These Changes" />
  @ <input type="submit" name="cancel" value="Cancel" />
  @ </div></form>
  if( !isSandbox ){
    manifest_clear(&m);
  }
  style_footer();
}

/*
394
395
396
397
398
399
400
401

402
403
404

405
406
407


408
409
410
411


412
413
414
415
416
417
418
395
396
397
398
399
400
401

402
403


404
405


406
407
408
409


410
411
412
413
414
415
416
417
418







-
+

-
-
+

-
-
+
+


-
-
+
+







    return;
  }  
  zName = PD("name","");
  if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){
    cgi_redirectf("wikiedit?name=%T", zName);
  }
  style_header("Create A New Wiki Page");
  @ <p>Rules for wiki page names:
  @ <p>Rules for wiki page names:</p>
  well_formed_wiki_name_rules();
  @ </p>
  @ <form method="POST" action="%s(g.zBaseURL)/wikinew">
  @ <form method="post" action="%s(g.zBaseURL)/wikinew">
  @ <p>Name of new wiki page:
  @ <input type="text" width="35" name="name" value="%h(zName)">
  @ <input type="submit" value="Create">
  @ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
  @ <input type="submit" value="Create" />
  @ </p></form>
  if( zName[0] ){
    @ <p><b><font color="red">
    @ "%h(zName)" is not a valid wiki page name!</font></b></p>
    @ <p><span class="wikiError">
    @ "%h(zName)" is not a valid wiki page name!</span></p>
  }
  style_footer();
}


/*
** Append the wiki text for an remark to the end of the given BLOB.
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
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







-
-
+
+


-
+


















-
+







    blob_reset(&preview);
  }
  zUser = PD("u", g.zLogin);
  @ <form method="POST" action="%s(g.zBaseURL)/wikiappend">
  login_insert_csrf_secret();
  @ <input type="hidden" name="name" value="%h(zPageName)">
  @ Your Name:
  @ <input type="text" name="u" size="20" value="%h(zUser)"><br>
  @ Comment to append:<br>
  @ <input type="text" name="u" size="20" value="%h(zUser)"><br />
  @ Comment to append:<br />
  @ <textarea name="r" class="wikiedit" cols="80" 
  @  rows="10" wrap="virtual">%h(PD("r",""))</textarea>
  @ <br>
  @ <br />
  @ <input type="submit" name="preview" value="Preview Your Comment">
  @ <input type="submit" name="submit" value="Append Your Changes">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>
  style_footer();
}

/*
** Name of the wiki history page being generated
*/
static const char *zWikiPageName;

/*
** Function called to output extra text at the end of each line in
** a wiki history listing.
*/
static void wiki_history_extra(int rid){
  if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d", rid) ){
    @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&a=%d(rid)">[diff]</a>
    @ <a href="%s(g.zTop)/wdiff?name=%t(zWikiPageName)&amp;a=%d(rid)">[diff]</a>
  }
}

/*
** WEBPAGE: whistory
** URL: /whistory?name=PAGENAME
**
738
739
740
741
742
743
744
745

746
747
748
749


750
751
752
753
754


755
756
757
758
759


760
761
762
763
764


765
766
767
768
769
770
771
772
773


774
775
776
777
778
779
780
738
739
740
741
742
743
744

745
746
747


748
749
750
751
752


753
754
755
756
757


758
759
760
761
762


763
764
765
766
767
768
769
770
771


772
773
774
775
776
777
778
779
780







-
+


-
-
+
+



-
-
+
+



-
-
+
+



-
-
+
+







-
-
+
+







  @ <li>Most ordinary HTML works.</li>
  @ <li>&lt;verbatim&gt; and &lt;nowiki&gt;.</li>
  @ </ol>
  @ <p>We call the first five rules above "wiki" formatting rules.  The
  @ last two rules are the HTML formatting rule.</p>
  @ <h2>Formatting Rule Details</h2>
  @ <ol>
  @ <li> <p><b>Paragraphs</b>.  Any sequence of one or more blank lines forms
  @ <li> <p><span class="wikiruleHead">Paragraphs</span>.  Any sequence of one or more blank lines forms
  @ a paragraph break.  Centered or right-justified paragraphs are not
  @ supported by wiki markup, but you can do these things if you need them
  @ using HTML.</p>
  @ <li> <p><b>Bullet Lists</b>.
  @ using HTML.</p></li>
  @ <li> <p><span class="wikiruleHead">Bullet Lists</span>.
  @ A bullet list item is a line that begins with a single "*" character
  @ surrounded on
  @ both sides by two or more spaces or by a tab.  Only a single level
  @ of bullet list is supported by wiki.  For nested lists, use HTML.</p>
  @ <li> <p><b>Enumeration Lists</b>.
  @ of bullet list is supported by wiki.  For nested lists, use HTML.</p></li>
  @ <li> <p><span class="wikiruleHead">Enumeration Lists</span>.
  @ An enumeration list item is a line that begins with a single "#" character
  @ surrounded on both sides by two or more spaces or by a tab.  Only a single
  @ level of enumeration list is supported by wiki.  For nested lists or for
  @ enumerations that count using letters or roman numerials, use HTML.</p>
  @ <li> <p><b>Indented Paragraphs</b>.
  @ enumerations that count using letters or roman numerials, use HTML.</p></li>
  @ <li> <p><span class="wikiruleHead">Indented Paragraphs</span>.
  @ Any paragraph that begins with two or more spaces or a tab and
  @ which is not a bullet or enumeration list item is rendered 
  @ indented.  Only a single level of indentation is supported by wiki; use
  @ HTML for deeper indentation.</p>
  @ <li> <p><b>Hyperlinks</b>.
  @ HTML for deeper indentation.</p></li>
  @ <li> <p><span class="wikiruleHead">Hyperlinks</span>.
  @ Text within square brackets ("[...]") becomes a hyperlink.  The
  @ target can be a wiki page name, the artifact ID of a check-in or ticket,
  @ the name of an image, or a URL.  By default, the target is displayed
  @ as the text of the hyperlink.  But you can specify alternative text
  @ after the target name separated by a "|" character.</p>
  @ <p>You can also link to internal anchor names using [#anchor-name], providing
  @ you have added the necessary "&lt;a name="anchor-name"&gt;&lt;/a&gt;"
  @ tag to your wiki page.</p>
  @ <li> <p><b>HTML</b>.
  @ tag to your wiki page.</p></li>
  @ <li> <p><span class="wikiruleHead">HTML</span>.
  @ The following standard HTML elements may be used:
  @ &lt;a&gt;
  @ &lt;address&gt;
  @ &lt;b&gt;
  @ &lt;big&gt;
  @ &lt;blockquote&gt;
  @ &lt;br&gt;
820
821
822
823
824
825
826
827

828
829
830
831
832

833
834
835
836
837
838
839
820
821
822
823
824
825
826

827
828
829
830
831

832
833
834
835
836
837
838
839







-
+




-
+







  @ &lt;var&gt;.
  @ In addition, there are two non-standard elements available:
  @ &lt;verbatim&gt; and &lt;nowiki&gt;.
  @ No other elements are allowed.  All attributes are checked and
  @ only a few benign attributes are allowed on each element.
  @ In particular, any attributes that specify javascript or CSS
  @ are elided.</p></li>
  @ <li><p><b>Special Markup.</b>
  @ <li><p><span class="wikiruleHead">Special Markup.</span>
  @ The &lt;nowiki&gt; tag disables all wiki formatting rules
  @ through the matching &lt;/nowiki&gt; element.
  @ The &lt;verbatim&gt; tag works like &lt;pre&gt; with the addition
  @ that it also disables all wiki and HTML markup
  @ through the matching &lt;/verbatim&gt;.
  @ through the matching &lt;/verbatim&gt;.</p></li>
  @ </ol>
  style_footer();
}

/*
** Add a new wiki page to the respository.  The page name is
** given by the zPageName parameter.  isNew must be true to create
Changes to src/wikiformat.c.
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336











337
338
339
340
341
342
343
319
320
321
322
323
324
325











326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343







-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+







  }
  return MARKUP_INVALID;
}

/*
** Token types
*/
#define TOKEN_MARKUP        1    /* <...> */
#define TOKEN_CHARACTER     2    /* "&" or "<" not part of markup */
#define TOKEN_LINK          3    /* [...] */
#define TOKEN_PARAGRAPH     4    /* blank lines */
#define TOKEN_NEWLINE       5    /* A single "\n" */
#define TOKEN_BUL_LI        6    /*  "  *  " */
#define TOKEN_NUM_LI        7    /*  "  #  " */
#define TOKEN_ENUM          8    /*  "  \(?\d+[.)]?  " */
#define TOKEN_INDENT        9    /*  "   " */
#define TOKEN_RAW           10   /* Output exactly (used when wiki-use-html==1) */
#define TOKEN_TEXT          11   /* None of the above */
#define TOKEN_MARKUP        1  /* <...> */
#define TOKEN_CHARACTER     2  /* "&" or "<" not part of markup */
#define TOKEN_LINK          3  /* [...] */
#define TOKEN_PARAGRAPH     4  /* blank lines */
#define TOKEN_NEWLINE       5  /* A single "\n" */
#define TOKEN_BUL_LI        6  /*  "  *  " */
#define TOKEN_NUM_LI        7  /*  "  #  " */
#define TOKEN_ENUM          8  /*  "  \(?\d+[.)]?  " */
#define TOKEN_INDENT        9  /*  "   " */
#define TOKEN_RAW           10 /* Output exactly (used when wiki-use-html==1) */
#define TOKEN_TEXT          11 /* None of the above */

/*
** State flags
*/
#define AT_NEWLINE          0x001  /* At start of a line */
#define AT_PARAGRAPH        0x002  /* At start of a paragraph */
#define ALLOW_WIKI          0x004  /* Allow wiki markup */
743
744
745
746
747
748
749



750
751
752
753
754
755
756
757
758
759
760



761
762
763
764
765
766
767
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773







+
+
+











+
+
+







** Render markup on the given blob.
*/
static void renderMarkup(Blob *pOut, ParsedMarkup *p){
  int i;
  if( p->endTag ){
    blob_appendf(pOut, "</%s>", aMarkup[p->iCode].zName);
  }else{
    /* Close active paragraph for several elements, which are not allowed
    ** in paragraphs in XHTML.
    */
    blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName);
    for(i=0; i<p->nAttr; i++){
      blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName);
      if( p->aAttr[i].zValue ){
        const char *zVal = p->aAttr[i].zValue;
        if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){
          blob_appendf(pOut, "=\"%s%s\"", g.zBaseURL, zVal);
        }else{
          blob_appendf(pOut, "=\"%s\"", zVal);
        }
      }
    }
    if (p->iType & MUTYPE_SINGLE){
      blob_append(pOut, " /", 2);
    }
    blob_append(pOut, ">", 1);
  }
}

/*
** When the markup was parsed, some "\000" may have been inserted.
883
884
885
886
887
888
889

890
891


892
893
894
895
896
897
898
889
890
891
892
893
894
895
896


897
898
899
900
901
902
903
904
905







+
-
-
+
+







  return p->aStack[i-1].iCode;
}

/*
** Begin a new paragraph if that something that is needed.
*/
static void startAutoParagraph(Renderer *p){
  if( p->wantAutoParagraph==0 ) return;
  if( p->wantAutoParagraph==0 || p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
  blob_appendf(p->pOut, "<p>", -1);
  if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
  blob_appendf(p->pOut, "<p type=\"auto\">", -1);
  pushStack(p, MARKUP_P);
  p->wantAutoParagraph = 0;
  p->inAutoParagraph = 1;
}

/*
** End a paragraph if we are in one.
1017
1018
1019
1020
1021
1022
1023
1024
1025



1026
1027

1028
1029
1030


1031
1032
1033
1034
1035
1036
1037
1024
1025
1026
1027
1028
1029
1030


1031
1032
1033
1034

1035
1036


1037
1038
1039
1040
1041
1042
1043
1044
1045







-
-
+
+
+

-
+

-
-
+
+







    int isClosed = 0;
    if( is_ticket(zTarget, &isClosed) ){
      /* Special display processing for tickets.  Display the hyperlink
      ** as crossed out if the ticket is closed.
      */
      if( isClosed ){
        if( g.okHistory ){
          blob_appendf(p->pOut,"<a href=\"%s/info/%s\"><s>",
              g.zBaseURL, zTarget
          blob_appendf(p->pOut,
             "<a href=\"%s/info/%s\"><span class=\"wikiTagCancelled\">",
             g.zBaseURL, zTarget
          );
          zTerm = "</s></a>";
          zTerm = "</span></a>";
        }else{
          blob_appendf(p->pOut,"<s>");
          zTerm = "</s>";
          blob_appendf(p->pOut,"<span class=\"wikiTagCancelled\">");
          zTerm = "</span>";
        }
      }else{
        if( g.okHistory ){
          blob_appendf(p->pOut,"<a href=\"%s/info/%s\">",
              g.zBaseURL, zTarget
          );
        }else{
1131
1132
1133
1134
1135
1136
1137

1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156

1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175

1176
1177
1178
1179
1180
1181
1182
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193







+



















+



















+







        if( inlineOnly ){
          blob_append(p->pOut, " &bull; ", -1);
        }else{
          if( p->wikiList!=MARKUP_UL ){
            if( p->wikiList ){
              popStackToTag(p, p->wikiList);
            }
            endAutoParagraph(p);
            pushStack(p, MARKUP_UL);
            blob_append(p->pOut, "<ul>", 4);
            p->wikiList = MARKUP_UL;
          }
          popStackToTag(p, MARKUP_LI);
          startAutoParagraph(p);
          pushStack(p, MARKUP_LI);
          blob_append(p->pOut, "<li>", 4);
        }
        break;
      }
      case TOKEN_NUM_LI: {
        if( inlineOnly ){
          blob_append(p->pOut, " # ", -1);
        }else{
          if( p->wikiList!=MARKUP_OL ){
            if( p->wikiList ){
              popStackToTag(p, p->wikiList);
            }
            endAutoParagraph(p);
            pushStack(p, MARKUP_OL);
            blob_append(p->pOut, "<ol>", 4);
            p->wikiList = MARKUP_OL;
          }
          popStackToTag(p, MARKUP_LI);
          startAutoParagraph(p);
          pushStack(p, MARKUP_LI);
          blob_append(p->pOut, "<li>", 4);
        }
        break;
      }
      case TOKEN_ENUM: {
        if( inlineOnly ){
          blob_appendf(p->pOut, " (%d) ", atoi(z));
        }else{
          if( p->wikiList!=MARKUP_OL ){
            if( p->wikiList ){
              popStackToTag(p, p->wikiList);
            }
            endAutoParagraph(p);
            pushStack(p, MARKUP_OL);
            blob_append(p->pOut, "<ol>", 4);
            p->wikiList = MARKUP_OL;
          }
          popStackToTag(p, MARKUP_LI);
          startAutoParagraph(p);
          pushStack(p, MARKUP_LI);
1231
1232
1233
1234
1235
1236
1237


1238

1239
1240
1241
1242
1243
1244
1245
1242
1243
1244
1245
1246
1247
1248
1249
1250

1251
1252
1253
1254
1255
1256
1257
1258







+
+
-
+







        p->state |= FONT_MARKUP_ONLY;
        wiki_render(p, zDisplay);
        p->state = savedState;
        blob_append(p->pOut, zClose, -1);
        break;
      }
      case TOKEN_TEXT: {
        int i;
        for(i=0; i<n && isspace(z[i]); i++){}
        startAutoParagraph(p);
        if( i<n ) startAutoParagraph(p);
        blob_append(p->pOut, z, n);
        break;
      }
      case TOKEN_RAW: {
        blob_append(p->pOut, z, n);
        break;
      }
1345
1346
1347
1348
1349
1350
1351
1352


1353

1354
1355
1356
1357

1358
1359
1360
1361
1362
1363
1364
1358
1359
1360
1361
1362
1363
1364

1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380







-
+
+

+




+







              p->zVerbatimId = markup.aAttr[0].zValue;
            }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
              blob_appendf(p->pOut, "<pre name='code' class='%s'>",
                markup.aAttr[vAttrIdx].zValue);
              vAttrDidAppend=1;
            }
          }
          if( !vAttrDidAppend )
          if( !vAttrDidAppend ) {
            endAutoParagraph(p);
            blob_append(p->pOut, "<pre class='verbatim'>",-1);
          }
          p->wantAutoParagraph = 0;
        }else
        if( markup.iType==MUTYPE_LI ){
          if( backupToType(p, MUTYPE_LIST)==0 ){
            endAutoParagraph(p);
            pushStack(p, MARKUP_UL);
            blob_append(p->pOut, "<ul>", 4);
          }
          pushStack(p, MARKUP_LI);
          renderMarkup(p->pOut, &markup);
        }else
        if( markup.iType==MUTYPE_TR ){
1382
1383
1384
1385
1386
1387
1388
1389

1390










1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404

1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423







-
+

+
+
+
+
+
+
+
+
+
+







          startAutoParagraph(p);
          renderMarkup(p->pOut, &markup);
          pushStack(p, markup.iCode);
        }else
        {
          if( markup.iType==MUTYPE_FONT ){
            startAutoParagraph(p);
          }else if( markup.iType==MUTYPE_BLOCK ){
          }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
            p->wantAutoParagraph = 0;
          }
          if(   markup.iCode==MARKUP_HR
             || markup.iCode==MARKUP_H1
             || markup.iCode==MARKUP_H2
             || markup.iCode==MARKUP_H3
             || markup.iCode==MARKUP_H4
             || markup.iCode==MARKUP_H5
             || markup.iCode==MARKUP_P
          ){
            endAutoParagraph(p);
          }
          if( (markup.iType & MUTYPE_STACK )!=0 ){
            pushStack(p, markup.iCode);
          }
          renderMarkup(p->pOut, &markup);
        }
        break;