Fossil SCM

Added a "wiki" link to the main menu. Added built-in description of wiki formatting rules. Added a wiki "homepage" that displays when the "wiki" URI is used without a pagename. Added support for a wiki sandbox that does not save to the repository.

drh 2007-10-28 23:27 trunk
Commit f08adf3d58c4afc0bc526618440f0e460278e3b1
2 files changed +3 +265 -108
--- src/style.c
+++ src/style.c
@@ -96,10 +96,13 @@
9696
@ <a href="%s(g.zBaseURL)/home">Home</a>
9797
if( g.okRead ){
9898
@ | <a href="%s(g.zBaseURL)/leaves">Leaves</a>
9999
@ | <a href="%s(g.zBaseURL)/timeline">Timeline</a>
100100
}
101
+ if( g.okRdWiki ){
102
+ @ | <a href="%s(g.zBaseURL)/wiki">Wiki</a>
103
+ }
101104
#if 0
102105
@ | <font color="#888888">Search</font>
103106
@ | <font color="#888888">Ticket</font>
104107
@ | <font color="#888888">Reports</font>
105108
#endif
106109
--- src/style.c
+++ src/style.c
@@ -96,10 +96,13 @@
96 @ <a href="%s(g.zBaseURL)/home">Home</a>
97 if( g.okRead ){
98 @ | <a href="%s(g.zBaseURL)/leaves">Leaves</a>
99 @ | <a href="%s(g.zBaseURL)/timeline">Timeline</a>
100 }
 
 
 
