Fossil SCM

Add the ability to include FORM elements on the submenu.

drh 2015-02-04 19:14 UTC trunk
Commit b17970e13bc314b499571caa091c4e142196979b
3 files changed +43 -4 +133 -19 +57 -47
+43 -4
--- src/cgi.c
+++ src/cgi.c
@@ -52,10 +52,11 @@
5252
*/
5353
#define P(x) cgi_parameter((x),0)
5454
#define PD(x,y) cgi_parameter((x),(y))
5555
#define PT(x) cgi_parameter_trimmed((x),0)
5656
#define PDT(x,y) cgi_parameter_trimmed((x),(y))
57
+#define PB(x) cgi_parameter_boolean(x)
5758
5859
5960
/*
6061
** Destinations for output text.
6162
*/
@@ -436,11 +437,12 @@
436437
static int seqQP = 0; /* Sequence numbers */
437438
static struct QParam { /* One entry for each query parameter or cookie */
438439
const char *zName; /* Parameter or cookie name */
439440
const char *zValue; /* Value of the query parameter or cookie */
440441
int seq; /* Order of insertion */
441
- int isQP; /* True for query parameters */
442
+ char isQP; /* True for query parameters */
443
+ char cTag; /* Tag on query parameters */
442444
} *aParamQP; /* An array of all parameters and cookies */
443445
444446
/*
445447
** Add another query parameter or cookie to the parameter set.
446448
** zName is the name of the query parameter or cookie and zValue
@@ -490,10 +492,21 @@
490492
aParamQP[i].zValue = zValue;
491493
return;
492494
}
493495
}
494496
cgi_set_parameter_nocopy(zName, zValue, 0);
497
+}
498
+void cgi_replace_query_parameter(const char *zName, const char *zValue){
499
+ int i;
500
+ for(i=0; i<nUsedQP; i++){
501
+ if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
502
+ aParamQP[i].zValue = zValue;
503
+ assert( aParamQP[0].isQP );
504
+ return;
505
+ }
506
+ }
507
+ cgi_set_parameter_nocopy(zName, zValue, 1);
495508
}
496509
497510
/*
498511
** Add a query parameter. The zName portion is fixed but a copy
499512
** must be made of zValue.
@@ -1064,10 +1077,20 @@
10641077
zOut = fossil_strdup(zIn);
10651078
for(i=0; zOut[i]; i++){}
10661079
while( i>0 && fossil_isspace(zOut[i-1]) ) zOut[--i] = 0;
10671080
return zOut;
10681081
}
1082
+
1083
+/*
1084
+** Return true if the CGI parameter zName exists and is not equal to 0,
1085
+** or "no" or "off".
1086
+*/
1087
+int cgi_parameter_boolean(const char *zName){
1088
+ const char *zIn = cgi_parameter(zName, 0);
1089
+ if( zIn==0 ) return 0;
1090
+ return is_truth(zIn);
1091
+}
10691092
10701093
/*
10711094
** Return the name of the i-th CGI parameter. Return NULL if there
10721095
** are fewer than i registered CGI parameters.
10731096
*/
@@ -1142,23 +1165,39 @@
11421165
cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue);
11431166
}
11441167
}
11451168
11461169
/*
1147
-** Export all query parameters (but not cookies or environment variables)
1148
-** as hidden values of a form.
1170
+** Export all untagged query parameters (but not cookies or environment
1171
+** variables) as hidden values of a form.
11491172
*/
11501173
void cgi_query_parameters_to_hidden(void){
11511174
int i;
11521175
const char *zN, *zV;
11531176
for(i=0; i<nUsedQP; i++){
1154
- if( aParamQP[i].isQP==0 ) continue;
1177
+ if( aParamQP[i].isQP==0 || aParamQP[i].cTag ) continue;
11551178
zN = aParamQP[i].zName;
11561179
zV = aParamQP[i].zValue;
11571180
@ <input type="hidden" name="%h(zN)" value="%h(zV)">
11581181
}
11591182
}
1183
+
1184
+/*
1185
+** Tag query parameter zName so that it is not exported by
1186
+** cgi_query_parameters_to_hidden(). Or if zName==0, then
1187
+** untag all query parameters.
1188
+*/
1189
+void cgi_tag_query_parameter(const char *zName){
1190
+ int i;
1191
+ if( zName==0 ){
1192
+ for(i=0; i<nUsedQP; i++) aParamQP[i].cTag = 0;
1193
+ }else{
1194
+ for(i=0; i<nUsedQP; i++){
1195
+ if( strcmp(zName,aParamQP[i].zName)==0 ) aParamQP[i].cTag = 1;
1196
+ }
1197
+ }
1198
+}
11601199
11611200
/*
11621201
** This routine works like "printf" except that it has the
11631202
** extra formatting capabilities such as %h and %t.
11641203
*/
11651204
--- src/cgi.c
+++ src/cgi.c
@@ -52,10 +52,11 @@
52 */
53 #define P(x) cgi_parameter((x),0)
54 #define PD(x,y) cgi_parameter((x),(y))
55 #define PT(x) cgi_parameter_trimmed((x),0)
56 #define PDT(x,y) cgi_parameter_trimmed((x),(y))
 
