Fossil SCM

fossil-scm / src / setup.c
Blame History Raw 2576 lines
1
/*
2
** Copyright (c) 2007 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
**
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** Implementation of the Setup page
19
*/
20
#include "config.h"
21
#include <assert.h>
22
#include "setup.h"
23
24
/*
25
** Increment the "cfgcnt" variable, so that ETags will know that
26
** the configuration has changed.
27
*/
28
void setup_incr_cfgcnt(void){
29
static int once = 1;
30
if( once ){
31
once = 0;
32
db_unprotect(PROTECT_CONFIG);
33
db_multi_exec("UPDATE config SET value=value+1 WHERE name='cfgcnt'");
34
if( db_changes()==0 ){
35
db_multi_exec("INSERT INTO config(name,value) VALUES('cfgcnt',1)");
36
}
37
db_protect_pop();
38
}
39
}
40
41
/*
42
** Output a single entry for a menu generated using an HTML table.
43
** If zLink is neither NULL nor an empty string, then it is the page that
44
** the menu entry will hyperlink to. If zLink is NULL or "", then
45
** the menu entry has no hyperlink - it is disabled.
46
*/
47
void setup_menu_entry(
48
const char *zTitle,
49
const char *zLink,
50
const char *zDesc /* Caution! Rendered using %s. May contain raw HTML. */
51
){
52
@ <tr><td valign="top" align="right">
53
if( zLink && zLink[0] ){
54
@ <a href="%s(zLink)"><nobr>%h(zTitle)</nobr></a>
55
}else{
56
@ <nobr>%h(zTitle)</nobr>
57
}
58
@ </td><td width="5"></td><td valign="top">%s(zDesc)</td></tr>
59
}
60
61
62
63
/*
64
** WEBPAGE: setup
65
**
66
** Main menu for the administrative pages. Requires Admin or Setup
67
** privileges. Links to sub-pages only usable by Setup users are
68
** shown only to Setup users.
69
*/
70
void setup_page(void){
71
int setup_user = 0;
72
login_check_credentials();
73
if( !g.perm.Admin ){
74
login_needed(0);
75
}
76
setup_user = g.perm.Setup;
77
78
style_set_current_feature("setup");
79
style_header("Server Administration");
80
81
/* Make sure the header contains <base href="...">. Issue a warning
82
** if it does not. */
83
if( !cgi_header_contains("<base href=") ){
84
@ <p class="generalError"><b>Configuration Error:</b> Please add
85
@ <tt>&lt;base href="$secureurl/$current_page"&gt;</tt> after
86
@ <tt>&lt;head&gt;</tt> in the
87
@ <a href="setup_skinedit?w=2">HTML header</a>!</p>
88
}
89
90
#if !defined(_WIN32)
91
/* Check for /dev/null and /dev/urandom. We want both devices to be present,
92
** but they are sometimes omitted (by mistake) from chroot jails. */
93
if( access("/dev/null", R_OK|W_OK) ){
94
@ <p class="generalError">WARNING: Device "/dev/null" is not available
95
@ for reading and writing.</p>
96
}
97
if( access("/dev/urandom", R_OK) ){
98
@ <p class="generalError">WARNING: Device "/dev/urandom" is not available
99
@ for reading. This means that the pseudo-random number generator used
100
@ by SQLite will be poorly seeded.</p>
101
}
102
#endif
103
104
@ <table border="0" cellspacing="3">
105
setup_menu_entry("Users", "setup_ulist",
106
"Grant privileges to individual users.");
107
if( setup_user ){
108
setup_menu_entry("Access", "setup_access",
109
"Control access settings.");
110
setup_menu_entry("Configuration", "setup_config",
111
"Configure the WWW components of the repository");
112
}
113
setup_menu_entry("Security-Audit", "secaudit0",
114
"Analyze the current configuration for security problems");
115
if( setup_user ){
116
setup_menu_entry("Robot-Defense", "setup_robot",
117
"Settings for configure defense against robots");
118
setup_menu_entry("Settings", "setup_settings",
119
"Web interface to the \"fossil settings\" command");
120
}
121
setup_menu_entry("Timeline", "setup_timeline",
122
"Timeline display preferences");
123
setup_menu_entry("Tarballs and ZIPs", "setup_download",
124
"Preferences for auto-generated tarballs and ZIP files");
125
if( setup_user ){
126
setup_menu_entry("Login-Group", "setup_login_group",
127
"Manage single sign-on between this repository and others"
128
" on the same server");
129
setup_menu_entry("Tickets", "tktsetup",
130
"Configure the trouble-ticketing system for this repository");
131
setup_menu_entry("Wiki", "setup_wiki",
132
"Configure the wiki for this repository");
133
setup_menu_entry("Interwiki Map", "intermap",
134
"Mapping keywords for interwiki links");
135
setup_menu_entry("Chat", "setup_chat",
136
"Configure the chatroom");
137
setup_menu_entry("Forum", "setup_forum",
138
"Forum config and metrics");
139
}
140
setup_menu_entry("Search","srchsetup",
141
"Configure the built-in search engine");
142
setup_menu_entry("URL Aliases", "waliassetup",
143
"Configure URL aliases");
144
if( setup_user ){
145
setup_menu_entry("Notification", "setup_notification",
146
"Automatic notifications of changes via outbound email");
147
#if 0 /* Disabled for now. Does this even work? */
148
setup_menu_entry("Transfers", "xfersetup",
149
"Configure the transfer system for this repository");
150
#endif
151
}
152
setup_menu_entry("Skins", "setup_skin_admin",
153
"Select and/or modify the web interface \"skins\"");
154
setup_menu_entry("Moderation", "setup_modreq",
155
"Enable/Disable requiring moderator approval of Wiki and/or Ticket"
156
" changes and attachments.");
157
setup_menu_entry("Ad-Unit", "setup_adunit",
158
"Edit HTML text for an ad unit inserted after the menu bar");
159
setup_menu_entry("URLs & Checkouts", "urllist",
160
"Show URLs used to access this repo and known check-outs");
161
if( setup_user ){
162
setup_menu_entry("Web-Cache", "cachestat",
163
"View the status of the expensive-page cache");
164
}
165
setup_menu_entry("Logo", "setup_logo",
166
"Change the logo and background images for the server");
167
setup_menu_entry("Shunned", "shun",
168
"Show artifacts that are shunned by this repository");
169
setup_menu_entry("Log Files", "setup-logmenu",
170
"A menu of available log files");
171
setup_menu_entry("Unversioned Files", "uvlist?byage=1",
172
"Show all unversioned files held");
173
setup_menu_entry("Stats", "stat",
174
"Repository Status Reports");
175
setup_menu_entry("Sitemap", "sitemap",
176
"Links to miscellaneous pages");
177
if( setup_user ){
178
setup_menu_entry("SQL", "admin_sql",
179
"Enter raw SQL commands");
180
setup_menu_entry("TH1", "admin_th1",
181
"Enter raw TH1 commands");
182
}
183
@ </table>
184
185
style_finish_page();
186
}
187
188
189
/*
190
** WEBPAGE: setup-logmenu
191
**
192
** Show a menu of available log renderings accessible to an administrator,
193
** together with a succinct explanation of each.
194
**
195
** This page is only accessible by administrators.
196
*/
197
void setup_logmenu_page(void){
198
Blob desc;
199
int bErrLog; /* True if Error Log enabled */
200
blob_init(&desc, 0, 0);
201
202
/* Administrator access only */
203
login_check_credentials();
204
if( !g.perm.Admin ){
205
login_needed(0);
206
return;
207
}
208
style_header("Log Menu");
209
@ <table border="0" cellspacing="3">
210
211
if( db_get_boolean("admin-log",1)==0 ){
212
blob_appendf(&desc,
213
"The admin log records configuration changes to the repository.\n"
214
"<b>Disabled</b>: Turn on the "
215
" <a href='%R/setup_settings'>admin-log setting</a> to enable."
216
);
217
setup_menu_entry("Admin Log", 0, blob_str(&desc));
218
blob_reset(&desc);
219
}else{
220
setup_menu_entry("Admin Log", "admin_log",
221
"The admin log records configuration changes to the repository\n"
222
"in the \"admin_log\" table.\n"
223
);
224
}
225
setup_menu_entry("Xfer Log", "rcvfromlist",
226
"The artifact log records when new content is added in the\n"
227
"\"rcvfrom\" table.\n"
228
);
229
if( db_get_boolean("access-log",1) ){
230
setup_menu_entry("User Log", "user_log",
231
"Login attempts recorded in the \"accesslog\" table."
232
);
233
}else{
234
blob_appendf(&desc,
235
"Login attempts recorded in the \"accesslog\" table.\n"
236
"<b>Disabled</b>: Turn on the "
237
"<a href='%R/setup_settings'>access-log setting</a> to enable."
238
);
239
setup_menu_entry("User Log", 0, blob_str(&desc));
240
blob_reset(&desc);
241
}
242
243
blob_appendf(&desc,
244
"A separate text file to which warning and error\n"
245
"messages are appended. A single error log can and often is shared\n"
246
"across multiple repositories.\n"
247
);
248
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
249
blob_appendf(&desc,"<b>Disabled</b>: "
250
"To enable the error log ");
251
if( fossil_strcmp(g.zCmdName, "cgi")==0 ){
252
blob_appendf(&desc,
253
"make an entry like \"errorlog: <i>FILENAME</i>\""
254
" in the CGI script at %h",
255
P("SCRIPT_FILENAME")
256
);
257
}else{
258
blob_appendf(&desc,
259
" add the \"--errorlog <i>FILENAME</i>\" option to the\n"
260
"\"%h %h\" command that launched the server.",
261
g.argv[0], g.zCmdName
262
);
263
}
264
bErrLog = 0;
265
}else{
266
blob_appendf(&desc,"In this repository, the error log is the file "
267
"named \"%s\".", g.zErrlog);
268
bErrLog = 1;
269
}
270
setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc));
271
blob_reset(&desc);
272
273
@ </table>
274
style_finish_page();
275
}
276
277
/*
278
** Generate a checkbox for an attribute.
279
*/
280
void onoff_attribute(
281
const char *zLabel, /* The text label on the checkbox */
282
const char *zVar, /* The corresponding row in the CONFIG table */
283
const char *zQParm, /* The query parameter */
284
int dfltVal, /* Default value if CONFIG table entry does not exist */
285
int disabled /* 1 if disabled */
286
){
287
const char *zQ = P(zQParm);
288
int iVal = db_get_boolean(zVar, dfltVal);
289
if( zQ==0 && !disabled && P("submit") ){
290
zQ = "off";
291
}
292
if( zQ ){
293
int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ);
294
if( iQ!=iVal && cgi_csrf_safe(2) ){
295
db_protect_only(PROTECT_NONE);
296
db_set(zVar/*works-like:"x"*/, iQ ? "1" : "0", 0);
297
db_protect_pop();
298
setup_incr_cfgcnt();
299
admin_log("Set option [%q] to [%q].",
300
zVar, iQ ? "on" : "off");
301
iVal = iQ;
302
}
303
}
304
@ <label><input type="checkbox" name="%s(zQParm)" \
305
@ aria-label="%h(zLabel[0]?zLabel:zQParm)" \
306
if( iVal ){
307
@ checked="checked" \
308
}
309
if( disabled ){
310
@ disabled="disabled" \
311
}
312
@ > <b>%s(zLabel)</b></label>
313
}
314
315
/*
316
** Generate an entry box for an attribute.
317
*/
318
void entry_attribute(
319
const char *zLabel, /* The text label on the entry box */
320
int width, /* Width of the entry box */
321
const char *zVar, /* The corresponding row in the CONFIG table */
322
const char *zQParm, /* The query parameter */
323
const char *zDflt, /* Default value if CONFIG table entry does not exist */
324
int disabled /* 1 if disabled */
325
){
326
const char *zVal = db_get(zVar, zDflt);
327
const char *zQ = P(zQParm);
328
if( zQ && fossil_strcmp(zQ,zVal)!=0 && cgi_csrf_safe(2) ){
329
const int nZQ = (int)strlen(zQ);
330
setup_incr_cfgcnt();
331
db_protect_only(PROTECT_NONE);
332
db_set(zVar/*works-like:"x"*/, zQ, 0);
333
db_protect_pop();
334
admin_log("Set entry_attribute %Q to: %.*s%s",
335
zVar, 20, zQ, (nZQ>20 ? "..." : ""));
336
zVal = zQ;
337
}
338
@ <input aria-label="%h(zLabel[0]?zLabel:zQParm)" type="text" \
339
@ id="%s(zQParm)" name="%s(zQParm)" value="%h(zVal)" size="%d(width)" \
340
if( disabled ){
341
@ disabled="disabled" \
342
}
343
@ > <b>%s(zLabel)</b>
344
}
345
346
/*
347
** Generate a text box for an attribute.
348
*/
349
const char *textarea_attribute(
350
const char *zLabel, /* The text label on the textarea */
351
int rows, /* Rows in the textarea */
352
int cols, /* Columns in the textarea */
353
const char *zVar, /* The corresponding row in the CONFIG table */
354
const char *zQP, /* The query parameter */
355
const char *zDflt, /* Default value if CONFIG table entry does not exist */
356
int disabled /* 1 if the textarea should not be editable */
357
){
358
const char *z = db_get(zVar, zDflt);
359
const char *zQ = P(zQP);
360
if( zQ && !disabled && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){
361
const int nZQ = (int)strlen(zQ);
362
db_protect_only(PROTECT_NONE);
363
db_set(zVar/*works-like:"x"*/, zQ, 0);
364
db_protect_pop();
365
setup_incr_cfgcnt();
366
admin_log("Set textarea_attribute %Q to: %.*s%s",
367
zVar, 20, zQ, (nZQ>20 ? "..." : ""));
368
z = zQ;
369
}
370
if( rows>0 && cols>0 ){
371
@ <textarea id="id%s(zQP)" name="%s(zQP)" rows="%d(rows)" \
372
@ aria-label="%h(zLabel[0]?zLabel:zQP)" \
373
if( disabled ){
374
@ disabled="disabled" \
375
}
376
@ cols="%d(cols)">%h(z)</textarea>
377
if( *zLabel ){
378
@ <span class="textareaLabel">%s(zLabel)</span>
379
}
380
}
381
return z;
382
}
383
384
/*
385
** Generate a text box for an attribute.
386
*/
387
void multiple_choice_attribute(
388
const char *zLabel, /* The text label on the menu */
389
const char *zVar, /* The corresponding row in the CONFIG table */
390
const char *zQP, /* The query parameter */
391
const char *zDflt, /* Default value if CONFIG table entry does not exist */
392
int nChoice, /* Number of choices */
393
const char *const *azChoice /* Choices in pairs (VAR value, Display) */
394
){
395
const char *z = db_get(zVar, zDflt);
396
const char *zQ = P(zQP);
397
int i;
398
if( zQ && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){
399
const int nZQ = (int)strlen(zQ);
400
db_unprotect(PROTECT_ALL);
401
db_set(zVar/*works-like:"x"*/, zQ, 0);
402
setup_incr_cfgcnt();
403
db_protect_pop();
404
admin_log("Set multiple_choice_attribute %Q to: %.*s%s",
405
zVar, 20, zQ, (nZQ>20 ? "..." : ""));
406
z = zQ;
407
}
408
@ <select aria-label="%h(zLabel)" size="1" name="%s(zQP)" id="id%s(zQP)">
409
for(i=0; i<nChoice*2; i+=2){
410
const char *zSel = fossil_strcmp(azChoice[i],z)==0 ? " selected" : "";
411
@ <option value="%h(azChoice[i])"%s(zSel)>%h(azChoice[i+1])</option>
412
}
413
@ </select> <b>%h(zLabel)</b>
414
}
415
416
/*
417
** Insert code into the current page that allows the user to configure
418
** auto-hyperlink related robot defense settings.
419
*/
420
static void addAutoHyperlinkSettings(void){
421
static const char *const azDefenseOpts[] = {
422
"0", "Off",
423
"2", "HTTP Header Only",
424
"1", "HTTP Header And Javascript",
425
};
426
multiple_choice_attribute(
427
"Enable hyperlinks base on HTTP Header and/or Javascript",
428
"auto-hyperlink", "autohyperlink", "1",
429
count(azDefenseOpts)/2, azDefenseOpts);
430
@ <br>
431
entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
432
"auto-hyperlink-delay", "ah-delay", "50", 0);
433
@ <br>
434
onoff_attribute("Also require a mouse event before enabling hyperlinks",
435
"auto-hyperlink-mouseover", "ahmo", 0, 0);
436
@ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
437
@ including user "nobody" if the request appears to be from a human.
438
@ Disabling hyperlinks helps prevent robots from walking your site and
439
@ soaking up all your CPU and bandwidth.
440
@ If this setting is "HTTP Header Only" (2) then only the HTTP header
441
@ content (including the UserAgent string and the fossil-client-ok cookie)
442
@ is considered when enabling hyperlinks. If the value of this
443
@ setting is "HTTP Header And Javascript" (1) then Javascript is added that
444
@ runs after the page loads and fills in the href= values of &lt;a&gt;
445
@ elements. In either case, &lt;a&gt; tags are not generated unless the
446
@ UserAgent string and/or the fossil-client-ok cookie indicate that the
447
@ client is likely human.
448
@ (Property: "auto-hyperlink")</p>
449
@
450
@ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
451
@ and "require a mouse event" should be turned on. These values only come
452
@ into play when the main auto-hyperlink settings is 2 ("UserAgent and
453
@ Javascript").
454
@ (Properties: "auto-hyperlink-delay" and "auto-hyperlink-mouseover")</p>
455
@
456
@ <p>To see if Javascript-base hyperlink enabling mechanism is working,
457
@ visit the <a href="%R/test-env">/test-env</a> page from a separate
458
@ web browser that is not logged in, even as "anonymous" and verify
459
@ that the "g.jsHref" value is "1".</p>
460
}
461
462
/*
463
** WEBPAGE: setup_robot
464
**
465
** Settings associated with defense against robots. Requires setup privilege.
466
*/
467
void setup_robots(void){
468
login_check_credentials();
469
if( !g.perm.Setup ){
470
login_needed(0);
471
return;
472
}
473
style_set_current_feature("setup");
474
style_header("Robot Defense Settings");
475
db_begin_transaction();
476
@ <p>A Fossil website can have billions of pages in its tree, even for a
477
@ modest project. Many of those pages (examples: diffs and tarballs)
478
@ might be expensive to compute. A robot that tries to walk the entire
479
@ website can present a crippling CPU and bandwidth load.
480
@
481
@ <p>The settings on this page are intended to help administrators
482
@ defend against abusive robots.
483
@
484
@ <form action="%R/setup_robot" method="post"><div>
485
login_insert_csrf_secret();
486
@ <input type="submit" name="submit" value="Apply Changes"></p>
487
@ <hr>
488
@ <p><b>Do not allow robots access to these pages.</b><br>
489
@ If the page name matches the GLOB pattern of this setting, and the
490
@ users is "nobody", and the client has not previously passed a captcha
491
@ test to show that it is not a robot, then the page is not displayed.
492
@ A captcha test is rendered instead.
493
@ The default value for this setting is:
494
@ <p>
495
@ &emsp;&emsp;&emsp;<tt>%h(robot_restrict_default())</tt>
496
@ <p>
497
@ Usually the tag should exactly match the page name. Exceptions:
498
@ <ul>
499
@ <li> The "diff" tag covers all diffing pages such as /vdiff,
500
@ /fdiff, and /vpatch.
501
@ <li> The "annotate" tag covers /annotate and also /blame and
502
@ /praise.
503
@ <li> The "zip" covers itself and also /tarball and /sqlar.
504
@ <li> If a tag has an "X" character appended (ex: "timelineX")
505
@ then it only applies if query parameters are such that
506
@ the page is expensive and/or unusual.
507
@ <li> The "ext" tag covers all extensions, but a tag like
508
@ "ext/PATH" only covers the specific extension at PATH.
509
@ </ul>
510
@ Make this setting "off" to disable all robot restrictions, or "*"
511
@ to exclude robots from all pages other than "/login" and "/xfer".
512
@ (Property: <a href="%R/help/robot-restrict">robot-restrict</a>)
513
@ <br>
514
textarea_attribute("", 2, 80,
515
"robot-restrict", "rbrestrict", robot_restrict_default(), 0);
516
517
@ <p><b>Exception #1</b><br>
518
@ If "zipX" appears in the robot-restrict list above, then tarballs,
519
@ ZIP-archives, and SQL-archives may be downloaded by robots if
520
@ the check-in is a leaf (robot-zip-leaf):<br>
521
onoff_attribute("Allow tarballs for leaf check-ins",
522
"robot-zip-leaf", "rzleaf", 0, 0);
523
524
@ <p><b>Exception #2</b><br>
525
@ If "zipX" appears in the robot-restrict list above, then tarballs,
526
@ ZIP-archives, and SQL-archives may be downloaded by robots if
527
@ the check-in has one or more tags that match the following
528
@ list of GLOB patterns: (robot-zip-tag)<br>
529
textarea_attribute("", 2, 80,
530
"robot-zip-tag", "rztag", "", 0);
531
532
@ <p><b>Exception #3</b><br>
533
@ If the request URI matches any of the following
534
@ <a href="%R/re_rules">regular expressions</a> (one per line), then the
535
@ request is exempt from anti-robot defenses.
536
@ The regular expression is matched against the REQUEST_URI with the
537
@ SCRIPT_NAME prefix removed, and with QUERY_STRING appended following
538
@ a "?" if QUERY_STRING exists. (Property: robot-exception)<br>
539
textarea_attribute("", 3, 80,
540
"robot-exception", "rbexcept", "", 0);
541
@ <hr>
542
addAutoHyperlinkSettings();
543
544
@ <hr>
545
entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
546
"anoncookls", "840", 0);
547
@ <p>The number of minutes for which an anonymous login cookie is valid.
548
@ Set to zero to disable anonymous login.
549
@ (property: anon-cookie-lifespan)
550
551
@ <hr>
552
entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
553
"0.0", 0);
554
@ <p>Some expensive operations (such as computing tarballs, zip archives,
555
@ or annotation/blame pages) are prohibited if the load average on the host
556
@ computer is too large. Set the threshold for disallowing expensive
557
@ computations here. Set this to 0.0 to disable the load average limit.
558
@ This limit is only enforced on Unix servers. On Linux systems,
559
@ access to the /proc virtual filesystem is required, which means this limit
560
@ might not work inside a chroot() jail.
561
@ (Property: "max-loadavg")</p>
562
@
563
@ <hr>
564
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
565
@ </div></form>
566
db_end_transaction(0);
567
style_finish_page();
568
}
569
570
/*
571
** WEBPAGE: setup_access
572
**
573
** The access-control settings page. Requires Setup privileges.
574
*/
575
void setup_access(void){
576
static const char *const azRedirectOpts[] = {
577
"0", "Off",
578
"1", "Login Page Only",
579
"2", "All Pages"
580
};
581
login_check_credentials();
582
if( !g.perm.Setup ){
583
login_needed(0);
584
return;
585
}
586
587
style_set_current_feature("setup");
588
style_header("Access Control Settings");
589
db_begin_transaction();
590
@ <form action="%R/setup_access" method="post"><div>
591
login_insert_csrf_secret();
592
@ <input type="submit" name="submit" value="Apply Changes"></p>
593
@ <hr>
594
multiple_choice_attribute("Redirect to HTTPS",
595
"redirect-to-https", "redirhttps", "0",
596
count(azRedirectOpts)/2, azRedirectOpts);
597
@ <p>Force the use of HTTPS by redirecting to HTTPS when an
598
@ unencrypted request is received. This feature can be enabled
599
@ for the Login page only, or for all pages.
600
@ <p>Further details: When enabled, this option causes the $secureurl TH1
601
@ variable is set to an "https:" variant of $baseurl. Otherwise,
602
@ $secureurl is just an alias for $baseurl.
603
@ (Property: "redirect-to-https". "0" for off, "1" for Login page only,
604
@ "2" otherwise.)
605
@ <hr>
606
onoff_attribute("Require password for local access",
607
"localauth", "localauth", 0, 0);
608
@ <p>When enabled, the password sign-in is always required for
609
@ web access. When disabled, unrestricted web access from 127.0.0.1
610
@ is allowed for the <a href="%R/help/ui">fossil ui</a> command or
611
@ from the <a href="%R/help/server">fossil server</a>,
612
@ <a href="%R/help/http">fossil http</a> commands when the
613
@ "--localauth" command line options is used, or from the
614
@ <a href="%R/help/cgi">fossil cgi</a> if a line containing
615
@ the word "localauth" appears in the CGI script.
616
@
617
@ <p>A password is always required if any one or more
618
@ of the following are true:
619
@ <ol>
620
@ <li> This button is checked
621
@ <li> The inbound TCP/IP connection is not from 127.0.0.1
622
@ <li> The server is started using either of the
623
@ <a href="%R/help/server">fossil server</a> or
624
@ <a href="%R/help/server">fossil http</a> commands
625
@ without the "--localauth" option.
626
@ <li> The server is started from CGI without the "localauth" keyword
627
@ in the CGI script.
628
@ </ol>
629
@ (Property: "localauth")
630
@
631
@ <hr>
632
onoff_attribute("Enable /test-env",
633
"test_env_enable", "test_env_enable", 0, 0);
634
@ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all
635
@ users. When disabled (the default) only users Admin and Setup can visit
636
@ the /test_env page.
637
@ (Property: "test_env_enable")
638
@ </p>
639
@
640
@ <hr>
641
onoff_attribute("Enable /artifact_stats",
642
"artifact_stats_enable", "artifact_stats_enable", 0, 0);
643
@ <p>When enabled, the %h(g.zBaseURL)/artifact_stats URL is available to all
644
@ users. When disabled (the default) only users with check-in privilege may
645
@ access the /artifact_stats page.
646
@ (Property: "artifact_stats_enable")
647
@ </p>
648
@
649
@ <hr>
650
onoff_attribute("Allow REMOTE_USER authentication",
651
"remote_user_ok", "remote_user_ok", 0, 0);
652
@ <p>When enabled, if the REMOTE_USER environment variable is set to the
653
@ login name of a valid user and no other login credentials are available,
654
@ then the REMOTE_USER is accepted as an authenticated user.
655
@ (Property: "remote_user_ok")
656
@ </p>
657
@
658
@ <hr>
659
onoff_attribute("Allow HTTP_AUTHENTICATION authentication",
660
"http_authentication_ok", "http_authentication_ok", 0, 0);
661
@ <p>When enabled, allow the use of the HTTP_AUTHENTICATION environment
662
@ variable or the "Authentication:" HTTP header to find the username and
663
@ password. This is another way of supporting Basic Authentication.
664
@ (Property: "http_authentication_ok")
665
@ </p>
666
@
667
@ <hr>
668
entry_attribute("Login expiration time", 6, "cookie-expire", "cex",
669
"8766", 0);
670
@ <p>The number of hours for which a login is valid. This must be a
671
@ positive number. The default is 8766 hours which is approximately equal
672
@ to a year.
673
@ (Property: "cookie-expire")</p>
674
675
@ <hr>
676
entry_attribute("Download packet limit", 10, "max-download", "mxdwn",
677
"5000000", 0);
678
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
679
@ to this many bytes, uncompressed. If the client requires more data
680
@ than this, then the client will issue multiple HTTP requests.
681
@ Values below 1 million are not recommended. 5 million is a
682
@ reasonable number. (Property: "max-download")</p>
683
684
@ <hr>
685
entry_attribute("Download time limit", 11, "max-download-time", "mxdwnt",
686
"30", 0);
687
688
@ <p>Fossil tries to spend less than this many seconds gathering
689
@ the out-bound data of sync, clone, and pull packets.
690
@ If the client request takes longer, a partial reply is given similar
691
@ to the download packet limit. 30s is a reasonable default.
692
@ (Property: "max-download-time")</p>
693
694
@ <a id="slal"></a>
695
@ <hr>
696
entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
697
"0.0", 0);
698
@ <p>Some expensive operations (such as computing tarballs, zip archives,
699
@ or annotation/blame pages) are prohibited if the load average on the host
700
@ computer is too large. Set the threshold for disallowing expensive
701
@ computations here. Set this to 0.0 to disable the load average limit.
702
@ This limit is only enforced on Unix servers. On Linux systems,
703
@ access to the /proc virtual filesystem is required, which means this limit
704
@ might not work inside a chroot() jail.
705
@ (Property: "max-loadavg")</p>
706
707
/* Add the auto-hyperlink settings controls. These same controls
708
** are also accessible from the /setup_robot page.
709
*/
710
@ <hr>
711
addAutoHyperlinkSettings();
712
713
@ <hr>
714
onoff_attribute("Require a CAPTCHA if not logged in",
715
"require-captcha", "reqcapt", 1, 0);
716
@ <p>Require a CAPTCHA for edit operations (appending, creating, or
717
@ editing wiki or tickets or adding attachments to wiki or tickets)
718
@ for users who are not logged in. (Property: "require-captcha")</p>
719
720
@ <hr>
721
entry_attribute("Public pages", 30, "public-pages",
722
"pubpage", "", 0);
723
@ <p>A comma-separated list of glob patterns for pages that are accessible
724
@ without needing a login and using the privileges given by the
725
@ "Default privileges" setting below.
726
@
727
@ <p>Example use case: Set this field to "/doc/trunk/www/*" and set
728
@ the "Default privileges" to include the "o" privilege
729
@ to give anonymous users read-only permission to the
730
@ latest version of the embedded documentation in the www/ folder without
731
@ allowing them to see the rest of the source code.
732
@ (Property: "public-pages")
733
@ </p>
734
735
@ <hr>
736
onoff_attribute("Allow users to register themselves",
737
"self-register", "selfreg", 0, 0);
738
@ <p>Allow users to register themselves on the /register webpage.
739
@ A self-registration creates a new entry in the USER table and
740
@ perhaps also in the SUBSCRIBER table if email notification is
741
@ enabled.
742
@ (Property: "self-register")</p>
743
744
@ <hr>
745
onoff_attribute("Allow users to reset their own passwords",
746
"self-pw-reset", "selfpw", 0, 0);
747
@ <p>Allow users to request that an email contains a hyperlink to a
748
@ password reset page be sent to their email address of record. This
749
@ enables forgetful users to recover their forgotten passwords without
750
@ administrator intervention.
751
@ (Property: "self-pw-reset")</p>
752
753
@ <hr>
754
onoff_attribute("Email verification required for self-registration",
755
"selfreg-verify", "sfverify", 0, 0);
756
@ <p>If enabled, self-registration creates a new entry in the USER table
757
@ with only capabilities "7". The default user capabilities are not
758
@ added until the email address associated with the self-registration
759
@ has been verified. This setting only makes sense if
760
@ email notifications are enabled.
761
@ (Property: "selfreg-verify")</p>
762
763
@ <hr>
764
onoff_attribute("Allow anonymous subscriptions",
765
"anon-subscribe", "anonsub", 1, 0);
766
@ <p>If disabled, email notification subscriptions are only allowed
767
@ for users with a login. If Nobody or Anonymous visit the /subscribe
768
@ page, they are redirected to /register or /login.
769
@ (Property: "anon-subscribe")</p>
770
771
@ <hr>
772
entry_attribute("Authorized subscription email addresses", 35,
773
"auth-sub-email", "asemail", "", 0);
774
@ <p>This is a comma-separated list of GLOB patterns that specify
775
@ email addresses that are authorized to subscriptions. If blank
776
@ (the usual case), then any email address can be used to self-register.
777
@ This setting is used to limit subscriptions to members of a particular
778
@ organization or group based on their email address.
779
@ (Property: "auth-sub-email")</p>
780
781
@ <hr>
782
entry_attribute("Default privileges", 10, "default-perms",
783
"defaultperms", "u", 0);
784
@ <p>Permissions given to users that... <ul><li>register themselves using
785
@ the self-registration procedure (if enabled), or <li>access "public"
786
@ pages identified by the public-pages glob pattern above, or <li>
787
@ are users newly created by the administrator.</ul>
788
@ <p>Recommended value: "u" for Reader.
789
@ <a href="%R/setup_ucap_list">Capability Key</a>.
790
@ (Property: "default-perms")
791
@ </p>
792
793
@ <hr>
794
onoff_attribute("Show javascript button to fill in CAPTCHA",
795
"auto-captcha", "autocaptcha", 0, 0);
796
@ <p>When enabled, a button appears on the login screen for user
797
@ "anonymous" that will automatically fill in the CAPTCHA password.
798
@ This is less secure than forcing the user to do it manually, but is
799
@ probably secure enough and it is certainly more convenient for
800
@ anonymous users. (Property: "auto-captcha")</p>
801
802
@ <hr>
803
entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
804
"anoncookls", "840", 0);
805
@ <p>The number of minutes for which an anonymous login cookie is valid.
806
@ Set to zero to disable anonymous logins.
807
@ (property: anon-cookie-lifespan)
808
809
@ <hr>
810
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
811
@ </div></form>
812
db_end_transaction(0);
813
style_finish_page();
814
}
815
816
/*
817
** WEBPAGE: setup_login_group
818
**
819
** Change how the current repository participates in a login
820
** group.
821
*/
822
void setup_login_group(void){
823
const char *zGroup;
824
char *zErrMsg = 0;
825
Stmt q;
826
Blob fullName;
827
char *zSelfRepo;
828
const char *zRepo = PD("repo", "");
829
const char *zLogin = PD("login", "");
830
const char *zPw = PD("pw", "");
831
const char *zNewName = PD("newname", "New Login Group");
832
833
login_check_credentials();
834
if( !g.perm.Setup ){
835
login_needed(0);
836
return;
837
}
838
file_canonical_name(g.zRepositoryName, &fullName, 0);
839
zSelfRepo = fossil_strdup(blob_str(&fullName));
840
blob_reset(&fullName);
841
if( P("join")!=0 ){
842
login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg);
843
}else if( P("leave") ){
844
login_group_leave(&zErrMsg);
845
}else if( P("rotate") ){
846
captcha_secret_rotate();
847
}
848
style_set_current_feature("setup");
849
style_header("Login Group Configuration");
850
if( zErrMsg ){
851
@ <p class="generalError">%s(zErrMsg)</p>
852
}
853
zGroup = login_group_name();
854
if( zGroup==0 ){
855
@ <p>This repository (in the file named "%h(zSelfRepo)")
856
@ is not currently part of any login-group.
857
@ To join a login group, fill out the form below.</p>
858
@
859
@ <form action="%R/setup_login_group" method="post"><div>
860
login_insert_csrf_secret();
861
@ <blockquote><table border="0">
862
@
863
@ <tr><th align="right" id="rfigtj">Repository filename \
864
@ in group to join:</th>
865
@ <td width="5"></td><td>
866
@ <input aria-labelledby="rfigtj" type="text" size="50" \
867
@ value="%h(zRepo)" name="repo"></td></tr>
868
@
869
@ <tr><th align="right" id="lotar">Login on the above repo:</th>
870
@ <td width="5"></td><td>
871
@ <input aria-labelledby="lotar" type="text" size="20" \
872
@ value="%h(zLogin)" name="login"></td></tr>
873
@
874
@ <tr><th align="right" id="lgpw">Password:</th>
875
@ <td width="5"></td><td>
876
@ <input aria-labelledby="lgpw" type="password" size="20" name="pw">\
877
@ </td></tr>
878
@
879
@ <tr><th align="right" id="nolg">Name of login-group:</th>
880
@ <td width="5"></td><td>
881
@ <input aria-labelledby="nolg" type="text" size="30" \
882
@ value="%h(zNewName)" name="newname">
883
@ (only used if creating a new login-group).</td></tr>
884
@
885
@ <tr><td colspan="3" align="center">
886
@ <input type="submit" value="Join" name="join"></td></tr>
887
@ </table></blockquote></div></form>
888
}else{
889
int n = 0;
890
@ <p>This repository (in the file "%h(zSelfRepo)")
891
@ is currently part of the "<b>%h(zGroup)</b>" login group.
892
@ Other repositories in that group are:</p>
893
@ <table border="0" cellspacing="4">
894
@ <tr><td colspan="2"><th align="left">Project Name<td>
895
@ <th align="left">Repository File</tr>
896
db_prepare(&q,
897
"SELECT value,"
898
" (SELECT value FROM config"
899
" WHERE name=('peer-name-' || substr(x.name,11)))"
900
" FROM config AS x"
901
" WHERE name GLOB 'peer-repo-*'"
902
" ORDER BY value"
903
);
904
while( db_step(&q)==SQLITE_ROW ){
905
const char *zRepo = db_column_text(&q, 0);
906
const char *zTitle = db_column_text(&q, 1);
907
n++;
908
@ <tr><td align="right">%d(n).</td><td width="4">
909
@ <td>%h(zTitle)<td width="10"><td>%h(zRepo)</tr>
910
}
911
db_finalize(&q);
912
@ </table>
913
@
914
@ <p><form action="%R/setup_login_group" method="post"><div>
915
login_insert_csrf_secret();
916
@ <p>To leave this login group press:
917
@ <input type="submit" value="Leave Login Group" name="leave">
918
@ <p>Setting a common captcha-secret on all repositories in the login-group
919
@ allows anonymous logins for one repository in the login group to be used
920
@ by all other repositories of the group within the same domain. Warning:
921
@ If a captcha dialog was painted before setting the common captcha-secret
922
@ and the "Speak password for 'anonymous'" button is pressed afterwards,
923
@ the spoken text will be incorrect.
924
@ <input type="submit" name="rotate" value="Set common captcha-secret">
925
@ </form></p>
926
}
927
@ <hr><h2>Implementation Details</h2>
928
@ <p>The following are fields from the CONFIG table related to login-groups.
929
@ </p>
930
@ <table border='1' cellspacing="0" cellpadding="4"\
931
@ class='sortable' data-column-types='ttt' data-init-sort='1'>
932
@ <thead><tr>
933
@ <th>Config.Name<th>Config.Value<th>Config.mtime</tr>
934
@ </thead><tbody>
935
db_prepare(&q, "SELECT name, value, datetime(mtime,'unixepoch') FROM config"
936
" WHERE name GLOB 'peer-*'"
937
" OR name GLOB 'project-*'"
938
" OR name GLOB 'login-group-*'"
939
" ORDER BY name");
940
while( db_step(&q)==SQLITE_ROW ){
941
@ <tr><td>%h(db_column_text(&q,0))</td>
942
@ <td>%h(db_column_text(&q,1))</td>
943
@ <td>%h(db_column_text(&q,2))</td></tr>
944
}
945
db_finalize(&q);
946
@ </tbody></table>
947
@ <h2>Interpretation</h2>
948
@ <ul>
949
@ <li><p><b>login-group-code</b> &rarr;
950
@ A random code assigned to each login-group. The login-group-code is
951
@ a unique identifier for the login-group.
952
@
953
@ <li><p><b>login-group-name</b> &rarr;
954
@ The human-readable name of the login-group.
955
@
956
@ <li><p><b>project-code</b> &rarr;
957
@ A random code assigned to each project. The project-code is
958
@ a unique identifier for the project. Multiple repositories can share
959
@ the same project-code. When two or more repositories have the same
960
@ project code, that mean those repositories are clones of each other.
961
@ Repositories are only able to sync if they share the same project-code.
962
@
963
@ <li><p><b>project-description</b> &rarr;
964
@ A description of project in this repository. This is a verbose form
965
@ of project-name. This description can be edited in the second entry
966
@ box on the <a href="./setup_config">Setup/Configuration page</a>.
967
@
968
@ <li><p><b>project-name</b> &rarr;
969
@ The human-readable name for the project. The project-name can be
970
@ modified in the first entry on the
971
@ <a href="./setup_config">Setup/Configuration page</a>.
972
@
973
@ <li><p><b>peer-repo-<i>CODE</i></b> &rarr;
974
@ <i>CODE</i> is 16-character prefix of the project-code for another
975
@ repository that is part of the same login-group. The value is the
976
@ filename for the peer repository.
977
@
978
@ <li><p><b>peer-name-<i>CODE</i></b> &rarr;
979
@ <i>CODE</i> is 16-character prefix of the project-code for another
980
@ repository that is part of the same login-group. The value is
981
@ project-name value for the other repository.
982
@ </ul>
983
style_table_sorter();
984
style_finish_page();
985
}
986
987
/*
988
** WEBPAGE: setup_timeline
989
**
990
** Edit administrative settings controlling the display of
991
** timelines.
992
*/
993
void setup_timeline(void){
994
double tmDiff;
995
char zTmDiff[20];
996
static const char *const azTimeFormats[] = {
997
"0", "HH:MM",
998
"1", "HH:MM:SS",
999
"2", "YYYY-MM-DD HH:MM",
1000
"3", "YYMMDD HH:MM",
1001
"4", "(off)"
1002
};
1003
static const char *const azLeafMark[] = {
1004
"0", "No",
1005
"1", "Yes",
1006
"2", "Yes - with emphasis",
1007
};
1008
login_check_credentials();
1009
if( !g.perm.Admin ){
1010
login_needed(0);
1011
return;
1012
}
1013
1014
style_set_current_feature("setup");
1015
style_header("Timeline Display Preferences");
1016
db_begin_transaction();
1017
@ <form action="%R/setup_timeline" method="post"><div>
1018
login_insert_csrf_secret();
1019
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
1020
1021
@ <hr>
1022
onoff_attribute("Plaintext comments on timelines",
1023
"timeline-plaintext", "tpt", 0, 0);
1024
@ <p>In timeline displays, check-in comments are displayed literally,
1025
@ without any wiki or HTML interpretation. Use CSS to change
1026
@ display formatting features such as fonts and line-wrapping behavior.
1027
@ (Property: "timeline-plaintext")</p>
1028
1029
@ <hr>
1030
onoff_attribute("Truncate comment at first blank line (Git-style)",
1031
"timeline-truncate-at-blank", "ttb", 0, 0);
1032
@ <p>In timeline displays, check-in comments are displayed only through
1033
@ the first blank line. This is the traditional way to display comments
1034
@ in Git repositories (Property: "timeline-truncate-at-blank")</p>
1035
1036
@ <hr>
1037
onoff_attribute("Break comments at newline characters",
1038
"timeline-hard-newlines", "thnl", 0, 0);
1039
@ <p>In timeline displays, newline characters in check-in comments force
1040
@ a line break on the display.
1041
@ (Property: "timeline-hard-newlines")</p>
1042
1043
@ <hr>
1044
onoff_attribute("Do not adjust user-selected background colors",
1045
"raw-bgcolor", "rbgc", 0, 0);
1046
@ <p>Fossil normally attempts to adjust the saturation and intensity of
1047
@ user-specified background colors on check-ins and branches so that the
1048
@ foreground text is easily readable on all skins. Enable this setting
1049
@ to omit that adjustment and use exactly the background color specified
1050
@ by users.
1051
@ (Property: "raw-bgcolor")</p>
1052
1053
@ <hr>
1054
onoff_attribute("Use Universal Coordinated Time (UTC)",
1055
"timeline-utc", "utc", 1, 0);
1056
@ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
1057
@ Zulu) instead of in local time. On this server, local time is currently
1058
tmDiff = db_double(0.0, "SELECT julianday('now')");
1059
tmDiff = db_double(0.0,
1060
"SELECT (julianday(%.17g,'localtime')-julianday(%.17g))*24.0",
1061
tmDiff, tmDiff);
1062
sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", tmDiff);
1063
if( strcmp(zTmDiff, "0.0")==0 ){
1064
@ the same as UTC and so this setting will make no difference in
1065
@ the display.</p>
1066
}else if( tmDiff<0.0 ){
1067
sqlite3_snprintf(sizeof(zTmDiff), zTmDiff, "%.1f", -tmDiff);
1068
@ %s(zTmDiff) hours behind UTC.</p>
1069
}else{
1070
@ %s(zTmDiff) hours ahead of UTC.</p>
1071
}
1072
@ <p>(Property: "timeline-utc")
1073
1074
@ <hr>
1075
multiple_choice_attribute("Style", "timeline-default-style",
1076
"tdss", "0", N_TIMELINE_VIEW_STYLE, timeline_view_styles);
1077
@ <p>The default timeline viewing style, for when the user has not
1078
@ specified an alternative. (Property: "timeline-default-style")</p>
1079
1080
@ <hr>
1081
entry_attribute("Default Number Of Rows", 6, "timeline-default-length",
1082
"tldl", "50", 0);
1083
@ <p>The maximum number of rows to show on a timeline in the absence
1084
@ of a user display preference cookie setting or an explicit n= query
1085
@ parameter. (Property: "timeline-default-length")</p>
1086
1087
@ <hr>
1088
multiple_choice_attribute("Per-Item Time Format", "timeline-date-format",
1089
"tdf", "0", count(azTimeFormats)/2, azTimeFormats);
1090
@ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown
1091
@ in a separate box (using CSS class "timelineDate") whenever the date
1092
@ changes. With the "YYYY-MM-DD&nbsp;HH:MM" and "YYMMDD ..." formats,
1093
@ the complete date and time is shown on every timeline entry using the
1094
@ CSS class "timelineTime". (Property: "timeline-date-format")</p>
1095
1096
@ <hr>
1097
multiple_choice_attribute("Leaf Markings", "timeline-mark-leaves",
1098
"tml", "1", count(azLeafMark)/2, azLeafMark);
1099
@ <p>Should timeline entries for leaf check-ins be identified in the
1100
@ detail section. (Property: "timeline-mark-leaves")</p>
1101
1102
@ <hr>
1103
entry_attribute("Max timeline comment length", 6,
1104
"timeline-max-comment", "tmc", "0", 0);
1105
@ <p>The maximum length of a comment to be displayed in a timeline.
1106
@ "0" there is no length limit.
1107
@ (Property: "timeline-max-comment")</p>
1108
1109
@ <hr>
1110
entry_attribute("Tooltip dwell time (milliseconds)", 6,
1111
"timeline-dwelltime", "tdt", "100", 0);
1112
@ <br>
1113
entry_attribute("Tooltip close time (milliseconds)", 6,
1114
"timeline-closetime", "tct", "250", 0);
1115
@ <p>The <strong>dwell time</strong> defines how long the mouse pointer
1116
@ should be stationary above an object of the graph before a tooltip
1117
@ appears.<br>
1118
@ The <strong>close time</strong> defines how long the mouse pointer
1119
@ can be away from an object before a tooltip is closed.</p>
1120
@ <p>Set <strong>dwell time</strong> to "0" to disable tooltips.<br>
1121
@ Set <strong>close time</strong> to "0" to keep tooltips visible until
1122
@ the mouse is clicked elsewhere.<p>
1123
@ <p>(Properties: "timeline-dwelltime", "timeline-closetime")</p>
1124
1125
@ <hr>
1126
onoff_attribute("Timestamp hyperlinks to /info",
1127
"timeline-tslink-info", "ttlti", 0, 0);
1128
@ <p>The hyperlink on the timestamp associated with each timeline entry,
1129
@ on the far left-hand side of the screen, normally targets another
1130
@ /timeline page that shows the entry in context. However, if this
1131
@ option is turned on, that hyperlink targets the /info page showing
1132
@ the details of the entry.
1133
@ <p>The /timeline link is the default since it is often useful to
1134
@ see an entry in context, and because that link is not otherwise
1135
@ accessible on the timeline. The /info link is also accessible by
1136
@ double-clicking the timeline node or by clicking on the hash that
1137
@ follows "check-in:" in the supplemental information section on the
1138
@ right of the entry.
1139
@ <p>(Properties: "timeline-tslink-info")
1140
1141
@ <hr>
1142
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
1143
@ </div></form>
1144
db_end_transaction(0);
1145
style_finish_page();
1146
}
1147
1148
/*
1149
** WEBPAGE: setup_settings
1150
**
1151
** Change or view miscellaneous settings. Part of the
1152
** /setup pages requiring Setup privileges.
1153
*/
1154
void setup_settings(void){
1155
int nSetting;
1156
int i;
1157
Setting const *pSet;
1158
int bIfChng = P("all")==0;
1159
const Setting *aSetting = setting_info(&nSetting);
1160
1161
login_check_credentials();
1162
if( !g.perm.Setup ){
1163
login_needed(0);
1164
return;
1165
}
1166
1167
style_set_current_feature("setup");
1168
style_header("Settings");
1169
if(!g.repositoryOpen){
1170
/* Provide read-only access to versioned settings,
1171
but only if no repo file was explicitly provided. */
1172
db_open_local(0);
1173
}
1174
db_begin_transaction();
1175
if( bIfChng ){
1176
@ <p>Only settings whose value is different from the default are shown.
1177
@ Click the "All" button above to set all settings.
1178
}
1179
@ <p>Settings marked with (v) are "versionable" and will be overridden
1180
@ by the contents of managed files named
1181
@ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
1182
@ If the file for a versionable setting exists, the value cannot be
1183
@ changed on this screen.</p><hr><p>
1184
@
1185
@ <form action="%R/setup_settings" method="post"><div>
1186
if( bIfChng ){
1187
style_submenu_element("All", "%R/setup_settings?all");
1188
}else{
1189
@ <input type="hidden" name="all" value="1">
1190
style_submenu_element("Changes-Only", "%R/setup_settings");
1191
}
1192
@ <table border="0"><tr><td valign="top">
1193
login_insert_csrf_secret();
1194
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
1195
if( pSet->width==0 ){
1196
int hasVersionableValue = pSet->versionable &&
1197
(db_get_versioned(pSet->name, NULL, NULL)!=0);
1198
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1199
continue;
1200
}
1201
onoff_attribute("", pSet->name,
1202
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
1203
is_truth(pSet->def), hasVersionableValue);
1204
@ <a href='%R/help/%s(pSet->name)'>%h(pSet->name)</a>
1205
if( pSet->versionable ){
1206
@ (v)<br>
1207
} else {
1208
@ <br>
1209
}
1210
}
1211
}
1212
@ <br><input type="submit" name="submit" value="Apply Changes">
1213
@ </td><td style="width:50px;"></td><td valign="top">
1214
@ <table>
1215
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
1216
if( pSet->width>0 && !pSet->forceTextArea ){
1217
int hasVersionableValue = pSet->versionable &&
1218
(db_get_versioned(pSet->name, NULL, NULL)!=0);
1219
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1220
continue;
1221
}
1222
@ <tr><td>
1223
@ <a href='%R/help/%s(pSet->name)'>%h(pSet->name)</a>
1224
if( pSet->versionable ){
1225
@ (v)
1226
} else {
1227
@
1228
}
1229
@</td><td>
1230
entry_attribute("", /*pSet->width*/ 25, pSet->name,
1231
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
1232
(char*)pSet->def, hasVersionableValue);
1233
@</td></tr>
1234
}
1235
}
1236
@</table>
1237
@ </td><td style="width:50px;"></td><td valign="top">
1238
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
1239
if( pSet->width>0 && pSet->forceTextArea ){
1240
int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0;
1241
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
1242
continue;
1243
}
1244
@ <a href='%R/help/%s(pSet->name)'>%s(pSet->name)</a>
1245
if( pSet->versionable ){
1246
@ (v)<br>
1247
} else {
1248
@ <br>
1249
}
1250
textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name,
1251
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
1252
(char*)pSet->def, hasVersionableValue);
1253
@<br>
1254
}
1255
}
1256
@ </td></tr></table>
1257
@ </div></form>
1258
db_end_transaction(0);
1259
style_finish_page();
1260
}
1261
1262
/*
1263
** SETTING: mainmenu width=70 block-text keep-empty
1264
**
1265
** The mainmenu setting specifies the entries on the main menu
1266
** for many skins. The mainmenu should be a TCL list. Each set
1267
** of four consecutive values defines a single main menu item:
1268
**
1269
** * The first term is text that appears on the menu.
1270
**
1271
** * The second term is a hyperlink to take when a user clicks on the
1272
** entry. Hyperlinks that start with "/" are relative to the
1273
** repository root.
1274
**
1275
** * The third term is an argument to the TH1 "capexpr" command.
1276
** If capexpr evaluates to true, then the entry is shown. If not,
1277
** the entry is omitted. "*" is always true. "{}" is never true.
1278
**
1279
** * The fourth term is a list of extra class names to apply to the
1280
** new menu entry. Some skins use classes "desktoponly" and
1281
** "wideonly" to only show the entries when the web browser
1282
** screen is wide or very wide, respectively.
1283
**
1284
** Some custom skins might not use this property. Whether the property
1285
** is used or not a choice made by the skin designer. Some skins may add
1286
** extra choices (such as the hamburger button) to the menu.
1287
*/
1288
/*
1289
** SETTING: sitemap-extra width=70 block-text
1290
**
1291
** The sitemap-extra setting defines extra links to appear on the
1292
** /sitemap web page as sub-items of the "Home Page" entry before the
1293
** "Documentation Search" entry (if any). For skins that use the /sitemap
1294
** page to construct a hamburger menu dropdown, new entries added here
1295
** will appear on the hamburger menu.
1296
**
1297
** This setting should be a TCL list divided into triples. Each
1298
** triple defines a new entry:
1299
**
1300
** * The first term is the display name of the /sitemap entry
1301
**
1302
** * The second term is a hyperlink to take when a user clicks on the
1303
** entry. Hyperlinks that start with "/" are relative to the
1304
** repository root.
1305
**
1306
** * The third term is an argument to the TH1 "capexpr" command.
1307
** If capexpr evaluates to true, then the entry is shown. If not,
1308
** the entry is omitted. "*" is always true.
1309
**
1310
** The default value is blank, meaning no added entries.
1311
*/
1312
1313
1314
/*
1315
** WEBPAGE: setup_config
1316
**
1317
** The "Admin/Configuration" page. Requires Setup privilege.
1318
*/
1319
void setup_config(void){
1320
login_check_credentials();
1321
if( !g.perm.Setup ){
1322
login_needed(0);
1323
return;
1324
}
1325
1326
style_set_current_feature("setup");
1327
style_header("WWW Configuration");
1328
db_begin_transaction();
1329
@ <form action="%R/setup_config" method="post"><div>
1330
login_insert_csrf_secret();
1331
@ <input type="submit" name="submit" value="Apply Changes"></p>
1332
@ <hr>
1333
entry_attribute("Project Name", 60, "project-name", "pn", "", 0);
1334
@ <p>A brief project name so visitors know what this site is about.
1335
@ The project name will also be used as the RSS feed title.
1336
@ (Property: "project-name")
1337
@ </p>
1338
@ <hr>
1339
textarea_attribute("Project Description", 3, 80,
1340
"project-description", "pd", "", 0);
1341
@ <p>Describe your project. This will be used in page headers for search
1342
@ engines, the repository listing and a short RSS description.
1343
@ (Property: "project-description")</p>
1344
@ <hr>
1345
entry_attribute("Canonical Server URL", 40, "email-url",
1346
"eurl", "", 0);
1347
@ <p>This is the URL used to access this repository as a server.
1348
@ Other repositories use this URL to clone or sync against this repository.
1349
@ This is also the basename for hyperlinks included in email alert text.
1350
@ Omit the trailing "/".
1351
@ If this repo will not be set up as a persistent server and will not
1352
@ be sending email alerts, then leave this entry blank.
1353
@ Suggested value: "%h(g.zBaseURL)"
1354
@ (Property: "email-url")</p>
1355
@ <hr>
1356
entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0);
1357
@ <p>Enter the pathname of the page to display when the "Home" menu
1358
@ option is selected and when no pathname is
1359
@ specified in the URL. For example, if you visit the url:</p>
1360
@
1361
@ <blockquote><p>%h(g.zBaseURL)</p></blockquote>
1362
@
1363
@ <p>And you have specified an index page of "/home" the above will
1364
@ automatically redirect to:</p>
1365
@
1366
@ <blockquote><p>%h(g.zBaseURL)/home</p></blockquote>
1367
@
1368
@ <p>The default "/home" page displays a Wiki page with the same name
1369
@ as the Project Name specified above. Some sites prefer to redirect
1370
@ to a documentation page (ex: "/doc/trunk/index.wiki") or to "/timeline".</p>
1371
@
1372
@ <p>Note: To avoid a redirect loop or other problems, this entry must
1373
@ begin with "/" and it must specify a valid page. For example,
1374
@ "<b>/home</b>" will work but "<b>home</b>" will not, since it omits the
1375
@ leading "/".</p>
1376
@ <p>(Property: "index-page")
1377
@ <hr>
1378
@ <p>The main menu for the web interface
1379
@ <p>
1380
@
1381
@ <p>This setting should be a TCL list. Each set of four consecutive
1382
@ values defines a single main menu item:
1383
@ <ol>
1384
@ <li> The first term is text that appears on the menu.
1385
@ <li> The second term is a hyperlink to take when a user clicks on the
1386
@ entry. Hyperlinks that start with "/" are relative to the
1387
@ repository root.
1388
@ <li> The third term is an argument to the TH1 "capexpr" command.
1389
@ If capexpr evaluates to true, then the entry is shown. If not,
1390
@ the entry is omitted. "*" is always true. "{}" is never true.
1391
@ <li> The fourth term is a list of extra class names to apply to the new
1392
@ menu entry. Some skins use classes "desktoponly" and "wideonly"
1393
@ to only show the entries when the web browser screen is wide or
1394
@ very wide, respectively.
1395
@ </ol>
1396
@
1397
@ <p>Some custom skins might not use this property. Whether the property
1398
@ is used or not a choice made by the skin designer. Some skins may add extra
1399
@ choices (such as the hamburger button) to the menu that are not shown
1400
@ on this list. (Property: mainmenu)
1401
@ <p>
1402
if(P("resetMenu")!=0){
1403
db_unset("mainmenu", 0);
1404
cgi_delete_parameter("mmenu");
1405
}
1406
textarea_attribute("Main Menu", 12, 80,
1407
"mainmenu", "mmenu", style_default_mainmenu(), 0);
1408
@ </p>
1409
@ <p><input type='checkbox' id='cbResetMenu' name='resetMenu' value='1'>
1410
@ <label for='cbResetMenu'>Reset menu to default value</label>
1411
@ </p>
1412
@ <hr>
1413
@ <p>Extra links to appear on the <a href="%R/sitemap">/sitemap</a> page,
1414
@ as sub-items of the "Home Page" entry, appearing before the
1415
@ "Documentation Search" entry (if any). In skins that use the /sitemap
1416
@ page to construct a hamburger menu dropdown, new entries added here
1417
@ will appear on the hamburger menu.
1418
@
1419
@ <p>This setting should be a TCL list divided into triples. Each
1420
@ triple defines a new entry:
1421
@ <ol>
1422
@ <li> The first term is the display name of the /sitemap entry
1423
@ <li> The second term is a hyperlink to take when a user clicks on the
1424
@ entry. Hyperlinks that start with "/" are relative to the
1425
@ repository root.
1426
@ <li> The third term is an argument to the TH1 "capexpr" command.
1427
@ If capexpr evaluates to true, then the entry is shown. If not,
1428
@ the entry is omitted. "*" is always true.
1429
@ </ol>
1430
@
1431
@ <p>The default value is blank, meaning no added entries.
1432
@ (Property: sitemap-extra)
1433
@ <p>
1434
textarea_attribute("Custom Sitemap Entries", 8, 80,
1435
"sitemap-extra", "smextra", "", 0);
1436
@ <hr>
1437
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
1438
@ </div></form>
1439
db_end_transaction(0);
1440
style_finish_page();
1441
}
1442
1443
/*
1444
** WEBPAGE: setup_download
1445
**
1446
** The "Admin/Download" page. Requires Setup privilege.
1447
*/
1448
void setup_download(void){
1449
login_check_credentials();
1450
if( !g.perm.Setup ){
1451
login_needed(0);
1452
return;
1453
}
1454
1455
style_set_current_feature("setup");
1456
style_header("Tarball and ZIP Downloads");
1457
db_begin_transaction();
1458
@ <form action="%R/setup_download" method="post"><div>
1459
login_insert_csrf_secret();
1460
@ <input type="submit" name="submit" value="Apply Changes"></p>
1461
@ <hr>
1462
entry_attribute("Tarball and ZIP Name Prefix", 20, "short-project-name",
1463
"spn", "", 0);
1464
@ <p>This is used as a prefix for the names of generated tarballs and
1465
@ ZIP archive. Keep this prefix brief and use only lower-case ASCII
1466
@ characters, digits, "_", "-" in the name. If this setting is blank,
1467
@ then the full <a href='%R/help/project-name'>project-name</a> setting
1468
@ is used instead.
1469
@ (Property: "short-project-name")
1470
@ </p>
1471
@ <hr>
1472
@ <p><b>Configuration for the <a href="%R/download">/download</a> page.</b>
1473
@ <p>The value is a TCL list divided into groups of four tokens:
1474
@ <ol>
1475
@ <li> Maximum number of matches (COUNT).
1476
@ <li> Tag to match using glob (TAG).
1477
@ <li> Maximum age of check-ins to match (MAX_AGE).
1478
@ <li> Comment to apply to matches (COMMENT).
1479
@ </ol>
1480
@ Each 4-tuple will match zero or more check-ins. The /download page
1481
@ displays the union of matches from all 4-tuples.
1482
@ See the <a href="%R/help/suggested-downloads">suggested-downloads</a>
1483
@ setting documentation for further detail.
1484
@ <p>
1485
@ The /download page is omitted from the <a href="%R/sitemap">/sitemap</a>
1486
@ if the first token is "0" or "off" or "no". The default value
1487
@ for this setting is "off".
1488
@ (Property: <a href="%R/help/suggested-downloads">suggested-downloads</a>)
1489
@ <p>
1490
textarea_attribute("", 4, 80,
1491
"suggested-downloads", "sgtrlst", "off", 0);
1492
@ <hr>
1493
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
1494
@ </div></form>
1495
db_end_transaction(0);
1496
style_finish_page();
1497
}
1498
1499
/*
1500
** WEBPAGE: setup_wiki
1501
**
1502
** The "Admin/Wiki" page. Requires Setup privilege.
1503
*/
1504
void setup_wiki(void){
1505
login_check_credentials();
1506
if( !g.perm.Setup ){
1507
login_needed(0);
1508
return;
1509
}
1510
1511
style_set_current_feature("setup");
1512
style_header("Wiki Configuration");
1513
db_begin_transaction();
1514
@ <form action="%R/setup_wiki" method="post"><div>
1515
login_insert_csrf_secret();
1516
@ <input type="submit" name="submit" value="Apply Changes"></p>
1517
@ <hr>
1518
onoff_attribute("Associate Wiki Pages With Branches, Tags, Tickets, or Checkins",
1519
"wiki-about", "wiki-about", 1, 0);
1520
@ <p>
1521
@ Associate wiki pages with branches, tags, tickets, or checkins, based on
1522
@ the wiki page name. Wiki pages that begin with "branch/", "checkin/",
1523
@ "tag/" or "ticket" and which continue with the name of an existing branch,
1524
@ check-in, tag or ticket are treated specially when this feature is enabled.
1525
@ <ul>
1526
@ <li> <b>branch/</b><i>branch-name</i>
1527
@ <li> <b>checkin/</b><i>full-check-in-hash</i>
1528
@ <li> <b>tag/</b><i>tag-name</i>
1529
@ <li> <b>ticket/</b><i>full-ticket-hash</i>
1530
@ </ul>
1531
@ (Property: "wiki-about")</p>
1532
@ <hr>
1533
entry_attribute("Allow Unsafe HTML In Markdown", 6,
1534
"safe-html", "safe-html", "", 0);
1535
@ <p>Allow "unsafe" HTML (ex: &lt;script&gt;, &lt;form&gt;, etc) to be
1536
@ generated by <a href="%R/md_rules">Markdown-formatted</a> documents.
1537
@ This setting is a string where each character indicates a "type" of
1538
@ document in which to allow unsafe HTML:
1539
@ <ul>
1540
@ <li> <b>b</b> &rarr; checked-in files, embedded documentation
1541
@ <li> <b>f</b> &rarr; forum posts
1542
@ <li> <b>t</b> &rarr; tickets
1543
@ <li> <b>w</b> &rarr; wiki pages
1544
@ </ul>
1545
@ Include letters for each type of document for which unsafe HTML should
1546
@ be allowed. For example, to allow unsafe HTML only for checked-in files,
1547
@ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except
1548
@ in forum posts, make this setting be "<b>btw</b>". The default is an
1549
@ empty string which means that Fossil never allows Markdown documents
1550
@ to generate unsafe HTML.
1551
@ (Property: "safe-html")</p>
1552
@ <hr>
1553
@ The current interwiki tag map is as follows:
1554
interwiki_append_map_table(cgi_output_blob());
1555
@ <p>Visit <a href="./intermap">%R/intermap</a> for details or to
1556
@ modify the interwiki tag map.
1557
@ <hr>
1558
onoff_attribute("Use HTML as wiki markup language",
1559
"wiki-use-html", "wiki-use-html", 0, 0);
1560
@ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
1561
@ but all other wiki formatting will be ignored.</p>
1562
@ <p><strong>CAUTION:</strong> when
1563
@ enabling, <i>all</i> HTML tags and attributes are accepted in the wiki.
1564
@ No sanitization is done. This means that it is very possible for malicious
1565
@ users to inject dangerous HTML, CSS and JavaScript code into your wiki.</p>
1566
@ <p>This should <strong>only</strong> be enabled when wiki editing is limited
1567
@ to trusted users. It should <strong>not</strong> be used on a publicly
1568
@ editable wiki.</p>
1569
@ (Property: "wiki-use-html")
1570
@ <hr>
1571
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
1572
@ </div></form>
1573
db_end_transaction(0);
1574
style_finish_page();
1575
}
1576
1577
/*
1578
** WEBPAGE: setup_chat
1579
**
1580
** The "Admin/Chat" page. Requires Setup privilege.
1581
*/
1582
void setup_chat(void){
1583
static const char *const azAlerts[] = {
1584
"alerts/plunk.wav", "Plunk",
1585
"alerts/bflat3.wav", "Tone-1",
1586
"alerts/bflat2.wav", "Tone-2",
1587
"alerts/bloop.wav", "Bloop",
1588
};
1589
1590
login_check_credentials();
1591
if( !g.perm.Setup ){
1592
login_needed(0);
1593
return;
1594
}
1595
1596
style_set_current_feature("setup");
1597
style_header("Chat Configuration");
1598
db_begin_transaction();
1599
if( P("rbldchatidx") && cgi_csrf_safe(2) ){
1600
chat_rebuild_index(1);
1601
}
1602
@ <form action="%R/setup_chat" method="post"><div>
1603
login_insert_csrf_secret();
1604
@ <input type="submit" name="submit" value="Apply Changes"></p>
1605
@ <hr>
1606
entry_attribute("Initial Chat History Size", 10,
1607
"chat-initial-history", "chatih", "50", 0);
1608
@ <p>When /chat first starts up, it preloads up to this many historical
1609
@ messages.
1610
@ (Property: "chat-initial-history")</p>
1611
@ <hr>
1612
entry_attribute("Minimum Number Of Historical Messages To Retain", 10,
1613
"chat-keep-count", "chatkc", "50", 0);
1614
@ <p>The chat subsystem purges older messages. But it will always retain
1615
@ the N most recent messages where N is the value of this setting.
1616
@ (Property: "chat-keep-count")</p>
1617
@ <hr>
1618
entry_attribute("Maximum Message Age In Days", 10,
1619
"chat-keep-days", "chatkd", "7", 0);
1620
@ <p>Chat message are removed after N days, where N is the value of
1621
@ this setting. N may be fractional. So, for example, to only keep
1622
@ an historical record of chat messages for 12 hours, set this value
1623
@ to 0.5.
1624
@ (Property: "chat-keep-days")</p>
1625
@ <hr>
1626
entry_attribute("Chat Polling Timeout", 10,
1627
"chat-poll-timeout", "chatpt", "420", 0);
1628
@ <p>New chat content is downloaded using the "long poll" technique.
1629
@ HTTP requests are made to /chat-poll which blocks waiting on new
1630
@ content to arrive. But the /chat-poll cannot block forever. It
1631
@ eventual must give up and return an empty message set. This setting
1632
@ determines how long /chat-poll will wait before giving up. The
1633
@ default setting of approximately 7 minutes works well on many systems.
1634
@ Shorter delays might be required on installations that use proxies
1635
@ or web-servers with short timeouts. For best efficiency, this value
1636
@ should be larger rather than smaller.
1637
@ (Property: "chat-poll-timeout")</p>
1638
@ <hr>
1639
entry_attribute("Chat Timeline Robot Username", 15,
1640
"chat-timeline-user", "chatrobot", "", 0);
1641
@ <p>If this setting is not an empty string, then any changes that appear
1642
@ on the timeline are announced in the chatroom under the username
1643
@ supplied. The username does not need to actually exist in the USER table.
1644
@ Suggested username: "chat-robot".
1645
@ (Property: "chat-timeline-user")</p>
1646
@ <hr>
1647
1648
multiple_choice_attribute("Alert sound",
1649
"chat-alert-sound", "snd", azAlerts[0],
1650
count(azAlerts)/2, azAlerts);
1651
@ <p>The sound used in the client-side chat to indicate that a new
1652
@ chat message has arrived.
1653
@ (Property: "chat-alert-sound")</p>
1654
1655
@ <hr/>
1656
@ <p><input type="submit" name="submit" value="Apply Changes">
1657
@ <input type="submit" name="rbldchatidx"\
1658
@ value="Rebuild Full-Text Search Index"></p>
1659
@ </div></form>
1660
1661
/* Validate the chat FTS search index */
1662
if( db_table_exists("repository","chatfts1") ){
1663
char *zMissing;
1664
zMissing = db_text(0,
1665
"SELECT group_concat(rowid,', ') FROM chat"
1666
" WHERE rowid NOT IN (SELECT rowid FROM chatfts1_docsize)"
1667
);
1668
if( zMissing && zMissing[0] ){
1669
@ <p><b>WARNING:</b> The following chat messages are missing
1670
@ from the full-text index. Press the "Rebuild Full-Text Search Index"
1671
@ button above to fix this.</p>
1672
@ <p>%h(zMissing)</p>
1673
}
1674
fossil_free(zMissing);
1675
}
1676
1677
db_end_transaction(0);
1678
@ <script nonce="%h(style_nonce())">
1679
@ (function(){
1680
@ var w = document.getElementById('idsnd');
1681
@ w.onchange = function(){
1682
@ var audio = new Audio('%s(g.zBaseURL)/builtin/' + w.value);
1683
@ audio.currentTime = 0;
1684
@ audio.play();
1685
@ }
1686
@ })();
1687
@ </script>
1688
style_finish_page();
1689
}
1690
1691
/*
1692
** WEBPAGE: setup_modreq
1693
**
1694
** Admin page for setting up moderation of tickets and wiki.
1695
*/
1696
void setup_modreq(void){
1697
login_check_credentials();
1698
if( !g.perm.Admin ){
1699
login_needed(0);
1700
return;
1701
}
1702
1703
style_set_current_feature("setup");
1704
style_header("Moderator For Wiki And Tickets");
1705
db_begin_transaction();
1706
@ <form action="%R/setup_modreq" method="post"><div>
1707
login_insert_csrf_secret();
1708
@ <hr>
1709
onoff_attribute("Moderate ticket changes",
1710
"modreq-tkt", "modreq-tkt", 0, 0);
1711
@ <p>When enabled, any change to tickets is subject to the approval
1712
@ by a ticket moderator - a user with the "q" or Mod-Tkt privilege.
1713
@ Ticket changes enter the system and are shown locally, but are not
1714
@ synced until they are approved. The moderator has the option to
1715
@ delete the change rather than approve it. Ticket changes made by
1716
@ a user who has the Mod-Tkt privilege are never subject to
1717
@ moderation. (Property: "modreq-tkt")
1718
@
1719
@ <hr>
1720
onoff_attribute("Moderate wiki changes",
1721
"modreq-wiki", "modreq-wiki", 0, 0);
1722
@ <p>When enabled, any change to wiki is subject to the approval
1723
@ by a wiki moderator - a user with the "l" or Mod-Wiki privilege.
1724
@ Wiki changes enter the system and are shown locally, but are not
1725
@ synced until they are approved. The moderator has the option to
1726
@ delete the change rather than approve it. Wiki changes made by
1727
@ a user who has the Mod-Wiki privilege are never subject to
1728
@ moderation. (Property: "modreq-wiki")
1729
@ </p>
1730
1731
@ <hr>
1732
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
1733
@ </div></form>
1734
db_end_transaction(0);
1735
style_finish_page();
1736
1737
}
1738
1739
/*
1740
** WEBPAGE: setup_adunit
1741
**
1742
** Administrative page for configuring and controlling ad units
1743
** and how they are displayed.
1744
*/
1745
void setup_adunit(void){
1746
login_check_credentials();
1747
if( !g.perm.Admin ){
1748
login_needed(0);
1749
return;
1750
}
1751
db_begin_transaction();
1752
if( P("clear")!=0 && cgi_csrf_safe(2) ){
1753
db_unprotect(PROTECT_CONFIG);
1754
db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'");
1755
db_protect_pop();
1756
cgi_replace_parameter("adunit","");
1757
cgi_replace_parameter("adright","");
1758
setup_incr_cfgcnt();
1759
}
1760
1761
style_set_current_feature("setup");
1762
style_header("Edit Ad Unit");
1763
@ <form action="%R/setup_adunit" method="post"><div>
1764
login_insert_csrf_secret();
1765
@ <b>Banner Ad-Unit:</b><br>
1766
textarea_attribute("", 6, 80, "adunit", "adunit", "", 0);
1767
@ <br>
1768
@ <b>Right-Column Ad-Unit:</b><br>
1769
textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0);
1770
@ <br>
1771
onoff_attribute("Omit ads to administrator",
1772
"adunit-omit-if-admin", "oia", 0, 0);
1773
@ <br>
1774
onoff_attribute("Omit ads to logged-in users",
1775
"adunit-omit-if-user", "oiu", 0, 0);
1776
@ <br>
1777
onoff_attribute("Temporarily disable all ads",
1778
"adunit-disable", "oall", 0, 0);
1779
@ <br>
1780
@ <input type="submit" name="submit" value="Apply Changes">
1781
@ <input type="submit" name="clear" value="Delete Ad-Unit">
1782
@ </div></form>
1783
@ <hr>
1784
@ <b>Ad-Unit Notes:</b><ul>
1785
@ <li>Leave both Ad-Units blank to disable all advertising.
1786
@ <li>The "Banner Ad-Unit" is used for wide pages.
1787
@ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content.
1788
@ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is
1789
@ used on all pages.
1790
@ <li>Properties: "adunit", "adunit-right", "adunit-omit-if-admin", and
1791
@ "adunit-omit-if-user".
1792
@ <li>Suggested <a href="setup_skinedit?w=0">CSS</a> changes:
1793
@ <blockquote><pre>
1794
@ div.adunit_banner {
1795
@ margin: auto;
1796
@ width: 100%%;
1797
@ }
1798
@ div.adunit_right {
1799
@ float: right;
1800
@ }
1801
@ div.adunit_right_container {
1802
@ min-height: <i>height-of-right-column-ad-unit</i>;
1803
@ }
1804
@ </pre></blockquote>
1805
@ <li>For a place-holder Ad-Unit for testing, Copy/Paste the following
1806
@ with appropriate adjustments to "width:" and "height:".
1807
@ <blockquote><pre>
1808
@ &lt;div style='
1809
@ margin: 0 auto;
1810
@ width: 600px;
1811
@ height: 90px;
1812
@ border: 1px solid #f11;
1813
@ background-color: #fcc;
1814
@ '&gt;Demo Ad&lt;/div&gt;
1815
@ </pre></blockquote>
1816
@ </li>
1817
style_finish_page();
1818
db_end_transaction(0);
1819
}
1820
1821
/*
1822
** WEBPAGE: setup_logo
1823
**
1824
** Administrative page for changing the logo, background, and icon images.
1825
*/
1826
void setup_logo(void){
1827
const char *zLogoMtime = db_get_mtime("logo-image", 0, 0);
1828
const char *zLogoMime = db_get("logo-mimetype","image/gif");
1829
const char *aLogoImg = P("logoim");
1830
int szLogoImg = atoi(PD("logoim:bytes","0"));
1831
const char *zBgMtime = db_get_mtime("background-image", 0, 0);
1832
const char *zBgMime = db_get("background-mimetype","image/gif");
1833
const char *aBgImg = P("bgim");
1834
int szBgImg = atoi(PD("bgim:bytes","0"));
1835
const char *zIconMtime = db_get_mtime("icon-image", 0, 0);
1836
const char *zIconMime = db_get("icon-mimetype","image/gif");
1837
const char *aIconImg = P("iconim");
1838
int szIconImg = atoi(PD("iconim:bytes","0"));
1839
if( szLogoImg>0 ){
1840
zLogoMime = PD("logoim:mimetype","image/gif");
1841
}
1842
if( szBgImg>0 ){
1843
zBgMime = PD("bgim:mimetype","image/gif");
1844
}
1845
if( szIconImg>0 ){
1846
zIconMime = PD("iconim:mimetype","image/gif");
1847
}
1848
login_check_credentials();
1849
if( !g.perm.Admin ){
1850
login_needed(0);
1851
return;
1852
}
1853
db_begin_transaction();
1854
if( !cgi_csrf_safe(2) ){
1855
/* Allow no state changes if not safe from CSRF */
1856
}else if( P("setlogo")!=0 && zLogoMime && zLogoMime[0] && szLogoImg>0 ){
1857
Blob img;
1858
Stmt ins;
1859
blob_init(&img, aLogoImg, szLogoImg);
1860
db_unprotect(PROTECT_CONFIG);
1861
db_prepare(&ins,
1862
"REPLACE INTO config(name,value,mtime)"
1863
" VALUES('logo-image',:bytes,now())"
1864
);
1865
db_bind_blob(&ins, ":bytes", &img);
1866
db_step(&ins);
1867
db_finalize(&ins);
1868
db_multi_exec(
1869
"REPLACE INTO config(name,value,mtime) VALUES('logo-mimetype',%Q,now())",
1870
zLogoMime
1871
);
1872
db_protect_pop();
1873
db_end_transaction(0);
1874
cgi_redirect("setup_logo");
1875
}else if( P("clrlogo")!=0 ){
1876
db_unprotect(PROTECT_CONFIG);
1877
db_multi_exec(
1878
"DELETE FROM config WHERE name IN "
1879
"('logo-image','logo-mimetype')"
1880
);
1881
db_protect_pop();
1882
db_end_transaction(0);
1883
cgi_redirect("setup_logo");
1884
}else if( P("setbg")!=0 && zBgMime && zBgMime[0] && szBgImg>0 ){
1885
Blob img;
1886
Stmt ins;
1887
blob_init(&img, aBgImg, szBgImg);
1888
db_unprotect(PROTECT_CONFIG);
1889
db_prepare(&ins,
1890
"REPLACE INTO config(name,value,mtime)"
1891
" VALUES('background-image',:bytes,now())"
1892
);
1893
db_bind_blob(&ins, ":bytes", &img);
1894
db_step(&ins);
1895
db_finalize(&ins);
1896
db_multi_exec(
1897
"REPLACE INTO config(name,value,mtime)"
1898
" VALUES('background-mimetype',%Q,now())",
1899
zBgMime
1900
);
1901
db_protect_pop();
1902
db_end_transaction(0);
1903
cgi_redirect("setup_logo");
1904
}else if( P("clrbg")!=0 ){
1905
db_unprotect(PROTECT_CONFIG);
1906
db_multi_exec(
1907
"DELETE FROM config WHERE name IN "
1908
"('background-image','background-mimetype')"
1909
);
1910
db_protect_pop();
1911
db_end_transaction(0);
1912
cgi_redirect("setup_logo");
1913
}else if( P("seticon")!=0 && zIconMime && zIconMime[0] && szIconImg>0 ){
1914
Blob img;
1915
Stmt ins;
1916
blob_init(&img, aIconImg, szIconImg);
1917
db_unprotect(PROTECT_CONFIG);
1918
db_prepare(&ins,
1919
"REPLACE INTO config(name,value,mtime)"
1920
" VALUES('icon-image',:bytes,now())"
1921
);
1922
db_bind_blob(&ins, ":bytes", &img);
1923
db_step(&ins);
1924
db_finalize(&ins);
1925
db_multi_exec(
1926
"REPLACE INTO config(name,value,mtime)"
1927
" VALUES('icon-mimetype',%Q,now())",
1928
zIconMime
1929
);
1930
db_protect_pop();
1931
db_end_transaction(0);
1932
cgi_redirect("setup_logo");
1933
}else if( P("clricon")!=0 ){
1934
db_unprotect(PROTECT_CONFIG);
1935
db_multi_exec(
1936
"DELETE FROM config WHERE name IN "
1937
"('icon-image','icon-mimetype')"
1938
);
1939
db_protect_pop();
1940
db_end_transaction(0);
1941
cgi_redirect("setup_logo");
1942
}
1943
style_set_current_feature("setup");
1944
style_header("Edit Project Logo And Background");
1945
@ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
1946
@ and looks like this:</p>
1947
@ <blockquote><p>
1948
@ <img src="%R/logo/%z(zLogoMtime)" alt="logo" border="1">
1949
@ </p></blockquote>
1950
@
1951
@ <form action="%R/setup_logo" method="post"
1952
@ enctype="multipart/form-data"><div>
1953
@ <p>The logo is accessible to all users at this URL:
1954
@ <a href="%s(g.zBaseURL)/logo">%s(g.zBaseURL)/logo</a>.
1955
@ The logo may or may not appear on each
1956
@ page depending on the <a href="setup_skinedit?w=0">CSS</a> and
1957
@ <a href="setup_skinedit?w=2">header setup</a>.
1958
@ To change the logo image, use the following form:</p>
1959
login_insert_csrf_secret();
1960
@ Logo Image file:
1961
@ <input type="file" name="logoim" size="60" accept="image/*">
1962
@ <p align="center">
1963
@ <input type="submit" name="setlogo" value="Change Logo">
1964
@ <input type="submit" name="clrlogo" value="Revert To Default"></p>
1965
@ <p>(Properties: "logo-image" and "logo-mimetype")
1966
@ </div></form>
1967
@ <hr>
1968
@
1969
@ <p>The current background image has a MIME-Type of <b>%h(zBgMime)</b>
1970
@ and looks like this:</p>
1971
@ <blockquote><p><img src="%R/background/%z(zBgMtime)" \
1972
@ alt="background" border=1>
1973
@ </p></blockquote>
1974
@
1975
@ <form action="%R/setup_logo" method="post"
1976
@ enctype="multipart/form-data"><div>
1977
@ <p>The background image is accessible to all users at this URL:
1978
@ <a href="%s(g.zBaseURL)/background">%s(g.zBaseURL)/background</a>.
1979
@ The background image may or may not appear on each
1980
@ page depending on the <a href="setup_skinedit?w=0">CSS</a> and
1981
@ <a href="setup_skinedit?w=2">header setup</a>.
1982
@ To change the background image, use the following form:</p>
1983
login_insert_csrf_secret();
1984
@ Background image file:
1985
@ <input type="file" name="bgim" size="60" accept="image/*">
1986
@ <p align="center">
1987
@ <input type="submit" name="setbg" value="Change Background">
1988
@ <input type="submit" name="clrbg" value="Revert To Default"></p>
1989
@ </div></form>
1990
@ <p>(Properties: "background-image" and "background-mimetype")
1991
@ <hr>
1992
@
1993
@ <p>The current icon image has a MIME-Type of <b>%h(zIconMime)</b>
1994
@ and looks like this:</p>
1995
@ <blockquote><p><img src="%R/favicon.ico/%z(zIconMtime)" \
1996
@ alt="icon" border=1>
1997
@ </p></blockquote>
1998
@
1999
@ <form action="%R/setup_logo" method="post"
2000
@ enctype="multipart/form-data"><div>
2001
@ <p>The icon image is accessible to all users at this URL:
2002
@ <a href="%s(g.zBaseURL)/favicon.ico">%s(g.zBaseURL)/favicon.ico</a>.
2003
@ The icon image may or may not appear on each
2004
@ page depending on the web browser in use and the MIME-Types that it
2005
@ supports for icon images.
2006
@ To change the icon image, use the following form:</p>
2007
login_insert_csrf_secret();
2008
@ Icon image file:
2009
@ <input type="file" name="iconim" size="60" accept="image/*">
2010
@ <p align="center">
2011
@ <input type="submit" name="seticon" value="Change Icon">
2012
@ <input type="submit" name="clricon" value="Revert To Default"></p>
2013
@ </div></form>
2014
@ <p>(Properties: "icon-image" and "icon-mimetype")
2015
@ <hr>
2016
@
2017
@ <p><span class="note">Note:</span> Your browser has probably cached these
2018
@ images, so you may need to press the Reload button before changes will
2019
@ take effect. </p>
2020
style_finish_page();
2021
db_end_transaction(0);
2022
}
2023
2024
/*
2025
** Prevent the RAW SQL feature from being used to ATTACH a different
2026
** database and query it.
2027
**
2028
** Actually, the RAW SQL feature only does a single statement per request.
2029
** So it is not possible to ATTACH and then do a separate query. This
2030
** routine is not strictly necessary, therefore. But it does not hurt
2031
** to be paranoid.
2032
*/
2033
int raw_sql_query_authorizer(
2034
void *pError,
2035
int code,
2036
const char *zArg1,
2037
const char *zArg2,
2038
const char *zArg3,
2039
const char *zArg4
2040
){
2041
if( code==SQLITE_ATTACH ){
2042
return SQLITE_DENY;
2043
}
2044
return SQLITE_OK;
2045
}
2046
2047
2048
/*
2049
** WEBPAGE: admin_sql
2050
**
2051
** Run raw SQL commands against the database file using the web interface.
2052
** Requires Setup privileges.
2053
*/
2054
void sql_page(void){
2055
const char *zQ;
2056
int go = P("go")!=0;
2057
login_check_credentials();
2058
if( !g.perm.Setup ){
2059
login_needed(0);
2060
return;
2061
}
2062
add_content_sql_commands(g.db);
2063
zQ = cgi_csrf_safe(2) ? P("q") : 0;
2064
style_set_current_feature("setup");
2065
style_header("Raw SQL Commands");
2066
@ <p><b>Caution:</b> There are no restrictions on the SQL that can be
2067
@ run by this page. You can do serious and irrepairable damage to the
2068
@ repository. Proceed with extreme caution.</p>
2069
@
2070
#if 0
2071
@ <p>Only the first statement in the entry box will be run.
2072
@ Any subsequent statements will be silently ignored.</p>
2073
@
2074
@ <p>Database names:<ul><li>repository
2075
if( g.zConfigDbName ){
2076
@ <li>configdb
2077
}
2078
if( g.localOpen ){
2079
@ <li>localdb
2080
}
2081
@ </ul></p>
2082
#endif
2083
2084
if( P("configtab") ){
2085
/* If the user presses the "CONFIG Table Query" button, populate the
2086
** query text with a pre-packaged query against the CONFIG table */
2087
zQ = "SELECT\n"
2088
" CASE WHEN length(name)<50 THEN name ELSE printf('%.50s...',name)"
2089
" END AS name,\n"
2090
" CASE WHEN typeof(value)<>'blob' AND length(value)<80 THEN value\n"
2091
" ELSE '...' END AS value,\n"
2092
" datetime(mtime, 'unixepoch') AS mtime\n"
2093
"FROM config\n"
2094
"-- ORDER BY mtime DESC; -- optional";
2095
go = 1;
2096
}
2097
@
2098
@ <form method="post" action="%R/admin_sql">
2099
login_insert_csrf_secret();
2100
@ SQL:<br>
2101
@ <textarea name="q" rows="8" cols="80">%h(zQ)</textarea><br>
2102
@ <input type="submit" name="go" value="Run SQL">
2103
@ <input type="submit" name="schema" value="Show Schema">
2104
@ <input type="submit" name="tablelist" value="List Tables">
2105
@ <input type="submit" name="configtab" value="CONFIG Table Query">
2106
@ </form>
2107
if( P("schema") ){
2108
zQ = sqlite3_mprintf(
2109
"SELECT sql FROM repository.sqlite_schema"
2110
" WHERE sql IS NOT NULL ORDER BY name");
2111
go = 1;
2112
}else if( P("tablelist") ){
2113
zQ = sqlite3_mprintf("SELECT*FROM pragma_table_list ORDER BY schema, name");
2114
go = 1;
2115
}
2116
if( go && cgi_csrf_safe(2) ){
2117
sqlite3_stmt *pStmt;
2118
int rc;
2119
const char *zTail;
2120
int nCol;
2121
int nRow = 0;
2122
int i;
2123
@ <hr>
2124
sqlite3_set_authorizer(g.db, raw_sql_query_authorizer, 0);
2125
search_sql_setup(g.db);
2126
rc = sqlite3_prepare_v2(g.db, zQ, -1, &pStmt, &zTail);
2127
if( rc!=SQLITE_OK ){
2128
@ <div class="generalError">%h(sqlite3_errmsg(g.db))</div>
2129
sqlite3_finalize(pStmt);
2130
}else if( pStmt==0 ){
2131
/* No-op */
2132
}else if( (nCol = sqlite3_column_count(pStmt))==0 ){
2133
sqlite3_step(pStmt);
2134
rc = sqlite3_finalize(pStmt);
2135
if( rc ){
2136
@ <div class="generalError">%h(sqlite3_errmsg(g.db))</div>
2137
}
2138
}else{
2139
@ <table border="1" cellpadding="4" cellspacing="0">
2140
while( sqlite3_step(pStmt)==SQLITE_ROW ){
2141
if( nRow==0 ){
2142
@ <tr>
2143
for(i=0; i<nCol; i++){
2144
@ <th>%h(sqlite3_column_name(pStmt, i))</th>
2145
}
2146
@ </tr>
2147
}
2148
nRow++;
2149
@ <tr>
2150
for(i=0; i<nCol; i++){
2151
switch( sqlite3_column_type(pStmt, i) ){
2152
case SQLITE_INTEGER:
2153
case SQLITE_FLOAT: {
2154
@ <td align="right" valign="top">
2155
@ %s(sqlite3_column_text(pStmt, i))</td>
2156
break;
2157
}
2158
case SQLITE_NULL: {
2159
@ <td valign="top" align="center"><i>NULL</i></td>
2160
break;
2161
}
2162
case SQLITE_TEXT: {
2163
const char *zText = (const char*)sqlite3_column_text(pStmt, i);
2164
@ <td align="left" valign="top"
2165
@ style="white-space:pre;">%h(zText)</td>
2166
break;
2167
}
2168
case SQLITE_BLOB: {
2169
@ <td valign="top" align="center">
2170
@ <i>%d(sqlite3_column_bytes(pStmt, i))-byte BLOB</i></td>
2171
break;
2172
}
2173
}
2174
}
2175
@ </tr>
2176
}
2177
sqlite3_finalize(pStmt);
2178
@ </table>
2179
}
2180
}
2181
style_finish_page();
2182
}
2183
2184
2185
/*
2186
** WEBPAGE: admin_th1
2187
**
2188
** Run raw TH1 commands using the web interface. If Tcl integration was
2189
** enabled at compile-time and the "tcl" setting is enabled, Tcl commands
2190
** may be run as well. Requires Admin privilege.
2191
*/
2192
void th1_page(void){
2193
const char *zQ = P("q");
2194
int go = P("go")!=0;
2195
login_check_credentials();
2196
if( !g.perm.Setup ){
2197
login_needed(0);
2198
return;
2199
}
2200
style_set_current_feature("setup");
2201
style_header("Raw TH1 Commands");
2202
@ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
2203
@ run by this page. If Tcl integration was enabled at compile-time and
2204
@ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
2205
@
2206
form_begin(0, "%R/admin_th1");
2207
@ TH1:<br>
2208
@ <textarea name="q" rows="5" cols="80">%h(zQ)</textarea><br>
2209
@ <input type="submit" name="go" value="Run TH1">
2210
@ </form>
2211
if( go && cgi_csrf_safe(2) ){
2212
const char *zR;
2213
int rc;
2214
int n;
2215
@ <hr>
2216
rc = Th_Eval(g.interp, 0, zQ, -1);
2217
zR = Th_GetResult(g.interp, &n);
2218
if( rc==TH_OK ){
2219
@ <pre class="th1result">%h(zR)</pre>
2220
}else{
2221
@ <pre class="th1error">%h(zR)</pre>
2222
}
2223
}
2224
style_finish_page();
2225
}
2226
2227
/*
2228
** WEBPAGE: admin_log
2229
**
2230
** Shows the contents of the admin_log table, which is only created if
2231
** the admin-log setting is enabled. Requires Admin or Setup ('a' or
2232
** 's') permissions.
2233
*/
2234
void page_admin_log(){
2235
Stmt stLog;
2236
int limit; /* How many entries to show */
2237
int ofst; /* Offset to the first entry */
2238
int fLogEnabled;
2239
int counter = 0;
2240
login_check_credentials();
2241
if( !g.perm.Admin ){
2242
login_needed(0);
2243
return;
2244
}
2245
style_set_current_feature("setup");
2246
style_header("Admin Log");
2247
style_submenu_element("Log-Menu", "setup-logmenu");
2248
create_admin_log_table();
2249
limit = atoi(PD("n","200"));
2250
ofst = atoi(PD("x","0"));
2251
fLogEnabled = db_get_boolean("admin-log", 1);
2252
@ <div>Admin logging is %s(fLogEnabled?"on":"off").
2253
@ (Change this on the <a href="setup_settings?all">settings</a> page.)</div>
2254
2255
if( ofst>0 ){
2256
int prevx = ofst - limit;
2257
if( prevx<0 ) prevx = 0;
2258
@ <p><a href="admin_log?n=%d(limit)&x=%d(prevx)">[Newer]</a></p>
2259
}
2260
db_prepare(&stLog,
2261
"SELECT datetime(time,'unixepoch'), who, page, what "
2262
"FROM admin_log "
2263
"ORDER BY time DESC, rowid DESC");
2264
style_table_sorter();
2265
@ <table class="sortable adminLogTable" width="100%%" \
2266
@ data-column-types='Tttx' data-init-sort='1'>
2267
@ <thead>
2268
@ <th>Time</th>
2269
@ <th>User</th>
2270
@ <th>Page</th>
2271
@ <th width="60%%">Message</th>
2272
@ </thead><tbody>
2273
while( SQLITE_ROW == db_step(&stLog) ){
2274
const char *zTime = db_column_text(&stLog, 0);
2275
const char *zUser = db_column_text(&stLog, 1);
2276
const char *zPage = db_column_text(&stLog, 2);
2277
const char *zMessage = db_column_text(&stLog, 3);
2278
counter++;
2279
if( counter<ofst ) continue;
2280
if( counter>ofst+limit ) break;
2281
@ <tr class="row%d(counter%2)">
2282
@ <td class="adminTime">%s(zTime)</td>
2283
@ <td>%s(zUser)</td>
2284
@ <td>%s(zPage)</td>
2285
@ <td>%h(zMessage)</td>
2286
@ </tr>
2287
}
2288
db_finalize(&stLog);
2289
@ </tbody></table>
2290
if( counter>ofst+limit ){
2291
@ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p>
2292
}
2293
style_finish_page();
2294
}
2295
2296
2297
/*
2298
** Renders a selection list of values for the search-tokenizer
2299
** setting, using the form field name "ftstok".
2300
*/
2301
static void select_fts_tokenizer(void){
2302
const char *const aTokenizer[] = {
2303
"off", "None",
2304
"porter", "Porter Stemmer",
2305
"unicode61", "Unicode without stemming",
2306
"trigram", "Trigram",
2307
};
2308
multiple_choice_attribute("FTS Tokenizer", "search-tokenizer",
2309
"ftstok", "off", 4, aTokenizer);
2310
}
2311
2312
/*
2313
** WEBPAGE: srchsetup
2314
**
2315
** Configure the search engine. Requires Admin privilege.
2316
*/
2317
void page_srchsetup(){
2318
const char *zMainBranch;
2319
login_check_credentials();
2320
if( !g.perm.Admin ){
2321
login_needed(0);
2322
return;
2323
}
2324
style_set_current_feature("setup");
2325
style_header("Search Configuration");
2326
@ <form action="%R/srchsetup" method="post"><div>
2327
login_insert_csrf_secret();
2328
@ <div style="text-align:center;font-weight:bold;">
2329
@ Server-specific settings that affect the
2330
@ <a href="%R/search">/search</a> webpage.
2331
@ </div>
2332
@ <hr>
2333
textarea_attribute("Document Glob List", 3, 35, "doc-glob", "dg", "", 0);
2334
@ <p>The "Document Glob List" is a comma- or newline-separated list
2335
@ of GLOB expressions that identify all documents within the source
2336
@ tree that are to be searched when "Document Search" is enabled.
2337
@ Some examples:
2338
@ <table border=0 cellpadding=2 align=center>
2339
@ <tr><td>*.wiki,*.html,*.md,*.txt<td style="width: 4x;">
2340
@ <td>Search all wiki, HTML, Markdown, and Text files</tr>
2341
@ <tr><td>doc/*.md,*/README.txt,README.txt<td>
2342
@ <td>Search all Markdown files in the doc/ subfolder and all README.txt
2343
@ files.</tr>
2344
@ <tr><td>*<td><td>Search all checked-in files</tr>
2345
@ <tr><td><i>(blank)</i><td>
2346
@ <td>Search nothing. (Disables document search).</tr>
2347
@ </table>
2348
@ <hr>
2349
zMainBranch = db_main_branch();
2350
entry_attribute("Document Branches", 20, "doc-branch", "db", zMainBranch, 0);
2351
@ <p>When searching documents, use the versions of the files found at the
2352
@ type of the "Document Branches" branch. Recommended value: the name of
2353
@ the main branch (usually "trunk").
2354
@ Document search is disabled if blank. It may be a list of branch names
2355
@ separated by spaces and/or commas.
2356
@ <hr>
2357
onoff_attribute("Search Check-in Comments", "search-ci", "sc", 0, 0);
2358
@ <br>
2359
onoff_attribute("Search Documents", "search-doc", "sd", 0, 0);
2360
@ <br>
2361
onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
2362
@ <br>
2363
onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0);
2364
@ <br>
2365
onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0);
2366
@ <br>
2367
onoff_attribute("Search Forum", "search-forum", "sf", 0, 0);
2368
@ <br>
2369
onoff_attribute("Search Built-in Help Text", "search-help", "sh", 0, 0);
2370
@ <hr>
2371
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
2372
@ <hr>
2373
if( cgi_csrf_safe(2) ){
2374
if( P("fts0") ){
2375
search_drop_index();
2376
}else if( P("fts1") ){
2377
const char *zTokenizer = PD("ftstok","off");
2378
search_set_tokenizer(zTokenizer);
2379
search_drop_index();
2380
search_create_index();
2381
search_fill_index();
2382
search_update_index(search_restrict(SRCH_ALL));
2383
}
2384
if( P("rbldchatidx") ){
2385
chat_rebuild_index(1);
2386
}
2387
}
2388
if( search_index_exists() ){
2389
int pgsz = db_int64(0, "PRAGMA repository.page_size;");
2390
i64 nTotal = db_int64(0, "PRAGMA repository.page_count;")*pgsz;
2391
i64 nFts = db_int64(0, "SELECT count(*) FROM dbstat"
2392
" WHERE schema='repository'"
2393
" AND name LIKE 'fts%%'")*pgsz;
2394
char zSize[30];
2395
approxSizeName(sizeof(zSize),zSize,nFts);
2396
@ <p>Currently using an SQLite FTS%d(search_index_type(0)) search index.
2397
@ The index helps search run faster, especially on large repositories,
2398
@ but takes up space. The index is currently using about %s(zSize)
2399
@ or %.1f(100.0*(double)nFts/(double)nTotal)%% of the repository.</p>
2400
select_fts_tokenizer();
2401
@ <p><input type="submit" name="fts0" value="Delete The Full-Text Index">
2402
@ <input type="submit" name="fts1" value="Rebuild The Full-Text Index">
2403
if( db_table_exists("repository","chat") ){
2404
@ <input type="submit" name="rbldchatidx" \
2405
@ value="Rebuild The Chat FTS Index">
2406
}
2407
style_submenu_element("FTS Index Debugging","%R/test-ftsdocs");
2408
}else{
2409
@ <p>The SQLite search index is disabled. All searching will be
2410
@ a full-text scan. This usually works fine, but can be slow for
2411
@ larger repositories.</p>
2412
select_fts_tokenizer();
2413
@ <p><input type="submit" name="fts1" value="Create A Full-Text Index">
2414
}
2415
@ </div></form>
2416
style_finish_page();
2417
}
2418
2419
/*
2420
** A URL Alias originally called zOldName is now zNewName/zValue.
2421
** Write SQL to make this change into pSql.
2422
**
2423
** If zNewName or zValue is an empty string, then delete the entry.
2424
**
2425
** If zOldName is an empty string, create a new entry.
2426
*/
2427
static void setup_update_url_alias(
2428
Blob *pSql,
2429
const char *zOldName,
2430
const char *zNewName,
2431
const char *zValue
2432
){
2433
if( !cgi_csrf_safe(2) ) return;
2434
if( zNewName[0]==0 || zValue[0]==0 ){
2435
if( zOldName[0] ){
2436
blob_append_sql(pSql,
2437
"DELETE FROM config WHERE name='walias:%q';\n",
2438
zOldName);
2439
}
2440
return;
2441
}
2442
if( zOldName[0]==0 ){
2443
blob_append_sql(pSql,
2444
"INSERT INTO config(name,value,mtime) VALUES('walias:%q',%Q,now());\n",
2445
zNewName, zValue);
2446
return;
2447
}
2448
if( strcmp(zOldName, zNewName)!=0 ){
2449
blob_append_sql(pSql,
2450
"UPDATE config SET name='walias:%q', value=%Q, mtime=now()"
2451
" WHERE name='walias:%q';\n",
2452
zNewName, zValue, zOldName);
2453
}else{
2454
blob_append_sql(pSql,
2455
"UPDATE config SET value=%Q, mtime=now()"
2456
" WHERE name='walias:%q' AND value<>%Q;\n",
2457
zValue, zOldName, zValue);
2458
}
2459
}
2460
2461
/*
2462
** WEBPAGE: waliassetup
2463
**
2464
** Configure the URL aliases
2465
*/
2466
void page_waliassetup(){
2467
Stmt q;
2468
int cnt = 0;
2469
Blob namelist;
2470
login_check_credentials();
2471
if( !g.perm.Admin ){
2472
login_needed(0);
2473
return;
2474
}
2475
style_set_current_feature("setup");
2476
style_header("URL Alias Configuration");
2477
if( P("submit")!=0 && cgi_csrf_safe(2) ){
2478
Blob token;
2479
Blob sql;
2480
const char *zNewName;
2481
const char *zValue;
2482
char zCnt[10];
2483
blob_init(&namelist, PD("namelist",""), -1);
2484
blob_init(&sql, 0, 0);
2485
while( blob_token(&namelist, &token) ){
2486
const char *zOldName = blob_str(&token);
2487
sqlite3_snprintf(sizeof(zCnt), zCnt, "n%d", cnt);
2488
zNewName = PD(zCnt, "");
2489
sqlite3_snprintf(sizeof(zCnt), zCnt, "v%d", cnt);
2490
zValue = PD(zCnt, "");
2491
setup_update_url_alias(&sql, zOldName, zNewName, zValue);
2492
cnt++;
2493
blob_reset(&token);
2494
}
2495
sqlite3_snprintf(sizeof(zCnt), zCnt, "n%d", cnt);
2496
zNewName = PD(zCnt,"");
2497
sqlite3_snprintf(sizeof(zCnt), zCnt, "v%d", cnt);
2498
zValue = PD(zCnt,"");
2499
setup_update_url_alias(&sql, "", zNewName, zValue);
2500
db_unprotect(PROTECT_CONFIG);
2501
db_multi_exec("%s", blob_sql_text(&sql));
2502
db_protect_pop();
2503
blob_reset(&sql);
2504
blob_reset(&namelist);
2505
cnt = 0;
2506
}
2507
db_prepare(&q,
2508
"SELECT substr(name,8), value FROM config WHERE name GLOB 'walias:/*'"
2509
" UNION ALL SELECT '', ''"
2510
);
2511
@ <form action="%R/waliassetup" method="post"><div>
2512
login_insert_csrf_secret();
2513
@ <table border=0 cellpadding=5>
2514
@ <tr><th>Alias<th>URI That The Alias Maps Into
2515
blob_init(&namelist, 0, 0);
2516
while( db_step(&q)==SQLITE_ROW ){
2517
const char *zName = db_column_text(&q, 0);
2518
const char *zValue = db_column_text(&q, 1);
2519
@ <tr><td>
2520
@ <input type='text' size='20' value='%h(zName)' name='n%d(cnt)'>
2521
@ </td><td>
2522
@ <input type='text' size='80' value='%h(zValue)' name='v%d(cnt)'>
2523
@ </td></tr>
2524
cnt++;
2525
if( blob_size(&namelist)>0 ) blob_append(&namelist, " ", 1);
2526
blob_append(&namelist, zName, -1);
2527
}
2528
db_finalize(&q);
2529
@ <tr><td>
2530
@ <input type='hidden' name='namelist' value='%h(blob_str(&namelist))'>
2531
@ <input type='submit' name='submit' value="Apply Changes">
2532
@ </td><td></td></tr>
2533
@ </table></form>
2534
@ <hr>
2535
@ <p>When the first term of an incoming URL exactly matches one of
2536
@ the "Aliases" on the left-hand side (LHS) above, the URL is
2537
@ converted into the corresponding form on the right-hand side (RHS).
2538
@ <ul>
2539
@ <li><p>
2540
@ The LHS is compared against only the first term of the incoming URL.
2541
@ All LHS entries in the alias table should therefore begin with a
2542
@ single "/" followed by a single path element.
2543
@ <li><p>
2544
@ The RHS entries in the alias table should begin with a single "/"
2545
@ followed by a path element, and optionally followed by "?" and a
2546
@ list of query parameters.
2547
@ <li><p>
2548
@ Query parameters on the RHS are added to the set of query parameters
2549
@ in the incoming URL.
2550
@ <li><p>
2551
@ If the same query parameter appears in both the incoming URL and
2552
@ on the RHS of the alias, the RHS query parameter value overwrites
2553
@ the value on the incoming URL.
2554
@ <li><p>
2555
@ If a query parameter on the RHS of the alias is of the form "X!"
2556
@ (a name followed by "!") then the X query parameter is removed
2557
@ from the incoming URL if
2558
@ it exists.
2559
@ <li><p>
2560
@ Only a single alias operation occurs. It is not possible to nest aliases.
2561
@ The RHS entries must be built-in webpage names.
2562
@ <li><p>
2563
@ The alias table is only checked if no built-in webpage matches
2564
@ the incoming URL.
2565
@ Hence, it is not possible to override a built-in webpage using aliases.
2566
@ This is by design.
2567
@ </ul>
2568
@
2569
@ <p>To delete an entry from the alias table, change its name or value to an
2570
@ empty string and press "Apply Changes".
2571
@
2572
@ <p>To add a new alias, fill in the name and value in the bottom row
2573
@ of the table above and press "Apply Changes".
2574
style_finish_page();
2575
}
2576

Keyboard Shortcuts

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