|
dbda8d6…
|
drh
|
1 |
/* |
|
c19f34c…
|
drh
|
2 |
** Copyright (c) 2006,2007 D. Richard Hipp |
|
dbda8d6…
|
drh
|
3 |
** |
|
dbda8d6…
|
drh
|
4 |
** This program is free software; you can redistribute it and/or |
|
c06edd2…
|
drh
|
5 |
** modify it under the terms of the Simplified BSD License (also |
|
c06edd2…
|
drh
|
6 |
** known as the "2-Clause License" or "FreeBSD License".) |
|
c06edd2…
|
drh
|
7 |
|
|
dbda8d6…
|
drh
|
8 |
** This program is distributed in the hope that it will be useful, |
|
c06edd2…
|
drh
|
9 |
** but without any warranty; without even the implied warranty of |
|
c06edd2…
|
drh
|
10 |
** merchantability or fitness for a particular purpose. |
|
dbda8d6…
|
drh
|
11 |
** |
|
dbda8d6…
|
drh
|
12 |
** Author contact information: |
|
dbda8d6…
|
drh
|
13 |
** [email protected] |
|
dbda8d6…
|
drh
|
14 |
** http://www.hwaci.com/drh/ |
|
dbda8d6…
|
drh
|
15 |
** |
|
dbda8d6…
|
drh
|
16 |
******************************************************************************* |
|
dbda8d6…
|
drh
|
17 |
** |
|
dbda8d6…
|
drh
|
18 |
** This file contains code to implement the basic web page look and feel. |
|
dbda8d6…
|
drh
|
19 |
** |
|
dbda8d6…
|
drh
|
20 |
*/ |
|
b065aff…
|
mistachkin
|
21 |
#include "VERSION.h" |
|
dbda8d6…
|
drh
|
22 |
#include "config.h" |
|
dbda8d6…
|
drh
|
23 |
#include "style.h" |
|
dbda8d6…
|
drh
|
24 |
|
|
dbda8d6…
|
drh
|
25 |
/* |
|
dbda8d6…
|
drh
|
26 |
** Elements of the submenu are collected into the following |
|
c0c0bae…
|
drh
|
27 |
** structure and displayed below the main menu. |
|
dbda8d6…
|
drh
|
28 |
** |
|
b8e3dc1…
|
jan.nijtmans
|
29 |
** Populate these structure with calls to |
|
c0c0bae…
|
drh
|
30 |
** |
|
c0c0bae…
|
drh
|
31 |
** style_submenu_element() |
|
c0c0bae…
|
drh
|
32 |
** style_submenu_entry() |
|
c0c0bae…
|
drh
|
33 |
** style_submenu_checkbox() |
|
3cb9ba4…
|
andygoth
|
34 |
** style_submenu_binary() |
|
c0c0bae…
|
drh
|
35 |
** style_submenu_multichoice() |
|
3cb9ba4…
|
andygoth
|
36 |
** style_submenu_sql() |
|
c0c0bae…
|
drh
|
37 |
** |
|
e6e8ea8…
|
wyoung
|
38 |
** prior to calling style_finish_page(). The style_finish_page() routine |
|
c0c0bae…
|
drh
|
39 |
** will generate the appropriate HTML text just below the main |
|
c0c0bae…
|
drh
|
40 |
** menu. |
|
dbda8d6…
|
drh
|
41 |
*/ |
|
dbda8d6…
|
drh
|
42 |
static struct Submenu { |
|
c0c0bae…
|
drh
|
43 |
const char *zLabel; /* Button label */ |
|
c0c0bae…
|
drh
|
44 |
const char *zLink; /* Jump to this link when button is pressed */ |
|
dbda8d6…
|
drh
|
45 |
} aSubmenu[30]; |
|
c0c0bae…
|
drh
|
46 |
static int nSubmenu = 0; /* Number of buttons */ |
|
c0c0bae…
|
drh
|
47 |
static struct SubmenuCtrl { |
|
037e06b…
|
drh
|
48 |
const char *zName; /* Form query parameter */ |
|
037e06b…
|
drh
|
49 |
const char *zLabel; /* Label. Might be NULL for FF_MULTI */ |
|
259074d…
|
drh
|
50 |
unsigned char eType; /* FF_ENTRY, FF_MULTI, FF_CHECKBOX */ |
|
259074d…
|
drh
|
51 |
unsigned char eVisible; /* STYLE_NORMAL or STYLE_DISABLED */ |
|
037e06b…
|
drh
|
52 |
short int iSize; /* Width for FF_ENTRY. Count for FF_MULTI */ |
|
037e06b…
|
drh
|
53 |
const char *const *azChoice; /* value/display pairs for FF_MULTI */ |
|
037e06b…
|
drh
|
54 |
const char *zFalse; /* FF_BINARY label when false */ |
|
037e06b…
|
drh
|
55 |
const char *zJS; /* Javascript to run on toggle */ |
|
c0c0bae…
|
drh
|
56 |
} aSubmenuCtrl[20]; |
|
c0c0bae…
|
drh
|
57 |
static int nSubmenuCtrl = 0; |
|
e91d267…
|
drh
|
58 |
#define FF_ENTRY 1 /* Text entry box */ |
|
e91d267…
|
drh
|
59 |
#define FF_MULTI 2 /* Combobox. Multiple choices. */ |
|
259074d…
|
drh
|
60 |
#define FF_BINARY 3 /* Control for binary query parameter */ |
|
259074d…
|
drh
|
61 |
#define FF_CHECKBOX 4 /* Check-box */ |
|
e91d267…
|
drh
|
62 |
|
|
e91d267…
|
drh
|
63 |
#if INTERFACE |
|
e91d267…
|
drh
|
64 |
#define STYLE_NORMAL 0 /* Normal display of control */ |
|
e91d267…
|
drh
|
65 |
#define STYLE_DISABLED 1 /* Control is disabled */ |
|
e91d267…
|
drh
|
66 |
#endif /* INTERFACE */ |
|
dcc4866…
|
drh
|
67 |
|
|
dcc4866…
|
drh
|
68 |
/* |
|
dcc4866…
|
drh
|
69 |
** Remember that the header has been generated. The footer is omitted |
|
dcc4866…
|
drh
|
70 |
** if an error occurs before the header. |
|
dcc4866…
|
drh
|
71 |
*/ |
|
dcc4866…
|
drh
|
72 |
static int headerHasBeenGenerated = 0; |
|
dcc4866…
|
drh
|
73 |
|
|
dcc4866…
|
drh
|
74 |
/* |
|
5a48a9b…
|
drh
|
75 |
** remember, if a sidebox was used |
|
5a48a9b…
|
drh
|
76 |
*/ |
|
5a48a9b…
|
drh
|
77 |
static int sideboxUsed = 0; |
|
5a48a9b…
|
drh
|
78 |
|
|
ff78d6d…
|
drh
|
79 |
/* |
|
ff78d6d…
|
drh
|
80 |
** Ad-unit styles. |
|
ff78d6d…
|
drh
|
81 |
*/ |
|
ff78d6d…
|
drh
|
82 |
static unsigned adUnitFlags = 0; |
|
ff78d6d…
|
drh
|
83 |
|
|
09494b0…
|
drh
|
84 |
/* |
|
220ed0a…
|
drh
|
85 |
** Submenu disable flag |
|
220ed0a…
|
drh
|
86 |
*/ |
|
220ed0a…
|
drh
|
87 |
static int submenuEnable = 1; |
|
220ed0a…
|
drh
|
88 |
|
|
220ed0a…
|
drh
|
89 |
/* |
|
6b645d6…
|
drh
|
90 |
** Flags for various javascript files needed prior to </body> |
|
6b645d6…
|
drh
|
91 |
*/ |
|
6908832…
|
drh
|
92 |
static int needHrefJs = 0; /* href.js */ |
|
ff747b5…
|
drh
|
93 |
|
|
ff747b5…
|
drh
|
94 |
/* |
|
ff747b5…
|
drh
|
95 |
** Extra JS added to the end of the file. |
|
ff747b5…
|
drh
|
96 |
*/ |
|
ff747b5…
|
drh
|
97 |
static Blob blobOnLoad = BLOB_INITIALIZER; |
|
433cde1…
|
drh
|
98 |
|
|
433cde1…
|
drh
|
99 |
/* |
|
99a319b…
|
wyoung
|
100 |
** Generate and return an anchor tag like this: |
|
433cde1…
|
drh
|
101 |
** |
|
433cde1…
|
drh
|
102 |
** <a href="URL"> |
|
433cde1…
|
drh
|
103 |
** or <a id="ID"> |
|
433cde1…
|
drh
|
104 |
** |
|
df337eb…
|
drh
|
105 |
** The form of the anchor tag is determined by the g.jsHref |
|
8dd7542…
|
drh
|
106 |
** and g.perm.Hyperlink variables. |
|
8dd7542…
|
drh
|
107 |
** |
|
df337eb…
|
drh
|
108 |
** g.perm.Hyperlink g.jsHref Returned anchor format |
|
df337eb…
|
drh
|
109 |
** ---------------- -------- ------------------------ |
|
df337eb…
|
drh
|
110 |
** 0 0 (empty string) |
|
df337eb…
|
drh
|
111 |
** 0 1 (empty string) |
|
df337eb…
|
drh
|
112 |
** 1 0 <a href="URL"> |
|
df337eb…
|
drh
|
113 |
** 1 1 <a data-href="URL"> |
|
8dd7542…
|
drh
|
114 |
** |
|
8dd7542…
|
drh
|
115 |
** No anchor tag is generated if g.perm.Hyperlink is false. |
|
df337eb…
|
drh
|
116 |
** The href="URL" form is used if g.jsHref is false. |
|
df337eb…
|
drh
|
117 |
** If g.jsHref is true then the data-href="URL" and |
|
8d4e114…
|
drh
|
118 |
** href="/honeypot" is generated and javascript is added to the footer |
|
8d4e114…
|
drh
|
119 |
** to cause data-href values to be inserted into href |
|
8d4e114…
|
drh
|
120 |
** after the page has loaded. The use of the data-href="URL" form |
|
8dd7542…
|
drh
|
121 |
** instead of href="URL" is a defense against bots. |
|
54085d5…
|
drh
|
122 |
** |
|
54085d5…
|
drh
|
123 |
** If the user lacks the Hyperlink (h) property and the "auto-hyperlink" |
|
54085d5…
|
drh
|
124 |
** setting is true, then g.perm.Hyperlink is changed from 0 to 1 and |
|
df337eb…
|
drh
|
125 |
** g.jsHref is set to 1 by login_check_credentials(). Thus |
|
8dd7542…
|
drh
|
126 |
** the g.perm.Hyperlink property will be true even if the user does not |
|
8dd7542…
|
drh
|
127 |
** have the "h" privilege if the "auto-hyperlink" setting is true. |
|
8dd7542…
|
drh
|
128 |
** |
|
df337eb…
|
drh
|
129 |
** User has "h" auto-hyperlink g.perm.Hyperlink g.jsHref |
|
8dd7542…
|
drh
|
130 |
** ------------ -------------- ---------------- --------------------- |
|
8dd7542…
|
drh
|
131 |
** 0 0 0 0 |
|
8dd7542…
|
drh
|
132 |
** 1 0 1 0 |
|
8dd7542…
|
drh
|
133 |
** 0 1 1 1 |
|
8dd7542…
|
drh
|
134 |
** 1 1 1 0 |
|
8dd7542…
|
drh
|
135 |
** |
|
8dd7542…
|
drh
|
136 |
** So, in other words, tracing input configuration to final actions we have: |
|
8dd7542…
|
drh
|
137 |
** |
|
8dd7542…
|
drh
|
138 |
** User has "h" auto-hyperlink Returned anchor format |
|
8dd7542…
|
drh
|
139 |
** ------------ -------------- ---------------------- |
|
8dd7542…
|
drh
|
140 |
** 0 0 (empty string) |
|
8dd7542…
|
drh
|
141 |
** 1 0 <a href="URL"> |
|
8d4e114…
|
drh
|
142 |
** 0 1 <a data-href="URL"> |
|
8d4e114…
|
drh
|
143 |
** 1 1 <a href="URL"> |
|
54085d5…
|
drh
|
144 |
** |
|
8dd7542…
|
drh
|
145 |
** The name of these routines are deliberately kept short so that can be |
|
433cde1…
|
drh
|
146 |
** easily used within @-lines. Example: |
|
433cde1…
|
drh
|
147 |
** |
|
433cde1…
|
drh
|
148 |
** @ %z(href("%R/artifact/%s",zUuid))%h(zFN)</a> |
|
433cde1…
|
drh
|
149 |
** |
|
433cde1…
|
drh
|
150 |
** Note %z format. The string returned by this function is always |
|
433cde1…
|
drh
|
151 |
** obtained from fossil_malloc() so rendering it with %z will reclaim |
|
433cde1…
|
drh
|
152 |
** that memory space. |
|
433cde1…
|
drh
|
153 |
** |
|
8394d2f…
|
drh
|
154 |
** There are three versions of this routine: |
|
8394d2f…
|
drh
|
155 |
** |
|
8394d2f…
|
drh
|
156 |
** (1) href() does a plain hyperlink |
|
8394d2f…
|
drh
|
157 |
** (2) xhref() adds extra attribute text |
|
8394d2f…
|
drh
|
158 |
** (3) chref() adds a class name |
|
54085d5…
|
drh
|
159 |
** |
|
54085d5…
|
drh
|
160 |
** g.perm.Hyperlink is true if the user has the Hyperlink (h) property. |
|
54085d5…
|
drh
|
161 |
** Most logged in users should have this property, since we can assume |
|
54085d5…
|
drh
|
162 |
** that a logged in user is not a bot. Only "nobody" lacks g.perm.Hyperlink, |
|
54085d5…
|
drh
|
163 |
** typically. |
|
433cde1…
|
drh
|
164 |
*/ |
|
433cde1…
|
drh
|
165 |
char *xhref(const char *zExtra, const char *zFormat, ...){ |
|
433cde1…
|
drh
|
166 |
char *zUrl; |
|
433cde1…
|
drh
|
167 |
va_list ap; |
|
b190858…
|
drh
|
168 |
if( !g.perm.Hyperlink ) return fossil_strdup(""); |
|
8394d2f…
|
drh
|
169 |
va_start(ap, zFormat); |
|
8394d2f…
|
drh
|
170 |
zUrl = vmprintf(zFormat, ap); |
|
8394d2f…
|
drh
|
171 |
va_end(ap); |
|
df337eb…
|
drh
|
172 |
if( !g.jsHref ){ |
|
774fb77…
|
drh
|
173 |
char *zHUrl; |
|
774fb77…
|
drh
|
174 |
if( zExtra ){ |
|
774fb77…
|
drh
|
175 |
zHUrl = mprintf("<a %s href=\"%h\">", zExtra, zUrl); |
|
774fb77…
|
drh
|
176 |
}else{ |
|
774fb77…
|
drh
|
177 |
zHUrl = mprintf("<a href=\"%h\">", zUrl); |
|
774fb77…
|
drh
|
178 |
} |
|
8394d2f…
|
drh
|
179 |
fossil_free(zUrl); |
|
8394d2f…
|
drh
|
180 |
return zHUrl; |
|
8394d2f…
|
drh
|
181 |
} |
|
09494b0…
|
drh
|
182 |
needHrefJs = 1; |
|
774fb77…
|
drh
|
183 |
if( zExtra==0 ){ |
|
774fb77…
|
drh
|
184 |
return mprintf("<a data-href='%z' href='%R/honeypot'>", zUrl); |
|
774fb77…
|
drh
|
185 |
}else{ |
|
774fb77…
|
drh
|
186 |
return mprintf("<a %s data-href='%z' href='%R/honeypot'>", |
|
774fb77…
|
drh
|
187 |
zExtra, zUrl); |
|
774fb77…
|
drh
|
188 |
} |
|
8394d2f…
|
drh
|
189 |
} |
|
8394d2f…
|
drh
|
190 |
char *chref(const char *zExtra, const char *zFormat, ...){ |
|
8394d2f…
|
drh
|
191 |
char *zUrl; |
|
8394d2f…
|
drh
|
192 |
va_list ap; |
|
b190858…
|
drh
|
193 |
if( !g.perm.Hyperlink ) return fossil_strdup(""); |
|
433cde1…
|
drh
|
194 |
va_start(ap, zFormat); |
|
433cde1…
|
drh
|
195 |
zUrl = vmprintf(zFormat, ap); |
|
433cde1…
|
drh
|
196 |
va_end(ap); |
|
df337eb…
|
drh
|
197 |
if( !g.jsHref ){ |
|
f54b4bf…
|
drh
|
198 |
char *zHUrl = mprintf("<a class=\"%s\" href=\"%h\">", zExtra, zUrl); |
|
d5c4684…
|
drh
|
199 |
fossil_free(zUrl); |
|
d5c4684…
|
drh
|
200 |
return zHUrl; |
|
d5c4684…
|
drh
|
201 |
} |
|
09494b0…
|
drh
|
202 |
needHrefJs = 1; |
|
8394d2f…
|
drh
|
203 |
return mprintf("<a class='%s' data-href='%z' href='%R/honeypot'>", |
|
8394d2f…
|
drh
|
204 |
zExtra, zUrl); |
|
433cde1…
|
drh
|
205 |
} |
|
433cde1…
|
drh
|
206 |
char *href(const char *zFormat, ...){ |
|
433cde1…
|
drh
|
207 |
char *zUrl; |
|
433cde1…
|
drh
|
208 |
va_list ap; |
|
b190858…
|
drh
|
209 |
if( !g.perm.Hyperlink ) return fossil_strdup(""); |
|
433cde1…
|
drh
|
210 |
va_start(ap, zFormat); |
|
433cde1…
|
drh
|
211 |
zUrl = vmprintf(zFormat, ap); |
|
433cde1…
|
drh
|
212 |
va_end(ap); |
|
df337eb…
|
drh
|
213 |
if( !g.jsHref ){ |
|
d5c4684…
|
drh
|
214 |
char *zHUrl = mprintf("<a href=\"%h\">", zUrl); |
|
d5c4684…
|
drh
|
215 |
fossil_free(zUrl); |
|
d5c4684…
|
drh
|
216 |
return zHUrl; |
|
d5c4684…
|
drh
|
217 |
} |
|
09494b0…
|
drh
|
218 |
needHrefJs = 1; |
|
8394d2f…
|
drh
|
219 |
return mprintf("<a data-href='%s' href='%R/honeypot'>", |
|
8394d2f…
|
drh
|
220 |
zUrl); |
|
433cde1…
|
drh
|
221 |
} |
|
433cde1…
|
drh
|
222 |
|
|
433cde1…
|
drh
|
223 |
/* |
|
8dd7542…
|
drh
|
224 |
** Generate <form method="post" action=ARG>. The ARG value is determined |
|
8dd7542…
|
drh
|
225 |
** by the arguments. |
|
8dd7542…
|
drh
|
226 |
** |
|
8dd7542…
|
drh
|
227 |
** As a defense against robots, the action=ARG might instead by data-action=ARG |
|
8dd7542…
|
drh
|
228 |
** and javascript (href.js) added to the page so that the data-action= is |
|
8dd7542…
|
drh
|
229 |
** changed into action= after the page loads. Whether or not this happens |
|
8dd7542…
|
drh
|
230 |
** depends on if the user has the "h" privilege and whether or not the |
|
e2bdc10…
|
danield
|
231 |
** auto-hyperlink setting is on. These settings determine the values of |
|
df337eb…
|
drh
|
232 |
** variables g.perm.Hyperlink and g.jsHref. |
|
8dd7542…
|
drh
|
233 |
** |
|
df337eb…
|
drh
|
234 |
** User has "h" auto-hyperlink g.perm.Hyperlink g.jsHref |
|
df337eb…
|
drh
|
235 |
** ------------ -------------- ---------------- -------- |
|
df337eb…
|
drh
|
236 |
** 1: 0 0 0 0 |
|
df337eb…
|
drh
|
237 |
** 2: 1 0 1 0 |
|
df337eb…
|
drh
|
238 |
** 3: 0 1 1 1 |
|
df337eb…
|
drh
|
239 |
** 4: 1 1 1 0 |
|
8dd7542…
|
drh
|
240 |
** |
|
8dd7542…
|
drh
|
241 |
** The data-action=ARG form is used for cases 1 and 3. In case 1, the href.js |
|
8dd7542…
|
drh
|
242 |
** javascript is omitted and so the form is effectively disabled. |
|
433cde1…
|
drh
|
243 |
*/ |
|
dfa3579…
|
drh
|
244 |
void form_begin(const char *zOtherArgs, const char *zAction, ...){ |
|
dfa3579…
|
drh
|
245 |
char *zLink; |
|
dfa3579…
|
drh
|
246 |
va_list ap; |
|
dfa3579…
|
drh
|
247 |
if( zOtherArgs==0 ) zOtherArgs = ""; |
|
dfa3579…
|
drh
|
248 |
va_start(ap, zAction); |
|
dfa3579…
|
drh
|
249 |
zLink = vmprintf(zAction, ap); |
|
dfa3579…
|
drh
|
250 |
va_end(ap); |
|
8dd7542…
|
drh
|
251 |
if( g.perm.Hyperlink ){ |
|
dfa3579…
|
drh
|
252 |
@ <form method="POST" action="%z(zLink)" %s(zOtherArgs)> |
|
dfa3579…
|
drh
|
253 |
}else{ |
|
09494b0…
|
drh
|
254 |
needHrefJs = 1; |
|
8394d2f…
|
drh
|
255 |
@ <form method="POST" data-action='%s(zLink)' action='%R/login' \ |
|
8394d2f…
|
drh
|
256 |
@ %s(zOtherArgs)> |
|
8394d2f…
|
drh
|
257 |
} |
|
920ace1…
|
drh
|
258 |
login_insert_csrf_secret(); |
|
433cde1…
|
drh
|
259 |
} |
|
433cde1…
|
drh
|
260 |
|
|
433cde1…
|
drh
|
261 |
/* |
|
dbda8d6…
|
drh
|
262 |
** Add a new element to the submenu |
|
dbda8d6…
|
drh
|
263 |
*/ |
|
dbda8d6…
|
drh
|
264 |
void style_submenu_element( |
|
dbda8d6…
|
drh
|
265 |
const char *zLabel, |
|
d0305b3…
|
aku
|
266 |
const char *zLink, |
|
d0305b3…
|
aku
|
267 |
... |
|
dbda8d6…
|
drh
|
268 |
){ |
|
d0305b3…
|
aku
|
269 |
va_list ap; |
|
3cb9ba4…
|
andygoth
|
270 |
assert( nSubmenu < count(aSubmenu) ); |
|
dbda8d6…
|
drh
|
271 |
aSubmenu[nSubmenu].zLabel = zLabel; |
|
d0305b3…
|
aku
|
272 |
va_start(ap, zLink); |
|
d0305b3…
|
aku
|
273 |
aSubmenu[nSubmenu].zLink = vmprintf(zLink, ap); |
|
d0305b3…
|
aku
|
274 |
va_end(ap); |
|
dbda8d6…
|
drh
|
275 |
nSubmenu++; |
|
dbda8d6…
|
drh
|
276 |
} |
|
c0c0bae…
|
drh
|
277 |
void style_submenu_entry( |
|
c0c0bae…
|
drh
|
278 |
const char *zName, /* Query parameter name */ |
|
c0c0bae…
|
drh
|
279 |
const char *zLabel, /* Label before the entry box */ |
|
f76cfac…
|
drh
|
280 |
int iSize, /* Size of the entry box */ |
|
e91d267…
|
drh
|
281 |
int eVisible /* Visible or disabled */ |
|
c0c0bae…
|
drh
|
282 |
){ |
|
3cb9ba4…
|
andygoth
|
283 |
assert( nSubmenuCtrl < count(aSubmenuCtrl) ); |
|
c0c0bae…
|
drh
|
284 |
aSubmenuCtrl[nSubmenuCtrl].zName = zName; |
|
c0c0bae…
|
drh
|
285 |
aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel; |
|
c0c0bae…
|
drh
|
286 |
aSubmenuCtrl[nSubmenuCtrl].iSize = iSize; |
|
e91d267…
|
drh
|
287 |
aSubmenuCtrl[nSubmenuCtrl].eVisible = eVisible; |
|
c0c0bae…
|
drh
|
288 |
aSubmenuCtrl[nSubmenuCtrl].eType = FF_ENTRY; |
|
3cb9ba4…
|
andygoth
|
289 |
nSubmenuCtrl++; |
|
3cb9ba4…
|
andygoth
|
290 |
} |
|
3cb9ba4…
|
andygoth
|
291 |
void style_submenu_checkbox( |
|
3cb9ba4…
|
andygoth
|
292 |
const char *zName, /* Query parameter name */ |
|
3cb9ba4…
|
andygoth
|
293 |
const char *zLabel, /* Label to display after the checkbox */ |
|
e91d267…
|
drh
|
294 |
int eVisible, /* Visible or disabled */ |
|
037e06b…
|
drh
|
295 |
const char *zJS /* Optional javascript to run on toggle */ |
|
3cb9ba4…
|
andygoth
|
296 |
){ |
|
3cb9ba4…
|
andygoth
|
297 |
assert( nSubmenuCtrl < count(aSubmenuCtrl) ); |
|
3cb9ba4…
|
andygoth
|
298 |
aSubmenuCtrl[nSubmenuCtrl].zName = zName; |
|
3cb9ba4…
|
andygoth
|
299 |
aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel; |
|
e91d267…
|
drh
|
300 |
aSubmenuCtrl[nSubmenuCtrl].eVisible = eVisible; |
|
037e06b…
|
drh
|
301 |
aSubmenuCtrl[nSubmenuCtrl].zJS = zJS; |
|
3cb9ba4…
|
andygoth
|
302 |
aSubmenuCtrl[nSubmenuCtrl].eType = FF_CHECKBOX; |
|
c0c0bae…
|
drh
|
303 |
nSubmenuCtrl++; |
|
c0c0bae…
|
drh
|
304 |
} |
|
c0c0bae…
|
drh
|
305 |
void style_submenu_binary( |
|
c0c0bae…
|
drh
|
306 |
const char *zName, /* Query parameter name */ |
|
c0c0bae…
|
drh
|
307 |
const char *zTrue, /* Label to show when parameter is true */ |
|
f76cfac…
|
drh
|
308 |
const char *zFalse, /* Label to show when the parameter is false */ |
|
e91d267…
|
drh
|
309 |
int eVisible /* Visible or disabled */ |
|
c0c0bae…
|
drh
|
310 |
){ |
|
3cb9ba4…
|
andygoth
|
311 |
assert( nSubmenuCtrl < count(aSubmenuCtrl) ); |
|
c0c0bae…
|
drh
|
312 |
aSubmenuCtrl[nSubmenuCtrl].zName = zName; |
|
c0c0bae…
|
drh
|
313 |
aSubmenuCtrl[nSubmenuCtrl].zLabel = zTrue; |
|
c0c0bae…
|
drh
|
314 |
aSubmenuCtrl[nSubmenuCtrl].zFalse = zFalse; |
|
e91d267…
|
drh
|
315 |
aSubmenuCtrl[nSubmenuCtrl].eVisible = eVisible; |
|
c0c0bae…
|
drh
|
316 |
aSubmenuCtrl[nSubmenuCtrl].eType = FF_BINARY; |
|
c0c0bae…
|
drh
|
317 |
nSubmenuCtrl++; |
|
c0c0bae…
|
drh
|
318 |
} |
|
c0c0bae…
|
drh
|
319 |
void style_submenu_multichoice( |
|
e91d267…
|
drh
|
320 |
const char *zName, /* Query parameter name */ |
|
e91d267…
|
drh
|
321 |
int nChoice, /* Number of options */ |
|
e91d267…
|
drh
|
322 |
const char *const *azChoice, /* value/display pairs. 2*nChoice entries */ |
|
e91d267…
|
drh
|
323 |
int eVisible /* Visible or disabled */ |
|
c0c0bae…
|
drh
|
324 |
){ |
|
3cb9ba4…
|
andygoth
|
325 |
assert( nSubmenuCtrl < count(aSubmenuCtrl) ); |
|
c0c0bae…
|
drh
|
326 |
aSubmenuCtrl[nSubmenuCtrl].zName = zName; |
|
c0c0bae…
|
drh
|
327 |
aSubmenuCtrl[nSubmenuCtrl].iSize = nChoice; |
|
c0c0bae…
|
drh
|
328 |
aSubmenuCtrl[nSubmenuCtrl].azChoice = azChoice; |
|
e91d267…
|
drh
|
329 |
aSubmenuCtrl[nSubmenuCtrl].eVisible = eVisible; |
|
c0c0bae…
|
drh
|
330 |
aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI; |
|
c0c0bae…
|
drh
|
331 |
nSubmenuCtrl++; |
|
16ab6ee…
|
drh
|
332 |
} |
|
16ab6ee…
|
drh
|
333 |
void style_submenu_sql( |
|
16ab6ee…
|
drh
|
334 |
const char *zName, /* Query parameter name */ |
|
16ab6ee…
|
drh
|
335 |
const char *zLabel, /* Label on the control */ |
|
16ab6ee…
|
drh
|
336 |
const char *zFormat, /* Format string for SQL command for choices */ |
|
16ab6ee…
|
drh
|
337 |
... /* Arguments to the format string */ |
|
16ab6ee…
|
drh
|
338 |
){ |
|
16ab6ee…
|
drh
|
339 |
Stmt q; |
|
16ab6ee…
|
drh
|
340 |
int n = 0; |
|
16ab6ee…
|
drh
|
341 |
int nAlloc = 0; |
|
16ab6ee…
|
drh
|
342 |
char **az = 0; |
|
16ab6ee…
|
drh
|
343 |
va_list ap; |
|
16ab6ee…
|
drh
|
344 |
|
|
16ab6ee…
|
drh
|
345 |
va_start(ap, zFormat); |
|
16ab6ee…
|
drh
|
346 |
db_vprepare(&q, 0, zFormat, ap); |
|
16ab6ee…
|
drh
|
347 |
va_end(ap); |
|
16ab6ee…
|
drh
|
348 |
while( SQLITE_ROW==db_step(&q) ){ |
|
16ab6ee…
|
drh
|
349 |
if( n+2>=nAlloc ){ |
|
16ab6ee…
|
drh
|
350 |
nAlloc += nAlloc + 20; |
|
16ab6ee…
|
drh
|
351 |
az = fossil_realloc(az, sizeof(char*)*nAlloc); |
|
16ab6ee…
|
drh
|
352 |
} |
|
16ab6ee…
|
drh
|
353 |
az[n++] = fossil_strdup(db_column_text(&q,0)); |
|
16ab6ee…
|
drh
|
354 |
az[n++] = fossil_strdup(db_column_text(&q,1)); |
|
16ab6ee…
|
drh
|
355 |
} |
|
16ab6ee…
|
drh
|
356 |
db_finalize(&q); |
|
16ab6ee…
|
drh
|
357 |
if( n>0 ){ |
|
16ab6ee…
|
drh
|
358 |
aSubmenuCtrl[nSubmenuCtrl].zName = zName; |
|
16ab6ee…
|
drh
|
359 |
aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel; |
|
16ab6ee…
|
drh
|
360 |
aSubmenuCtrl[nSubmenuCtrl].iSize = n/2; |
|
b43681d…
|
jan.nijtmans
|
361 |
aSubmenuCtrl[nSubmenuCtrl].azChoice = (const char *const *)az; |
|
e91d267…
|
drh
|
362 |
aSubmenuCtrl[nSubmenuCtrl].eVisible = STYLE_NORMAL; |
|
16ab6ee…
|
drh
|
363 |
aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI; |
|
16ab6ee…
|
drh
|
364 |
nSubmenuCtrl++; |
|
16ab6ee…
|
drh
|
365 |
} |
|
e91d267…
|
drh
|
366 |
} |
|
e91d267…
|
drh
|
367 |
|
|
220ed0a…
|
drh
|
368 |
/* |
|
220ed0a…
|
drh
|
369 |
** Disable or enable the submenu |
|
220ed0a…
|
drh
|
370 |
*/ |
|
220ed0a…
|
drh
|
371 |
void style_submenu_enable(int onOff){ |
|
220ed0a…
|
drh
|
372 |
submenuEnable = onOff; |
|
220ed0a…
|
drh
|
373 |
} |
|
220ed0a…
|
drh
|
374 |
|
|
dbda8d6…
|
drh
|
375 |
|
|
dbda8d6…
|
drh
|
376 |
/* |
|
dbda8d6…
|
drh
|
377 |
** Compare two submenu items for sorting purposes |
|
dbda8d6…
|
drh
|
378 |
*/ |
|
dbda8d6…
|
drh
|
379 |
static int submenuCompare(const void *a, const void *b){ |
|
dbda8d6…
|
drh
|
380 |
const struct Submenu *A = (const struct Submenu*)a; |
|
d0305b3…
|
aku
|
381 |
const struct Submenu *B = (const struct Submenu*)b; |
|
31c52c7…
|
drh
|
382 |
return fossil_strcmp(A->zLabel, B->zLabel); |
|
31c52c7…
|
drh
|
383 |
} |
|
31c52c7…
|
drh
|
384 |
|
|
064c1c9…
|
stephan
|
385 |
/* Use this for the $current_page variable if it is not NULL. If it |
|
064c1c9…
|
stephan
|
386 |
** is NULL then use g.zPath. |
|
a116d97…
|
drh
|
387 |
*/ |
|
a116d97…
|
drh
|
388 |
static char *local_zCurrentPage = 0; |
|
a116d97…
|
drh
|
389 |
|
|
a116d97…
|
drh
|
390 |
/* |
|
a116d97…
|
drh
|
391 |
** Set the desired $current_page to something other than g.zPath |
|
a116d97…
|
drh
|
392 |
*/ |
|
a116d97…
|
drh
|
393 |
void style_set_current_page(const char *zFormat, ...){ |
|
a116d97…
|
drh
|
394 |
fossil_free(local_zCurrentPage); |
|
a116d97…
|
drh
|
395 |
if( zFormat==0 ){ |
|
a116d97…
|
drh
|
396 |
local_zCurrentPage = 0; |
|
a116d97…
|
drh
|
397 |
}else{ |
|
a116d97…
|
drh
|
398 |
va_list ap; |
|
a116d97…
|
drh
|
399 |
va_start(ap, zFormat); |
|
a116d97…
|
drh
|
400 |
local_zCurrentPage = vmprintf(zFormat, ap); |
|
a116d97…
|
drh
|
401 |
va_end(ap); |
|
a116d97…
|
drh
|
402 |
} |
|
a116d97…
|
drh
|
403 |
} |
|
a116d97…
|
drh
|
404 |
|
|
a116d97…
|
drh
|
405 |
/* |
|
6acd87f…
|
drh
|
406 |
** Create a TH1 variable containing the URL for the stylesheet. |
|
6acd87f…
|
drh
|
407 |
** |
|
6acd87f…
|
drh
|
408 |
** The name of the new variable will be "stylesheet_url". |
|
6acd87f…
|
drh
|
409 |
** |
|
6acd87f…
|
drh
|
410 |
** The value will be a URL for accessing the appropriate stylesheet. |
|
6acd87f…
|
drh
|
411 |
** This URL will include query parameters such as "id=" and "once&skin=" |
|
6acd87f…
|
drh
|
412 |
** to cause the correct stylesheet to be loaded after a skin change |
|
6acd87f…
|
drh
|
413 |
** or after a change to the stylesheet. |
|
daff9d2…
|
joel
|
414 |
*/ |
|
6acd87f…
|
drh
|
415 |
static void stylesheet_url_var(void){ |
|
6acd87f…
|
drh
|
416 |
char *zBuiltin; /* Auxiliary page-specific CSS page */ |
|
6acd87f…
|
drh
|
417 |
Blob url; /* The URL */ |
|
fc85382…
|
stephan
|
418 |
const char * zPage = local_zCurrentPage ? local_zCurrentPage : g.zPath; |
|
6acd87f…
|
drh
|
419 |
|
|
6acd87f…
|
drh
|
420 |
/* Initialize the URL to its baseline */ |
|
6acd87f…
|
drh
|
421 |
url = empty_blob; |
|
6acd87f…
|
drh
|
422 |
blob_appendf(&url, "%R/style.css"); |
|
6acd87f…
|
drh
|
423 |
|
|
6acd87f…
|
drh
|
424 |
/* If page-specific CSS exists for the current page, then append |
|
6acd87f…
|
drh
|
425 |
** the pathname for the page-specific CSS. The default CSS is |
|
6acd87f…
|
drh
|
426 |
** |
|
6acd87f…
|
drh
|
427 |
** /style.css |
|
6acd87f…
|
drh
|
428 |
** |
|
6acd87f…
|
drh
|
429 |
** But for the "/wikiedit" page (to name but one example), we |
|
6acd87f…
|
drh
|
430 |
** append a path as follows: |
|
6acd87f…
|
drh
|
431 |
** |
|
6acd87f…
|
drh
|
432 |
** /style.css/wikiedit |
|
6acd87f…
|
drh
|
433 |
** |
|
6acd87f…
|
drh
|
434 |
** The /style.css page (implemented below) will detect this extra "wikiedit" |
|
6acd87f…
|
drh
|
435 |
** path information and include the page-specific CSS along with the |
|
6acd87f…
|
drh
|
436 |
** default CSS when it delivers the page. |
|
6acd87f…
|
drh
|
437 |
*/ |
|
fc85382…
|
stephan
|
438 |
zBuiltin = mprintf("style.%s.css", zPage); |
|
6acd87f…
|
drh
|
439 |
if( builtin_file(zBuiltin,0)!=0 ){ |
|
fc85382…
|
stephan
|
440 |
blob_appendf(&url, "/%s", zPage); |
|
6acd87f…
|
drh
|
441 |
} |
|
6acd87f…
|
drh
|
442 |
fossil_free(zBuiltin); |
|
6acd87f…
|
drh
|
443 |
|
|
6acd87f…
|
drh
|
444 |
/* Add query parameters that will change whenever the skin changes |
|
6acd87f…
|
drh
|
445 |
** or after any updates to the CSS files |
|
6acd87f…
|
drh
|
446 |
*/ |
|
6acd87f…
|
drh
|
447 |
blob_appendf(&url, "?id=%x", skin_id("css")); |
|
6acd87f…
|
drh
|
448 |
if( P("once")!=0 && P("skin")!=0 ){ |
|
6acd87f…
|
drh
|
449 |
blob_appendf(&url, "&skin=%s&once", skin_in_use()); |
|
6acd87f…
|
drh
|
450 |
} |
|
6acd87f…
|
drh
|
451 |
|
|
275da70…
|
danield
|
452 |
/* Generate the CSS URL variable */ |
|
6acd87f…
|
drh
|
453 |
Th_Store("stylesheet_url", blob_str(&url)); |
|
6acd87f…
|
drh
|
454 |
blob_reset(&url); |
|
daff9d2…
|
joel
|
455 |
} |
|
daff9d2…
|
joel
|
456 |
|
|
daff9d2…
|
joel
|
457 |
/* |
|
6acd87f…
|
drh
|
458 |
** Create a TH1 variable containing the URL for the specified image. |
|
daff9d2…
|
joel
|
459 |
** The resulting variable name will be of the form $[zImageName]_image_url. |
|
6acd87f…
|
drh
|
460 |
** The value will be a URL that includes an id= query parameter that |
|
6acd87f…
|
drh
|
461 |
** changes if the underlying resource changes or if a different skin |
|
6acd87f…
|
drh
|
462 |
** is selected. |
|
daff9d2…
|
joel
|
463 |
*/ |
|
daff9d2…
|
joel
|
464 |
static void image_url_var(const char *zImageName){ |
|
6acd87f…
|
drh
|
465 |
char *zVarName; /* Name of the new TH1 variable */ |
|
6acd87f…
|
drh
|
466 |
char *zResource; /* Name of CONFIG entry holding content */ |
|
6acd87f…
|
drh
|
467 |
char *zUrl; /* The URL */ |
|
6acd87f…
|
drh
|
468 |
|
|
6acd87f…
|
drh
|
469 |
zResource = mprintf("%s-image", zImageName); |
|
6acd87f…
|
drh
|
470 |
zUrl = mprintf("%R/%s?id=%x", zImageName, skin_id(zResource)); |
|
6acd87f…
|
drh
|
471 |
free(zResource); |
|
275da70…
|
danield
|
472 |
zVarName = mprintf("%s_image_url", zImageName); |
|
6acd87f…
|
drh
|
473 |
Th_Store(zVarName, zUrl); |
|
6acd87f…
|
drh
|
474 |
free(zVarName); |
|
6acd87f…
|
drh
|
475 |
free(zUrl); |
|
759fbda…
|
drh
|
476 |
} |
|
759fbda…
|
drh
|
477 |
|
|
759fbda…
|
drh
|
478 |
/* |
|
759fbda…
|
drh
|
479 |
** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js |
|
759fbda…
|
drh
|
480 |
** Javascript module, and generates HTML elements with the following IDs: |
|
759fbda…
|
drh
|
481 |
** |
|
759fbda…
|
drh
|
482 |
** TARGETID: The <span> wrapper around TEXT. |
|
63712b6…
|
florian
|
483 |
** copy-TARGETID: The <button> for the copy button. |
|
759fbda…
|
drh
|
484 |
** |
|
759fbda…
|
drh
|
485 |
** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT. |
|
759fbda…
|
drh
|
486 |
** |
|
759fbda…
|
drh
|
487 |
** The COPYLENGTH argument defines the length of the substring of TEXT copied to |
|
759fbda…
|
drh
|
488 |
** clipboard: |
|
759fbda…
|
drh
|
489 |
** |
|
759fbda…
|
drh
|
490 |
** <= 0: No limit (default if the argument is omitted). |
|
759fbda…
|
drh
|
491 |
** >= 3: Truncate TEXT after COPYLENGTH (single-byte) characters. |
|
759fbda…
|
drh
|
492 |
** 1: Use the "hash-digits" setting as the limit. |
|
759fbda…
|
drh
|
493 |
** 2: Use the length appropriate for URLs as the limit (defined at |
|
759fbda…
|
drh
|
494 |
** compile-time by FOSSIL_HASH_DIGITS_URL, defaults to 16). |
|
759fbda…
|
drh
|
495 |
*/ |
|
759fbda…
|
drh
|
496 |
char *style_copy_button( |
|
759fbda…
|
drh
|
497 |
int bOutputCGI, /* Don't return result, but send to cgi_printf(). */ |
|
759fbda…
|
drh
|
498 |
const char *zTargetId, /* The TARGETID argument. */ |
|
759fbda…
|
drh
|
499 |
int bFlipped, /* The FLIPPED argument. */ |
|
759fbda…
|
drh
|
500 |
int cchLength, /* The COPYLENGTH argument. */ |
|
759fbda…
|
drh
|
501 |
const char *zTextFmt, /* Formatting of the TEXT argument (htmlized). */ |
|
759fbda…
|
drh
|
502 |
... /* Formatting parameters of the TEXT argument. */ |
|
759fbda…
|
drh
|
503 |
){ |
|
759fbda…
|
drh
|
504 |
va_list ap; |
|
759fbda…
|
drh
|
505 |
char *zText; |
|
759fbda…
|
drh
|
506 |
char *zResult = 0; |
|
759fbda…
|
drh
|
507 |
va_start(ap,zTextFmt); |
|
759fbda…
|
drh
|
508 |
zText = vmprintf(zTextFmt/*works-like:?*/,ap); |
|
759fbda…
|
drh
|
509 |
va_end(ap); |
|
759fbda…
|
drh
|
510 |
if( cchLength==1 ) cchLength = hash_digits(0); |
|
759fbda…
|
drh
|
511 |
else if( cchLength==2 ) cchLength = hash_digits(1); |
|
759fbda…
|
drh
|
512 |
if( !bFlipped ){ |
|
759fbda…
|
drh
|
513 |
const char *zBtnFmt = |
|
759fbda…
|
drh
|
514 |
"<span class=\"nobr\">" |
|
63712b6…
|
florian
|
515 |
"<button " |
|
63712b6…
|
florian
|
516 |
"class=\"copy-button\" " |
|
63712b6…
|
florian
|
517 |
"id=\"copy-%h\" " |
|
63712b6…
|
florian
|
518 |
"data-copytarget=\"%h\" " |
|
63712b6…
|
florian
|
519 |
"data-copylength=\"%d\">" |
|
63712b6…
|
florian
|
520 |
"<span>" |
|
63712b6…
|
florian
|
521 |
"</span>" |
|
63712b6…
|
florian
|
522 |
"</button>" |
|
759fbda…
|
drh
|
523 |
"<span id=\"%h\">" |
|
63712b6…
|
florian
|
524 |
"%s" |
|
759fbda…
|
drh
|
525 |
"</span>" |
|
759fbda…
|
drh
|
526 |
"</span>"; |
|
759fbda…
|
drh
|
527 |
if( bOutputCGI ){ |
|
759fbda…
|
drh
|
528 |
cgi_printf( |
|
759fbda…
|
drh
|
529 |
zBtnFmt/*works-like:"%h%h%d%h%s"*/, |
|
759fbda…
|
drh
|
530 |
zTargetId,zTargetId,cchLength,zTargetId,zText); |
|
759fbda…
|
drh
|
531 |
}else{ |
|
759fbda…
|
drh
|
532 |
zResult = mprintf( |
|
759fbda…
|
drh
|
533 |
zBtnFmt/*works-like:"%h%h%d%h%s"*/, |
|
759fbda…
|
drh
|
534 |
zTargetId,zTargetId,cchLength,zTargetId,zText); |
|
759fbda…
|
drh
|
535 |
} |
|
759fbda…
|
drh
|
536 |
}else{ |
|
759fbda…
|
drh
|
537 |
const char *zBtnFmt = |
|
759fbda…
|
drh
|
538 |
"<span class=\"nobr\">" |
|
759fbda…
|
drh
|
539 |
"<span id=\"%h\">" |
|
63712b6…
|
florian
|
540 |
"%s" |
|
63712b6…
|
florian
|
541 |
"</span>" |
|
63712b6…
|
florian
|
542 |
"<button " |
|
63712b6…
|
florian
|
543 |
"class=\"copy-button copy-button-flipped\" " |
|
63712b6…
|
florian
|
544 |
"id=\"copy-%h\" " |
|
63712b6…
|
florian
|
545 |
"data-copytarget=\"%h\" " |
|
63712b6…
|
florian
|
546 |
"data-copylength=\"%d\">" |
|
63712b6…
|
florian
|
547 |
"<span>" |
|
63712b6…
|
florian
|
548 |
"</span>" |
|
63712b6…
|
florian
|
549 |
"</button>" |
|
759fbda…
|
drh
|
550 |
"</span>"; |
|
759fbda…
|
drh
|
551 |
if( bOutputCGI ){ |
|
759fbda…
|
drh
|
552 |
cgi_printf( |
|
759fbda…
|
drh
|
553 |
zBtnFmt/*works-like:"%h%s%h%h%d"*/, |
|
759fbda…
|
drh
|
554 |
zTargetId,zText,zTargetId,zTargetId,cchLength); |
|
759fbda…
|
drh
|
555 |
}else{ |
|
759fbda…
|
drh
|
556 |
zResult = mprintf( |
|
759fbda…
|
drh
|
557 |
zBtnFmt/*works-like:"%h%s%h%h%d"*/, |
|
759fbda…
|
drh
|
558 |
zTargetId,zText,zTargetId,zTargetId,cchLength); |
|
759fbda…
|
drh
|
559 |
} |
|
759fbda…
|
drh
|
560 |
} |
|
759fbda…
|
drh
|
561 |
free(zText); |
|
036a9d5…
|
drh
|
562 |
builtin_request_js("copybtn.js"); |
|
759fbda…
|
drh
|
563 |
return zResult; |
|
ff747b5…
|
drh
|
564 |
} |
|
ff747b5…
|
drh
|
565 |
|
|
ff747b5…
|
drh
|
566 |
/* |
|
ff747b5…
|
drh
|
567 |
** Return a random nonce that is stored in static space. For a particular |
|
ff747b5…
|
drh
|
568 |
** run, the same nonce is always returned. |
|
ff747b5…
|
drh
|
569 |
*/ |
|
ff747b5…
|
drh
|
570 |
char *style_nonce(void){ |
|
ff747b5…
|
drh
|
571 |
static char zNonce[52]; |
|
ff747b5…
|
drh
|
572 |
if( zNonce[0]==0 ){ |
|
ff747b5…
|
drh
|
573 |
unsigned char zSeed[24]; |
|
ff747b5…
|
drh
|
574 |
sqlite3_randomness(24, zSeed); |
|
ff747b5…
|
drh
|
575 |
encode16(zSeed,(unsigned char*)zNonce,24); |
|
ff747b5…
|
drh
|
576 |
} |
|
ff747b5…
|
drh
|
577 |
return zNonce; |
|
ff747b5…
|
drh
|
578 |
} |
|
ff747b5…
|
drh
|
579 |
|
|
ff747b5…
|
drh
|
580 |
/* |
|
14c81d9…
|
drh
|
581 |
** Return the default Content Security Policy (CSP) string. |
|
14c81d9…
|
drh
|
582 |
** If the toHeader argument is true, then also add the |
|
14c81d9…
|
drh
|
583 |
** CSP to the HTTP reply header. |
|
14c81d9…
|
drh
|
584 |
** |
|
14c81d9…
|
drh
|
585 |
** The CSP comes from the "default-csp" setting if it exists and |
|
14c81d9…
|
drh
|
586 |
** is non-empty. If that setting is an empty string, then the following |
|
14c81d9…
|
drh
|
587 |
** default is used instead: |
|
14c81d9…
|
drh
|
588 |
** |
|
14c81d9…
|
drh
|
589 |
** default-src 'self' data:; |
|
14c81d9…
|
drh
|
590 |
** script-src 'self' 'nonce-$nonce'; |
|
14c81d9…
|
drh
|
591 |
** style-src 'self' 'unsafe-inline'; |
|
c184d64…
|
drh
|
592 |
** img-src * data:; |
|
14c81d9…
|
drh
|
593 |
** |
|
14c81d9…
|
drh
|
594 |
** The text '$nonce' is replaced by style_nonce() if and whereever it |
|
14c81d9…
|
drh
|
595 |
** occurs in the input string. |
|
14c81d9…
|
drh
|
596 |
** |
|
14c81d9…
|
drh
|
597 |
** The string returned is obtained from fossil_malloc() and |
|
14c81d9…
|
drh
|
598 |
** should be released by the caller. |
|
14c81d9…
|
drh
|
599 |
*/ |
|
14c81d9…
|
drh
|
600 |
char *style_csp(int toHeader){ |
|
275da70…
|
danield
|
601 |
static const char zBackupCSP[] = |
|
14c81d9…
|
drh
|
602 |
"default-src 'self' data:; " |
|
14c81d9…
|
drh
|
603 |
"script-src 'self' 'nonce-$nonce'; " |
|
025a007…
|
drh
|
604 |
"style-src 'self' 'unsafe-inline'; " |
|
c184d64…
|
drh
|
605 |
"img-src * data:"; |
|
02961b8…
|
drh
|
606 |
const char *zFormat; |
|
14c81d9…
|
drh
|
607 |
Blob csp; |
|
14c81d9…
|
drh
|
608 |
char *zNonce; |
|
14c81d9…
|
drh
|
609 |
char *zCsp; |
|
e0f2283…
|
drh
|
610 |
int i; |
|
1f6ae1e…
|
drh
|
611 |
zFormat = db_get("default-csp",0); |
|
6b5606d…
|
drh
|
612 |
if( zFormat==0 || zFormat[0]==0 ){ |
|
14c81d9…
|
drh
|
613 |
zFormat = zBackupCSP; |
|
14c81d9…
|
drh
|
614 |
} |
|
14c81d9…
|
drh
|
615 |
blob_init(&csp, 0, 0); |
|
14c81d9…
|
drh
|
616 |
while( zFormat[0] && (zNonce = strstr(zFormat,"$nonce"))!=0 ){ |
|
14c81d9…
|
drh
|
617 |
blob_append(&csp, zFormat, (int)(zNonce - zFormat)); |
|
14c81d9…
|
drh
|
618 |
blob_append(&csp, style_nonce(), -1); |
|
14c81d9…
|
drh
|
619 |
zFormat = zNonce + 6; |
|
14c81d9…
|
drh
|
620 |
} |
|
14c81d9…
|
drh
|
621 |
blob_append(&csp, zFormat, -1); |
|
14c81d9…
|
drh
|
622 |
zCsp = blob_str(&csp); |
|
e0f2283…
|
drh
|
623 |
/* No whitespace other than actual space characters allowed in the CSP |
|
e0f2283…
|
drh
|
624 |
** string. See https://fossil-scm.org/forum/forumpost/d29e3af43c */ |
|
e0f2283…
|
drh
|
625 |
for(i=0; zCsp[i]; i++){ if( fossil_isspace(zCsp[i]) ) zCsp[i] = ' '; } |
|
14c81d9…
|
drh
|
626 |
if( toHeader ){ |
|
14c81d9…
|
drh
|
627 |
cgi_printf_header("Content-Security-Policy: %s\r\n", zCsp); |
|
14c81d9…
|
drh
|
628 |
} |
|
14c81d9…
|
drh
|
629 |
return zCsp; |
|
14c81d9…
|
drh
|
630 |
} |
|
14c81d9…
|
drh
|
631 |
|
|
14c81d9…
|
drh
|
632 |
/* |
|
f1bb72e…
|
drh
|
633 |
** Default HTML page header text through <body>. If the repository-specific |
|
f1bb72e…
|
drh
|
634 |
** header template lacks a <body> tag, then all of the following is |
|
f1bb72e…
|
drh
|
635 |
** prepended. |
|
f1bb72e…
|
drh
|
636 |
*/ |
|
275da70…
|
danield
|
637 |
static const char zDfltHeader[] = |
|
f1bb72e…
|
drh
|
638 |
@ <html> |
|
f1bb72e…
|
drh
|
639 |
@ <head> |
|
1997b71…
|
drh
|
640 |
@ <meta charset="UTF-8"> |
|
f5482a0…
|
wyoung
|
641 |
@ <base href="$baseurl/$current_page"> |
|
f5482a0…
|
wyoung
|
642 |
@ <meta http-equiv="Content-Security-Policy" content="$default_csp"> |
|
9131af2…
|
drh
|
643 |
@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
f1bb72e…
|
drh
|
644 |
@ <title>$<project_name>: $<title></title> |
|
8394d2f…
|
drh
|
645 |
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ |
|
f5482a0…
|
wyoung
|
646 |
@ href="$home/timeline.rss"> |
|
f5482a0…
|
wyoung
|
647 |
@ <link rel="stylesheet" href="$stylesheet_url" type="text/css"> |
|
f1bb72e…
|
drh
|
648 |
@ </head> |
|
b05a6c6…
|
george
|
649 |
@ <body class="$current_feature rpage-$requested_page cpage-$canonical_page"> |
|
c1cb688…
|
mistachkin
|
650 |
; |
|
c1cb688…
|
mistachkin
|
651 |
|
|
c1cb688…
|
mistachkin
|
652 |
/* |
|
c1cb688…
|
mistachkin
|
653 |
** Returns the default page header. |
|
c1cb688…
|
mistachkin
|
654 |
*/ |
|
c1cb688…
|
mistachkin
|
655 |
const char *get_default_header(){ |
|
c1cb688…
|
mistachkin
|
656 |
return zDfltHeader; |
|
5f22712…
|
drh
|
657 |
} |
|
5f22712…
|
drh
|
658 |
|
|
5f22712…
|
drh
|
659 |
/* |
|
5f22712…
|
drh
|
660 |
** The default TCL list that defines the main menu. |
|
5f22712…
|
drh
|
661 |
*/ |
|
275da70…
|
danield
|
662 |
static const char zDfltMainMenu[] = |
|
5f22712…
|
drh
|
663 |
@ Home /home * {} |
|
5f22712…
|
drh
|
664 |
@ Timeline /timeline {o r j} {} |
|
5f22712…
|
drh
|
665 |
@ Files /dir?ci=tip oh desktoponly |
|
5f22712…
|
drh
|
666 |
@ Branches /brlist o wideonly |
|
5f22712…
|
drh
|
667 |
@ Tags /taglist o wideonly |
|
5f22712…
|
drh
|
668 |
@ Forum /forum {@2 3 4 5 6} wideonly |
|
5f22712…
|
drh
|
669 |
@ Chat /chat C wideonly |
|
5f22712…
|
drh
|
670 |
@ Tickets /ticket r wideonly |
|
5f22712…
|
drh
|
671 |
@ Wiki /wiki j wideonly |
|
7260ff2…
|
wyoung
|
672 |
@ Admin /setup {a s} desktoponly |
|
d7e4b48…
|
drh
|
673 |
@ Logout /logout L wideonly |
|
d7e4b48…
|
drh
|
674 |
@ Login /login !L wideonly |
|
5f22712…
|
drh
|
675 |
; |
|
5f22712…
|
drh
|
676 |
|
|
5f22712…
|
drh
|
677 |
/* |
|
5f22712…
|
drh
|
678 |
** Return the default menu |
|
5f22712…
|
drh
|
679 |
*/ |
|
5f22712…
|
drh
|
680 |
const char *style_default_mainmenu(void){ |
|
5f22712…
|
drh
|
681 |
return zDfltMainMenu; |
|
112c713…
|
drh
|
682 |
} |
|
112c713…
|
drh
|
683 |
|
|
112c713…
|
drh
|
684 |
/* |
|
112c713…
|
drh
|
685 |
** Given a URL path, extract the first element as a "feature" name, |
|
112c713…
|
drh
|
686 |
** used as the <body class="FEATURE"> value by default, though |
|
112c713…
|
drh
|
687 |
** later-running code may override this, typically to group multiple |
|
112c713…
|
drh
|
688 |
** Fossil UI URLs into a single "feature" so you can have per-feature |
|
112c713…
|
drh
|
689 |
** CSS rules. |
|
112c713…
|
drh
|
690 |
** |
|
112c713…
|
drh
|
691 |
** For example, "body.forum div.markdown blockquote" targets only |
|
112c713…
|
drh
|
692 |
** block quotes made in forum posts, leaving other Markdown quotes |
|
112c713…
|
drh
|
693 |
** alone. Because feature class "forum" groups /forummain, /forumpost, |
|
112c713…
|
drh
|
694 |
** and /forume2, it works across all renderings of Markdown to HTML |
|
112c713…
|
drh
|
695 |
** within the Fossil forum feature. |
|
112c713…
|
drh
|
696 |
*/ |
|
112c713…
|
drh
|
697 |
static const char* feature_from_page_path(const char *zPath){ |
|
112c713…
|
drh
|
698 |
const char* zSlash = strchr(zPath, '/'); |
|
112c713…
|
drh
|
699 |
if (zSlash) { |
|
112c713…
|
drh
|
700 |
return fossil_strndup(zPath, zSlash - zPath); |
|
112c713…
|
drh
|
701 |
} else { |
|
112c713…
|
drh
|
702 |
return zPath; |
|
112c713…
|
drh
|
703 |
} |
|
112c713…
|
drh
|
704 |
} |
|
112c713…
|
drh
|
705 |
|
|
112c713…
|
drh
|
706 |
/* |
|
112c713…
|
drh
|
707 |
** Override the value of the TH1 variable current_feature, its default |
|
112c713…
|
drh
|
708 |
** set by feature_from_page_path(). We do not call this from |
|
112c713…
|
drh
|
709 |
** style_init_th1_vars() because that uses Th_MaybeStore() instead to |
|
112c713…
|
drh
|
710 |
** allow webpage implementations to call this before style_header() |
|
112c713…
|
drh
|
711 |
** to override that "maybe" default with something better. |
|
112c713…
|
drh
|
712 |
*/ |
|
112c713…
|
drh
|
713 |
void style_set_current_feature(const char* zFeature){ |
|
112c713…
|
drh
|
714 |
Th_Store("current_feature", zFeature); |
|
112c713…
|
drh
|
715 |
} |
|
112c713…
|
drh
|
716 |
|
|
112c713…
|
drh
|
717 |
/* |
|
73ca280…
|
drh
|
718 |
** Returns the current mainmenu value from either the --mainmenu flag |
|
73ca280…
|
drh
|
719 |
** (handled by the server/ui/cgi commands), the "mainmenu" config |
|
73ca280…
|
drh
|
720 |
** setting, or style_default_mainmenu(), in that order, returning the |
|
73ca280…
|
drh
|
721 |
** first of those which is defined. |
|
73ca280…
|
drh
|
722 |
*/ |
|
1f6ae1e…
|
drh
|
723 |
const char *style_get_mainmenu(){ |
|
73ca280…
|
drh
|
724 |
static const char *zMenu = 0; |
|
73ca280…
|
drh
|
725 |
if(!zMenu){ |
|
73ca280…
|
drh
|
726 |
if(g.zMainMenuFile){ |
|
73ca280…
|
drh
|
727 |
Blob b = empty_blob; |
|
73ca280…
|
drh
|
728 |
blob_read_from_file(&b, g.zMainMenuFile, ExtFILE); |
|
73ca280…
|
drh
|
729 |
zMenu = blob_str(&b); |
|
73ca280…
|
drh
|
730 |
}else{ |
|
73ca280…
|
drh
|
731 |
zMenu = db_get("mainmenu", style_default_mainmenu()); |
|
73ca280…
|
drh
|
732 |
} |
|
73ca280…
|
drh
|
733 |
} |
|
73ca280…
|
drh
|
734 |
return zMenu; |
|
73ca280…
|
drh
|
735 |
} |
|
73ca280…
|
drh
|
736 |
|
|
73ca280…
|
drh
|
737 |
/* |
|
9c88799…
|
drh
|
738 |
** Initialize all the default TH1 variables |
|
9c88799…
|
drh
|
739 |
*/ |
|
9c88799…
|
drh
|
740 |
static void style_init_th1_vars(const char *zTitle){ |
|
8a65cd1…
|
mistachkin
|
741 |
const char *zNonce = style_nonce(); |
|
a44e3c7…
|
george
|
742 |
char *zDfltCsp; |
|
14c81d9…
|
drh
|
743 |
|
|
14c81d9…
|
drh
|
744 |
zDfltCsp = style_csp(1); |
|
8a65cd1…
|
mistachkin
|
745 |
/* |
|
8a65cd1…
|
mistachkin
|
746 |
** Do not overwrite the TH1 variable "default_csp" if it exists, as this |
|
8a65cd1…
|
mistachkin
|
747 |
** allows it to be properly overridden via the TH1 setup script (i.e. it |
|
8a65cd1…
|
mistachkin
|
748 |
** is evaluated before the header is rendered). |
|
8a65cd1…
|
mistachkin
|
749 |
*/ |
|
8a65cd1…
|
mistachkin
|
750 |
Th_MaybeStore("default_csp", zDfltCsp); |
|
14c81d9…
|
drh
|
751 |
fossil_free(zDfltCsp); |
|
8a65cd1…
|
mistachkin
|
752 |
Th_Store("nonce", zNonce); |
|
2116238…
|
drh
|
753 |
Th_StoreUnsafe("project_name", |
|
2116238…
|
drh
|
754 |
db_get("project-name","Unnamed Fossil Project")); |
|
2116238…
|
drh
|
755 |
Th_StoreUnsafe("project_description", db_get("project-description","")); |
|
5ea6e15…
|
drh
|
756 |
if( zTitle ) Th_Store("title", html_lookalike(zTitle,-1)); |
|
9c88799…
|
drh
|
757 |
Th_Store("baseurl", g.zBaseURL); |
|
4aba9ea…
|
drh
|
758 |
Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
|
9c88799…
|
drh
|
759 |
Th_Store("home", g.zTop); |
|
9c88799…
|
drh
|
760 |
Th_Store("index_page", db_get("index-page","/home")); |
|
9c88799…
|
drh
|
761 |
if( local_zCurrentPage==0 ) style_set_current_page("%T", g.zPath); |
|
9c88799…
|
drh
|
762 |
Th_Store("current_page", local_zCurrentPage); |
|
a44e3c7…
|
george
|
763 |
if( g.zPath ){ /* store the first segment of a path; */ |
|
a44e3c7…
|
george
|
764 |
char *pSlash = strchr(g.zPath,'/'); |
|
a44e3c7…
|
george
|
765 |
if( pSlash ) *pSlash = 0; /* make a temporary cut if necessary */ |
|
c68fa2e…
|
drh
|
766 |
Th_Store("requested_page", escape_quotes(g.zPath)); |
|
a44e3c7…
|
george
|
767 |
if( pSlash ) *pSlash = '/'; |
|
a44e3c7…
|
george
|
768 |
}else{ |
|
a44e3c7…
|
george
|
769 |
Th_Store("requested_page", ""); |
|
c68fa2e…
|
drh
|
770 |
} |
|
b05a6c6…
|
george
|
771 |
Th_Store("canonical_page", escape_quotes(g.zPhase+1)); |
|
9c88799…
|
drh
|
772 |
Th_Store("csrf_token", g.zCsrfToken); |
|
9c88799…
|
drh
|
773 |
Th_Store("release_version", RELEASE_VERSION); |
|
9c88799…
|
drh
|
774 |
Th_Store("manifest_version", MANIFEST_VERSION); |
|
9c88799…
|
drh
|
775 |
Th_Store("manifest_date", MANIFEST_DATE); |
|
9c88799…
|
drh
|
776 |
Th_Store("compiler_name", COMPILER_NAME); |
|
73ca280…
|
drh
|
777 |
Th_Store("mainmenu", style_get_mainmenu()); |
|
6acd87f…
|
drh
|
778 |
stylesheet_url_var(); |
|
9c88799…
|
drh
|
779 |
image_url_var("logo"); |
|
9c88799…
|
drh
|
780 |
image_url_var("background"); |
|
9c88799…
|
drh
|
781 |
if( !login_is_nobody() ){ |
|
5ea6e15…
|
drh
|
782 |
Th_Store("login", html_lookalike(g.zLogin,-1)); |
|
112c713…
|
drh
|
783 |
} |
|
112c713…
|
drh
|
784 |
Th_MaybeStore("current_feature", feature_from_page_path(local_zCurrentPage) ); |
|
3990518…
|
george
|
785 |
if( g.ftntsIssues[0] || g.ftntsIssues[1] || |
|
3990518…
|
george
|
786 |
g.ftntsIssues[2] || g.ftntsIssues[3] ){ |
|
3990518…
|
george
|
787 |
char buf[80]; |
|
275da70…
|
danield
|
788 |
sqlite3_snprintf(sizeof(buf), buf, "%i %i %i %i", g.ftntsIssues[0], |
|
275da70…
|
danield
|
789 |
g.ftntsIssues[1], g.ftntsIssues[2], g.ftntsIssues[3]); |
|
3990518…
|
george
|
790 |
Th_Store("footnotes_issues_counters", buf); |
|
3990518…
|
george
|
791 |
} |
|
9c88799…
|
drh
|
792 |
} |
|
daff9d2…
|
joel
|
793 |
|
|
daff9d2…
|
joel
|
794 |
/* |
|
dbda8d6…
|
drh
|
795 |
** Draw the header. |
|
dbda8d6…
|
drh
|
796 |
*/ |
|
55342eb…
|
drh
|
797 |
void style_header(const char *zTitleFormat, ...){ |
|
55342eb…
|
drh
|
798 |
va_list ap; |
|
55342eb…
|
drh
|
799 |
char *zTitle; |
|
ed36e2e…
|
drh
|
800 |
const char *zHeader = skin_get("header"); |
|
b312f5f…
|
drh
|
801 |
login_check_credentials(); |
|
55342eb…
|
drh
|
802 |
|
|
55342eb…
|
drh
|
803 |
va_start(ap, zTitleFormat); |
|
55342eb…
|
drh
|
804 |
zTitle = vmprintf(zTitleFormat, ap); |
|
55342eb…
|
drh
|
805 |
va_end(ap); |
|
d13143e…
|
jan.nijtmans
|
806 |
|
|
b312f5f…
|
drh
|
807 |
cgi_destination(CGI_HEADER); |
|
97d651b…
|
drh
|
808 |
|
|
97d651b…
|
drh
|
809 |
@ <!DOCTYPE html> |
|
d13143e…
|
jan.nijtmans
|
810 |
|
|
f5482a0…
|
wyoung
|
811 |
if( g.thTrace ) Th_Trace("BEGIN_HEADER<br>\n", -1); |
|
f55c6a1…
|
drh
|
812 |
|
|
b312f5f…
|
drh
|
813 |
/* Generate the header up through the main menu */ |
|
9c88799…
|
drh
|
814 |
style_init_th1_vars(zTitle); |
|
24ecb3b…
|
drh
|
815 |
if( sqlite3_strlike("%<body%", zHeader, 0)!=0 ){ |
|
f1bb72e…
|
drh
|
816 |
Th_Render(zDfltHeader); |
|
f1bb72e…
|
drh
|
817 |
} |
|
f5482a0…
|
wyoung
|
818 |
if( g.thTrace ) Th_Trace("BEGIN_HEADER_SCRIPT<br>\n", -1); |
|
588bb7c…
|
aku
|
819 |
Th_Render(zHeader); |
|
f5482a0…
|
wyoung
|
820 |
if( g.thTrace ) Th_Trace("END_HEADER<br>\n", -1); |
|
68c24b1…
|
drh
|
821 |
Th_Unstore("title"); /* Avoid collisions with ticket field names */ |
|
588bb7c…
|
aku
|
822 |
cgi_destination(CGI_BODY); |
|
96722b6…
|
drh
|
823 |
g.cgiOutput = 1; |
|
dcc4866…
|
drh
|
824 |
headerHasBeenGenerated = 1; |
|
5a48a9b…
|
drh
|
825 |
sideboxUsed = 0; |
|
99fcc43…
|
drh
|
826 |
if( g.perm.Debug && P("showqp") ){ |
|
99fcc43…
|
drh
|
827 |
@ <div class="debug"> |
|
0204f4a…
|
drh
|
828 |
cgi_print_all(0, 0, 0); |
|
99fcc43…
|
drh
|
829 |
@ </div> |
|
99fcc43…
|
drh
|
830 |
} |
|
37ae94b…
|
drh
|
831 |
fossil_free(zTitle); |
|
ff78d6d…
|
drh
|
832 |
} |
|
ff78d6d…
|
drh
|
833 |
|
|
ff78d6d…
|
drh
|
834 |
#if INTERFACE |
|
ff78d6d…
|
drh
|
835 |
/* Allowed parameters for style_adunit() */ |
|
ff78d6d…
|
drh
|
836 |
#define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */ |
|
ff78d6d…
|
drh
|
837 |
#define ADUNIT_RIGHT_OK 0x0002 /* Right-side vertical ads ok here */ |
|
9e318f6…
|
jan.nijtmans
|
838 |
#endif |
|
ff78d6d…
|
drh
|
839 |
|
|
ff78d6d…
|
drh
|
840 |
/* |
|
ff78d6d…
|
drh
|
841 |
** Various page implementations can invoke this interface to let the |
|
ff78d6d…
|
drh
|
842 |
** style manager know what kinds of ads are appropriate for this page. |
|
ff78d6d…
|
drh
|
843 |
*/ |
|
ff78d6d…
|
drh
|
844 |
void style_adunit_config(unsigned int mFlags){ |
|
ff78d6d…
|
drh
|
845 |
adUnitFlags = mFlags; |
|
4bf5cdc…
|
drh
|
846 |
} |
|
4bf5cdc…
|
drh
|
847 |
|
|
4bf5cdc…
|
drh
|
848 |
/* |
|
ff78d6d…
|
drh
|
849 |
** Return the text of an ad-unit, if one should be rendered. Return |
|
ff78d6d…
|
drh
|
850 |
** NULL if no ad-unit is desired. |
|
ff78d6d…
|
drh
|
851 |
** |
|
ff78d6d…
|
drh
|
852 |
** The *pAdFlag value might be set to ADUNIT_RIGHT_OK if this is |
|
ff78d6d…
|
drh
|
853 |
** a right-hand vertical ad. |
|
4bf5cdc…
|
drh
|
854 |
*/ |
|
ff78d6d…
|
drh
|
855 |
static const char *style_adunit_text(unsigned int *pAdFlag){ |
|
ff78d6d…
|
drh
|
856 |
const char *zAd = 0; |
|
ff78d6d…
|
drh
|
857 |
*pAdFlag = 0; |
|
ff78d6d…
|
drh
|
858 |
if( adUnitFlags & ADUNIT_OFF ) return 0; /* Disallow ads on this page */ |
|
48d8af2…
|
drh
|
859 |
if( db_get_boolean("adunit-disable",0) ) return 0; |
|
4bf5cdc…
|
drh
|
860 |
if( g.perm.Admin && db_get_boolean("adunit-omit-if-admin",0) ){ |
|
ff78d6d…
|
drh
|
861 |
return 0; |
|
840b762…
|
drh
|
862 |
} |
|
840b762…
|
drh
|
863 |
if( !login_is_nobody() |
|
840b762…
|
drh
|
864 |
&& fossil_strcmp(g.zLogin,"anonymous")!=0 |
|
840b762…
|
drh
|
865 |
&& db_get_boolean("adunit-omit-if-user",0) |
|
840b762…
|
drh
|
866 |
){ |
|
ff78d6d…
|
drh
|
867 |
return 0; |
|
ff78d6d…
|
drh
|
868 |
} |
|
ff78d6d…
|
drh
|
869 |
if( (adUnitFlags & ADUNIT_RIGHT_OK)!=0 |
|
ff78d6d…
|
drh
|
870 |
&& !fossil_all_whitespace(zAd = db_get("adunit-right", 0)) |
|
ff78d6d…
|
drh
|
871 |
&& !cgi_body_contains("<table") |
|
ff78d6d…
|
drh
|
872 |
){ |
|
ff78d6d…
|
drh
|
873 |
*pAdFlag = ADUNIT_RIGHT_OK; |
|
ff78d6d…
|
drh
|
874 |
return zAd; |
|
ff78d6d…
|
drh
|
875 |
}else if( !fossil_all_whitespace(zAd = db_get("adunit",0)) ){ |
|
ff78d6d…
|
drh
|
876 |
return zAd; |
|
ff78d6d…
|
drh
|
877 |
} |
|
ff78d6d…
|
drh
|
878 |
return 0; |
|
ff78d6d…
|
drh
|
879 |
} |
|
ff78d6d…
|
drh
|
880 |
|
|
ff78d6d…
|
drh
|
881 |
/* |
|
6b645d6…
|
drh
|
882 |
** Indicate that the table-sorting javascript is needed. |
|
09494b0…
|
drh
|
883 |
*/ |
|
6b645d6…
|
drh
|
884 |
void style_table_sorter(void){ |
|
036a9d5…
|
drh
|
885 |
builtin_request_js("sorttable.js"); |
|
09494b0…
|
drh
|
886 |
} |
|
09494b0…
|
drh
|
887 |
|
|
09494b0…
|
drh
|
888 |
/* |
|
09494b0…
|
drh
|
889 |
** Generate code to load all required javascript files. |
|
09494b0…
|
drh
|
890 |
*/ |
|
09494b0…
|
drh
|
891 |
static void style_load_all_js_files(void){ |
|
b190858…
|
drh
|
892 |
if( needHrefJs && g.perm.Hyperlink ){ |
|
09494b0…
|
drh
|
893 |
int nDelay = db_get_int("auto-hyperlink-delay",0); |
|
cef15ed…
|
drh
|
894 |
int bMouseover = db_get_boolean("auto-hyperlink-mouseover",0) |
|
cef15ed…
|
drh
|
895 |
&& sqlite3_strglob("*Android*",PD("HTTP_USER_AGENT","")); |
|
7fcb462…
|
stephan
|
896 |
@ <script id='href-data' type='text/json'>\ |
|
09494b0…
|
drh
|
897 |
@ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script> |
|
ff747b5…
|
drh
|
898 |
} |
|
6854244…
|
drh
|
899 |
@ <script nonce="%h(style_nonce())">/* style.c:%d(__LINE__) */ |
|
6908832…
|
drh
|
900 |
@ function debugMsg(msg){ |
|
6908832…
|
drh
|
901 |
@ var n = document.getElementById("debugMsg"); |
|
6908832…
|
drh
|
902 |
@ if(n){n.textContent=msg;} |
|
6908832…
|
drh
|
903 |
@ } |
|
b190858…
|
drh
|
904 |
if( needHrefJs && g.perm.Hyperlink ){ |
|
036a9d5…
|
drh
|
905 |
@ /* href.js */ |
|
ff747b5…
|
drh
|
906 |
cgi_append_content(builtin_text("href.js"),-1); |
|
ff747b5…
|
drh
|
907 |
} |
|
ff747b5…
|
drh
|
908 |
if( blob_size(&blobOnLoad)>0 ){ |
|
ff747b5…
|
drh
|
909 |
@ window.onload = function(){ |
|
ff747b5…
|
drh
|
910 |
cgi_append_content(blob_buffer(&blobOnLoad), blob_size(&blobOnLoad)); |
|
ff747b5…
|
drh
|
911 |
cgi_append_content("\n}\n", -1); |
|
ff747b5…
|
drh
|
912 |
} |
|
ff747b5…
|
drh
|
913 |
@ </script> |
|
036a9d5…
|
drh
|
914 |
builtin_fulfill_js_requests(); |
|
e8a051e…
|
george
|
915 |
} |
|
e8a051e…
|
george
|
916 |
|
|
e8a051e…
|
george
|
917 |
/* |
|
e2bdc10…
|
danield
|
918 |
** Transform input string into a token that is safe for inclusion into |
|
e8a051e…
|
george
|
919 |
** class attribute. Digits and low-case letter are passed unchanged, |
|
e8a051e…
|
george
|
920 |
** upper-case letters are transformed to low-case, everything else is |
|
e2bdc10…
|
danield
|
921 |
** transformed into hyphens; consecutive and pending hyphens are squeezed. |
|
e8a051e…
|
george
|
922 |
** If result does not fit into szOut chars then it is truncated. |
|
e8a051e…
|
george
|
923 |
** Result is always terminated with null. |
|
e8a051e…
|
george
|
924 |
*/ |
|
e8a051e…
|
george
|
925 |
void style_derive_classname(const char *zIn, char *zOut, int szOut){ |
|
e8a051e…
|
george
|
926 |
assert( zOut ); |
|
e8a051e…
|
george
|
927 |
assert( szOut>0 ); |
|
e8a051e…
|
george
|
928 |
if( zIn ){ |
|
e8a051e…
|
george
|
929 |
int n = 0; /* number of chars written to zOut */ |
|
e8a051e…
|
george
|
930 |
char c; |
|
e8a051e…
|
george
|
931 |
for(--szOut; (c=*zIn) && n<szOut; zIn++) { |
|
e8a051e…
|
george
|
932 |
if( ('a'<=c && c<='z') || ('0'<=c && c<='9') ){ |
|
e8a051e…
|
george
|
933 |
*zOut = c; |
|
e8a051e…
|
george
|
934 |
}else if( 'A'<=c && c<='Z' ){ |
|
e8a051e…
|
george
|
935 |
*zOut = c - 'A' + 'a'; |
|
e8a051e…
|
george
|
936 |
}else{ |
|
e8a051e…
|
george
|
937 |
if( n==0 || zOut[-1]=='-' ) continue; |
|
e8a051e…
|
george
|
938 |
*zOut = '-'; |
|
e8a051e…
|
george
|
939 |
} |
|
e8a051e…
|
george
|
940 |
zOut++; |
|
e8a051e…
|
george
|
941 |
n++; |
|
e8a051e…
|
george
|
942 |
} |
|
e8a051e…
|
george
|
943 |
if( n && zOut[-1]=='-' ) zOut--; |
|
e8a051e…
|
george
|
944 |
} |
|
e8a051e…
|
george
|
945 |
*zOut = 0; |
|
112c713…
|
drh
|
946 |
} |
|
112c713…
|
drh
|
947 |
|
|
112c713…
|
drh
|
948 |
/* |
|
3d6444f…
|
drh
|
949 |
** Invoke this routine after all of the content for a webpage has been |
|
3d6444f…
|
drh
|
950 |
** generated. This routine should be called once for every webpage, at |
|
3d6444f…
|
drh
|
951 |
** or near the end of page generation. This routine does the following: |
|
3d6444f…
|
drh
|
952 |
** |
|
3d6444f…
|
drh
|
953 |
** * Populates the header of the page, including setting up appropriate |
|
3d6444f…
|
drh
|
954 |
** submenu elements. The header generation is deferred until this point |
|
3d6444f…
|
drh
|
955 |
** so that we know that all style_submenu_element() and similar have |
|
3d6444f…
|
drh
|
956 |
** been received. |
|
3d6444f…
|
drh
|
957 |
** |
|
3d6444f…
|
drh
|
958 |
** * Finalizes the page content. |
|
3d6444f…
|
drh
|
959 |
** |
|
3d6444f…
|
drh
|
960 |
** * Appends the footer. |
|
4bf5cdc…
|
drh
|
961 |
*/ |
|
112c713…
|
drh
|
962 |
void style_finish_page(){ |
|
b312f5f…
|
drh
|
963 |
const char *zFooter; |
|
ff78d6d…
|
drh
|
964 |
const char *zAd = 0; |
|
ff78d6d…
|
drh
|
965 |
unsigned int mAdFlags = 0; |
|
dcc4866…
|
drh
|
966 |
|
|
dcc4866…
|
drh
|
967 |
if( !headerHasBeenGenerated ) return; |
|
d13143e…
|
jan.nijtmans
|
968 |
|
|
b312f5f…
|
drh
|
969 |
/* Go back and put the submenu at the top of the page. We delay the |
|
b312f5f…
|
drh
|
970 |
** creation of the submenu until the end so that we can add elements |
|
b312f5f…
|
drh
|
971 |
** to the submenu while generating page text. |
|
b312f5f…
|
drh
|
972 |
*/ |
|
23ed5e2…
|
eric
|
973 |
cgi_destination(CGI_HEADER); |
|
220ed0a…
|
drh
|
974 |
if( submenuEnable && nSubmenu+nSubmenuCtrl>0 ){ |
|
249f1be…
|
drh
|
975 |
int i; |
|
e8a051e…
|
george
|
976 |
char zClass[32]; /* reduced form of the main attribute */ |
|
c0c0bae…
|
drh
|
977 |
if( nSubmenuCtrl ){ |
|
802939b…
|
drh
|
978 |
@ <form id='f01' method='GET' action='%R/%s(g.zPath)'> |
|
802939b…
|
drh
|
979 |
@ <input type='hidden' name='udc' value='1'> |
|
0f9b648…
|
drh
|
980 |
cgi_tag_query_parameter("udc"); |
|
c0c0bae…
|
drh
|
981 |
} |
|
249f1be…
|
drh
|
982 |
@ <div class="submenu"> |
|
c0c0bae…
|
drh
|
983 |
if( nSubmenu>0 ){ |
|
c0c0bae…
|
drh
|
984 |
qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare); |
|
c0c0bae…
|
drh
|
985 |
for(i=0; i<nSubmenu; i++){ |
|
c0c0bae…
|
drh
|
986 |
struct Submenu *p = &aSubmenu[i]; |
|
e8a051e…
|
george
|
987 |
style_derive_classname(p->zLabel, zClass, sizeof zClass); |
|
3e35068…
|
george
|
988 |
/* switching away from the %h formatting below might be dangerous |
|
3e35068…
|
george
|
989 |
** because some places use %s to compose zLabel and zLink; |
|
71b2216…
|
george
|
990 |
** e.g. /rptview page and the submenuCmd() function. |
|
71b2216…
|
george
|
991 |
** "sml" stands for submenu link. |
|
3e35068…
|
george
|
992 |
*/ |
|
9c21101…
|
andygoth
|
993 |
if( p->zLink==0 ){ |
|
e8a051e…
|
george
|
994 |
@ <span class="label sml-%s(zClass)">%h(p->zLabel)</span> |
|
9c21101…
|
andygoth
|
995 |
}else{ |
|
e8a051e…
|
george
|
996 |
@ <a class="label sml-%s(zClass)" href="%h(p->zLink)">%h(p->zLabel)</a> |
|
9c21101…
|
andygoth
|
997 |
} |
|
9c21101…
|
andygoth
|
998 |
} |
|
9c21101…
|
andygoth
|
999 |
} |
|
c0b9b44…
|
stephan
|
1000 |
fossil_strcpy(zClass,"smc-"); /* common prefix for submenu controls */ |
|
9c21101…
|
andygoth
|
1001 |
for(i=0; i<nSubmenuCtrl; i++){ |
|
9c21101…
|
andygoth
|
1002 |
const char *zQPN = aSubmenuCtrl[i].zName; |
|
e91d267…
|
drh
|
1003 |
const char *zDisabled = ""; |
|
e91d267…
|
drh
|
1004 |
const char *zXtraClass = ""; |
|
e91d267…
|
drh
|
1005 |
if( aSubmenuCtrl[i].eVisible & STYLE_DISABLED ){ |
|
e91d267…
|
drh
|
1006 |
zDisabled = " disabled"; |
|
e91d267…
|
drh
|
1007 |
}else if( zQPN ){ |
|
9c21101…
|
andygoth
|
1008 |
cgi_tag_query_parameter(zQPN); |
|
9c21101…
|
andygoth
|
1009 |
} |
|
e8a051e…
|
george
|
1010 |
style_derive_classname(zQPN, zClass+4, sizeof(zClass)-4); |
|
9c21101…
|
andygoth
|
1011 |
switch( aSubmenuCtrl[i].eType ){ |
|
9c21101…
|
andygoth
|
1012 |
case FF_ENTRY: |
|
e8a051e…
|
george
|
1013 |
@ <span class='submenuctrl%s(zXtraClass) %s(zClass)'>\ |
|
9c21101…
|
andygoth
|
1014 |
@ %h(aSubmenuCtrl[i].zLabel)\ |
|
9c21101…
|
andygoth
|
1015 |
@ <input type='text' name='%s(zQPN)' value='%h(PD(zQPN, ""))' \ |
|
9c21101…
|
andygoth
|
1016 |
if( aSubmenuCtrl[i].iSize<0 ){ |
|
9c21101…
|
andygoth
|
1017 |
@ size='%d(-aSubmenuCtrl[i].iSize)' \ |
|
9c21101…
|
andygoth
|
1018 |
}else if( aSubmenuCtrl[i].iSize>0 ){ |
|
9c21101…
|
andygoth
|
1019 |
@ size='%d(aSubmenuCtrl[i].iSize)' \ |
|
9c21101…
|
andygoth
|
1020 |
@ maxlength='%d(aSubmenuCtrl[i].iSize)' \ |
|
9c21101…
|
andygoth
|
1021 |
} |
|
3969757…
|
drh
|
1022 |
@ id='submenuctrl-%d(i)'%s(zDisabled)></span> |
|
3cb9ba4…
|
andygoth
|
1023 |
break; |
|
3cb9ba4…
|
andygoth
|
1024 |
case FF_MULTI: { |
|
3cb9ba4…
|
andygoth
|
1025 |
int j; |
|
3cb9ba4…
|
andygoth
|
1026 |
const char *zVal = P(zQPN); |
|
e91d267…
|
drh
|
1027 |
if( zXtraClass[0] ){ |
|
e8a051e…
|
george
|
1028 |
@ <span class='%s(zXtraClass+1) %s(zClass)'> |
|
e91d267…
|
drh
|
1029 |
} |
|
3cb9ba4…
|
andygoth
|
1030 |
if( aSubmenuCtrl[i].zLabel ){ |
|
9c21101…
|
andygoth
|
1031 |
@ %h(aSubmenuCtrl[i].zLabel)\ |
|
3cb9ba4…
|
andygoth
|
1032 |
} |
|
e8a051e…
|
george
|
1033 |
@ <select class='submenuctrl %s(zClass)' size='1' name='%s(zQPN)' \ |
|
3969757…
|
drh
|
1034 |
@ id='submenuctrl-%d(i)'%s(zDisabled)> |
|
3cb9ba4…
|
andygoth
|
1035 |
for(j=0; j<aSubmenuCtrl[i].iSize*2; j+=2){ |
|
3cb9ba4…
|
andygoth
|
1036 |
const char *zQPV = aSubmenuCtrl[i].azChoice[j]; |
|
9c21101…
|
andygoth
|
1037 |
@ <option value='%h(zQPV)'\ |
|
9c21101…
|
andygoth
|
1038 |
if( fossil_strcmp(zVal, zQPV)==0 ){ |
|
9c21101…
|
andygoth
|
1039 |
@ selected\ |
|
9c21101…
|
andygoth
|
1040 |
} |
|
9c21101…
|
andygoth
|
1041 |
@ >%h(aSubmenuCtrl[i].azChoice[j+1])</option> |
|
3cb9ba4…
|
andygoth
|
1042 |
} |
|
3cb9ba4…
|
andygoth
|
1043 |
@ </select> |
|
e91d267…
|
drh
|
1044 |
if( zXtraClass[0] ){ |
|
e91d267…
|
drh
|
1045 |
@ </span> |
|
e91d267…
|
drh
|
1046 |
} |
|
3cb9ba4…
|
andygoth
|
1047 |
break; |
|
3cb9ba4…
|
andygoth
|
1048 |
} |
|
3cb9ba4…
|
andygoth
|
1049 |
case FF_BINARY: { |
|
3cb9ba4…
|
andygoth
|
1050 |
int isTrue = PB(zQPN); |
|
259074d…
|
drh
|
1051 |
@ <select class='submenuctrl%s(zXtraClass)' size='1' \ |
|
3969757…
|
drh
|
1052 |
@ name='%s(zQPN)' id='submenuctrl-%d(i)'%s(zDisabled)> |
|
9c21101…
|
andygoth
|
1053 |
@ <option value='1'\ |
|
9c21101…
|
andygoth
|
1054 |
if( isTrue ){ |
|
9c21101…
|
andygoth
|
1055 |
@ selected\ |
|
9c21101…
|
andygoth
|
1056 |
} |
|
9c21101…
|
andygoth
|
1057 |
@ >%h(aSubmenuCtrl[i].zLabel)</option> |
|
9c21101…
|
andygoth
|
1058 |
@ <option value='0'\ |
|
9c21101…
|
andygoth
|
1059 |
if( !isTrue ){ |
|
9c21101…
|
andygoth
|
1060 |
@ selected\ |
|
9c21101…
|
andygoth
|
1061 |
} |
|
9c21101…
|
andygoth
|
1062 |
@ >%h(aSubmenuCtrl[i].zFalse)</option> |
|
3cb9ba4…
|
andygoth
|
1063 |
@ </select> |
|
3cb9ba4…
|
andygoth
|
1064 |
break; |
|
3cb9ba4…
|
andygoth
|
1065 |
} |
|
037e06b…
|
drh
|
1066 |
case FF_CHECKBOX: { |
|
e8a051e…
|
george
|
1067 |
@ <label class='submenuctrl submenuckbox%s(zXtraClass) %s(zClass)'>\ |
|
3969757…
|
drh
|
1068 |
@ <input type='checkbox' name='%s(zQPN)' id='submenuctrl-%d(i)' \ |
|
9c21101…
|
andygoth
|
1069 |
if( PB(zQPN) ){ |
|
9c21101…
|
andygoth
|
1070 |
@ checked \ |
|
9c21101…
|
andygoth
|
1071 |
} |
|
037e06b…
|
drh
|
1072 |
if( aSubmenuCtrl[i].zJS ){ |
|
3969757…
|
drh
|
1073 |
@ data-ctrl='%s(aSubmenuCtrl[i].zJS)'%s(zDisabled)>\ |
|
037e06b…
|
drh
|
1074 |
}else{ |
|
3969757…
|
drh
|
1075 |
@ %s(zDisabled)>\ |
|
037e06b…
|
drh
|
1076 |
} |
|
9c21101…
|
andygoth
|
1077 |
@ %h(aSubmenuCtrl[i].zLabel)</label> |
|
3cb9ba4…
|
andygoth
|
1078 |
break; |
|
037e06b…
|
drh
|
1079 |
} |
|
249f1be…
|
drh
|
1080 |
} |
|
249f1be…
|
drh
|
1081 |
} |
|
ff78d6d…
|
drh
|
1082 |
@ </div> |
|
c0c0bae…
|
drh
|
1083 |
if( nSubmenuCtrl ){ |
|
c0c0bae…
|
drh
|
1084 |
cgi_query_parameters_to_hidden(); |
|
c0c0bae…
|
drh
|
1085 |
cgi_tag_query_parameter(0); |
|
c0c0bae…
|
drh
|
1086 |
@ </form> |
|
036a9d5…
|
drh
|
1087 |
builtin_request_js("menu.js"); |
|
c0c0bae…
|
drh
|
1088 |
} |
|
ff78d6d…
|
drh
|
1089 |
} |
|
ff78d6d…
|
drh
|
1090 |
|
|
ff78d6d…
|
drh
|
1091 |
zAd = style_adunit_text(&mAdFlags); |
|
ff78d6d…
|
drh
|
1092 |
if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){ |
|
ff78d6d…
|
drh
|
1093 |
@ <div class="content adunit_right_container"> |
|
ff78d6d…
|
drh
|
1094 |
@ <div class="adunit_right"> |
|
ff78d6d…
|
drh
|
1095 |
cgi_append_content(zAd, -1); |
|
ff78d6d…
|
drh
|
1096 |
@ </div> |
|
3d6444f…
|
drh
|
1097 |
}else if( zAd ){ |
|
3d6444f…
|
drh
|
1098 |
@ <div class="adunit_banner"> |
|
3d6444f…
|
drh
|
1099 |
cgi_append_content(zAd, -1); |
|
3d6444f…
|
drh
|
1100 |
@ </div> |
|
3d6444f…
|
drh
|
1101 |
} |
|
3d6444f…
|
drh
|
1102 |
|
|
112c713…
|
drh
|
1103 |
@ <div class="content"><span id="debugMsg"></span> |
|
249f1be…
|
drh
|
1104 |
cgi_destination(CGI_BODY); |
|
249f1be…
|
drh
|
1105 |
|
|
38421a9…
|
jan.nijtmans
|
1106 |
if( sideboxUsed ){ |
|
5a48a9b…
|
drh
|
1107 |
@ <div class="endContent"></div> |
|
5a48a9b…
|
drh
|
1108 |
} |
|
3243e63…
|
drh
|
1109 |
@ </div> |
|
7fb59a6…
|
drh
|
1110 |
|
|
3d6444f…
|
drh
|
1111 |
/* Put the footer at the bottom of the page. */ |
|
ed36e2e…
|
drh
|
1112 |
zFooter = skin_get("footer"); |
|
8394d2f…
|
drh
|
1113 |
if( sqlite3_strlike("%</body>%", zFooter, 0)==0 ){ |
|
09494b0…
|
drh
|
1114 |
style_load_all_js_files(); |
|
8394d2f…
|
drh
|
1115 |
} |
|
f5482a0…
|
wyoung
|
1116 |
if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br>\n", -1); |
|
249f1be…
|
drh
|
1117 |
Th_Render(zFooter); |
|
f5482a0…
|
wyoung
|
1118 |
if( g.thTrace ) Th_Trace("END_FOOTER<br>\n", -1); |
|
d13143e…
|
jan.nijtmans
|
1119 |
|
|
249f1be…
|
drh
|
1120 |
/* Render trace log if TH1 tracing is enabled. */ |
|
249f1be…
|
drh
|
1121 |
if( g.thTrace ){ |
|
f5482a0…
|
wyoung
|
1122 |
cgi_append_content("<span class=\"thTrace\"><hr>\n", -1); |
|
249f1be…
|
drh
|
1123 |
cgi_append_content(blob_str(&g.thLog), blob_size(&g.thLog)); |
|
5a48a9b…
|
drh
|
1124 |
cgi_append_content("</span>\n", -1); |
|
5a48a9b…
|
drh
|
1125 |
} |
|
f1bb72e…
|
drh
|
1126 |
|
|
f1bb72e…
|
drh
|
1127 |
/* Add document end mark if it was not in the footer */ |
|
f1bb72e…
|
drh
|
1128 |
if( sqlite3_strlike("%</body>%", zFooter, 0)!=0 ){ |
|
09494b0…
|
drh
|
1129 |
style_load_all_js_files(); |
|
8394d2f…
|
drh
|
1130 |
@ </body> |
|
8394d2f…
|
drh
|
1131 |
@ </html> |
|
f1bb72e…
|
drh
|
1132 |
} |
|
5feac63…
|
stephan
|
1133 |
/* Update the user display prefs cookie if it was modified */ |
|
3339420…
|
drh
|
1134 |
cookie_render(); |
|
83ac468…
|
drh
|
1135 |
} |
|
83ac468…
|
drh
|
1136 |
|
|
83ac468…
|
drh
|
1137 |
/* |
|
83ac468…
|
drh
|
1138 |
** Begin a side-box on the right-hand side of a page. The title and |
|
83ac468…
|
drh
|
1139 |
** the width of the box are given as arguments. The width is usually |
|
83ac468…
|
drh
|
1140 |
** a percentage of total screen width. |
|
83ac468…
|
drh
|
1141 |
*/ |
|
83ac468…
|
drh
|
1142 |
void style_sidebox_begin(const char *zTitle, const char *zWidth){ |
|
5a48a9b…
|
drh
|
1143 |
sideboxUsed = 1; |
|
34f9b9d…
|
drh
|
1144 |
@ <div class="sidebox" style="width:%s(zWidth)"> |
|
34f9b9d…
|
drh
|
1145 |
@ <div class="sideboxTitle">%h(zTitle)</div> |
|
83ac468…
|
drh
|
1146 |
} |
|
83ac468…
|
drh
|
1147 |
|
|
83ac468…
|
drh
|
1148 |
/* End the side-box |
|
83ac468…
|
drh
|
1149 |
*/ |
|
83ac468…
|
drh
|
1150 |
void style_sidebox_end(void){ |
|
34f9b9d…
|
drh
|
1151 |
@ </div> |
|
34f9b9d…
|
drh
|
1152 |
} |
|
34f9b9d…
|
drh
|
1153 |
|
|
ca869aa…
|
drh
|
1154 |
/* |
|
ca869aa…
|
drh
|
1155 |
** Search string zCss for zSelector. |
|
a00a140…
|
drh
|
1156 |
** |
|
a00a140…
|
drh
|
1157 |
** Return true if found. Return false if not found |
|
a00a140…
|
drh
|
1158 |
*/ |
|
ca869aa…
|
drh
|
1159 |
static int containsSelector(const char *zCss, const char *zSelector){ |
|
31c81ac…
|
drh
|
1160 |
const char *z; |
|
a00a140…
|
drh
|
1161 |
int n; |
|
ca869aa…
|
drh
|
1162 |
int selectorLen = (int)strlen(zSelector); |
|
a00a140…
|
drh
|
1163 |
|
|
31c81ac…
|
drh
|
1164 |
for(z=zCss; *z; z+=selectorLen){ |
|
ca869aa…
|
drh
|
1165 |
z = strstr(z, zSelector); |
|
a00a140…
|
drh
|
1166 |
if( z==0 ) return 0; |
|
ca869aa…
|
drh
|
1167 |
if( z!=zCss ){ |
|
ca869aa…
|
drh
|
1168 |
for( n=-1; z+n!=zCss && fossil_isspace(z[n]); n--); |
|
ca869aa…
|
drh
|
1169 |
if( z+n!=zCss && z[n]!=',' && z[n]!= '}' && z[n]!='/' ) continue; |
|
a00a140…
|
drh
|
1170 |
} |
|
ca869aa…
|
drh
|
1171 |
for( n=selectorLen; z[n] && fossil_isspace(z[n]); n++ ); |
|
ca869aa…
|
drh
|
1172 |
if( z[n]==',' || z[n]=='{' || z[n]=='/' ) return 1; |
|
a00a140…
|
drh
|
1173 |
} |
|
a00a140…
|
drh
|
1174 |
return 0; |
|
a00a140…
|
drh
|
1175 |
} |
|
b8e3dc1…
|
jan.nijtmans
|
1176 |
|
|
ca869aa…
|
drh
|
1177 |
/* |
|
ca869aa…
|
drh
|
1178 |
** COMMAND: test-contains-selector |
|
ca869aa…
|
drh
|
1179 |
** |
|
ca869aa…
|
drh
|
1180 |
** Usage: %fossil test-contains-selector FILENAME SELECTOR |
|
ca869aa…
|
drh
|
1181 |
** |
|
ca869aa…
|
drh
|
1182 |
** Determine if the CSS stylesheet FILENAME contains SELECTOR. |
|
064c1c9…
|
stephan
|
1183 |
** |
|
064c1c9…
|
stephan
|
1184 |
** Note that as of 2020-05-28, the default rules are always emitted, |
|
064c1c9…
|
stephan
|
1185 |
** so the containsSelector() logic is no longer applied when emitting |
|
064c1c9…
|
stephan
|
1186 |
** style.css. It is unclear whether this test command is now obsolete |
|
064c1c9…
|
stephan
|
1187 |
** or whether it may still serve a purpose. |
|
ca869aa…
|
drh
|
1188 |
*/ |
|
ca869aa…
|
drh
|
1189 |
void contains_selector_cmd(void){ |
|
ca869aa…
|
drh
|
1190 |
int found; |
|
ca869aa…
|
drh
|
1191 |
char *zSelector; |
|
ca869aa…
|
drh
|
1192 |
Blob css; |
|
ca869aa…
|
drh
|
1193 |
if( g.argc!=4 ) usage("FILENAME SELECTOR"); |
|
1772357…
|
drh
|
1194 |
blob_read_from_file(&css, g.argv[2], ExtFILE); |
|
ca869aa…
|
drh
|
1195 |
zSelector = g.argv[3]; |
|
ca869aa…
|
drh
|
1196 |
found = containsSelector(blob_str(&css), zSelector); |
|
ca869aa…
|
drh
|
1197 |
fossil_print("%s %s\n", zSelector, found ? "found" : "not found"); |
|
ca869aa…
|
drh
|
1198 |
blob_reset(&css); |
|
ca869aa…
|
drh
|
1199 |
} |
|
ca869aa…
|
drh
|
1200 |
|
|
9c88799…
|
drh
|
1201 |
/* |
|
9c88799…
|
drh
|
1202 |
** WEBPAGE: script.js |
|
9c88799…
|
drh
|
1203 |
** |
|
9c88799…
|
drh
|
1204 |
** Return the "Javascript" content for the current skin (if there is any) |
|
9c88799…
|
drh
|
1205 |
*/ |
|
9c88799…
|
drh
|
1206 |
void page_script_js(void){ |
|
9c88799…
|
drh
|
1207 |
const char *zScript = skin_get("js"); |
|
9c88799…
|
drh
|
1208 |
if( P("test") ){ |
|
9c88799…
|
drh
|
1209 |
/* Render the script as plain-text for testing purposes, if the "test" |
|
9c88799…
|
drh
|
1210 |
** query parameter is present */ |
|
9c88799…
|
drh
|
1211 |
cgi_set_content_type("text/plain"); |
|
9c88799…
|
drh
|
1212 |
}else{ |
|
9c88799…
|
drh
|
1213 |
/* Default behavior is to return javascript */ |
|
7fcb462…
|
stephan
|
1214 |
cgi_set_content_type("text/javascript"); |
|
9c88799…
|
drh
|
1215 |
} |
|
9c88799…
|
drh
|
1216 |
style_init_th1_vars(0); |
|
9c88799…
|
drh
|
1217 |
Th_Render(zScript?zScript:""); |
|
9c88799…
|
drh
|
1218 |
} |
|
9c88799…
|
drh
|
1219 |
|
|
064c1c9…
|
stephan
|
1220 |
/* |
|
d4c91b4…
|
drh
|
1221 |
** Check for "name" or "page" query parameters on an /style.css |
|
d4c91b4…
|
drh
|
1222 |
** page request. If present, then page-specific CSS is requested, |
|
d4c91b4…
|
drh
|
1223 |
** so add that CSS to pOut. If the "name" and "page" query parameters |
|
e2bdc10…
|
danield
|
1224 |
** are omitted, then pOut is unchanged. |
|
8eec01d…
|
stephan
|
1225 |
*/ |
|
8eec01d…
|
stephan
|
1226 |
static void page_style_css_append_page_style(Blob *pOut){ |
|
8eec01d…
|
stephan
|
1227 |
const char *zPage = PD("name",P("page")); |
|
8eec01d…
|
stephan
|
1228 |
char * zFile; |
|
8eec01d…
|
stephan
|
1229 |
int nFile = 0; |
|
8eec01d…
|
stephan
|
1230 |
const char *zBuiltin; |
|
8eec01d…
|
stephan
|
1231 |
|
|
8eec01d…
|
stephan
|
1232 |
if(zPage==0 || zPage[0]==0){ |
|
8eec01d…
|
stephan
|
1233 |
return; |
|
8eec01d…
|
stephan
|
1234 |
} |
|
8eec01d…
|
stephan
|
1235 |
zFile = mprintf("style.%s.css", zPage); |
|
8eec01d…
|
stephan
|
1236 |
zBuiltin = (const char *)builtin_file(zFile, &nFile); |
|
8eec01d…
|
stephan
|
1237 |
if(nFile>0){ |
|
8eec01d…
|
stephan
|
1238 |
blob_appendf(pOut, |
|
8eec01d…
|
stephan
|
1239 |
"\n/***********************************************************\n" |
|
d4c91b4…
|
drh
|
1240 |
"** Page-specific CSS for \"%s\"\n" |
|
8eec01d…
|
stephan
|
1241 |
"***********************************************************/\n", |
|
8eec01d…
|
stephan
|
1242 |
zPage); |
|
8eec01d…
|
stephan
|
1243 |
blob_append(pOut, zBuiltin, nFile); |
|
8eec01d…
|
stephan
|
1244 |
fossil_free(zFile); |
|
8eec01d…
|
stephan
|
1245 |
return; |
|
8eec01d…
|
stephan
|
1246 |
} |
|
8eec01d…
|
stephan
|
1247 |
/* Potential TODO: check for aliases/page groups. e.g. group all |
|
8eec01d…
|
stephan
|
1248 |
** /forumXYZ CSS into one file, all /setupXYZ into another, etc. As |
|
8eec01d…
|
stephan
|
1249 |
** of this writing, doing so would only shave a few kb from |
|
8eec01d…
|
stephan
|
1250 |
** default.css. */ |
|
8eec01d…
|
stephan
|
1251 |
fossil_free(zFile); |
|
8eec01d…
|
stephan
|
1252 |
} |
|
8eec01d…
|
stephan
|
1253 |
|
|
8eec01d…
|
stephan
|
1254 |
/* |
|
9413395…
|
drh
|
1255 |
** WEBPAGE: style.css loadavg-exempt |
|
d4c91b4…
|
drh
|
1256 |
** |
|
e2bdc10…
|
danield
|
1257 |
** Return the style sheet. The style sheet is assembled from |
|
d4c91b4…
|
drh
|
1258 |
** multiple sources, in order: |
|
d4c91b4…
|
drh
|
1259 |
** |
|
d4c91b4…
|
drh
|
1260 |
** (1) The built-in "default.css" style sheet containing basic defaults. |
|
d4c91b4…
|
drh
|
1261 |
** |
|
d4c91b4…
|
drh
|
1262 |
** (2) The page-specific style sheet taken from the built-in |
|
d4c91b4…
|
drh
|
1263 |
** called "PAGENAME.css" where PAGENAME is the value of the name= |
|
d4c91b4…
|
drh
|
1264 |
** or page= query parameters. If neither name= nor page= exist, |
|
d4c91b4…
|
drh
|
1265 |
** then this section is a no-op. |
|
d4c91b4…
|
drh
|
1266 |
** |
|
d4c91b4…
|
drh
|
1267 |
** (3) The skin-specific "css.txt" file, if there one. |
|
d4c91b4…
|
drh
|
1268 |
** |
|
d4c91b4…
|
drh
|
1269 |
** All of (1), (2), and (3) above (or as many as exist) are concatenated. |
|
d4c91b4…
|
drh
|
1270 |
** The result is then run through TH1 with the following variables set: |
|
d4c91b4…
|
drh
|
1271 |
** |
|
d4c91b4…
|
drh
|
1272 |
** * $basename |
|
d4c91b4…
|
drh
|
1273 |
** * $secureurl |
|
d4c91b4…
|
drh
|
1274 |
** * $home |
|
d4c91b4…
|
drh
|
1275 |
** * $logo |
|
d4c91b4…
|
drh
|
1276 |
** * $background |
|
7ab0328…
|
drh
|
1277 |
** |
|
d4c91b4…
|
drh
|
1278 |
** The output from TH1 becomes the style sheet. Fossil always reports |
|
275da70…
|
danield
|
1279 |
** that the style sheet is cacheable. |
|
1942d58…
|
drh
|
1280 |
*/ |
|
1942d58…
|
drh
|
1281 |
void page_style_css(void){ |
|
064c1c9…
|
stephan
|
1282 |
Blob css = empty_blob; |
|
34f9b9d…
|
drh
|
1283 |
int i; |
|
8eec01d…
|
stephan
|
1284 |
const char * zDefaults; |
|
d4c91b4…
|
drh
|
1285 |
const char *zSkin; |
|
1942d58…
|
drh
|
1286 |
|
|
1942d58…
|
drh
|
1287 |
cgi_set_content_type("text/css"); |
|
ec5a063…
|
drh
|
1288 |
etag_check(0, 0); |
|
064c1c9…
|
stephan
|
1289 |
/* Emit all default rules... */ |
|
8eec01d…
|
stephan
|
1290 |
zDefaults = (const char*)builtin_file("default.css", &i); |
|
8eec01d…
|
stephan
|
1291 |
blob_append(&css, zDefaults, i); |
|
8eec01d…
|
stephan
|
1292 |
/* Page-specific CSS, if any... */ |
|
8eec01d…
|
stephan
|
1293 |
page_style_css_append_page_style(&css); |
|
d4c91b4…
|
drh
|
1294 |
zSkin = skin_in_use(); |
|
d4c91b4…
|
drh
|
1295 |
if( zSkin==0 ) zSkin = "this repository"; |
|
d4c91b4…
|
drh
|
1296 |
blob_appendf(&css, |
|
8eec01d…
|
stephan
|
1297 |
"\n/***********************************************************\n" |
|
d4c91b4…
|
drh
|
1298 |
"** Skin-specific CSS for %s\n" |
|
8eec01d…
|
stephan
|
1299 |
"***********************************************************/\n", |
|
d4c91b4…
|
drh
|
1300 |
zSkin); |
|
8eec01d…
|
stephan
|
1301 |
blob_append(&css,skin_get("css"),-1); |
|
6239845…
|
drh
|
1302 |
/* Process through TH1 in order to give an opportunity to substitute |
|
6239845…
|
drh
|
1303 |
** variables such as $baseurl. |
|
6239845…
|
drh
|
1304 |
*/ |
|
6239845…
|
drh
|
1305 |
Th_Store("baseurl", g.zBaseURL); |
|
4aba9ea…
|
drh
|
1306 |
Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
|
6239845…
|
drh
|
1307 |
Th_Store("home", g.zTop); |
|
daff9d2…
|
joel
|
1308 |
image_url_var("logo"); |
|
daff9d2…
|
joel
|
1309 |
image_url_var("background"); |
|
6239845…
|
drh
|
1310 |
Th_Render(blob_str(&css)); |
|
37ae94b…
|
drh
|
1311 |
blob_reset(&css); |
|
6239845…
|
drh
|
1312 |
|
|
6239845…
|
drh
|
1313 |
/* Tell CGI that the content returned by this page is considered cacheable */ |
|
1942d58…
|
drh
|
1314 |
g.isConst = 1; |
|
1942d58…
|
drh
|
1315 |
} |
|
1942d58…
|
drh
|
1316 |
|
|
1942d58…
|
drh
|
1317 |
/* |
|
99fcc43…
|
drh
|
1318 |
** All possible capabilities |
|
99fcc43…
|
drh
|
1319 |
*/ |
|
275da70…
|
danield
|
1320 |
static const char allCap[] = |
|
99fcc43…
|
drh
|
1321 |
"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL"; |
|
99fcc43…
|
drh
|
1322 |
|
|
99fcc43…
|
drh
|
1323 |
/* |
|
99fcc43…
|
drh
|
1324 |
** Compute the current login capabilities |
|
99fcc43…
|
drh
|
1325 |
*/ |
|
99fcc43…
|
drh
|
1326 |
static char *find_capabilities(char *zCap){ |
|
99fcc43…
|
drh
|
1327 |
int i, j; |
|
99fcc43…
|
drh
|
1328 |
char c; |
|
99fcc43…
|
drh
|
1329 |
for(i=j=0; (c = allCap[j])!=0; j++){ |
|
99fcc43…
|
drh
|
1330 |
if( login_has_capability(&c, 1, 0) ) zCap[i++] = c; |
|
99fcc43…
|
drh
|
1331 |
} |
|
99fcc43…
|
drh
|
1332 |
zCap[i] = 0; |
|
99fcc43…
|
drh
|
1333 |
return zCap; |
|
99fcc43…
|
drh
|
1334 |
} |
|
99fcc43…
|
drh
|
1335 |
|
|
99fcc43…
|
drh
|
1336 |
/* |
|
99fcc43…
|
drh
|
1337 |
** Compute the current login capabilities that were |
|
99fcc43…
|
drh
|
1338 |
** contributed by Anonymous |
|
99fcc43…
|
drh
|
1339 |
*/ |
|
99fcc43…
|
drh
|
1340 |
static char *find_anon_capabilities(char *zCap){ |
|
99fcc43…
|
drh
|
1341 |
int i, j; |
|
99fcc43…
|
drh
|
1342 |
char c; |
|
99fcc43…
|
drh
|
1343 |
for(i=j=0; (c = allCap[j])!=0; j++){ |
|
99fcc43…
|
drh
|
1344 |
if( login_has_capability(&c, 1, LOGIN_ANON) |
|
99fcc43…
|
drh
|
1345 |
&& !login_has_capability(&c, 1, 0) ) zCap[i++] = c; |
|
99fcc43…
|
drh
|
1346 |
} |
|
99fcc43…
|
drh
|
1347 |
zCap[i] = 0; |
|
99fcc43…
|
drh
|
1348 |
return zCap; |
|
99fcc43…
|
drh
|
1349 |
} |
|
99fcc43…
|
drh
|
1350 |
|
|
99fcc43…
|
drh
|
1351 |
/* |
|
af57f63…
|
drh
|
1352 |
** WEBPAGE: test-title |
|
af57f63…
|
drh
|
1353 |
** |
|
af57f63…
|
drh
|
1354 |
** Render a test page in which the page title is set by the "title" |
|
af57f63…
|
drh
|
1355 |
** query parameter. This can be used to show that HTML or Javascript |
|
af57f63…
|
drh
|
1356 |
** content in the title does not leak through into generated page, resulting |
|
af57f63…
|
drh
|
1357 |
** in an XSS issue. |
|
af57f63…
|
drh
|
1358 |
** |
|
af57f63…
|
drh
|
1359 |
** Due to the potential for abuse, this webpage is only available to |
|
af57f63…
|
drh
|
1360 |
** administrators. |
|
af57f63…
|
drh
|
1361 |
*/ |
|
af57f63…
|
drh
|
1362 |
void page_test_title(void){ |
|
af57f63…
|
drh
|
1363 |
const char *zTitle; |
|
af57f63…
|
drh
|
1364 |
login_check_credentials(); |
|
af57f63…
|
drh
|
1365 |
if( !g.perm.Admin ){ |
|
af57f63…
|
drh
|
1366 |
login_needed(0); |
|
af57f63…
|
drh
|
1367 |
} |
|
af57f63…
|
drh
|
1368 |
zTitle = P("title"); |
|
af57f63…
|
drh
|
1369 |
if( zTitle==0 ){ |
|
af57f63…
|
drh
|
1370 |
zTitle = "(No Title)"; |
|
af57f63…
|
drh
|
1371 |
} |
|
af57f63…
|
drh
|
1372 |
style_header("%s", zTitle); |
|
af57f63…
|
drh
|
1373 |
@ <p> |
|
af57f63…
|
drh
|
1374 |
@ This page sets its title to the value of the "title" query parameter. |
|
af57f63…
|
drh
|
1375 |
@ The form below is a convenient way to set the title query parameter: |
|
af57f63…
|
drh
|
1376 |
@ |
|
af57f63…
|
drh
|
1377 |
@ <form method="GET"> |
|
af57f63…
|
drh
|
1378 |
@ Title: <input type="text" size="50" name="title" value="%h(zTitle)"> |
|
af57f63…
|
drh
|
1379 |
@ <input type="submit" value="Submit"> |
|
af57f63…
|
drh
|
1380 |
@ </form> |
|
af57f63…
|
drh
|
1381 |
style_finish_page(); |
|
af57f63…
|
drh
|
1382 |
} |
|
af57f63…
|
drh
|
1383 |
|
|
af57f63…
|
drh
|
1384 |
/* |
|
caf286d…
|
drh
|
1385 |
** WEBPAGE: test-env |
|
caf286d…
|
drh
|
1386 |
** WEBPAGE: test_env alias |
|
7ab0328…
|
drh
|
1387 |
** |
|
7ab0328…
|
drh
|
1388 |
** Display CGI-variables and other aspects of the run-time |
|
7ab0328…
|
drh
|
1389 |
** environment, for debugging and trouble-shooting purposes. |
|
7ab0328…
|
drh
|
1390 |
*/ |
|
7ab0328…
|
drh
|
1391 |
void page_test_env(void){ |
|
99fcc43…
|
drh
|
1392 |
webpage_error(""); |
|
99fcc43…
|
drh
|
1393 |
} |
|
99fcc43…
|
drh
|
1394 |
|
|
99fcc43…
|
drh
|
1395 |
/* |
|
99fcc43…
|
drh
|
1396 |
** Webpages that encounter an error due to missing or incorrect |
|
99fcc43…
|
drh
|
1397 |
** query parameters can jump to this routine to render an error |
|
99fcc43…
|
drh
|
1398 |
** message screen. |
|
99fcc43…
|
drh
|
1399 |
** |
|
99fcc43…
|
drh
|
1400 |
** For administators, or if the test_env_enable setting is true, then |
|
99fcc43…
|
drh
|
1401 |
** details of the request environment are displayed. Otherwise, just |
|
99fcc43…
|
drh
|
1402 |
** the error message is shown. |
|
99fcc43…
|
drh
|
1403 |
** |
|
caf286d…
|
drh
|
1404 |
** If zFormat is an empty string, then this is the /test-env page. |
|
99fcc43…
|
drh
|
1405 |
*/ |
|
99fcc43…
|
drh
|
1406 |
void webpage_error(const char *zFormat, ...){ |
|
9c40ddb…
|
drh
|
1407 |
int showAll = 0; |
|
99fcc43…
|
drh
|
1408 |
char *zErr = 0; |
|
99fcc43…
|
drh
|
1409 |
int isAuth = 0; |
|
99fcc43…
|
drh
|
1410 |
char zCap[100]; |
|
7ab0328…
|
drh
|
1411 |
|
|
7ab0328…
|
drh
|
1412 |
login_check_credentials(); |
|
99fcc43…
|
drh
|
1413 |
if( g.perm.Admin || g.perm.Setup || db_get_boolean("test_env_enable",0) ){ |
|
99fcc43…
|
drh
|
1414 |
isAuth = 1; |
|
99fcc43…
|
drh
|
1415 |
} |
|
4a7760e…
|
drh
|
1416 |
cgi_load_environment(); |
|
f2a26bc…
|
drh
|
1417 |
style_set_current_feature(zFormat[0]==0 ? "test" : "error"); |
|
99fcc43…
|
drh
|
1418 |
if( zFormat[0] ){ |
|
99fcc43…
|
drh
|
1419 |
va_list ap; |
|
99fcc43…
|
drh
|
1420 |
va_start(ap, zFormat); |
|
99fcc43…
|
drh
|
1421 |
zErr = vmprintf(zFormat, ap); |
|
99fcc43…
|
drh
|
1422 |
va_end(ap); |
|
99fcc43…
|
drh
|
1423 |
style_header("Bad Request"); |
|
99fcc43…
|
drh
|
1424 |
@ <h1>/%h(g.zPath): %h(zErr)</h1> |
|
99fcc43…
|
drh
|
1425 |
showAll = 0; |
|
99fcc43…
|
drh
|
1426 |
cgi_set_status(500, "Bad Request"); |
|
99fcc43…
|
drh
|
1427 |
}else if( !isAuth ){ |
|
7ab0328…
|
drh
|
1428 |
login_needed(0); |
|
7ab0328…
|
drh
|
1429 |
return; |
|
99fcc43…
|
drh
|
1430 |
}else{ |
|
99fcc43…
|
drh
|
1431 |
style_header("Environment Test"); |
|
99fcc43…
|
drh
|
1432 |
showAll = PB("showall"); |
|
99fcc43…
|
drh
|
1433 |
style_submenu_checkbox("showall", "Cookies", 0, 0); |
|
99fcc43…
|
drh
|
1434 |
style_submenu_element("Stats", "%R/stat"); |
|
99fcc43…
|
drh
|
1435 |
} |
|
99fcc43…
|
drh
|
1436 |
|
|
99fcc43…
|
drh
|
1437 |
if( isAuth ){ |
|
99fcc43…
|
drh
|
1438 |
#if !defined(_WIN32) |
|
f5482a0…
|
wyoung
|
1439 |
@ uid=%d(getuid()), gid=%d(getgid())<br> |
|
99fcc43…
|
drh
|
1440 |
#endif |
|
f5482a0…
|
wyoung
|
1441 |
@ g.zBaseURL = %h(g.zBaseURL)<br> |
|
f5482a0…
|
wyoung
|
1442 |
@ g.zHttpsURL = %h(g.zHttpsURL)<br> |
|
f5482a0…
|
wyoung
|
1443 |
@ g.zTop = %h(g.zTop)<br> |
|
f5482a0…
|
wyoung
|
1444 |
@ g.zPath = %h(g.zPath)<br> |
|
f5482a0…
|
wyoung
|
1445 |
@ g.userUid = %d(g.userUid)<br> |
|
f5482a0…
|
wyoung
|
1446 |
@ g.zLogin = %h(g.zLogin)<br> |
|
10006db…
|
drh
|
1447 |
if( g.eAuthMethod!=AUTH_NONE ){ |
|
10006db…
|
drh
|
1448 |
const char *zMethod[] = { "COOKIE", "LOCAL", "PW", "ENV", "HTTP" }; |
|
10006db…
|
drh
|
1449 |
@ g.eAuthMethod = %d(g.eAuthMethod) (%h(zMethod[g.eAuthMethod-1]))\ |
|
10006db…
|
drh
|
1450 |
@ <br> |
|
10006db…
|
drh
|
1451 |
} |
|
16b3309…
|
drh
|
1452 |
@ g.isRobot = %d(g.isRobot)<br> |
|
f5482a0…
|
wyoung
|
1453 |
@ g.jsHref = %d(g.jsHref)<br> |
|
3df5d40…
|
drh
|
1454 |
if( g.zLocalRoot ){ |
|
f5482a0…
|
wyoung
|
1455 |
@ g.zLocalRoot = %h(g.zLocalRoot)<br> |
|
3df5d40…
|
drh
|
1456 |
}else{ |
|
f5482a0…
|
wyoung
|
1457 |
@ g.zLocalRoot = <i>none</i><br> |
|
3df5d40…
|
drh
|
1458 |
} |
|
99fcc43…
|
drh
|
1459 |
if( g.nRequest ){ |
|
f5482a0…
|
wyoung
|
1460 |
@ g.nRequest = %d(g.nRequest)<br> |
|
99fcc43…
|
drh
|
1461 |
} |
|
99fcc43…
|
drh
|
1462 |
if( g.nPendingRequest>1 ){ |
|
f5482a0…
|
wyoung
|
1463 |
@ g.nPendingRequest = %d(g.nPendingRequest)<br> |
|
99fcc43…
|
drh
|
1464 |
} |
|
f5482a0…
|
wyoung
|
1465 |
@ capabilities = %s(find_capabilities(zCap))<br> |
|
99fcc43…
|
drh
|
1466 |
if( zCap[0] ){ |
|
f5482a0…
|
wyoung
|
1467 |
@ anonymous-adds = %s(find_anon_capabilities(zCap))<br> |
|
b00e20c…
|
drh
|
1468 |
} |
|
f5482a0…
|
wyoung
|
1469 |
@ g.zRepositoryName = %h(g.zRepositoryName)<br> |
|
f5482a0…
|
wyoung
|
1470 |
@ load_average() = %f(load_average())<br> |
|
b00e20c…
|
drh
|
1471 |
#ifndef _WIN32 |
|
f5482a0…
|
wyoung
|
1472 |
@ RSS = %.2f(fossil_rss()/1000000.0) MB</br> |
|
b00e20c…
|
drh
|
1473 |
#endif |
|
920ace1…
|
drh
|
1474 |
(void)cgi_csrf_safe(2); |
|
920ace1…
|
drh
|
1475 |
switch( g.okCsrf ){ |
|
920ace1…
|
drh
|
1476 |
case 1: { |
|
920ace1…
|
drh
|
1477 |
@ CSRF safety = Same origin<br> |
|
920ace1…
|
drh
|
1478 |
break; |
|
920ace1…
|
drh
|
1479 |
} |
|
920ace1…
|
drh
|
1480 |
case 2: { |
|
920ace1…
|
drh
|
1481 |
@ CSRF safety = Same origin, POST<br> |
|
920ace1…
|
drh
|
1482 |
break; |
|
920ace1…
|
drh
|
1483 |
} |
|
920ace1…
|
drh
|
1484 |
case 3: { |
|
920ace1…
|
drh
|
1485 |
@ CSRF safety = Same origin, POST, CSRF token<br> |
|
920ace1…
|
drh
|
1486 |
break; |
|
920ace1…
|
drh
|
1487 |
} |
|
920ace1…
|
drh
|
1488 |
default: { |
|
920ace1…
|
drh
|
1489 |
@ CSRF safety = unsafe<br> |
|
920ace1…
|
drh
|
1490 |
break; |
|
920ace1…
|
drh
|
1491 |
} |
|
920ace1…
|
drh
|
1492 |
} |
|
275da70…
|
danield
|
1493 |
|
|
f5482a0…
|
wyoung
|
1494 |
@ fossil_exe_id() = %h(fossil_exe_id())<br> |
|
4350f32…
|
drh
|
1495 |
if( g.perm.Admin ){ |
|
4350f32…
|
drh
|
1496 |
int k; |
|
4350f32…
|
drh
|
1497 |
for(k=0; g.argvOrig[k]; k++){ |
|
4350f32…
|
drh
|
1498 |
Blob t; |
|
4350f32…
|
drh
|
1499 |
blob_init(&t, 0, 0); |
|
4350f32…
|
drh
|
1500 |
blob_append_escaped_arg(&t, g.argvOrig[k], 0); |
|
f5482a0…
|
wyoung
|
1501 |
@ argv[%d(k)] = %h(blob_str(&t))<br> |
|
4350f32…
|
drh
|
1502 |
blob_zero(&t); |
|
4350f32…
|
drh
|
1503 |
} |
|
4350f32…
|
drh
|
1504 |
} |
|
f5482a0…
|
wyoung
|
1505 |
@ <hr> |
|
99fcc43…
|
drh
|
1506 |
P("HTTP_USER_AGENT"); |
|
f1729c4…
|
drh
|
1507 |
P("SERVER_SOFTWARE"); |
|
0204f4a…
|
drh
|
1508 |
cgi_print_all(showAll, 0, 0); |
|
caf286d…
|
drh
|
1509 |
@ <p><form method="POST" action="%R/test-env"> |
|
9c40ddb…
|
drh
|
1510 |
@ <input type="hidden" name="showall" value="%d(showAll)"> |
|
9c40ddb…
|
drh
|
1511 |
@ <input type="submit" name="post-test-button" value="POST Test"> |
|
9c40ddb…
|
drh
|
1512 |
@ </form> |
|
99fcc43…
|
drh
|
1513 |
if( showAll && blob_size(&g.httpHeader)>0 ){ |
|
f5482a0…
|
wyoung
|
1514 |
@ <hr> |
|
99fcc43…
|
drh
|
1515 |
@ <pre> |
|
99fcc43…
|
drh
|
1516 |
@ %h(blob_str(&g.httpHeader)) |
|
99fcc43…
|
drh
|
1517 |
@ </pre> |
|
99fcc43…
|
drh
|
1518 |
} |
|
99fcc43…
|
drh
|
1519 |
} |
|
79f7808…
|
drh
|
1520 |
if( zErr && zErr[0] ){ |
|
112c713…
|
drh
|
1521 |
style_finish_page(); |
|
99fcc43…
|
drh
|
1522 |
cgi_reply(); |
|
99fcc43…
|
drh
|
1523 |
fossil_exit(1); |
|
79f7808…
|
drh
|
1524 |
}else{ |
|
112c713…
|
drh
|
1525 |
style_finish_page(); |
|
99fcc43…
|
drh
|
1526 |
} |
|
99fcc43…
|
drh
|
1527 |
} |
|
99fcc43…
|
drh
|
1528 |
|
|
99fcc43…
|
drh
|
1529 |
/* |
|
99fcc43…
|
drh
|
1530 |
** Generate a Not Yet Implemented error page. |
|
99fcc43…
|
drh
|
1531 |
*/ |
|
99fcc43…
|
drh
|
1532 |
void webpage_not_yet_implemented(void){ |
|
99fcc43…
|
drh
|
1533 |
webpage_error("Not yet implemented"); |
|
99fcc43…
|
drh
|
1534 |
} |
|
99fcc43…
|
drh
|
1535 |
|
|
99fcc43…
|
drh
|
1536 |
/* |
|
99fcc43…
|
drh
|
1537 |
** Generate a webpage for a webpage_assert(). |
|
99fcc43…
|
drh
|
1538 |
*/ |
|
99fcc43…
|
drh
|
1539 |
void webpage_assert_page(const char *zFile, int iLine, const char *zExpr){ |
|
99fcc43…
|
drh
|
1540 |
fossil_warning("assertion fault at %s:%d - %s", zFile, iLine, zExpr); |
|
99fcc43…
|
drh
|
1541 |
cgi_reset_content(); |
|
99fcc43…
|
drh
|
1542 |
webpage_error("assertion fault at %s:%d - %s", zFile, iLine, zExpr); |
|
93a5d65…
|
drh
|
1543 |
} |
|
93a5d65…
|
drh
|
1544 |
|
|
93a5d65…
|
drh
|
1545 |
/* |
|
93a5d65…
|
drh
|
1546 |
** Issue a 404 Not Found error for a webpage |
|
93a5d65…
|
drh
|
1547 |
*/ |
|
93a5d65…
|
drh
|
1548 |
void webpage_notfound_error(const char *zFormat, ...){ |
|
93a5d65…
|
drh
|
1549 |
char *zMsg; |
|
93a5d65…
|
drh
|
1550 |
va_list ap; |
|
93a5d65…
|
drh
|
1551 |
if( zFormat ){ |
|
93a5d65…
|
drh
|
1552 |
va_start(ap, zFormat); |
|
93a5d65…
|
drh
|
1553 |
zMsg = vmprintf(zFormat, ap); |
|
93a5d65…
|
drh
|
1554 |
va_end(ap); |
|
93a5d65…
|
drh
|
1555 |
}else{ |
|
93a5d65…
|
drh
|
1556 |
zMsg = "Not Found"; |
|
93a5d65…
|
drh
|
1557 |
} |
|
112c713…
|
drh
|
1558 |
style_set_current_feature("enotfound"); |
|
93a5d65…
|
drh
|
1559 |
style_header("Not Found"); |
|
93a5d65…
|
drh
|
1560 |
@ <p>%h(zMsg)</p> |
|
93a5d65…
|
drh
|
1561 |
cgi_set_status(404, "Not Found"); |
|
112c713…
|
drh
|
1562 |
style_finish_page(); |
|
1243bf3…
|
stephan
|
1563 |
} |
|
1243bf3…
|
stephan
|
1564 |
|
|
99fcc43…
|
drh
|
1565 |
#if INTERFACE |
|
99fcc43…
|
drh
|
1566 |
# define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);} |
|
63220d9…
|
jan.nijtmans
|
1567 |
#endif |
|
1243bf3…
|
stephan
|
1568 |
|
|
1243bf3…
|
stephan
|
1569 |
/* |
|
1243bf3…
|
stephan
|
1570 |
** Returns a pseudo-random input field ID, for use in associating an |
|
1243bf3…
|
stephan
|
1571 |
** ID-less input field with a label. The memory is owned by the |
|
1243bf3…
|
stephan
|
1572 |
** caller. |
|
1243bf3…
|
stephan
|
1573 |
*/ |
|
1243bf3…
|
stephan
|
1574 |
static char * style_next_input_id(){ |
|
1243bf3…
|
stephan
|
1575 |
static int inputID = 0; |
|
1243bf3…
|
stephan
|
1576 |
++inputID; |
|
1243bf3…
|
stephan
|
1577 |
return mprintf("input-id-%d", inputID); |
|
1243bf3…
|
stephan
|
1578 |
} |
|
1243bf3…
|
stephan
|
1579 |
|
|
1243bf3…
|
stephan
|
1580 |
/* |
|
1243bf3…
|
stephan
|
1581 |
** Outputs a labeled checkbox element. zWrapperId is an optional ID |
|
1243bf3…
|
stephan
|
1582 |
** value for the containing element (see below). zFieldName is the |
|
1243bf3…
|
stephan
|
1583 |
** form element name. zLabel is the label for the checkbox. zValue is |
|
1243bf3…
|
stephan
|
1584 |
** the optional value for the checkbox. zTip is an optional tooltip, |
|
1243bf3…
|
stephan
|
1585 |
** which gets set as the "title" attribute of the outermost |
|
1243bf3…
|
stephan
|
1586 |
** element. If isChecked is true, the checkbox gets the "checked" |
|
1243bf3…
|
stephan
|
1587 |
** attribute set, else it is not. |
|
1243bf3…
|
stephan
|
1588 |
** |
|
1243bf3…
|
stephan
|
1589 |
** Resulting structure: |
|
1243bf3…
|
stephan
|
1590 |
** |
|
34f7fd7…
|
stephan
|
1591 |
** <div class='input-with-label' title={{zTip}} id={{zWrapperId}}> |
|
1243bf3…
|
stephan
|
1592 |
** <input type='checkbox' name={{zFieldName}} value={{zValue}} |
|
1243bf3…
|
stephan
|
1593 |
** id='A RANDOM VALUE' |
|
1243bf3…
|
stephan
|
1594 |
** {{isChecked ? " checked : ""}}/> |
|
1243bf3…
|
stephan
|
1595 |
** <label for='ID OF THE INPUT FIELD'>{{zLabel}}</label> |
|
34f7fd7…
|
stephan
|
1596 |
** </div> |
|
1243bf3…
|
stephan
|
1597 |
** |
|
1243bf3…
|
stephan
|
1598 |
** zLabel, and zValue are required. zFieldName, zWrapperId, and zTip |
|
1243bf3…
|
stephan
|
1599 |
** are may be NULL or empty. |
|
1243bf3…
|
stephan
|
1600 |
** |
|
1243bf3…
|
stephan
|
1601 |
** Be sure that the input-with-label CSS class is defined sensibly, in |
|
1243bf3…
|
stephan
|
1602 |
** particular, having its display:inline-block is useful for alignment |
|
1243bf3…
|
stephan
|
1603 |
** purposes. |
|
1243bf3…
|
stephan
|
1604 |
*/ |
|
1243bf3…
|
stephan
|
1605 |
void style_labeled_checkbox(const char * zWrapperId, |
|
1243bf3…
|
stephan
|
1606 |
const char *zFieldName, const char * zLabel, |
|
1243bf3…
|
stephan
|
1607 |
const char * zValue, int isChecked, |
|
1243bf3…
|
stephan
|
1608 |
const char * zTip){ |
|
1243bf3…
|
stephan
|
1609 |
char * zLabelID = style_next_input_id(); |
|
34f7fd7…
|
stephan
|
1610 |
CX("<div class='input-with-label'"); |
|
1243bf3…
|
stephan
|
1611 |
if(zTip && *zTip){ |
|
1243bf3…
|
stephan
|
1612 |
CX(" title='%h'", zTip); |
|
1243bf3…
|
stephan
|
1613 |
} |
|
1243bf3…
|
stephan
|
1614 |
if(zWrapperId && *zWrapperId){ |
|
1243bf3…
|
stephan
|
1615 |
CX(" id='%s'",zWrapperId); |
|
1243bf3…
|
stephan
|
1616 |
} |
|
1243bf3…
|
stephan
|
1617 |
CX("><input type='checkbox' id='%s' ", zLabelID); |
|
1243bf3…
|
stephan
|
1618 |
if(zFieldName && *zFieldName){ |
|
1243bf3…
|
stephan
|
1619 |
CX("name='%s' ",zFieldName); |
|
1243bf3…
|
stephan
|
1620 |
} |
|
1243bf3…
|
stephan
|
1621 |
CX("value='%T'%s/>", |
|
1243bf3…
|
stephan
|
1622 |
zValue ? zValue : "", isChecked ? " checked" : ""); |
|
34f7fd7…
|
stephan
|
1623 |
CX("<label for='%s'>%h</label></div>", zLabelID, zLabel); |
|
1243bf3…
|
stephan
|
1624 |
fossil_free(zLabelID); |
|
1243bf3…
|
stephan
|
1625 |
} |
|
1243bf3…
|
stephan
|
1626 |
|
|
1243bf3…
|
stephan
|
1627 |
/* |
|
1243bf3…
|
stephan
|
1628 |
** Outputs a SELECT list from a compile-time list of integers. |
|
1243bf3…
|
stephan
|
1629 |
** The vargs must be a list of (const char *, int) pairs, terminated |
|
1243bf3…
|
stephan
|
1630 |
** with a single NULL. Each pair is interpreted as... |
|
1243bf3…
|
stephan
|
1631 |
** |
|
1243bf3…
|
stephan
|
1632 |
** If the (const char *) is NULL, it is the end of the list, else |
|
1243bf3…
|
stephan
|
1633 |
** a new OPTION entry is created. If the string is empty, the |
|
1243bf3…
|
stephan
|
1634 |
** label and value of the OPTION is the integer part of the pair. |
|
1243bf3…
|
stephan
|
1635 |
** If the string is not empty, it becomes the label and the integer |
|
1243bf3…
|
stephan
|
1636 |
** the value. If that value == selectedValue then that OPTION |
|
1243bf3…
|
stephan
|
1637 |
** element gets the 'selected' attribute. |
|
1243bf3…
|
stephan
|
1638 |
** |
|
1243bf3…
|
stephan
|
1639 |
** Note that the pairs are not in (int, const char *) order because |
|
1243bf3…
|
stephan
|
1640 |
** there is no well-known integer value which we can definitively use |
|
1243bf3…
|
stephan
|
1641 |
** as a list terminator. |
|
1243bf3…
|
stephan
|
1642 |
** |
|
1243bf3…
|
stephan
|
1643 |
** zWrapperId is an optional ID value for the containing element (see |
|
1243bf3…
|
stephan
|
1644 |
** below). |
|
1243bf3…
|
stephan
|
1645 |
** |
|
1243bf3…
|
stephan
|
1646 |
** zFieldName is the value of the form element's name attribute. Note |
|
1243bf3…
|
stephan
|
1647 |
** that fossil prefers underscores over '-' for separators in form |
|
1243bf3…
|
stephan
|
1648 |
** element names. |
|
1243bf3…
|
stephan
|
1649 |
** |
|
1243bf3…
|
stephan
|
1650 |
** zLabel is an optional string to use as a "label" for the element |
|
1243bf3…
|
stephan
|
1651 |
** (see below). |
|
1243bf3…
|
stephan
|
1652 |
** |
|
1243bf3…
|
stephan
|
1653 |
** zTooltip is an optional value for the SELECT's title attribute. |
|
1243bf3…
|
stephan
|
1654 |
** |
|
1243bf3…
|
stephan
|
1655 |
** The structure of the emitted HTML is: |
|
1243bf3…
|
stephan
|
1656 |
** |
|
34f7fd7…
|
stephan
|
1657 |
** <div class='input-with-label' title={{zToolTip}} id={{zWrapperId}}> |
|
1243bf3…
|
stephan
|
1658 |
** <label for='SELECT ELEMENT ID'>{{zLabel}}</label> |
|
1243bf3…
|
stephan
|
1659 |
** <select id='RANDOM ID' name={{zFieldName}}>...</select> |
|
34f7fd7…
|
stephan
|
1660 |
** </div> |
|
1243bf3…
|
stephan
|
1661 |
** |
|
1243bf3…
|
stephan
|
1662 |
** Example: |
|
1243bf3…
|
stephan
|
1663 |
** |
|
1243bf3…
|
stephan
|
1664 |
** style_select_list_int("my-grapes", "my_grapes", "Grapes", |
|
1243bf3…
|
stephan
|
1665 |
** "Select the number of grapes", |
|
1243bf3…
|
stephan
|
1666 |
** atoi(PD("my_field","0")), |
|
1243bf3…
|
stephan
|
1667 |
** "", 1, "2", 2, "Three", 3, |
|
1243bf3…
|
stephan
|
1668 |
** NULL); |
|
275da70…
|
danield
|
1669 |
** |
|
1243bf3…
|
stephan
|
1670 |
*/ |
|
1243bf3…
|
stephan
|
1671 |
void style_select_list_int(const char * zWrapperId, |
|
1243bf3…
|
stephan
|
1672 |
const char *zFieldName, const char * zLabel, |
|
1243bf3…
|
stephan
|
1673 |
const char * zToolTip, int selectedVal, |
|
1243bf3…
|
stephan
|
1674 |
... ){ |
|
1243bf3…
|
stephan
|
1675 |
char * zLabelID = style_next_input_id(); |
|
1243bf3…
|
stephan
|
1676 |
va_list vargs; |
|
1243bf3…
|
stephan
|
1677 |
|
|
1243bf3…
|
stephan
|
1678 |
va_start(vargs,selectedVal); |
|
34f7fd7…
|
stephan
|
1679 |
CX("<div class='input-with-label'"); |
|
1243bf3…
|
stephan
|
1680 |
if(zToolTip && *zToolTip){ |
|
1243bf3…
|
stephan
|
1681 |
CX(" title='%h'",zToolTip); |
|
1243bf3…
|
stephan
|
1682 |
} |
|
1243bf3…
|
stephan
|
1683 |
if(zWrapperId && *zWrapperId){ |
|
1243bf3…
|
stephan
|
1684 |
CX(" id='%s'",zWrapperId); |
|
1243bf3…
|
stephan
|
1685 |
} |
|
1243bf3…
|
stephan
|
1686 |
CX(">"); |
|
1243bf3…
|
stephan
|
1687 |
if(zLabel && *zLabel){ |
|
b82cb27…
|
stephan
|
1688 |
CX("<label for='%s'>%h</label>", zLabelID, zLabel); |
|
1243bf3…
|
stephan
|
1689 |
} |
|
1243bf3…
|
stephan
|
1690 |
CX("<select name='%s' id='%s'>",zFieldName, zLabelID); |
|
1243bf3…
|
stephan
|
1691 |
while(1){ |
|
1243bf3…
|
stephan
|
1692 |
const char * zOption = va_arg(vargs,char *); |
|
1243bf3…
|
stephan
|
1693 |
int v; |
|
1243bf3…
|
stephan
|
1694 |
if(NULL==zOption){ |
|
1243bf3…
|
stephan
|
1695 |
break; |
|
1243bf3…
|
stephan
|
1696 |
} |
|
1243bf3…
|
stephan
|
1697 |
v = va_arg(vargs,int); |
|
1243bf3…
|
stephan
|
1698 |
CX("<option value='%d'%s>", |
|
1243bf3…
|
stephan
|
1699 |
v, v==selectedVal ? " selected" : ""); |
|
1243bf3…
|
stephan
|
1700 |
if(*zOption){ |
|
1243bf3…
|
stephan
|
1701 |
CX("%s", zOption); |
|
1243bf3…
|
stephan
|
1702 |
}else{ |
|
1243bf3…
|
stephan
|
1703 |
CX("%d",v); |
|
1243bf3…
|
stephan
|
1704 |
} |
|
1243bf3…
|
stephan
|
1705 |
CX("</option>\n"); |
|
1243bf3…
|
stephan
|
1706 |
} |
|
1243bf3…
|
stephan
|
1707 |
CX("</select>\n"); |
|
34f7fd7…
|
stephan
|
1708 |
CX("</div>\n"); |
|
1243bf3…
|
stephan
|
1709 |
va_end(vargs); |
|
1243bf3…
|
stephan
|
1710 |
fossil_free(zLabelID); |
|
1243bf3…
|
stephan
|
1711 |
} |
|
1243bf3…
|
stephan
|
1712 |
|
|
1243bf3…
|
stephan
|
1713 |
/* |
|
1243bf3…
|
stephan
|
1714 |
** The C-string counterpart of style_select_list_int(), this variant |
|
1243bf3…
|
stephan
|
1715 |
** differs only in that its variadic arguments are C-strings in pairs |
|
1243bf3…
|
stephan
|
1716 |
** of (optionLabel, optionValue). If a given optionLabel is an empty |
|
1243bf3…
|
stephan
|
1717 |
** string, the corresponding optionValue is used as its label. If any |
|
1243bf3…
|
stephan
|
1718 |
** given value matches zSelectedVal, that option gets preselected. If |
|
1243bf3…
|
stephan
|
1719 |
** no options match zSelectedVal then the first entry is selected by |
|
1243bf3…
|
stephan
|
1720 |
** default. |
|
1243bf3…
|
stephan
|
1721 |
** |
|
1243bf3…
|
stephan
|
1722 |
** Any of (zWrapperId, zTooltip, zSelectedVal) may be NULL or empty. |
|
1243bf3…
|
stephan
|
1723 |
** |
|
1243bf3…
|
stephan
|
1724 |
** Example: |
|
1243bf3…
|
stephan
|
1725 |
** |
|
1243bf3…
|
stephan
|
1726 |
** style_select_list_str("my-grapes", "my_grapes", "Grapes", |
|
1243bf3…
|
stephan
|
1727 |
** "Select the number of grapes", |
|
1243bf3…
|
stephan
|
1728 |
** P("my_field"), |
|
1243bf3…
|
stephan
|
1729 |
** "1", "One", "2", "Two", "", "3", |
|
1243bf3…
|
stephan
|
1730 |
** NULL); |
|
1243bf3…
|
stephan
|
1731 |
*/ |
|
1243bf3…
|
stephan
|
1732 |
void style_select_list_str(const char * zWrapperId, |
|
1243bf3…
|
stephan
|
1733 |
const char *zFieldName, const char * zLabel, |
|
1243bf3…
|
stephan
|
1734 |
const char * zToolTip, char const * zSelectedVal, |
|
1243bf3…
|
stephan
|
1735 |
... ){ |
|
1243bf3…
|
stephan
|
1736 |
char * zLabelID = style_next_input_id(); |
|
1243bf3…
|
stephan
|
1737 |
va_list vargs; |
|
1243bf3…
|
stephan
|
1738 |
|
|
1243bf3…
|
stephan
|
1739 |
va_start(vargs,zSelectedVal); |
|
1243bf3…
|
stephan
|
1740 |
if(!zSelectedVal){ |
|
1243bf3…
|
stephan
|
1741 |
zSelectedVal = __FILE__/*some string we'll never match*/; |
|
1243bf3…
|
stephan
|
1742 |
} |
|
34f7fd7…
|
stephan
|
1743 |
CX("<div class='input-with-label'"); |
|
1243bf3…
|
stephan
|
1744 |
if(zToolTip && *zToolTip){ |
|
1243bf3…
|
stephan
|
1745 |
CX(" title='%h'",zToolTip); |
|
1243bf3…
|
stephan
|
1746 |
} |
|
1243bf3…
|
stephan
|
1747 |
if(zWrapperId && *zWrapperId){ |
|
1243bf3…
|
stephan
|
1748 |
CX(" id='%s'",zWrapperId); |
|
1243bf3…
|
stephan
|
1749 |
} |
|
1243bf3…
|
stephan
|
1750 |
CX(">"); |
|
1243bf3…
|
stephan
|
1751 |
if(zLabel && *zLabel){ |
|
1243bf3…
|
stephan
|
1752 |
CX("<label for='%s'>%h</label>", zLabelID, zLabel); |
|
1243bf3…
|
stephan
|
1753 |
} |
|
1243bf3…
|
stephan
|
1754 |
CX("<select name='%s' id='%s'>",zFieldName, zLabelID); |
|
1243bf3…
|
stephan
|
1755 |
while(1){ |
|
1243bf3…
|
stephan
|
1756 |
const char * zLabel = va_arg(vargs,char *); |
|
1243bf3…
|
stephan
|
1757 |
const char * zVal; |
|
1243bf3…
|
stephan
|
1758 |
if(NULL==zLabel){ |
|
1243bf3…
|
stephan
|
1759 |
break; |
|
1243bf3…
|
stephan
|
1760 |
} |
|
1243bf3…
|
stephan
|
1761 |
zVal = va_arg(vargs,char *); |
|
1243bf3…
|
stephan
|
1762 |
CX("<option value='%T'%s>", |
|
1243bf3…
|
stephan
|
1763 |
zVal, 0==fossil_strcmp(zVal, zSelectedVal) ? " selected" : ""); |
|
1243bf3…
|
stephan
|
1764 |
if(*zLabel){ |
|
1243bf3…
|
stephan
|
1765 |
CX("%s", zLabel); |
|
1243bf3…
|
stephan
|
1766 |
}else{ |
|
1243bf3…
|
stephan
|
1767 |
CX("%h",zVal); |
|
1243bf3…
|
stephan
|
1768 |
} |
|
1243bf3…
|
stephan
|
1769 |
CX("</option>\n"); |
|
1243bf3…
|
stephan
|
1770 |
} |
|
1243bf3…
|
stephan
|
1771 |
CX("</select>\n"); |
|
34f7fd7…
|
stephan
|
1772 |
CX("</div>\n"); |
|
1243bf3…
|
stephan
|
1773 |
va_end(vargs); |
|
1243bf3…
|
stephan
|
1774 |
fossil_free(zLabelID); |
|
1243bf3…
|
stephan
|
1775 |
} |
|
1243bf3…
|
stephan
|
1776 |
|
|
34f7fd7…
|
stephan
|
1777 |
/* |
|
6854244…
|
drh
|
1778 |
** Generate a <script> with an appropriate nonce. |
|
6854244…
|
drh
|
1779 |
** |
|
6854244…
|
drh
|
1780 |
** zOrigin and iLine are the source code filename and line number |
|
6854244…
|
drh
|
1781 |
** that generated this request. |
|
6854244…
|
drh
|
1782 |
*/ |
|
6854244…
|
drh
|
1783 |
void style_script_begin(const char *zOrigin, int iLine){ |
|
6854244…
|
drh
|
1784 |
const char *z; |
|
6854244…
|
drh
|
1785 |
for(z=zOrigin; z[0]!=0; z++){ |
|
6854244…
|
drh
|
1786 |
if( z[0]=='/' || z[0]=='\\' ){ |
|
6854244…
|
drh
|
1787 |
zOrigin = z+1; |
|
6854244…
|
drh
|
1788 |
} |
|
6854244…
|
drh
|
1789 |
} |
|
6854244…
|
drh
|
1790 |
CX("<script nonce='%s'>/* %s:%d */\n", style_nonce(), zOrigin, iLine); |
|
6854244…
|
drh
|
1791 |
} |
|
6854244…
|
drh
|
1792 |
|
|
275da70…
|
danield
|
1793 |
/* Generate the closing </script> tag |
|
070716d…
|
stephan
|
1794 |
*/ |
|
6854244…
|
drh
|
1795 |
void style_script_end(void){ |
|
6854244…
|
drh
|
1796 |
CX("</script>\n"); |
|
070716d…
|
stephan
|
1797 |
} |
|
070716d…
|
stephan
|
1798 |
|
|
070716d…
|
stephan
|
1799 |
/* |
|
070716d…
|
stephan
|
1800 |
** Emits a NOSCRIPT tag with an error message stating that JS is |
|
070716d…
|
stephan
|
1801 |
** required for the current page. This "should" be called near the top |
|
070716d…
|
stephan
|
1802 |
** of pages which *require* JS. The inner DIV has the CSS class |
|
070716d…
|
stephan
|
1803 |
** 'error' and can be styled via a (noscript > .error) CSS selector. |
|
070716d…
|
stephan
|
1804 |
*/ |
|
070716d…
|
stephan
|
1805 |
void style_emit_noscript_for_js_page(void){ |
|
070716d…
|
stephan
|
1806 |
CX("<noscript><div class='error'>" |
|
070716d…
|
stephan
|
1807 |
"This page requires JavaScript (ES2015, a.k.a. ES6, or newer)." |
|
070716d…
|
stephan
|
1808 |
"</div></noscript>"); |
|
cadfcba…
|
drh
|
1809 |
} |
|
cadfcba…
|
drh
|
1810 |
|
|
cadfcba…
|
drh
|
1811 |
/* |
|
cadfcba…
|
drh
|
1812 |
** SETTING: robots-txt width=70 block-text keep-empty |
|
cadfcba…
|
drh
|
1813 |
** |
|
cadfcba…
|
drh
|
1814 |
** This setting is the override value for the /robots.txt file that |
|
cadfcba…
|
drh
|
1815 |
** Fossil returns when run as a stand-alone server for a domain. As |
|
cadfcba…
|
drh
|
1816 |
** Fossil is seldom run as a stand-alone server (and is more commonly |
|
cadfcba…
|
drh
|
1817 |
** deployed as a CGI or SCGI or behind a reverse proxy) this setting |
|
cadfcba…
|
drh
|
1818 |
** rarely needed. A reasonable default robots.txt is sent if this |
|
cadfcba…
|
drh
|
1819 |
** setting is empty. |
|
cadfcba…
|
drh
|
1820 |
*/ |
|
cadfcba…
|
drh
|
1821 |
|
|
cadfcba…
|
drh
|
1822 |
/* |
|
cadfcba…
|
drh
|
1823 |
** WEBPAGE: robots.txt |
|
cadfcba…
|
drh
|
1824 |
** |
|
cadfcba…
|
drh
|
1825 |
** Return text/plain which is the content of the "robots-txt" setting, if |
|
cadfcba…
|
drh
|
1826 |
** such a setting exists and is non-empty. Or construct an RFC-9309 complaint |
|
cadfcba…
|
drh
|
1827 |
** robots.txt file and return that if there is not "robots.txt" setting. |
|
cadfcba…
|
drh
|
1828 |
** |
|
cadfcba…
|
drh
|
1829 |
** This is useful for robot exclusion in cases where Fossil is run as a |
|
cadfcba…
|
drh
|
1830 |
** stand-alone server in its own domain. For the more common case where |
|
cadfcba…
|
drh
|
1831 |
** Fossil is run as a CGI, or SCGI, or a server that responding to a reverse |
|
cadfcba…
|
drh
|
1832 |
** proxy, the returns robots.txt file will not be at the top level of the |
|
cadfcba…
|
drh
|
1833 |
** domain, and so it will be pointless. |
|
cadfcba…
|
drh
|
1834 |
*/ |
|
cadfcba…
|
drh
|
1835 |
void robotstxt_page(void){ |
|
cadfcba…
|
drh
|
1836 |
const char *z; |
|
cadfcba…
|
drh
|
1837 |
static const char *zDflt = |
|
cadfcba…
|
drh
|
1838 |
"User-agent: *\n" |
|
cadfcba…
|
drh
|
1839 |
"Allow: /doc\n" |
|
cadfcba…
|
drh
|
1840 |
"Allow: /home\n" |
|
cadfcba…
|
drh
|
1841 |
"Allow: /forum\n" |
|
cadfcba…
|
drh
|
1842 |
"Allow: /technote\n" |
|
cadfcba…
|
drh
|
1843 |
"Allow: /tktview\n" |
|
cadfcba…
|
drh
|
1844 |
"Allow: /wiki\n" |
|
cadfcba…
|
drh
|
1845 |
"Allow: /uv/\n" |
|
cadfcba…
|
drh
|
1846 |
"Allow: /$\n" |
|
cadfcba…
|
drh
|
1847 |
"Disallow: /*\n" |
|
cadfcba…
|
drh
|
1848 |
; |
|
cadfcba…
|
drh
|
1849 |
z = db_get("robots-txt",zDflt); |
|
cadfcba…
|
drh
|
1850 |
cgi_set_content_type("text/plain"); |
|
cadfcba…
|
drh
|
1851 |
cgi_append_content(z, -1); |
|
1243bf3…
|
stephan
|
1852 |
} |