57
58
59 /*
60 ** Destinations for output text.
61 */
@@ -436,11 +437,12 @@
436 static int seqQP = 0; /* Sequence numbers */
437 static struct QParam { /* One entry for each query parameter or cookie */
438 const char *zName; /* Parameter or cookie name */
439 const char *zValue; /* Value of the query parameter or cookie */
440 int seq; /* Order of insertion */
441 int isQP; /* True for query parameters */
 
442 } *aParamQP; /* An array of all parameters and cookies */
443
444 /*
445 ** Add another query parameter or cookie to the parameter set.
446 ** zName is the name of the query parameter or cookie and zValue
@@ -490,10 +492,21 @@
490 aParamQP[i].zValue = zValue;
491 return;
492 }
493 }
494 cgi_set_parameter_nocopy(zName, zValue, 0);
 
 
 
 
 
 
 
 
 
 
 
495 }
496
497 /*
498 ** Add a query parameter. The zName portion is fixed but a copy
499 ** must be made of zValue.
@@ -1064,10 +1077,20 @@
1064 zOut = fossil_strdup(zIn);
1065 for(i=0; zOut[i]; i++){}
1066 while( i>0 && fossil_isspace(zOut[i-1]) ) zOut[--i] = 0;
1067 return zOut;
1068 }
 
 
 
 
 
 
 
 
 
 
1069
1070 /*
1071 ** Return the name of the i-th CGI parameter. Return NULL if there
1072 ** are fewer than i registered CGI parameters.
1073 */
@@ -1142,23 +1165,39 @@
1142 cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue);
1143 }
1144 }
1145
1146 /*
1147 ** Export all query parameters (but not cookies or environment variables)
1148 ** as hidden values of a form.
1149 */
1150 void cgi_query_parameters_to_hidden(void){
1151 int i;
1152 const char *zN, *zV;
1153 for(i=0; i<nUsedQP; i++){
1154 if( aParamQP[i].isQP==0 ) continue;
1155 zN = aParamQP[i].zName;
1156 zV = aParamQP[i].zValue;
1157 @ <input type="hidden" name="%h(zN)" value="%h(zV)">
1158 }
1159 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1160
1161 /*
1162 ** This routine works like "printf" except that it has the
1163 ** extra formatting capabilities such as %h and %t.
1164 */
1165
--- src/cgi.c
+++ src/cgi.c
@@ -52,10 +52,11 @@
52 */
53 #define P(x) cgi_parameter((x),0)
54 #define PD(x,y) cgi_parameter((x),(y))
55 #define PT(x) cgi_parameter_trimmed((x),0)
56 #define PDT(x,y) cgi_parameter_trimmed((x),(y))
57 #define PB(x) cgi_parameter_boolean(x)
58
59
60 /*
61 ** Destinations for output text.
62 */
@@ -436,11 +437,12 @@
437 static int seqQP = 0; /* Sequence numbers */
438 static struct QParam { /* One entry for each query parameter or cookie */
439 const char *zName; /* Parameter or cookie name */
440 const char *zValue; /* Value of the query parameter or cookie */
441 int seq; /* Order of insertion */
442 char isQP; /* True for query parameters */
443 char cTag; /* Tag on query parameters */
444 } *aParamQP; /* An array of all parameters and cookies */
445
446 /*
447 ** Add another query parameter or cookie to the parameter set.
448 ** zName is the name of the query parameter or cookie and zValue
@@ -490,10 +492,21 @@
492 aParamQP[i].zValue = zValue;
493 return;
494 }
495 }
496 cgi_set_parameter_nocopy(zName, zValue, 0);
497 }
498 void cgi_replace_query_parameter(const char *zName, const char *zValue){
499 int i;
500 for(i=0; i<nUsedQP; i++){
501 if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
502 aParamQP[i].zValue = zValue;
503 assert( aParamQP[0].isQP );
504 return;
505 }
506 }
507 cgi_set_parameter_nocopy(zName, zValue, 1);
508 }
509
510 /*
511 ** Add a query parameter. The zName portion is fixed but a copy
512 ** must be made of zValue.
@@ -1064,10 +1077,20 @@
1077 zOut = fossil_strdup(zIn);
1078 for(i=0; zOut[i]; i++){}
1079 while( i>0 && fossil_isspace(zOut[i-1]) ) zOut[--i] = 0;
1080 return zOut;
1081 }
1082
1083 /*
1084 ** Return true if the CGI parameter zName exists and is not equal to 0,
1085 ** or "no" or "off".
1086 */
1087 int cgi_parameter_boolean(const char *zName){
1088 const char *zIn = cgi_parameter(zName, 0);
1089 if( zIn==0 ) return 0;
1090 return is_truth(zIn);
1091 }
1092
1093 /*
1094 ** Return the name of the i-th CGI parameter. Return NULL if there
1095 ** are fewer than i registered CGI parameters.
1096 */
@@ -1142,23 +1165,39 @@
1165 cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue);
1166 }
1167 }
1168
1169 /*
1170 ** Export all untagged query parameters (but not cookies or environment
1171 ** variables) as hidden values of a form.
1172 */
1173 void cgi_query_parameters_to_hidden(void){
1174 int i;
1175 const char *zN, *zV;
1176 for(i=0; i<nUsedQP; i++){
1177 if( aParamQP[i].isQP==0 || aParamQP[i].cTag ) continue;
1178 zN = aParamQP[i].zName;
1179 zV = aParamQP[i].zValue;
1180 @ <input type="hidden" name="%h(zN)" value="%h(zV)">
1181 }
1182 }
1183
1184 /*
1185 ** Tag query parameter zName so that it is not exported by
1186 ** cgi_query_parameters_to_hidden(). Or if zName==0, then
1187 ** untag all query parameters.
1188 */
1189 void cgi_tag_query_parameter(const char *zName){
1190 int i;
1191 if( zName==0 ){
1192 for(i=0; i<nUsedQP; i++) aParamQP[i].cTag = 0;
1193 }else{
1194 for(i=0; i<nUsedQP; i++){
1195 if( strcmp(zName,aParamQP[i].zName)==0 ) aParamQP[i].cTag = 1;
1196 }
1197 }
1198 }
1199
1200 /*
1201 ** This routine works like "printf" except that it has the
1202 ** extra formatting capabilities such as %h and %t.
1203 */
1204
+133 -19
--- src/style.c
+++ src/style.c
@@ -23,21 +23,40 @@
2323
#include "style.h"
2424
2525
2626
/*
2727
** Elements of the submenu are collected into the following
28
-** structure and displayed below the main menu by style_header().
28
+** structure and displayed below the main menu.
29
+**
30
+** Populate these structure with calls to
31
+**
32
+** style_submenu_element()
33
+** style_submenu_entry()
34
+** style_submenu_checkbox()
35
+** style_submenu_multichoice()
2936
**
30
-** Populate this structure with calls to style_submenu_element()
31
-** prior to calling style_header().
37
+** prior to calling style_footer(). The style_footer() routine
38
+** will generate the appropriate HTML text just below the main
39
+** menu.
3240
*/
3341
static struct Submenu {
34
- const char *zLabel;
42
+ const char *zLabel; /* Button label */
3543
const char *zTitle;
36
- const char *zLink;
44
+ const char *zLink; /* Jump to this link when button is pressed */
3745
} aSubmenu[30];
38
-static int nSubmenu = 0;
46
+static int nSubmenu = 0; /* Number of buttons */
47
+static struct SubmenuCtrl {
48
+ const char *zName; /* Form query parameter */
49
+ const char *zLabel; /* Label. Might be NULL for FF_MULTI */
50
+ int eType; /* FF_ENTRY, FF_CKBOX, FF_MULTI */
51
+ int iSize; /* Width for FF_ENTRY. Count for FF_MULTI */
52
+ const char **azChoice; /* value/display pairs for FF_MULTI */
53
+} aSubmenuCtrl[20];
54
+static int nSubmenuCtrl = 0;
55
+#define FF_ENTRY 1
56
+#define FF_CKBOX 2
57
+#define FF_MULTI 3
3958
4059
/*
4160
** Remember that the header has been generated. The footer is omitted
4261
** if an error occurs before the header.
4362
*/
@@ -216,16 +235,51 @@
216235
...
217236
){
218237
va_list ap;
219238
assert( nSubmenu < sizeof(aSubmenu)/sizeof(aSubmenu[0]) );
220239
aSubmenu[nSubmenu].zLabel = zLabel;
221
- aSubmenu[nSubmenu].zTitle = zTitle;
240
+ aSubmenu[nSubmenu].zTitle = zTitle ? zTitle : zLabel;
222241
va_start(ap, zLink);
223242
aSubmenu[nSubmenu].zLink = vmprintf(zLink, ap);
224243
va_end(ap);
225244
nSubmenu++;
226245
}
246
+void style_submenu_entry(
247
+ const char *zName, /* Query parameter name */
248
+ const char *zLabel, /* Label before the entry box */
249
+ int iSize /* Size of the entry box */
250
+){
251
+ assert( nSubmenuCtrl < ArraySize(aSubmenuCtrl) );
252
+ aSubmenuCtrl[nSubmenuCtrl].zName = zName;
253
+ aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel;
254
+ aSubmenuCtrl[nSubmenuCtrl].iSize = iSize;
255
+ aSubmenuCtrl[nSubmenuCtrl].eType = FF_ENTRY;
256
+ nSubmenuCtrl++;
257
+}
258
+void style_submenu_checkbox(
259
+ const char *zName, /* Query parameter name */
260
+ const char *zLabel /* Label before the checkbox */
261
+){
262
+ assert( nSubmenuCtrl < ArraySize(aSubmenuCtrl) );
263
+ aSubmenuCtrl[nSubmenuCtrl].zName = zName;
264
+ aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel;
265
+ aSubmenuCtrl[nSubmenuCtrl].eType = FF_CKBOX;
266
+ nSubmenuCtrl++;
267
+}
268
+void style_submenu_multichoice(
269
+ const char *zName, /* Query parameter name */
270
+ int nChoice, /* Number of options */
271
+ const char **azChoice /* value/display pairs. 2*nChoice entries */
272
+){
273
+ assert( nSubmenuCtrl < ArraySize(aSubmenuCtrl) );
274
+ aSubmenuCtrl[nSubmenuCtrl].zName = zName;
275
+ aSubmenuCtrl[nSubmenuCtrl].iSize = nChoice;
276
+ aSubmenuCtrl[nSubmenuCtrl].azChoice = azChoice;
277
+ aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI;
278
+ nSubmenuCtrl++;
279
+}
280
+
227281
228282
/*
229283
** Compare two submenu items for sorting purposes
230284
*/
231285
static int submenuCompare(const void *a, const void *b){
@@ -411,23 +465,84 @@
411465
/* Go back and put the submenu at the top of the page. We delay the
412466
** creation of the submenu until the end so that we can add elements
413467
** to the submenu while generating page text.
414468
*/
415469
cgi_destination(CGI_HEADER);
416
- if( nSubmenu>0 ){
470
+ if( nSubmenu+nSubmenuCtrl>0 ){
417471
int i;
472
+ if( nSubmenuCtrl ){
473
+ cgi_printf("<form id='f01' method='GET' action='%R/%s'>", g.zPath);
474
+ }
418475
@ <div class="submenu">
419
- qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
420
- for(i=0; i<nSubmenu; i++){
421
- struct Submenu *p = &aSubmenu[i];
422
- if( p->zLink==0 ){
423
- @ <span class="label">%h(p->zLabel)</span>
424
- }else{
425
- @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a>
476
+ if( nSubmenu>0 ){
477
+ qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
478
+ for(i=0; i<nSubmenu; i++){
479
+ struct Submenu *p = &aSubmenu[i];
480
+ if( p->zLink==0 ){
481
+ @ <span class="label">%h(p->zLabel)</span>
482
+ }else{
483
+ @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a>
484
+ }
485
+ }
486
+ }
487
+ if( nSubmenuCtrl>0 ){
488
+ for(i=0; i<nSubmenuCtrl; i++){
489
+ const char *zQPN = aSubmenuCtrl[i].zName;
490
+ cgi_tag_query_parameter(zQPN);
491
+ switch( aSubmenuCtrl[i].eType ){
492
+ case FF_ENTRY: {
493
+ cgi_printf(
494
+ "<span class='submenuctrl'>"
495
+ "%h:&nbsp;<input type='text' name='%s' size='%d' "
496
+ "value='%h'></span>\n",
497
+ aSubmenuCtrl[i].zLabel,
498
+ zQPN,
499
+ aSubmenuCtrl[i].iSize,
500
+ PD(zQPN,"")
501
+ );
502
+ break;
503
+ }
504
+ case FF_CKBOX: {
505
+ cgi_printf(
506
+ "<span class='submenuctrl'>"
507
+ "%h:&nbsp;<input type='checkbox' name='%s'%s "
508
+ "onchange='gebi(\"f01\").submit();'></span>\n",
509
+ aSubmenuCtrl[i].zLabel,
510
+ zQPN,
511
+ PB(zQPN) ? " checked":""
512
+ );
513
+ break;
514
+ }
515
+ case FF_MULTI: {
516
+ int j;
517
+ const char *zVal = P(zQPN);
518
+ cgi_printf(
519
+ "<select class='submenuctrl' size='1' name='%s' "
520
+ "onchange='gebi(\"f01\").submit();'>\n",
521
+ zQPN
522
+ );
523
+ for(j=0; j<aSubmenuCtrl[i].iSize*2; j+=2){
524
+ const char *zQPV = aSubmenuCtrl[i].azChoice[j];
525
+ cgi_printf(
526
+ "<option value='%h'%s>%h</option>\n",
527
+ zQPV,
528
+ fossil_strcmp(zVal,zQPV)==0 ? " selected" : "",
529
+ aSubmenuCtrl[i].azChoice[j+1]
530
+ );
531
+ }
532
+ @ </select>
533
+ break;
534
+ }
535
+ }
426536
}
427537
}
428538
@ </div>
539
+ if( nSubmenuCtrl ){
540
+ cgi_query_parameters_to_hidden();
541
+ cgi_tag_query_parameter(0);
542
+ @ </form>
543
+ }
429544
}
430545
431546
zAd = style_adunit_text(&mAdFlags);
432547
if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){
433548
@ <div class="content adunit_right_container">
@@ -1246,22 +1361,21 @@
12461361
}
12471362
for(i=0; i<count(azCgiVars); i++) (void)P(azCgiVars[i]);
12481363
style_header("Environment Test");
12491364
showAll = atoi(PD("showall","0"));
12501365
if( !showAll ){
1251
- style_submenu_element("Show Cookies", "Show Cookies",
1252
- "%s/test_env?showall=1", g.zTop);
1366
+ style_submenu_element("Show Cookies", 0, "%R/test_env?showall=1");
12531367
}else{
1254
- style_submenu_element("Hide Cookies", "Hide Cookies",
1255
- "%s/test_env", g.zTop);
1368
+ style_submenu_element("Hide Cookies", 0, "%R/test_env");
12561369
}
12571370
#if !defined(_WIN32)
12581371
@ uid=%d(getuid()), gid=%d(getgid())<br />
12591372
#endif
12601373
@ g.zBaseURL = %h(g.zBaseURL)<br />
12611374
@ g.zHttpsURL = %h(g.zHttpsURL)<br />
12621375
@ g.zTop = %h(g.zTop)<br />
1376
+ @ g.zPath = %h(g.zPath)<br />
12631377
for(i=0, c='a'; c<='z'; c++){
12641378
if( login_has_capability(&c, 1) ) zCap[i++] = c;
12651379
}
12661380
zCap[i] = 0;
12671381
@ g.userUid = %d(g.userUid)<br />
12681382
--- src/style.c
+++ src/style.c
@@ -23,21 +23,40 @@
23 #include "style.h"
24
25
26 /*
27 ** Elements of the submenu are collected into the following
28 ** structure and displayed below the main menu by style_header().
 
 
 
 
 
 
 
29 **
30 ** Populate this structure with calls to style_submenu_element()
31 ** prior to calling style_header().
 
32 */
33 static struct Submenu {
34 const char *zLabel;
35 const char *zTitle;
36 const char *zLink;
37 } aSubmenu[30];
38 static int nSubmenu = 0;
 
 
 
 
 
 
 
 
 
 
 
39
40 /*
41 ** Remember that the header has been generated. The footer is omitted
42 ** if an error occurs before the header.
43 */
@@ -216,16 +235,51 @@
216 ...
217 ){
218 va_list ap;
219 assert( nSubmenu < sizeof(aSubmenu)/sizeof(aSubmenu[0]) );
220 aSubmenu[nSubmenu].zLabel = zLabel;
221 aSubmenu[nSubmenu].zTitle = zTitle;
222 va_start(ap, zLink);
223 aSubmenu[nSubmenu].zLink = vmprintf(zLink, ap);
224 va_end(ap);
225 nSubmenu++;
226 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
228 /*
229 ** Compare two submenu items for sorting purposes
230 */
231 static int submenuCompare(const void *a, const void *b){
@@ -411,23 +465,84 @@
411 /* Go back and put the submenu at the top of the page. We delay the
412 ** creation of the submenu until the end so that we can add elements
413 ** to the submenu while generating page text.
414 */
415 cgi_destination(CGI_HEADER);
416 if( nSubmenu>0 ){
417 int i;
 
 
 
418 @ <div class="submenu">
419 qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
420 for(i=0; i<nSubmenu; i++){
421 struct Submenu *p = &aSubmenu[i];
422 if( p->zLink==0 ){
423 @ <span class="label">%h(p->zLabel)</span>
424 }else{
425 @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426 }
427 }
428 @ </div>
 
 
 
 
 
429 }
430
431 zAd = style_adunit_text(&mAdFlags);
432 if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){
433 @ <div class="content adunit_right_container">
@@ -1246,22 +1361,21 @@
1246 }
1247 for(i=0; i<count(azCgiVars); i++) (void)P(azCgiVars[i]);
1248 style_header("Environment Test");
1249 showAll = atoi(PD("showall","0"));
1250 if( !showAll ){
1251 style_submenu_element("Show Cookies", "Show Cookies",
1252 "%s/test_env?showall=1", g.zTop);
1253 }else{
1254 style_submenu_element("Hide Cookies", "Hide Cookies",
1255 "%s/test_env", g.zTop);
1256 }
1257 #if !defined(_WIN32)
1258 @ uid=%d(getuid()), gid=%d(getgid())<br />
1259 #endif
1260 @ g.zBaseURL = %h(g.zBaseURL)<br />
1261 @ g.zHttpsURL = %h(g.zHttpsURL)<br />
1262 @ g.zTop = %h(g.zTop)<br />
 
1263 for(i=0, c='a'; c<='z'; c++){
1264 if( login_has_capability(&c, 1) ) zCap[i++] = c;
1265 }
1266 zCap[i] = 0;
1267 @ g.userUid = %d(g.userUid)<br />
1268
--- src/style.c
+++ src/style.c
@@ -23,21 +23,40 @@
23 #include "style.h"
24
25
26 /*
27 ** Elements of the submenu are collected into the following
28 ** structure and displayed below the main menu.
29 **
30 ** Populate these structure with calls to
31 **
32 ** style_submenu_element()
33 ** style_submenu_entry()
34 ** style_submenu_checkbox()
35 ** style_submenu_multichoice()
36 **
37 ** prior to calling style_footer(). The style_footer() routine
38 ** will generate the appropriate HTML text just below the main
39 ** menu.
40 */
41 static struct Submenu {
42 const char *zLabel; /* Button label */
43 const char *zTitle;
44 const char *zLink; /* Jump to this link when button is pressed */
45 } aSubmenu[30];
46 static int nSubmenu = 0; /* Number of buttons */
47 static struct SubmenuCtrl {
48 const char *zName; /* Form query parameter */
49 const char *zLabel; /* Label. Might be NULL for FF_MULTI */
50 int eType; /* FF_ENTRY, FF_CKBOX, FF_MULTI */
51 int iSize; /* Width for FF_ENTRY. Count for FF_MULTI */
52 const char **azChoice; /* value/display pairs for FF_MULTI */
53 } aSubmenuCtrl[20];
54 static int nSubmenuCtrl = 0;
55 #define FF_ENTRY 1
56 #define FF_CKBOX 2
57 #define FF_MULTI 3
58
59 /*
60 ** Remember that the header has been generated. The footer is omitted
61 ** if an error occurs before the header.
62 */
@@ -216,16 +235,51 @@
235 ...
236 ){
237 va_list ap;
238 assert( nSubmenu < sizeof(aSubmenu)/sizeof(aSubmenu[0]) );
239 aSubmenu[nSubmenu].zLabel = zLabel;
240 aSubmenu[nSubmenu].zTitle = zTitle ? zTitle : zLabel;
241 va_start(ap, zLink);
242 aSubmenu[nSubmenu].zLink = vmprintf(zLink, ap);
243 va_end(ap);
244 nSubmenu++;
245 }
246 void style_submenu_entry(
247 const char *zName, /* Query parameter name */
248 const char *zLabel, /* Label before the entry box */
249 int iSize /* Size of the entry box */
250 ){
251 assert( nSubmenuCtrl < ArraySize(aSubmenuCtrl) );
252 aSubmenuCtrl[nSubmenuCtrl].zName = zName;
253 aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel;
254 aSubmenuCtrl[nSubmenuCtrl].iSize = iSize;
255 aSubmenuCtrl[nSubmenuCtrl].eType = FF_ENTRY;
256 nSubmenuCtrl++;
257 }
258 void style_submenu_checkbox(
259 const char *zName, /* Query parameter name */
260 const char *zLabel /* Label before the checkbox */
261 ){
262 assert( nSubmenuCtrl < ArraySize(aSubmenuCtrl) );
263 aSubmenuCtrl[nSubmenuCtrl].zName = zName;
264 aSubmenuCtrl[nSubmenuCtrl].zLabel = zLabel;
265 aSubmenuCtrl[nSubmenuCtrl].eType = FF_CKBOX;
266 nSubmenuCtrl++;
267 }
268 void style_submenu_multichoice(
269 const char *zName, /* Query parameter name */
270 int nChoice, /* Number of options */
271 const char **azChoice /* value/display pairs. 2*nChoice entries */
272 ){
273 assert( nSubmenuCtrl < ArraySize(aSubmenuCtrl) );
274 aSubmenuCtrl[nSubmenuCtrl].zName = zName;
275 aSubmenuCtrl[nSubmenuCtrl].iSize = nChoice;
276 aSubmenuCtrl[nSubmenuCtrl].azChoice = azChoice;
277 aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI;
278 nSubmenuCtrl++;
279 }
280
281
282 /*
283 ** Compare two submenu items for sorting purposes
284 */
285 static int submenuCompare(const void *a, const void *b){
@@ -411,23 +465,84 @@
465 /* Go back and put the submenu at the top of the page. We delay the
466 ** creation of the submenu until the end so that we can add elements
467 ** to the submenu while generating page text.
468 */
469 cgi_destination(CGI_HEADER);
470 if( nSubmenu+nSubmenuCtrl>0 ){
471 int i;
472 if( nSubmenuCtrl ){
473 cgi_printf("<form id='f01' method='GET' action='%R/%s'>", g.zPath);
474 }
475 @ <div class="submenu">
476 if( nSubmenu>0 ){
477 qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
478 for(i=0; i<nSubmenu; i++){
479 struct Submenu *p = &aSubmenu[i];
480 if( p->zLink==0 ){
481 @ <span class="label">%h(p->zLabel)</span>
482 }else{
483 @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a>
484 }
485 }
486 }
487 if( nSubmenuCtrl>0 ){
488 for(i=0; i<nSubmenuCtrl; i++){
489 const char *zQPN = aSubmenuCtrl[i].zName;
490 cgi_tag_query_parameter(zQPN);
491 switch( aSubmenuCtrl[i].eType ){
492 case FF_ENTRY: {
493 cgi_printf(
494 "<span class='submenuctrl'>"
495 "%h:&nbsp;<input type='text' name='%s' size='%d' "
496 "value='%h'></span>\n",
497 aSubmenuCtrl[i].zLabel,
498 zQPN,
499 aSubmenuCtrl[i].iSize,
500 PD(zQPN,"")
501 );
502 break;
503 }
504 case FF_CKBOX: {
505 cgi_printf(
506 "<span class='submenuctrl'>"
507 "%h:&nbsp;<input type='checkbox' name='%s'%s "
508 "onchange='gebi(\"f01\").submit();'></span>\n",
509 aSubmenuCtrl[i].zLabel,
510 zQPN,
511 PB(zQPN) ? " checked":""
512 );
513 break;
514 }
515 case FF_MULTI: {
516 int j;
517 const char *zVal = P(zQPN);
518 cgi_printf(
519 "<select class='submenuctrl' size='1' name='%s' "
520 "onchange='gebi(\"f01\").submit();'>\n",
521 zQPN
522 );
523 for(j=0; j<aSubmenuCtrl[i].iSize*2; j+=2){
524 const char *zQPV = aSubmenuCtrl[i].azChoice[j];
525 cgi_printf(
526 "<option value='%h'%s>%h</option>\n",
527 zQPV,
528 fossil_strcmp(zVal,zQPV)==0 ? " selected" : "",
529 aSubmenuCtrl[i].azChoice[j+1]
530 );
531 }
532 @ </select>
533 break;
534 }
535 }
536 }
537 }
538 @ </div>
539 if( nSubmenuCtrl ){
540 cgi_query_parameters_to_hidden();
541 cgi_tag_query_parameter(0);
542 @ </form>
543 }
544 }
545
546 zAd = style_adunit_text(&mAdFlags);
547 if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){
548 @ <div class="content adunit_right_container">
@@ -1246,22 +1361,21 @@
1361 }
1362 for(i=0; i<count(azCgiVars); i++) (void)P(azCgiVars[i]);
1363 style_header("Environment Test");
1364 showAll = atoi(PD("showall","0"));
1365 if( !showAll ){
1366 style_submenu_element("Show Cookies", 0, "%R/test_env?showall=1");
 
1367 }else{
1368 style_submenu_element("Hide Cookies", 0, "%R/test_env");
 
1369 }
1370 #if !defined(_WIN32)
1371 @ uid=%d(getuid()), gid=%d(getgid())<br />
1372 #endif
1373 @ g.zBaseURL = %h(g.zBaseURL)<br />
1374 @ g.zHttpsURL = %h(g.zHttpsURL)<br />
1375 @ g.zTop = %h(g.zTop)<br />
1376 @ g.zPath = %h(g.zPath)<br />
1377 for(i=0, c='a'; c<='z'; c++){
1378 if( login_has_capability(&c, 1) ) zCap[i++] = c;
1379 }
1380 zCap[i] = 0;
1381 @ g.userUid = %d(g.userUid)<br />
1382
+57 -47
--- src/timeline.c
+++ src/timeline.c
@@ -1011,10 +1011,47 @@
10111011
}
10121012
db_finalize(&q);
10131013
return blob_str(&out);
10141014
}
10151015
1016
+
1017
+/*
1018
+** Add the select/option box to the timeline submenu that is used to
1019
+** set the y= parameter that determines which elements to display
1020
+** on the timeline.
1021
+*/
1022
+static void timeline_y_submenu(void){
1023
+ static int i = 0;
1024
+ static const char *az[12];
1025
+ if( i==0 ){
1026
+ az[0] = "all";
1027
+ az[1] = "All Types";
1028
+ i = 2;
1029
+ if( g.perm.RdWiki ){
1030
+ az[i++] = "e";
1031
+ az[i++] = "Blog Posts";
1032
+ }
1033
+ if( g.perm.Read ){
1034
+ az[i++] = "ci";
1035
+ az[i++] = "Check-ins";
1036
+ az[i++] = "g";
1037
+ az[i++] = "Tags";
1038
+ }
1039
+ if( g.perm.RdTkt ){
1040
+ az[i++] = "t";
1041
+ az[i++] = "Tickets";
1042
+ }
1043
+ if( g.perm.RdWiki ){
1044
+ az[i++] = "w";
1045
+ az[i++] = "Wiki";
1046
+ }
1047
+ assert( i<=ArraySize(az) );
1048
+ }
1049
+ if( i>2 ){
1050
+ style_submenu_multichoice("y", i/2, az);
1051
+ }
1052
+}
10161053
10171054
/*
10181055
** WEBPAGE: timeline
10191056
**
10201057
** Query parameters:
@@ -1055,11 +1092,11 @@
10551092
*/
10561093
void page_timeline(void){
10571094
Stmt q; /* Query used to generate the timeline */
10581095
Blob sql; /* text of SQL used to generate timeline */
10591096
Blob desc; /* Description of the timeline */
1060
- int nEntry = atoi(PD("n","20")); /* Max number of entries on timeline */
1097
+ int nEntry; /* Max number of entries on timeline */
10611098
int p_rid = name_to_typed_rid(P("p"),"ci"); /* artifact p and its parents */
10621099
int d_rid = name_to_typed_rid(P("d"),"ci"); /* artifact d and descendants */
10631100
int f_rid = name_to_typed_rid(P("f"),"ci"); /* artifact f and close family */
10641101
const char *zUser = P("u"); /* All entries by this user if not NULL */
10651102
const char *zType = PD("y","all"); /* Type of events. All if NULL */
@@ -1084,10 +1121,20 @@
10841121
int noMerge = P("shortest")==0; /* Follow merge links if shorter */
10851122
int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
10861123
int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
10871124
int pd_rid;
10881125
double rBefore, rAfter, rCirca; /* Boundary times */
1126
+ const char *z;
1127
+
1128
+ /* Set number of rows to display */
1129
+ z = P("n");
1130
+ if( z ){
1131
+ nEntry = atoi(z);
1132
+ }else{
1133
+ cgi_replace_query_parameter("n","50");
1134
+ nEntry = 50;
1135
+ }
10891136
10901137
/* To view the timeline, must have permission to read project data.
10911138
*/
10921139
pd_rid = name_to_typed_rid(P("dp"),"ci");
10931140
if( pd_rid ){
@@ -1161,11 +1208,11 @@
11611208
timeline_temp_table();
11621209
blob_zero(&sql);
11631210
blob_zero(&desc);
11641211
blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
11651212
blob_append(&sql, timeline_query_for_www(), -1);
1166
- if( P("fc")!=0 || P("v")!=0 || P("detail")!=0 ){
1213
+ if( PB("fc") || PB("v") || PB("detail") ){
11671214
tmFlags |= TIMELINE_FCHANGES;
11681215
url_add_parameter(&url, "v", 0);
11691216
}
11701217
if( (tmFlags & TIMELINE_UNHIDE)==0 ){
11711218
blob_append_sql(&sql,
@@ -1251,24 +1298,13 @@
12511298
/* If both p= and d= are set, we don't have the uuid of d yet. */
12521299
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid);
12531300
}
12541301
url_add_parameter(&url, "d", zUuid);
12551302
}
1256
- if( nEntry>20 ){
1257
- timeline_submenu(&url, "20 Entries", "n", "20", 0);
1258
- }
1259
- if( nEntry<200 && nEntry>0 ){
1260
- timeline_submenu(&url, "200 Entries", "n", "200", 0);
1261
- }
1262
- if( tmFlags & TIMELINE_FCHANGES ){
1263
- timeline_submenu(&url, "Hide Files", "v", 0, 0);
1264
- }else{
1265
- timeline_submenu(&url, "Show Files", "v", "", 0);
1266
- }
1267
- if( (tmFlags & TIMELINE_UNHIDE)==0 ){
1268
- timeline_submenu(&url, "Unhide", "unhide", "", 0);
1269
- }
1303
+ style_submenu_checkbox("v","Files");
1304
+ style_submenu_entry("n","Lines",1);
1305
+ timeline_y_submenu();
12701306
}else if( f_rid && g.perm.Read ){
12711307
/* If f= is present, ignore all other parameters other than n= */
12721308
char *zUuid;
12731309
db_multi_exec(
12741310
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
@@ -1506,10 +1542,11 @@
15061542
}
15071543
if( zSearch ){
15081544
blob_appendf(&desc, " matching \"%h\"", zSearch);
15091545
}
15101546
if( g.perm.Hyperlink ){
1547
+ style_submenu_checkbox("v","Show Files");
15111548
if( zAfter || n==nEntry ){
15121549
zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
15131550
timeline_submenu(&url, "Older", "b", zDate, "a");
15141551
free(zDate);
15151552
}
@@ -1516,41 +1553,14 @@
15161553
if( zBefore || (zAfter && n==nEntry) ){
15171554
zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
15181555
timeline_submenu(&url, "Newer", "a", zDate, "b");
15191556
free(zDate);
15201557
}else if( tagid==0 && zUses==0 ){
1521
- if( zType[0]!='a' ){
1522
- timeline_submenu(&url, "All Types", "y", "all", 0);
1523
- }
1524
- if( zType[0]!='w' && g.perm.RdWiki ){
1525
- timeline_submenu(&url, "Wiki Only", "y", "w", 0);
1526
- }
1527
- if( zType[0]!='c' && g.perm.Read ){
1528
- timeline_submenu(&url, "Checkins Only", "y", "ci", 0);
1529
- }
1530
- if( zType[0]!='t' && g.perm.RdTkt ){
1531
- timeline_submenu(&url, "Tickets Only", "y", "t", 0);
1532
- }
1533
- if( zType[0]!='e' && g.perm.RdWiki ){
1534
- timeline_submenu(&url, "Events Only", "y", "e", 0);
1535
- }
1536
- if( zType[0]!='g' && g.perm.Read ){
1537
- timeline_submenu(&url, "Tags Only", "y", "g", 0);
1538
- }
1539
- }
1540
- if( nEntry>20 ){
1541
- timeline_submenu(&url, "20 Entries", "n", "20", 0);
1542
- }
1543
- if( nEntry<200 && nEntry>0 ){
1544
- timeline_submenu(&url, "200 Entries", "n", "200", 0);
1545
- }
1546
- if( zType[0]=='a' || zType[0]=='c' ){
1547
- if( tmFlags & TIMELINE_FCHANGES ){
1548
- timeline_submenu(&url, "Hide Files", "v", 0, 0);
1549
- }else{
1550
- timeline_submenu(&url, "Show Files", "v", "", 0);
1551
- }
1558
+ timeline_y_submenu();
1559
+ }
1560
+ style_submenu_entry("n","Lines",1);
1561
+ if( zType[0]=='a' || zType[0]=='c' ){
15521562
if( (tmFlags & TIMELINE_UNHIDE)==0 ){
15531563
timeline_submenu(&url, "Unhide", "unhide", "", 0);
15541564
}
15551565
}
15561566
}
15571567
--- src/timeline.c
+++ src/timeline.c
@@ -1011,10 +1011,47 @@
1011 }
1012 db_finalize(&q);
1013 return blob_str(&out);
1014 }
1015
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1016
1017 /*
1018 ** WEBPAGE: timeline
1019 **
1020 ** Query parameters:
@@ -1055,11 +1092,11 @@
1055 */
1056 void page_timeline(void){
1057 Stmt q; /* Query used to generate the timeline */
1058 Blob sql; /* text of SQL used to generate timeline */
1059 Blob desc; /* Description of the timeline */
1060 int nEntry = atoi(PD("n","20")); /* Max number of entries on timeline */
1061 int p_rid = name_to_typed_rid(P("p"),"ci"); /* artifact p and its parents */
1062 int d_rid = name_to_typed_rid(P("d"),"ci"); /* artifact d and descendants */
1063 int f_rid = name_to_typed_rid(P("f"),"ci"); /* artifact f and close family */
1064 const char *zUser = P("u"); /* All entries by this user if not NULL */
1065 const char *zType = PD("y","all"); /* Type of events. All if NULL */
@@ -1084,10 +1121,20 @@
1084 int noMerge = P("shortest")==0; /* Follow merge links if shorter */
1085 int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
1086 int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
1087 int pd_rid;
1088 double rBefore, rAfter, rCirca; /* Boundary times */
 
 
 
 
 
 
 
 
 
 
1089
1090 /* To view the timeline, must have permission to read project data.
1091 */
1092 pd_rid = name_to_typed_rid(P("dp"),"ci");
1093 if( pd_rid ){
@@ -1161,11 +1208,11 @@
1161 timeline_temp_table();
1162 blob_zero(&sql);
1163 blob_zero(&desc);
1164 blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
1165 blob_append(&sql, timeline_query_for_www(), -1);
1166 if( P("fc")!=0 || P("v")!=0 || P("detail")!=0 ){
1167 tmFlags |= TIMELINE_FCHANGES;
1168 url_add_parameter(&url, "v", 0);
1169 }
1170 if( (tmFlags & TIMELINE_UNHIDE)==0 ){
1171 blob_append_sql(&sql,
@@ -1251,24 +1298,13 @@
1251 /* If both p= and d= are set, we don't have the uuid of d yet. */
1252 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid);
1253 }
1254 url_add_parameter(&url, "d", zUuid);
1255 }
1256 if( nEntry>20 ){
1257 timeline_submenu(&url, "20 Entries", "n", "20", 0);
1258 }
1259 if( nEntry<200 && nEntry>0 ){
1260 timeline_submenu(&url, "200 Entries", "n", "200", 0);
1261 }
1262 if( tmFlags & TIMELINE_FCHANGES ){
1263 timeline_submenu(&url, "Hide Files", "v", 0, 0);
1264 }else{
1265 timeline_submenu(&url, "Show Files", "v", "", 0);
1266 }
1267 if( (tmFlags & TIMELINE_UNHIDE)==0 ){
1268 timeline_submenu(&url, "Unhide", "unhide", "", 0);
1269 }
1270 }else if( f_rid && g.perm.Read ){
1271 /* If f= is present, ignore all other parameters other than n= */
1272 char *zUuid;
1273 db_multi_exec(
1274 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
@@ -1506,10 +1542,11 @@
1506 }
1507 if( zSearch ){
1508 blob_appendf(&desc, " matching \"%h\"", zSearch);
1509 }
1510 if( g.perm.Hyperlink ){
 
1511 if( zAfter || n==nEntry ){
1512 zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
1513 timeline_submenu(&url, "Older", "b", zDate, "a");
1514 free(zDate);
1515 }
@@ -1516,41 +1553,14 @@
1516 if( zBefore || (zAfter && n==nEntry) ){
1517 zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
1518 timeline_submenu(&url, "Newer", "a", zDate, "b");
1519 free(zDate);
1520 }else if( tagid==0 && zUses==0 ){
1521 if( zType[0]!='a' ){
1522 timeline_submenu(&url, "All Types", "y", "all", 0);
1523 }
1524 if( zType[0]!='w' && g.perm.RdWiki ){
1525 timeline_submenu(&url, "Wiki Only", "y", "w", 0);
1526 }
1527 if( zType[0]!='c' && g.perm.Read ){
1528 timeline_submenu(&url, "Checkins Only", "y", "ci", 0);
1529 }
1530 if( zType[0]!='t' && g.perm.RdTkt ){
1531 timeline_submenu(&url, "Tickets Only", "y", "t", 0);
1532 }
1533 if( zType[0]!='e' && g.perm.RdWiki ){
1534 timeline_submenu(&url, "Events Only", "y", "e", 0);
1535 }
1536 if( zType[0]!='g' && g.perm.Read ){
1537 timeline_submenu(&url, "Tags Only", "y", "g", 0);
1538 }
1539 }
1540 if( nEntry>20 ){
1541 timeline_submenu(&url, "20 Entries", "n", "20", 0);
1542 }
1543 if( nEntry<200 && nEntry>0 ){
1544 timeline_submenu(&url, "200 Entries", "n", "200", 0);
1545 }
1546 if( zType[0]=='a' || zType[0]=='c' ){
1547 if( tmFlags & TIMELINE_FCHANGES ){
1548 timeline_submenu(&url, "Hide Files", "v", 0, 0);
1549 }else{
1550 timeline_submenu(&url, "Show Files", "v", "", 0);
1551 }
1552 if( (tmFlags & TIMELINE_UNHIDE)==0 ){
1553 timeline_submenu(&url, "Unhide", "unhide", "", 0);
1554 }
1555 }
1556 }
1557
--- src/timeline.c
+++ src/timeline.c
@@ -1011,10 +1011,47 @@
1011 }
1012 db_finalize(&q);
1013 return blob_str(&out);
1014 }
1015
1016
1017 /*
1018 ** Add the select/option box to the timeline submenu that is used to
1019 ** set the y= parameter that determines which elements to display
1020 ** on the timeline.
1021 */
1022 static void timeline_y_submenu(void){
1023 static int i = 0;
1024 static const char *az[12];
1025 if( i==0 ){
1026 az[0] = "all";
1027 az[1] = "All Types";
1028 i = 2;
1029 if( g.perm.RdWiki ){
1030 az[i++] = "e";
1031 az[i++] = "Blog Posts";
1032 }
1033 if( g.perm.Read ){
1034 az[i++] = "ci";
1035 az[i++] = "Check-ins";
1036 az[i++] = "g";
1037 az[i++] = "Tags";
1038 }
1039 if( g.perm.RdTkt ){
1040 az[i++] = "t";
1041 az[i++] = "Tickets";
1042 }
1043 if( g.perm.RdWiki ){
1044 az[i++] = "w";
1045 az[i++] = "Wiki";
1046 }
1047 assert( i<=ArraySize(az) );
1048 }
1049 if( i>2 ){
1050 style_submenu_multichoice("y", i/2, az);
1051 }
1052 }
1053
1054 /*
1055 ** WEBPAGE: timeline
1056 **
1057 ** Query parameters:
@@ -1055,11 +1092,11 @@
1092 */
1093 void page_timeline(void){
1094 Stmt q; /* Query used to generate the timeline */
1095 Blob sql; /* text of SQL used to generate timeline */
1096 Blob desc; /* Description of the timeline */
1097 int nEntry; /* Max number of entries on timeline */
1098 int p_rid = name_to_typed_rid(P("p"),"ci"); /* artifact p and its parents */
1099 int d_rid = name_to_typed_rid(P("d"),"ci"); /* artifact d and descendants */
1100 int f_rid = name_to_typed_rid(P("f"),"ci"); /* artifact f and close family */
1101 const char *zUser = P("u"); /* All entries by this user if not NULL */
1102 const char *zType = PD("y","all"); /* Type of events. All if NULL */
@@ -1084,10 +1121,20 @@
1121 int noMerge = P("shortest")==0; /* Follow merge links if shorter */
1122 int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
1123 int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
1124 int pd_rid;
1125 double rBefore, rAfter, rCirca; /* Boundary times */
1126 const char *z;
1127
1128 /* Set number of rows to display */
1129 z = P("n");
1130 if( z ){
1131 nEntry = atoi(z);
1132 }else{
1133 cgi_replace_query_parameter("n","50");
1134 nEntry = 50;
1135 }
1136
1137 /* To view the timeline, must have permission to read project data.
1138 */
1139 pd_rid = name_to_typed_rid(P("dp"),"ci");
1140 if( pd_rid ){
@@ -1161,11 +1208,11 @@
1208 timeline_temp_table();
1209 blob_zero(&sql);
1210 blob_zero(&desc);
1211 blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
1212 blob_append(&sql, timeline_query_for_www(), -1);
1213 if( PB("fc") || PB("v") || PB("detail") ){
1214 tmFlags |= TIMELINE_FCHANGES;
1215 url_add_parameter(&url, "v", 0);
1216 }
1217 if( (tmFlags & TIMELINE_UNHIDE)==0 ){
1218 blob_append_sql(&sql,
@@ -1251,24 +1298,13 @@
1298 /* If both p= and d= are set, we don't have the uuid of d yet. */
1299 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid);
1300 }
1301 url_add_parameter(&url, "d", zUuid);
1302 }
1303 style_submenu_checkbox("v","Files");
1304 style_submenu_entry("n","Lines",1);
1305 timeline_y_submenu();
 
 
 
 
 
 
 
 
 
 
 
1306 }else if( f_rid && g.perm.Read ){
1307 /* If f= is present, ignore all other parameters other than n= */
1308 char *zUuid;
1309 db_multi_exec(
1310 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
@@ -1506,10 +1542,11 @@
1542 }
1543 if( zSearch ){
1544 blob_appendf(&desc, " matching \"%h\"", zSearch);
1545 }
1546 if( g.perm.Hyperlink ){
1547 style_submenu_checkbox("v","Show Files");
1548 if( zAfter || n==nEntry ){
1549 zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
1550 timeline_submenu(&url, "Older", "b", zDate, "a");
1551 free(zDate);
1552 }
@@ -1516,41 +1553,14 @@
1553 if( zBefore || (zAfter && n==nEntry) ){
1554 zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
1555 timeline_submenu(&url, "Newer", "a", zDate, "b");
1556 free(zDate);
1557 }else if( tagid==0 && zUses==0 ){
1558 timeline_y_submenu();
1559 }
1560 style_submenu_entry("n","Lines",1);
1561 if( zType[0]=='a' || zType[0]=='c' ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1562 if( (tmFlags & TIMELINE_UNHIDE)==0 ){
1563 timeline_submenu(&url, "Unhide", "unhide", "", 0);
1564 }
1565 }
1566 }
1567

Keyboard Shortcuts

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