|
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><base href="$secureurl/$current_page"></tt> after |
|
86
|
@ <tt><head></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 <a> |
|
444
|
@ elements. In either case, <a> 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
|
@    <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> → |
|
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> → |
|
951
|
@ The human-readable name of the login-group. |
|
952
|
@ |
|
953
|
@ <li><p><b>project-code</b> → |
|
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> → |
|
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> → |
|
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> → |
|
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> → |
|
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 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: <script>, <form>, 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> → checked-in files, embedded documentation |
|
1538
|
@ <li> <b>f</b> → forum posts |
|
1539
|
@ <li> <b>t</b> → tickets |
|
1540
|
@ <li> <b>w</b> → 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
|
@ <div style=' |
|
1806
|
@ margin: 0 auto; |
|
1807
|
@ width: 600px; |
|
1808
|
@ height: 90px; |
|
1809
|
@ border: 1px solid #f11; |
|
1810
|
@ background-color: #fcc; |
|
1811
|
@ '>Demo Ad</div> |
|
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
|
|