101 #if 0
102 @ | <font color="#888888">Search</font>
103 @ | <font color="#888888">Ticket</font>
104 @ | <font color="#888888">Reports</font>
105 #endif
106
--- src/style.c
+++ src/style.c
@@ -96,10 +96,13 @@
96 @ <a href="%s(g.zBaseURL)/home">Home</a>
97 if( g.okRead ){
98 @ | <a href="%s(g.zBaseURL)/leaves">Leaves</a>
99 @ | <a href="%s(g.zBaseURL)/timeline">Timeline</a>
100 }
101 if( g.okRdWiki ){
102 @ | <a href="%s(g.zBaseURL)/wiki">Wiki</a>
103 }
104 #if 0
105 @ | <font color="#888888">Search</font>
106 @ | <font color="#888888">Ticket</font>
107 @ | <font color="#888888">Reports</font>
108 #endif
109
+265 -108
--- src/wiki.c
+++ src/wiki.c
@@ -96,18 +96,27 @@
9696
@ and establish a "Project Name". Then create a
9797
@ wiki page with that name. The content of that wiki page
9898
@ will be displayed in place of this message.
9999
style_footer();
100100
}
101
+
102
+/*
103
+** Return true if the given pagename is the name of the sandbox
104
+*/
105
+static int is_sandbox(const char *zPagename){
106
+ return strcasecmp(zPagename,"sandbox")==0 ||
107
+ strcasecmp(zPagename,"sand box")==0;
108
+}
101109
102110
/*
103111
** WEBPAGE: wiki
104112
** URL: /wiki?name=PAGENAME
105113
*/
106114
void wiki_page(void){
107115
char *zTag;
108116
int rid;
117
+ int isSandbox;
109118
Blob wiki;
110119
Manifest m;
111120
const char *zPageName;
112121
char *zHtmlPageName;
113122
char *zBody = mprintf("%s","<i>Empty Page</i>");
@@ -114,49 +123,67 @@
114123
115124
login_check_credentials();
116125
if( !g.okRdWiki ){ login_needed(); return; }
117126
zPageName = P("name");
118127
if( zPageName==0 ){
119
- wcontent_page();
128
+ style_header("Wiki");
129
+ @ <ul>
130
+ @ <li> <a href="%s(g.zBaseURL)/timeline?y=w">Recent changes</a> to wiki
131
+ @ pages. </li>
132
+ @ <li> <a href="%s(g.zBaseURL)/wiki_rules">Formatting rules</a> for
133
+ @ wiki.</li>
134
+ @ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a>
135
+ @ to experiment.</li>
136
+ @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
137
+ @ available on this server.</li>
138
+ @ </ul>
139
+ style_footer();
120140
return;
121141
}
122142
if( check_name(zPageName) ) return;
123
- zTag = mprintf("wiki-%s", zPageName);
124
- rid = db_int(0,
125
- "SELECT rid FROM tagxref"
126
- " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
127
- " ORDER BY mtime DESC", zTag
128
- );
129
- free(zTag);
130
- memset(&m, 0, sizeof(m));
131
- blob_zero(&m.content);
132
- if( rid ){
133
- Blob content;
134
- content_get(rid, &content);
135
- manifest_parse(&m, &content);
136
- if( m.type==CFTYPE_WIKI ){
137
- zBody = m.zWiki;
138
- }
139
- }
140
- if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
143
+ isSandbox = is_sandbox(zPageName);
144
+ if( isSandbox ){
145
+ zBody = db_get("sandbox",zBody);
146
+ }else{
147
+ zTag = mprintf("wiki-%s", zPageName);
148
+ rid = db_int(0,
149
+ "SELECT rid FROM tagxref"
150
+ " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
151
+ " ORDER BY mtime DESC", zTag
152
+ );
153
+ free(zTag);
154
+ memset(&m, 0, sizeof(m));
155
+ blob_zero(&m.content);
156
+ if( rid ){
157
+ Blob content;
158
+ content_get(rid, &content);
159
+ manifest_parse(&m, &content);
160
+ if( m.type==CFTYPE_WIKI ){
161
+ zBody = m.zWiki;
162
+ }
163
+ }
164
+ }
165
+ if( isSandbox || (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
141166
style_submenu_element("Edit", "Edit Wiki Page",
142167
mprintf("%s/wikiedit?name=%T", g.zTop, zPageName));
143168
}
144
- if( rid && g.okApndWiki ){
169
+ if( isSandbox || (rid && g.okApndWiki) ){
145170
style_submenu_element("Append", "Add A Comment",
146171
mprintf("%s/wikiappend?name=%T", g.zTop, zPageName));
147172
}
148
- if( g.okHistory ){
173
+ if( !isSandbox && g.okHistory ){
149174
style_submenu_element("History", "History",
150175
mprintf("%s/whistory?name=%T", g.zTop, zPageName));
151176
}
152177
zHtmlPageName = mprintf("%h", zPageName);
153178
style_header(zHtmlPageName);
154179
blob_init(&wiki, zBody, -1);
155180
wiki_convert(&wiki, 0, 0);
156181
blob_reset(&wiki);
157
- manifest_clear(&m);
182
+ if( !isSandbox ){
183
+ manifest_clear(&m);
184
+ }
158185
style_footer();
159186
}
160187
161188
/*
162189
** WEBPAGE: wikiedit
@@ -163,10 +190,11 @@
163190
** URL: /wikiedit?name=PAGENAME
164191
*/
165192
void wikiedit_page(void){
166193
char *zTag;
167194
int rid;
195
+ int isSandbox;
168196
Blob wiki;
169197
Manifest m;
170198
const char *zPageName;
171199
char *zHtmlPageName;
172200
int n;
@@ -177,59 +205,70 @@
177205
zBody = mprintf("%s", zBody);
178206
}
179207
login_check_credentials();
180208
zPageName = PD("name","");
181209
if( check_name(zPageName) ) return;
182
- zTag = mprintf("wiki-%s", zPageName);
183
- rid = db_int(0,
184
- "SELECT rid FROM tagxref"
185
- " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
186
- " ORDER BY mtime DESC", zTag
187
- );
188
- free(zTag);
189
- if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
190
- login_needed();
191
- return;
192
- }
193
- memset(&m, 0, sizeof(m));
194
- blob_zero(&m.content);
195
- if( rid && zBody==0 ){
196
- Blob content;
197
- content_get(rid, &content);
198
- manifest_parse(&m, &content);
199
- if( m.type==CFTYPE_WIKI ){
200
- zBody = m.zWiki;
210
+ isSandbox = is_sandbox(zPageName);
211
+ if( isSandbox ){
212
+ if( zBody==0 ){
213
+ zBody = db_get("sandbox","");
214
+ }
215
+ }else{
216
+ zTag = mprintf("wiki-%s", zPageName);
217
+ rid = db_int(0,
218
+ "SELECT rid FROM tagxref"
219
+ " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
220
+ " ORDER BY mtime DESC", zTag
221
+ );
222
+ free(zTag);
223
+ if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
224
+ login_needed();
225
+ return;
226
+ }
227
+ memset(&m, 0, sizeof(m));
228
+ blob_zero(&m.content);
229
+ if( rid && zBody==0 ){
230
+ Blob content;
231
+ content_get(rid, &content);
232
+ manifest_parse(&m, &content);
233
+ if( m.type==CFTYPE_WIKI ){
234
+ zBody = m.zWiki;
235
+ }
201236
}
202237
}
203238
if( P("submit")!=0 && zBody!=0 ){
204239
char *zDate;
205240
Blob cksum;
206241
int nrid;
207242
blob_zero(&wiki);
208243
db_begin_transaction();
209
- zDate = db_text(0, "SELECT datetime('now')");
210
- zDate[10] = 'T';
211
- blob_appendf(&wiki, "D %s\n", zDate);
212
- free(zDate);
213
- blob_appendf(&wiki, "L %F\n", zPageName);
214
- if( rid ){
215
- char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
216
- blob_appendf(&wiki, "P %s\n", zUuid);
217
- free(zUuid);
218
- }
219
- if( g.zLogin ){
220
- blob_appendf(&wiki, "U %F\n", g.zLogin);
221
- }
222
- blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody);
223
- md5sum_blob(&wiki, &cksum);
224
- blob_appendf(&wiki, "Z %b\n", &cksum);
225
- blob_reset(&cksum);
226
- nrid = content_put(&wiki, 0, 0);
227
- db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
228
- manifest_crosslink(nrid, &wiki);
229
- blob_reset(&wiki);
230
- content_deltify(rid, nrid, 0);
244
+ if( isSandbox ){
245
+ db_set("sandbox",zBody,0);
246
+ }else{
247
+ zDate = db_text(0, "SELECT datetime('now')");
248
+ zDate[10] = 'T';
249
+ blob_appendf(&wiki, "D %s\n", zDate);
250
+ free(zDate);
251
+ blob_appendf(&wiki, "L %F\n", zPageName);
252
+ if( rid ){
253
+ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
254
+ blob_appendf(&wiki, "P %s\n", zUuid);
255
+ free(zUuid);
256
+ }
257
+ if( g.zLogin ){
258
+ blob_appendf(&wiki, "U %F\n", g.zLogin);
259
+ }
260
+ blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody);
261
+ md5sum_blob(&wiki, &cksum);
262
+ blob_appendf(&wiki, "Z %b\n", &cksum);
263
+ blob_reset(&cksum);
264
+ nrid = content_put(&wiki, 0, 0);
265
+ db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
266
+ manifest_crosslink(nrid, &wiki);
267
+ blob_reset(&wiki);
268
+ content_deltify(rid, nrid, 0);
269
+ }
231270
db_end_transaction(0);
232271
cgi_redirectf("wiki?name=%T", zPageName);
233272
}
234273
if( P("cancel")!=0 ){
235274
cgi_redirectf("wiki?name=%T", zPageName);
@@ -260,11 +299,13 @@
260299
@ <br>
261300
@ <input type="submit" name="preview" value="Preview Your Changes">
262301
@ <input type="submit" name="submit" value="Apply These Changes">
263302
@ <input type="submit" name="cancel" value="Cancel">
264303
@ </form>
265
- manifest_clear(&m);
304
+ if( !isSandbox ){
305
+ manifest_clear(&m);
306
+ }
266307
style_footer();
267308
}
268309
269310
/*
270311
** Append the wiki text for an remark to the end of the given BLOB.
@@ -273,44 +314,48 @@
273314
char *zDate;
274315
const char *zUser;
275316
const char *zRemark;
276317
277318
zDate = db_text(0, "SELECT datetime('now')");
278
- blob_appendf(p, "On %s UTC %h", zDate, g.zLogin);
319
+ blob_appendf(p, "\n\n<hr><i>On %s UTC %h", zDate, g.zLogin);
279320
free(zDate);
280321
zUser = PD("u",g.zLogin);
281322
if( zUser[0] && strcmp(zUser,g.zLogin) ){
282323
blob_appendf(p, " (claiming to be %h)", zUser);
283324
}
284325
zRemark = PD("r","");
285
- blob_appendf(p, " added:\n\n%s", zRemark);
326
+ blob_appendf(p, " added:</i><br />\n%s", zRemark);
286327
}
287328
288329
/*
289330
** WEBPAGE: wikiappend
290331
** URL: /wikiappend?name=PAGENAME
291332
*/
292333
void wikiappend_page(void){
293334
char *zTag;
294335
int rid;
336
+ int isSandbox;
295337
const char *zPageName;
296338
char *zHtmlPageName;
297339
const char *zUser;
298340
299341
login_check_credentials();
300342
zPageName = PD("name","");
301343
if( check_name(zPageName) ) return;
302
- zTag = mprintf("wiki-%s", zPageName);
303
- rid = db_int(0,
304
- "SELECT rid FROM tagxref"
305
- " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
306
- " ORDER BY mtime DESC", zTag
307
- );
308
- free(zTag);
309
- if( !rid ){
310
- cgi_redirect("index");
311
- return;
344
+ isSandbox = is_sandbox(zPageName);
345
+ if( !isSandbox ){
346
+ zTag = mprintf("wiki-%s", zPageName);
347
+ rid = db_int(0,
348
+ "SELECT rid FROM tagxref"
349
+ " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
350
+ " ORDER BY mtime DESC", zTag
351
+ );
352
+ free(zTag);
353
+ if( !rid ){
354
+ cgi_redirect("index");
355
+ return;
356
+ }
312357
}
313358
if( !g.okApndWiki ){
314359
login_needed();
315360
return;
316361
}
@@ -321,43 +366,49 @@
321366
Blob body;
322367
Blob content;
323368
Blob wiki;
324369
Manifest m;
325370
326
- content_get(rid, &content);
327
- manifest_parse(&m, &content);
328371
blob_zero(&body);
329
- if( m.type==CFTYPE_WIKI ){
330
- blob_appendf(&body, m.zWiki, -1);
331
- }
332
- manifest_clear(&m);
333
- blob_zero(&wiki);
334
- db_begin_transaction();
335
- zDate = db_text(0, "SELECT datetime('now')");
336
- zDate[10] = 'T';
337
- blob_appendf(&wiki, "D %s\n", zDate);
338
- blob_appendf(&wiki, "L %F\n", zPageName);
339
- if( rid ){
340
- char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
341
- blob_appendf(&wiki, "P %s\n", zUuid);
342
- free(zUuid);
343
- }
344
- if( g.zLogin ){
345
- blob_appendf(&wiki, "U %F\n", g.zLogin);
346
- }
347
- blob_appendf(&body, "\n<hr>\n");
348
- appendRemark(&body);
349
- blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body));
350
- md5sum_blob(&wiki, &cksum);
351
- blob_appendf(&wiki, "Z %b\n", &cksum);
352
- blob_reset(&cksum);
353
- nrid = content_put(&wiki, 0, 0);
354
- db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
355
- manifest_crosslink(nrid, &wiki);
356
- blob_reset(&wiki);
357
- content_deltify(rid, nrid, 0);
358
- db_end_transaction(0);
372
+ if( isSandbox ){
373
+ blob_appendf(&body, db_get("sandbox",""));
374
+ appendRemark(&body);
375
+ db_set("sandbox", blob_str(&body), 0);
376
+ }else{
377
+ content_get(rid, &content);
378
+ manifest_parse(&m, &content);
379
+ if( m.type==CFTYPE_WIKI ){
380
+ blob_appendf(&body, m.zWiki, -1);
381
+ }
382
+ manifest_clear(&m);
383
+ blob_zero(&wiki);
384
+ db_begin_transaction();
385
+ zDate = db_text(0, "SELECT datetime('now')");
386
+ zDate[10] = 'T';
387
+ blob_appendf(&wiki, "D %s\n", zDate);
388
+ blob_appendf(&wiki, "L %F\n", zPageName);
389
+ if( rid ){
390
+ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
391
+ blob_appendf(&wiki, "P %s\n", zUuid);
392
+ free(zUuid);
393
+ }
394
+ if( g.zLogin ){
395
+ blob_appendf(&wiki, "U %F\n", g.zLogin);
396
+ }
397
+ blob_appendf(&body, "\n<hr>\n");
398
+ appendRemark(&body);
399
+ blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body));
400
+ md5sum_blob(&wiki, &cksum);
401
+ blob_appendf(&wiki, "Z %b\n", &cksum);
402
+ blob_reset(&cksum);
403
+ nrid = content_put(&wiki, 0, 0);
404
+ db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
405
+ manifest_crosslink(nrid, &wiki);
406
+ blob_reset(&wiki);
407
+ content_deltify(rid, nrid, 0);
408
+ db_end_transaction(0);
409
+ }
359410
cgi_redirectf("wiki?name=%T", zPageName);
360411
}
361412
if( P("cancel")!=0 ){
362413
cgi_redirectf("wiki?name=%T", zPageName);
363414
return;
@@ -440,5 +491,111 @@
440491
}
441492
db_finalize(&q);
442493
@ </ul>
443494
style_footer();
444495
}
496
+
497
+/*
498
+** WEBPAGE: wiki_rules
499
+*/
500
+void wikirules_page(void){
501
+ style_header("Wiki Formatting Rules");
502
+ @ <h2>Formatting Rule Summary</h2>
503
+ @ <ol>
504
+ @ <li> Blank lines are paragraph breaks
505
+ @ <li> Bullet list items are a "*" at the beginning of the line.
506
+ @ <li> Enumeration list items are a number at the beginning of a line.
507
+ @ <li> Indented pargraphs begin with a tab or two spaces.
508
+ @ <li> Hyperlinks are contained with square brackets: "[target]"
509
+ @ <li> Most ordinary HTML works.
510
+ @ <li> &lt;verbatim&gt; and &lt;nowiki&gt;.
511
+ @ </ol>
512
+ @ <p>We call the first five rules above "wiki" formatting rules. The
513
+ @ last two rules are the HTML formatting rule.</p>
514
+ @ <h2>Formatting Rule Details</h2>
515
+ @ <ol>
516
+ @ <li> <p><b>Paragraphs</b>. Any sequence of one or more blank lines forms
517
+ @ a paragraph break. Centered or right-justified paragraphs are not
518
+ @ supported by wiki markup, but you can do these things if you need them
519
+ @ using HTML.</p>
520
+ @ <li> <p><b>Bullet Lists</b>.
521
+ @ A bullet list item begins with a single "*" character surrounded on
522
+ @ both sides by two or more spaces or by a tab. Only a single level
523
+ @ of bullet list is supported by wiki. For tested lists, use HTML.</p>
524
+ @ <li> <p><b>Enumeration Lists</b>.
525
+ @ An enumeration list item begins with one or more digits optionally
526
+ @ followed by a "." surrounded on both sides by two or more spaces or
527
+ @ by a tab. The number is significant and becomes the number shown
528
+ @ in the rendered enumeration item. Only a single level of enumeration
529
+ @ list is supported by wiki. For nested enumerations or for
530
+ @ enumerations that count using letters or roman numerials, use HTML.</p>
531
+ @ <li> <p><b>Indented Paragraphs</b>.
532
+ @ Any paragraph that begins with two or more spaces or a tab and
533
+ @ which is not a bullet or enumeration list item is rendered
534
+ @ indented. Only a single level of indentation is supported by</p>
535
+ @ <li> <p><b>Hyperlinks</b>.
536
+ @ Text within square brackets ("[...]") becomes a hyperlink. The
537
+ @ target can be a wiki page name, the UUID of a check-in or ticket,
538
+ @ the name of an image, or a URL. By default, the target is displayed
539
+ @ as the text of the hyperlink. But you can specify alternative text
540
+ @ after the target name separated by a "|" character.</p>
541
+ @ <li> <p><b>HTML</b>.
542
+ @ The following standard HTML elements may be used:
543
+ @ &lt;a&gt;
544
+ @ &lt;address&gt;
545
+ @ &lt;b&gt;
546
+ @ &lt;big&gt;
547
+ @ &lt;blockquote&gt;
548
+ @ &lt;br&gt;
549
+ @ &lt;center&gt;
550
+ @ &lt;cite&gt;
551
+ @ &lt;code&gt;
552
+ @ &lt;dd&gt;
553
+ @ &lt;dfn&gt;
554
+ @ &lt;dl&gt;
555
+ @ &lt;dt&gt;
556
+ @ &lt;em&gt;
557
+ @ &lt;font&gt;
558
+ @ &lt;h1&gt;
559
+ @ &lt;h2&gt;
560
+ @ &lt;h3&gt;
561
+ @ &lt;h4&gt;
562
+ @ &lt;h5&gt;
563
+ @ &lt;h6&gt;
564
+ @ &lt;hr&gt;
565
+ @ &lt;img&gt;
566
+ @ &lt;i&gt;
567
+ @ &lt;kbd&gt;
568
+ @ &lt;li&gt;
569
+ @ &lt;nobr&gt;
570
+ @ &lt;ol&gt;
571
+ @ &lt;p&gt;
572
+ @ &lt;pre&gt;
573
+ @ &lt;s&gt;
574
+ @ &lt;samp&gt;
575
+ @ &lt;small&gt;
576
+ @ &lt;strike&gt;
577
+ @ &lt;strong&gt;
578
+ @ &lt;sub&gt;
579
+ @ &lt;sup&gt;
580
+ @ &lt;table&gt;
581
+ @ &lt;td&gt;
582
+ @ &lt;th&gt;
583
+ @ &lt;tr&gt;
584
+ @ &lt;tt&gt;
585
+ @ &lt;u&gt;
586
+ @ &lt;ul&gt;
587
+ @ &lt;var&gt;.
588
+ @ In addition, there are two non-standard elements available:
589
+ @ &lt;verbatim&gt; and &lt;nowiki&gt;.
590
+ @ No other elements are allowed. All attributes are checked and
591
+ @ only a few benign attributes are allowed on each element.
592
+ @ In particular, any attributes that specify javascript or CSS
593
+ @ are elided.</p>
594
+ @ <p>The &lt;verbatim&gt; tag disables all wiki and HTML markup
595
+ @ up through the next &lt;/verbatim&gt;. The &lt;nowiki&gt; tag
596
+ @ disables all wiki formatting rules through the matching
597
+ @ &lt;/nowiki&gt; element.
598
+ @ </ol>
599
+ style_footer();
600
+}
601
+
445602
--- src/wiki.c
+++ src/wiki.c
@@ -96,18 +96,27 @@
96 @ and establish a "Project Name". Then create a
97 @ wiki page with that name. The content of that wiki page
98 @ will be displayed in place of this message.
99 style_footer();
100 }
 
 
 
 
 
 
 
 
101
102 /*
103 ** WEBPAGE: wiki
104 ** URL: /wiki?name=PAGENAME
105 */
106 void wiki_page(void){
107 char *zTag;
108 int rid;
 
109 Blob wiki;
110 Manifest m;
111 const char *zPageName;
112 char *zHtmlPageName;
113 char *zBody = mprintf("%s","<i>Empty Page</i>");
@@ -114,49 +123,67 @@
114
115 login_check_credentials();
116 if( !g.okRdWiki ){ login_needed(); return; }
117 zPageName = P("name");
118 if( zPageName==0 ){
119 wcontent_page();
 
 
 
 
 
 
 
 
 
 
 
120 return;
121 }
122 if( check_name(zPageName) ) return;
123 zTag = mprintf("wiki-%s", zPageName);
124 rid = db_int(0,
125 "SELECT rid FROM tagxref"
126 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
127 " ORDER BY mtime DESC", zTag
128 );
129 free(zTag);
130 memset(&m, 0, sizeof(m));
131 blob_zero(&m.content);
132 if( rid ){
133 Blob content;
134 content_get(rid, &content);
135 manifest_parse(&m, &content);
136 if( m.type==CFTYPE_WIKI ){
137 zBody = m.zWiki;
138 }
139 }
140 if( (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
 
 
 
 
 
141 style_submenu_element("Edit", "Edit Wiki Page",
142 mprintf("%s/wikiedit?name=%T", g.zTop, zPageName));
143 }
144 if( rid && g.okApndWiki ){
145 style_submenu_element("Append", "Add A Comment",
146 mprintf("%s/wikiappend?name=%T", g.zTop, zPageName));
147 }
148 if( g.okHistory ){
149 style_submenu_element("History", "History",
150 mprintf("%s/whistory?name=%T", g.zTop, zPageName));
151 }
152 zHtmlPageName = mprintf("%h", zPageName);
153 style_header(zHtmlPageName);
154 blob_init(&wiki, zBody, -1);
155 wiki_convert(&wiki, 0, 0);
156 blob_reset(&wiki);
157 manifest_clear(&m);
 
 
158 style_footer();
159 }
160
161 /*
162 ** WEBPAGE: wikiedit
@@ -163,10 +190,11 @@
163 ** URL: /wikiedit?name=PAGENAME
164 */
165 void wikiedit_page(void){
166 char *zTag;
167 int rid;
 
168 Blob wiki;
169 Manifest m;
170 const char *zPageName;
171 char *zHtmlPageName;
172 int n;
@@ -177,59 +205,70 @@
177 zBody = mprintf("%s", zBody);
178 }
179 login_check_credentials();
180 zPageName = PD("name","");
181 if( check_name(zPageName) ) return;
182 zTag = mprintf("wiki-%s", zPageName);
183 rid = db_int(0,
184 "SELECT rid FROM tagxref"
185 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
186 " ORDER BY mtime DESC", zTag
187 );
188 free(zTag);
189 if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
190 login_needed();
191 return;
192 }
193 memset(&m, 0, sizeof(m));
194 blob_zero(&m.content);
195 if( rid && zBody==0 ){
196 Blob content;
197 content_get(rid, &content);
198 manifest_parse(&m, &content);
199 if( m.type==CFTYPE_WIKI ){
200 zBody = m.zWiki;
 
 
 
 
 
 
 
201 }
202 }
203 if( P("submit")!=0 && zBody!=0 ){
204 char *zDate;
205 Blob cksum;
206 int nrid;
207 blob_zero(&wiki);
208 db_begin_transaction();
209 zDate = db_text(0, "SELECT datetime('now')");
210 zDate[10] = 'T';
211 blob_appendf(&wiki, "D %s\n", zDate);
212 free(zDate);
213 blob_appendf(&wiki, "L %F\n", zPageName);
214 if( rid ){
215 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
216 blob_appendf(&wiki, "P %s\n", zUuid);
217 free(zUuid);
218 }
219 if( g.zLogin ){
220 blob_appendf(&wiki, "U %F\n", g.zLogin);
221 }
222 blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody);
223 md5sum_blob(&wiki, &cksum);
224 blob_appendf(&wiki, "Z %b\n", &cksum);
225 blob_reset(&cksum);
226 nrid = content_put(&wiki, 0, 0);
227 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
228 manifest_crosslink(nrid, &wiki);
229 blob_reset(&wiki);
230 content_deltify(rid, nrid, 0);
 
 
 
 
231 db_end_transaction(0);
232 cgi_redirectf("wiki?name=%T", zPageName);
233 }
234 if( P("cancel")!=0 ){
235 cgi_redirectf("wiki?name=%T", zPageName);
@@ -260,11 +299,13 @@
260 @ <br>
261 @ <input type="submit" name="preview" value="Preview Your Changes">
262 @ <input type="submit" name="submit" value="Apply These Changes">
263 @ <input type="submit" name="cancel" value="Cancel">
264 @ </form>
265 manifest_clear(&m);
 
 
266 style_footer();
267 }
268
269 /*
270 ** Append the wiki text for an remark to the end of the given BLOB.
@@ -273,44 +314,48 @@
273 char *zDate;
274 const char *zUser;
275 const char *zRemark;
276
277 zDate = db_text(0, "SELECT datetime('now')");
278 blob_appendf(p, "On %s UTC %h", zDate, g.zLogin);
279 free(zDate);
280 zUser = PD("u",g.zLogin);
281 if( zUser[0] && strcmp(zUser,g.zLogin) ){
282 blob_appendf(p, " (claiming to be %h)", zUser);
283 }
284 zRemark = PD("r","");
285 blob_appendf(p, " added:\n\n%s", zRemark);
286 }
287
288 /*
289 ** WEBPAGE: wikiappend
290 ** URL: /wikiappend?name=PAGENAME
291 */
292 void wikiappend_page(void){
293 char *zTag;
294 int rid;
 
295 const char *zPageName;
296 char *zHtmlPageName;
297 const char *zUser;
298
299 login_check_credentials();
300 zPageName = PD("name","");
301 if( check_name(zPageName) ) return;
302 zTag = mprintf("wiki-%s", zPageName);
303 rid = db_int(0,
304 "SELECT rid FROM tagxref"
305 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
306 " ORDER BY mtime DESC", zTag
307 );
308 free(zTag);
309 if( !rid ){
310 cgi_redirect("index");
311 return;
 
 
 
312 }
313 if( !g.okApndWiki ){
314 login_needed();
315 return;
316 }
@@ -321,43 +366,49 @@
321 Blob body;
322 Blob content;
323 Blob wiki;
324 Manifest m;
325
326 content_get(rid, &content);
327 manifest_parse(&m, &content);
328 blob_zero(&body);
329 if( m.type==CFTYPE_WIKI ){
330 blob_appendf(&body, m.zWiki, -1);
331 }
332 manifest_clear(&m);
333 blob_zero(&wiki);
334 db_begin_transaction();
335 zDate = db_text(0, "SELECT datetime('now')");
336 zDate[10] = 'T';
337 blob_appendf(&wiki, "D %s\n", zDate);
338 blob_appendf(&wiki, "L %F\n", zPageName);
339 if( rid ){
340 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
341 blob_appendf(&wiki, "P %s\n", zUuid);
342 free(zUuid);
343 }
344 if( g.zLogin ){
345 blob_appendf(&wiki, "U %F\n", g.zLogin);
346 }
347 blob_appendf(&body, "\n<hr>\n");
348 appendRemark(&body);
349 blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body));
350 md5sum_blob(&wiki, &cksum);
351 blob_appendf(&wiki, "Z %b\n", &cksum);
352 blob_reset(&cksum);
353 nrid = content_put(&wiki, 0, 0);
354 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
355 manifest_crosslink(nrid, &wiki);
356 blob_reset(&wiki);
357 content_deltify(rid, nrid, 0);
358 db_end_transaction(0);
 
 
 
 
 
 
 
 
359 cgi_redirectf("wiki?name=%T", zPageName);
360 }
361 if( P("cancel")!=0 ){
362 cgi_redirectf("wiki?name=%T", zPageName);
363 return;
@@ -440,5 +491,111 @@
440 }
441 db_finalize(&q);
442 @ </ul>
443 style_footer();
444 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
--- src/wiki.c
+++ src/wiki.c
@@ -96,18 +96,27 @@
96 @ and establish a "Project Name". Then create a
97 @ wiki page with that name. The content of that wiki page
98 @ will be displayed in place of this message.
99 style_footer();
100 }
101
102 /*
103 ** Return true if the given pagename is the name of the sandbox
104 */
105 static int is_sandbox(const char *zPagename){
106 return strcasecmp(zPagename,"sandbox")==0 ||
107 strcasecmp(zPagename,"sand box")==0;
108 }
109
110 /*
111 ** WEBPAGE: wiki
112 ** URL: /wiki?name=PAGENAME
113 */
114 void wiki_page(void){
115 char *zTag;
116 int rid;
117 int isSandbox;
118 Blob wiki;
119 Manifest m;
120 const char *zPageName;
121 char *zHtmlPageName;
122 char *zBody = mprintf("%s","<i>Empty Page</i>");
@@ -114,49 +123,67 @@
123
124 login_check_credentials();
125 if( !g.okRdWiki ){ login_needed(); return; }
126 zPageName = P("name");
127 if( zPageName==0 ){
128 style_header("Wiki");
129 @ <ul>
130 @ <li> <a href="%s(g.zBaseURL)/timeline?y=w">Recent changes</a> to wiki
131 @ pages. </li>
132 @ <li> <a href="%s(g.zBaseURL)/wiki_rules">Formatting rules</a> for
133 @ wiki.</li>
134 @ <li> Use the <a href="%s(g.zBaseURL)/wiki?name=Sandbox">Sandbox</a>
135 @ to experiment.</li>
136 @ <li> <a href="%s(g.zBaseURL)/wcontent">List of All Wiki Pages</a>
137 @ available on this server.</li>
138 @ </ul>
139 style_footer();
140 return;
141 }
142 if( check_name(zPageName) ) return;
143 isSandbox = is_sandbox(zPageName);
144 if( isSandbox ){
145 zBody = db_get("sandbox",zBody);
146 }else{
147 zTag = mprintf("wiki-%s", zPageName);
148 rid = db_int(0,
149 "SELECT rid FROM tagxref"
150 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
151 " ORDER BY mtime DESC", zTag
152 );
153 free(zTag);
154 memset(&m, 0, sizeof(m));
155 blob_zero(&m.content);
156 if( rid ){
157 Blob content;
158 content_get(rid, &content);
159 manifest_parse(&m, &content);
160 if( m.type==CFTYPE_WIKI ){
161 zBody = m.zWiki;
162 }
163 }
164 }
165 if( isSandbox || (rid && g.okWrWiki) || (!rid && g.okNewWiki) ){
166 style_submenu_element("Edit", "Edit Wiki Page",
167 mprintf("%s/wikiedit?name=%T", g.zTop, zPageName));
168 }
169 if( isSandbox || (rid && g.okApndWiki) ){
170 style_submenu_element("Append", "Add A Comment",
171 mprintf("%s/wikiappend?name=%T", g.zTop, zPageName));
172 }
173 if( !isSandbox && g.okHistory ){
174 style_submenu_element("History", "History",
175 mprintf("%s/whistory?name=%T", g.zTop, zPageName));
176 }
177 zHtmlPageName = mprintf("%h", zPageName);
178 style_header(zHtmlPageName);
179 blob_init(&wiki, zBody, -1);
180 wiki_convert(&wiki, 0, 0);
181 blob_reset(&wiki);
182 if( !isSandbox ){
183 manifest_clear(&m);
184 }
185 style_footer();
186 }
187
188 /*
189 ** WEBPAGE: wikiedit
@@ -163,10 +190,11 @@
190 ** URL: /wikiedit?name=PAGENAME
191 */
192 void wikiedit_page(void){
193 char *zTag;
194 int rid;
195 int isSandbox;
196 Blob wiki;
197 Manifest m;
198 const char *zPageName;
199 char *zHtmlPageName;
200 int n;
@@ -177,59 +205,70 @@
205 zBody = mprintf("%s", zBody);
206 }
207 login_check_credentials();
208 zPageName = PD("name","");
209 if( check_name(zPageName) ) return;
210 isSandbox = is_sandbox(zPageName);
211 if( isSandbox ){
212 if( zBody==0 ){
213 zBody = db_get("sandbox","");
214 }
215 }else{
216 zTag = mprintf("wiki-%s", zPageName);
217 rid = db_int(0,
218 "SELECT rid FROM tagxref"
219 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
220 " ORDER BY mtime DESC", zTag
221 );
222 free(zTag);
223 if( (rid && !g.okWrWiki) || (!rid && !g.okNewWiki) ){
224 login_needed();
225 return;
226 }
227 memset(&m, 0, sizeof(m));
228 blob_zero(&m.content);
229 if( rid && zBody==0 ){
230 Blob content;
231 content_get(rid, &content);
232 manifest_parse(&m, &content);
233 if( m.type==CFTYPE_WIKI ){
234 zBody = m.zWiki;
235 }
236 }
237 }
238 if( P("submit")!=0 && zBody!=0 ){
239 char *zDate;
240 Blob cksum;
241 int nrid;
242 blob_zero(&wiki);
243 db_begin_transaction();
244 if( isSandbox ){
245 db_set("sandbox",zBody,0);
246 }else{
247 zDate = db_text(0, "SELECT datetime('now')");
248 zDate[10] = 'T';
249 blob_appendf(&wiki, "D %s\n", zDate);
250 free(zDate);
251 blob_appendf(&wiki, "L %F\n", zPageName);
252 if( rid ){
253 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
254 blob_appendf(&wiki, "P %s\n", zUuid);
255 free(zUuid);
256 }
257 if( g.zLogin ){
258 blob_appendf(&wiki, "U %F\n", g.zLogin);
259 }
260 blob_appendf(&wiki, "W %d\n%s\n", strlen(zBody), zBody);
261 md5sum_blob(&wiki, &cksum);
262 blob_appendf(&wiki, "Z %b\n", &cksum);
263 blob_reset(&cksum);
264 nrid = content_put(&wiki, 0, 0);
265 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
266 manifest_crosslink(nrid, &wiki);
267 blob_reset(&wiki);
268 content_deltify(rid, nrid, 0);
269 }
270 db_end_transaction(0);
271 cgi_redirectf("wiki?name=%T", zPageName);
272 }
273 if( P("cancel")!=0 ){
274 cgi_redirectf("wiki?name=%T", zPageName);
@@ -260,11 +299,13 @@
299 @ <br>
300 @ <input type="submit" name="preview" value="Preview Your Changes">
301 @ <input type="submit" name="submit" value="Apply These Changes">
302 @ <input type="submit" name="cancel" value="Cancel">
303 @ </form>
304 if( !isSandbox ){
305 manifest_clear(&m);
306 }
307 style_footer();
308 }
309
310 /*
311 ** Append the wiki text for an remark to the end of the given BLOB.
@@ -273,44 +314,48 @@
314 char *zDate;
315 const char *zUser;
316 const char *zRemark;
317
318 zDate = db_text(0, "SELECT datetime('now')");
319 blob_appendf(p, "\n\n<hr><i>On %s UTC %h", zDate, g.zLogin);
320 free(zDate);
321 zUser = PD("u",g.zLogin);
322 if( zUser[0] && strcmp(zUser,g.zLogin) ){
323 blob_appendf(p, " (claiming to be %h)", zUser);
324 }
325 zRemark = PD("r","");
326 blob_appendf(p, " added:</i><br />\n%s", zRemark);
327 }
328
329 /*
330 ** WEBPAGE: wikiappend
331 ** URL: /wikiappend?name=PAGENAME
332 */
333 void wikiappend_page(void){
334 char *zTag;
335 int rid;
336 int isSandbox;
337 const char *zPageName;
338 char *zHtmlPageName;
339 const char *zUser;
340
341 login_check_credentials();
342 zPageName = PD("name","");
343 if( check_name(zPageName) ) return;
344 isSandbox = is_sandbox(zPageName);
345 if( !isSandbox ){
346 zTag = mprintf("wiki-%s", zPageName);
347 rid = db_int(0,
348 "SELECT rid FROM tagxref"
349 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
350 " ORDER BY mtime DESC", zTag
351 );
352 free(zTag);
353 if( !rid ){
354 cgi_redirect("index");
355 return;
356 }
357 }
358 if( !g.okApndWiki ){
359 login_needed();
360 return;
361 }
@@ -321,43 +366,49 @@
366 Blob body;
367 Blob content;
368 Blob wiki;
369 Manifest m;
370
 
 
371 blob_zero(&body);
372 if( isSandbox ){
373 blob_appendf(&body, db_get("sandbox",""));
374 appendRemark(&body);
375 db_set("sandbox", blob_str(&body), 0);
376 }else{
377 content_get(rid, &content);
378 manifest_parse(&m, &content);
379 if( m.type==CFTYPE_WIKI ){
380 blob_appendf(&body, m.zWiki, -1);
381 }
382 manifest_clear(&m);
383 blob_zero(&wiki);
384 db_begin_transaction();
385 zDate = db_text(0, "SELECT datetime('now')");
386 zDate[10] = 'T';
387 blob_appendf(&wiki, "D %s\n", zDate);
388 blob_appendf(&wiki, "L %F\n", zPageName);
389 if( rid ){
390 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
391 blob_appendf(&wiki, "P %s\n", zUuid);
392 free(zUuid);
393 }
394 if( g.zLogin ){
395 blob_appendf(&wiki, "U %F\n", g.zLogin);
396 }
397 blob_appendf(&body, "\n<hr>\n");
398 appendRemark(&body);
399 blob_appendf(&wiki, "W %d\n%s\n", blob_size(&body), blob_str(&body));
400 md5sum_blob(&wiki, &cksum);
401 blob_appendf(&wiki, "Z %b\n", &cksum);
402 blob_reset(&cksum);
403 nrid = content_put(&wiki, 0, 0);
404 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
405 manifest_crosslink(nrid, &wiki);
406 blob_reset(&wiki);
407 content_deltify(rid, nrid, 0);
408 db_end_transaction(0);
409 }
410 cgi_redirectf("wiki?name=%T", zPageName);
411 }
412 if( P("cancel")!=0 ){
413 cgi_redirectf("wiki?name=%T", zPageName);
414 return;
@@ -440,5 +491,111 @@
491 }
492 db_finalize(&q);
493 @ </ul>
494 style_footer();
495 }
496
497 /*
498 ** WEBPAGE: wiki_rules
499 */
500 void wikirules_page(void){
501 style_header("Wiki Formatting Rules");
502 @ <h2>Formatting Rule Summary</h2>
503 @ <ol>
504 @ <li> Blank lines are paragraph breaks
505 @ <li> Bullet list items are a "*" at the beginning of the line.
506 @ <li> Enumeration list items are a number at the beginning of a line.
507 @ <li> Indented pargraphs begin with a tab or two spaces.
508 @ <li> Hyperlinks are contained with square brackets: "[target]"
509 @ <li> Most ordinary HTML works.
510 @ <li> &lt;verbatim&gt; and &lt;nowiki&gt;.
511 @ </ol>
512 @ <p>We call the first five rules above "wiki" formatting rules. The
513 @ last two rules are the HTML formatting rule.</p>
514 @ <h2>Formatting Rule Details</h2>
515 @ <ol>
516 @ <li> <p><b>Paragraphs</b>. Any sequence of one or more blank lines forms
517 @ a paragraph break. Centered or right-justified paragraphs are not
518 @ supported by wiki markup, but you can do these things if you need them
519 @ using HTML.</p>
520 @ <li> <p><b>Bullet Lists</b>.
521 @ A bullet list item begins with a single "*" character surrounded on
522 @ both sides by two or more spaces or by a tab. Only a single level
523 @ of bullet list is supported by wiki. For tested lists, use HTML.</p>
524 @ <li> <p><b>Enumeration Lists</b>.
525 @ An enumeration list item begins with one or more digits optionally
526 @ followed by a "." surrounded on both sides by two or more spaces or
527 @ by a tab. The number is significant and becomes the number shown
528 @ in the rendered enumeration item. Only a single level of enumeration
529 @ list is supported by wiki. For nested enumerations or for
530 @ enumerations that count using letters or roman numerials, use HTML.</p>
531 @ <li> <p><b>Indented Paragraphs</b>.
532 @ Any paragraph that begins with two or more spaces or a tab and
533 @ which is not a bullet or enumeration list item is rendered
534 @ indented. Only a single level of indentation is supported by</p>
535 @ <li> <p><b>Hyperlinks</b>.
536 @ Text within square brackets ("[...]") becomes a hyperlink. The
537 @ target can be a wiki page name, the UUID of a check-in or ticket,
538 @ the name of an image, or a URL. By default, the target is displayed
539 @ as the text of the hyperlink. But you can specify alternative text
540 @ after the target name separated by a "|" character.</p>
541 @ <li> <p><b>HTML</b>.
542 @ The following standard HTML elements may be used:
543 @ &lt;a&gt;
544 @ &lt;address&gt;
545 @ &lt;b&gt;
546 @ &lt;big&gt;
547 @ &lt;blockquote&gt;
548 @ &lt;br&gt;
549 @ &lt;center&gt;
550 @ &lt;cite&gt;
551 @ &lt;code&gt;
552 @ &lt;dd&gt;
553 @ &lt;dfn&gt;
554 @ &lt;dl&gt;
555 @ &lt;dt&gt;
556 @ &lt;em&gt;
557 @ &lt;font&gt;
558 @ &lt;h1&gt;
559 @ &lt;h2&gt;
560 @ &lt;h3&gt;
561 @ &lt;h4&gt;
562 @ &lt;h5&gt;
563 @ &lt;h6&gt;
564 @ &lt;hr&gt;
565 @ &lt;img&gt;
566 @ &lt;i&gt;
567 @ &lt;kbd&gt;
568 @ &lt;li&gt;
569 @ &lt;nobr&gt;
570 @ &lt;ol&gt;
571 @ &lt;p&gt;
572 @ &lt;pre&gt;
573 @ &lt;s&gt;
574 @ &lt;samp&gt;
575 @ &lt;small&gt;
576 @ &lt;strike&gt;
577 @ &lt;strong&gt;
578 @ &lt;sub&gt;
579 @ &lt;sup&gt;
580 @ &lt;table&gt;
581 @ &lt;td&gt;
582 @ &lt;th&gt;
583 @ &lt;tr&gt;
584 @ &lt;tt&gt;
585 @ &lt;u&gt;
586 @ &lt;ul&gt;
587 @ &lt;var&gt;.
588 @ In addition, there are two non-standard elements available:
589 @ &lt;verbatim&gt; and &lt;nowiki&gt;.
590 @ No other elements are allowed. All attributes are checked and
591 @ only a few benign attributes are allowed on each element.
592 @ In particular, any attributes that specify javascript or CSS
593 @ are elided.</p>
594 @ <p>The &lt;verbatim&gt; tag disables all wiki and HTML markup
595 @ up through the next &lt;/verbatim&gt;. The &lt;nowiki&gt; tag
596 @ disables all wiki formatting rules through the matching
597 @ &lt;/nowiki&gt; element.
598 @ </ol>
599 style_footer();
600 }
601
602

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button