Fossil SCM

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

Keyboard Shortcuts

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