Fossil SCM

Allow a page's submenu to have multiple parametric hyperlinks induced by several different query string parameters. Use a common suffixes (smpl,smpl1,...smpl9) to probe for defined parameters. Relax constraints on hyperlink values to allow linking to wiki pages (for example). As a proof of concept add support of paralinks to the /wiki page. Get rid of dangling '&' at the endings.

george 2021-03-26 17:20 rptview-submenu-paralink
Commit d075801aa8629e2492133e6e4d6087d1a893d024435acd7062398a56ac64894f
3 files changed +17 -5 +55 -13 +1
+17 -5
--- src/report.c
+++ src/report.c
@@ -989,11 +989,10 @@
989989
char *zClrKey;
990990
int tabs;
991991
Stmt q;
992992
char *zErr1 = 0;
993993
char *zErr2 = 0;
994
- const char *zQS; /* QUERY_STRING */
995994
996995
login_check_credentials();
997996
if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
998997
tabs = P("tablist")!=0;
999998
db_prepare(&q,
@@ -1035,17 +1034,30 @@
10351034
}
10361035
10371036
count = 0;
10381037
if( !tabs ){
10391038
struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1039
+ const char *zQS = PD("QUERY_STRING","");
10401040
10411041
db_multi_exec("PRAGMA empty_result_callbacks=ON");
10421042
style_set_current_feature("report");
1043
- zQS = PD("QUERY_STRING","");
1044
- style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS);
1045
- style_submenu_element("Reports","%R/reportlist?&%s",zQS);
1046
- style_submenu_parametric("rvsmpl");
1043
+ /*
1044
+ ** Lets use a funcy button for /reportlist since that page may be
1045
+ ** heavily customized by the user. Some variants: ⊚ ⦾ ❊ ⊛ ⚛ ⸎ 💠
1046
+ ** Enclosing it inside of square brackets makes its position
1047
+ ** determenistic and clearly distincts regular submenu links from
1048
+ ** those that are induced by the query string parameters.
1049
+ */
1050
+ if( zQS[0] ){
1051
+ style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS);
1052
+ style_submenu_element("[⊚]","%R/reportlist?%s",zQS);
1053
+ } else {
1054
+ style_submenu_element("Raw","%R/%s?tablist=1",g.zPath);
1055
+ style_submenu_element("[⊚]","%R/reportlist");
1056
+ }
1057
+ style_submenu_parametric("rptview_",5);
1058
+ style_submenu_parametric("rv",5);
10471059
10481060
if( g.perm.Admin
10491061
|| (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
10501062
style_submenu_element("Edit", "rptedit?rn=%d", rn);
10511063
}
10521064
--- src/report.c
+++ src/report.c
@@ -989,11 +989,10 @@
989 char *zClrKey;
990 int tabs;
991 Stmt q;
992 char *zErr1 = 0;
993 char *zErr2 = 0;
994 const char *zQS; /* QUERY_STRING */
995
996 login_check_credentials();
997 if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
998 tabs = P("tablist")!=0;
999 db_prepare(&q,
@@ -1035,17 +1034,30 @@
1035 }
1036
1037 count = 0;
1038 if( !tabs ){
1039 struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
1040
1041 db_multi_exec("PRAGMA empty_result_callbacks=ON");
1042 style_set_current_feature("report");
1043 zQS = PD("QUERY_STRING","");
1044 style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS);
1045 style_submenu_element("Reports","%R/reportlist?&%s",zQS);
1046 style_submenu_parametric("rvsmpl");
 
 
 
 
 
 
 
 
 
 
 
 
1047
1048 if( g.perm.Admin
1049 || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
1050 style_submenu_element("Edit", "rptedit?rn=%d", rn);
1051 }
1052
--- src/report.c
+++ src/report.c
@@ -989,11 +989,10 @@
989 char *zClrKey;
990 int tabs;
991 Stmt q;
992 char *zErr1 = 0;
993 char *zErr2 = 0;
 
994
995 login_check_credentials();
996 if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
997 tabs = P("tablist")!=0;
998 db_prepare(&q,
@@ -1035,17 +1034,30 @@
1034 }
1035
1036 count = 0;
1037 if( !tabs ){
1038 struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1039 const char *zQS = PD("QUERY_STRING","");
1040
1041 db_multi_exec("PRAGMA empty_result_callbacks=ON");
1042 style_set_current_feature("report");
1043 /*
1044 ** Lets use a funcy button for /reportlist since that page may be
1045 ** heavily customized by the user. Some variants: ⊚ ⦾ ❊ ⊛ ⚛ ⸎ 💠
1046 ** Enclosing it inside of square brackets makes its position
1047 ** determenistic and clearly distincts regular submenu links from
1048 ** those that are induced by the query string parameters.
1049 */
1050 if( zQS[0] ){
1051 style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS);
1052 style_submenu_element("[⊚]","%R/reportlist?%s",zQS);
1053 } else {
1054 style_submenu_element("Raw","%R/%s?tablist=1",g.zPath);
1055 style_submenu_element("[⊚]","%R/reportlist");
1056 }
1057 style_submenu_parametric("rptview_",5);
1058 style_submenu_parametric("rv",5);
1059
1060 if( g.perm.Admin
1061 || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
1062 style_submenu_element("Edit", "rptedit?rn=%d", rn);
1063 }
1064
+55 -13
--- src/style.c
+++ src/style.c
@@ -330,26 +330,68 @@
330330
aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI;
331331
nSubmenuCtrl++;
332332
}
333333
}
334334
335
-/* Add submenu hyperlink based on the value of arbitrary parameter
336
- * in the request's query string.
337
- */
335
+/* Add hyperlinks depending on the existence and values of special
336
+** parameters in the request's query string. The names of these
337
+** parameters that are investigated are obtainted by concatenation
338
+** of zPrefix with suffix "smplX", where X is either nothing or
339
+** a positive digit <= nMaxDigit. zPrefix must start with a lowercase
340
+** letter, be short and have no strange characters. A value is
341
+** well-formed if its first filepath segment (separated by '/')
342
+** has no strange characters. The labels of the resulting submenu items
343
+** are equal to the well-formed values that are prepended by "✧"
344
+** unless a value starts with a lowercase letter.
345
+** Malformed values are silently ignored.
346
+*/
338347
void style_submenu_parametric(
339
- const char *zName /* Query parameter name */
348
+ const char *zPrefix, /* common prefix of the query parameters names */
349
+ const int nMaxDigit /* maximal digit on the end of param names */
340350
){
341
- const char *zV; /* value of the corresponding parameter */
342
- if( zName == 0 || zName[0] == 0 || !fossil_islower(zName[0]) ||
343
- !fossil_no_strange_characters(zName)) {
344
- return;
345
- }
346
- zV = PD(zName,"");
347
- if( zV[0] && fossil_no_strange_characters( zV )){
351
+ const char *zQS; /* QUERY_STRING */
352
+ const char *suffix = "smpl"; /* common suffix for all parameters */
353
+ const short sfxlen = 4; /* length of the above suffix */
354
+ char zN[32]; /* short names => no dynamic allocations */
355
+ short i,l;
356
+
357
+ /* zPrefix must be tidy and short; also filter out ENV/CGI variables */
358
+ assert( zPrefix != 0 && fossil_islower(zPrefix[0]) );
359
+ l = strnlen( zPrefix, sizeof(zN) );
360
+ assert( l+sfxlen+2 <= sizeof(zN) );
361
+ assert( fossil_no_strange_characters(zPrefix) );
362
+ /* concatenate zPrefix and suffix */
363
+ strcpy( zN, zPrefix );
364
+ strcpy( zN + l, suffix );
365
+ l += sfxlen;
366
+ zN[l+1] = 0; /* nul-terminator after digit's placeholder (if any) */
367
+ zQS = PD("QUERY_STRING","");
368
+ for( i = 0; i <= 9 && i <= nMaxDigit; i++ ){
369
+ const char *zV, *z;
370
+ zN[l] = ( i == 0 ? 0 : '0' + i ); /* ...smpl instead of ...smpl0 */
371
+ zV = PD(zN,"");
372
+ if( zV[0] == 0 || zV[0] == '/' ){
373
+ continue;
374
+ }
375
+ /* require the first path segment to be unfancy ASCII string */
376
+ for( z = zV; z[0] && z[0] != '/' ;){
377
+ if( fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-' ) z++;
378
+ else break;
379
+ }
380
+ if( z[0] != 0 && z[0] != '/' )
381
+ continue;
348382
assert( nSubmenu < count(aSubmenu) );
349
- aSubmenu[nSubmenu].zLabel = mprintf("[ %s ]",zV); /* memory leak? */
350
- aSubmenu[nSubmenu].zLink = mprintf("%R/%s?%s",zV,PD("QUERY_STRING",""));
383
+ if(fossil_islower(zV[0])){
384
+ aSubmenu[nSubmenu].zLabel = mprintf( "%s",zV); /* memory leak? */
385
+ }else{
386
+ aSubmenu[nSubmenu].zLabel = mprintf("✧%s",zV); /* maybe: ◦✧⸰⸎ ✨ */
387
+ }
388
+ if( zQS[0] ){
389
+ aSubmenu[nSubmenu].zLink = mprintf("%R/%s?%s",zV,zQS);
390
+ }else{
391
+ aSubmenu[nSubmenu].zLink = mprintf("%R/%s",zV);
392
+ }
351393
nSubmenu++;
352394
}
353395
}
354396
355397
/*
356398
--- src/style.c
+++ src/style.c
@@ -330,26 +330,68 @@
330 aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI;
331 nSubmenuCtrl++;
332 }
333 }
334
335 /* Add submenu hyperlink based on the value of arbitrary parameter
336 * in the request's query string.
337 */
 
 
 
 
 
 
 
 
 
338 void style_submenu_parametric(
339 const char *zName /* Query parameter name */
 
340 ){
341 const char *zV; /* value of the corresponding parameter */
342 if( zName == 0 || zName[0] == 0 || !fossil_islower(zName[0]) ||
343 !fossil_no_strange_characters(zName)) {
344 return;
345 }
346 zV = PD(zName,"");
347 if( zV[0] && fossil_no_strange_characters( zV )){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348 assert( nSubmenu < count(aSubmenu) );
349 aSubmenu[nSubmenu].zLabel = mprintf("[ %s ]",zV); /* memory leak? */
350 aSubmenu[nSubmenu].zLink = mprintf("%R/%s?%s",zV,PD("QUERY_STRING",""));
 
 
 
 
 
 
 
 
351 nSubmenu++;
352 }
353 }
354
355 /*
356
--- src/style.c
+++ src/style.c
@@ -330,26 +330,68 @@
330 aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI;
331 nSubmenuCtrl++;
332 }
333 }
334
335 /* Add hyperlinks depending on the existence and values of special
336 ** parameters in the request's query string. The names of these
337 ** parameters that are investigated are obtainted by concatenation
338 ** of zPrefix with suffix "smplX", where X is either nothing or
339 ** a positive digit <= nMaxDigit. zPrefix must start with a lowercase
340 ** letter, be short and have no strange characters. A value is
341 ** well-formed if its first filepath segment (separated by '/')
342 ** has no strange characters. The labels of the resulting submenu items
343 ** are equal to the well-formed values that are prepended by "✧"
344 ** unless a value starts with a lowercase letter.
345 ** Malformed values are silently ignored.
346 */
347 void style_submenu_parametric(
348 const char *zPrefix, /* common prefix of the query parameters names */
349 const int nMaxDigit /* maximal digit on the end of param names */
350 ){
351 const char *zQS; /* QUERY_STRING */
352 const char *suffix = "smpl"; /* common suffix for all parameters */
353 const short sfxlen = 4; /* length of the above suffix */
354 char zN[32]; /* short names => no dynamic allocations */
355 short i,l;
356
357 /* zPrefix must be tidy and short; also filter out ENV/CGI variables */
358 assert( zPrefix != 0 && fossil_islower(zPrefix[0]) );
359 l = strnlen( zPrefix, sizeof(zN) );
360 assert( l+sfxlen+2 <= sizeof(zN) );
361 assert( fossil_no_strange_characters(zPrefix) );
362 /* concatenate zPrefix and suffix */
363 strcpy( zN, zPrefix );
364 strcpy( zN + l, suffix );
365 l += sfxlen;
366 zN[l+1] = 0; /* nul-terminator after digit's placeholder (if any) */
367 zQS = PD("QUERY_STRING","");
368 for( i = 0; i <= 9 && i <= nMaxDigit; i++ ){
369 const char *zV, *z;
370 zN[l] = ( i == 0 ? 0 : '0' + i ); /* ...smpl instead of ...smpl0 */
371 zV = PD(zN,"");
372 if( zV[0] == 0 || zV[0] == '/' ){
373 continue;
374 }
375 /* require the first path segment to be unfancy ASCII string */
376 for( z = zV; z[0] && z[0] != '/' ;){
377 if( fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-' ) z++;
378 else break;
379 }
380 if( z[0] != 0 && z[0] != '/' )
381 continue;
382 assert( nSubmenu < count(aSubmenu) );
383 if(fossil_islower(zV[0])){
384 aSubmenu[nSubmenu].zLabel = mprintf( "%s",zV); /* memory leak? */
385 }else{
386 aSubmenu[nSubmenu].zLabel = mprintf("✧%s",zV); /* maybe: ◦✧⸰⸎ ✨ */
387 }
388 if( zQS[0] ){
389 aSubmenu[nSubmenu].zLink = mprintf("%R/%s?%s",zV,zQS);
390 }else{
391 aSubmenu[nSubmenu].zLink = mprintf("%R/%s",zV);
392 }
393 nSubmenu++;
394 }
395 }
396
397 /*
398
+1
--- src/wiki.c
+++ src/wiki.c
@@ -593,10 +593,11 @@
593593
}else if( rid && g.perm.ApndWiki ){
594594
style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName);
595595
}
596596
if( g.perm.Hyperlink ){
597597
style_submenu_element("History", "%R/whistory?name=%T", zPageName);
598
+ style_submenu_parametric("wiki",7);
598599
}
599600
}
600601
if( !isPopup ){
601602
style_set_current_page("%T?name=%T", g.zPath, zPageName);
602603
wiki_page_header(WIKITYPE_UNKNOWN, zPageName, "");
603604
--- src/wiki.c
+++ src/wiki.c
@@ -593,10 +593,11 @@
593 }else if( rid && g.perm.ApndWiki ){
594 style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName);
595 }
596 if( g.perm.Hyperlink ){
597 style_submenu_element("History", "%R/whistory?name=%T", zPageName);
 
598 }
599 }
600 if( !isPopup ){
601 style_set_current_page("%T?name=%T", g.zPath, zPageName);
602 wiki_page_header(WIKITYPE_UNKNOWN, zPageName, "");
603
--- src/wiki.c
+++ src/wiki.c
@@ -593,10 +593,11 @@
593 }else if( rid && g.perm.ApndWiki ){
594 style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName);
595 }
596 if( g.perm.Hyperlink ){
597 style_submenu_element("History", "%R/whistory?name=%T", zPageName);
598 style_submenu_parametric("wiki",7);
599 }
600 }
601 if( !isPopup ){
602 style_set_current_page("%T?name=%T", g.zPath, zPageName);
603 wiki_page_header(WIKITYPE_UNKNOWN, zPageName, "");
604

Keyboard Shortcuts

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