Fossil

Check-in [d075801aa8]
Login

Check-in [d075801aa8]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment: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.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | rptview-submenu-paralink
Files: files | file ages | folders
SHA3-256: d075801aa8629e2492133e6e4d6087d1a893d024435acd7062398a56ac64894f
User & Date: george 2021-03-26 17:20:39.932
Context
2021-03-26
17:59
Merge-in changes from trunk (~ version 2.15) ... (check-in: 290671078c user: george tags: rptview-submenu-paralink)
17:20
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. ... (check-in: d075801aa8 user: george tags: rptview-submenu-paralink)
2021-03-25
04:11
At /rptview page show link to /reportslist. If request's query string contains <b>rvsmpl</b> parameter then also show an auxiliary link to the page defined by the value of this parameter. Labels and links rendered via %s format specifier because %h and %T did not work properly. In this particular case this seems fine because style_finish_page() renders HTML via %h(...). Code review is very welcome though. ... (check-in: b982c00150 user: george tags: rptview-submenu-paralink)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/report.c.
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
  char *zTitle;
  char *zOwner;
  char *zClrKey;
  int tabs;
  Stmt q;
  char *zErr1 = 0;
  char *zErr2 = 0;
  const char *zQS;   /* QUERY_STRING  */

  login_check_credentials();
  if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
  tabs = P("tablist")!=0;
  db_prepare(&q,
    "SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE rn=%d",
     atoi(PD("rn","0")));







<







987
988
989
990
991
992
993

994
995
996
997
998
999
1000
  char *zTitle;
  char *zOwner;
  char *zClrKey;
  int tabs;
  Stmt q;
  char *zErr1 = 0;
  char *zErr2 = 0;


  login_check_credentials();
  if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
  tabs = P("tablist")!=0;
  db_prepare(&q,
    "SELECT title, sqlcode, owner, cols, rn FROM reportfmt WHERE rn=%d",
     atoi(PD("rn","0")));
1033
1034
1035
1036
1037
1038
1039

1040
1041
1042







1043
1044



1045

1046

1047
1048
1049
1050
1051
1052
1053
      zSql = mprintf("SELECT * FROM (%s) ORDER BY %d %s", zSql, nField, zDir);
    }
  }

  count = 0;
  if( !tabs ){
    struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };


    db_multi_exec("PRAGMA empty_result_callbacks=ON");
    style_set_current_feature("report");







    zQS = PD("QUERY_STRING","");
    style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS);



    style_submenu_element("Reports","%R/reportlist?&%s",zQS);

    style_submenu_parametric("rvsmpl");


    if( g.perm.Admin
       || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
      style_submenu_element("Edit", "rptedit?rn=%d", rn);
    }
    if( g.perm.TktFmt ){
      style_submenu_element("SQL", "rptsql?rn=%d",rn);







>



>
>
>
>
>
>
>
|
|
>
>
>
|
>
|
>







1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
      zSql = mprintf("SELECT * FROM (%s) ORDER BY %d %s", zSql, nField, zDir);
    }
  }

  count = 0;
  if( !tabs ){
    struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    const char *zQS = PD("QUERY_STRING","");

    db_multi_exec("PRAGMA empty_result_callbacks=ON");
    style_set_current_feature("report");
    /*
    ** Lets use a funcy button for /reportlist since that page may be
    ** heavily customized by the user. Some variants: ⊚ ⦾  ❊ ⊛ ⚛ ⸎  💠
    ** Enclosing it inside of square brackets makes its  position
    ** determenistic and clearly distincts regular submenu links from
    ** those that are induced by the query string parameters.
    */
    if( zQS[0] ){
      style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS);
      style_submenu_element("[⊚]","%R/reportlist?%s",zQS);
    } else {
      style_submenu_element("Raw","%R/%s?tablist=1",g.zPath);
      style_submenu_element("[⊚]","%R/reportlist");
    }
    style_submenu_parametric("rptview_",5);
    style_submenu_parametric("rv",5);

    if( g.perm.Admin
       || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
      style_submenu_element("Edit", "rptedit?rn=%d", rn);
    }
    if( g.perm.TktFmt ){
      style_submenu_element("SQL", "rptsql?rn=%d",rn);
Changes to src/style.c.
328
329
330
331
332
333
334
335
336









337
338
339

340





341

342


343
















344
345
346
347

348

349




350



351
352
353
354
355
356
357
    aSubmenuCtrl[nSubmenuCtrl].azChoice = (const char *const *)az;
    aSubmenuCtrl[nSubmenuCtrl].eVisible = STYLE_NORMAL;
    aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI;
    nSubmenuCtrl++;
  }
}

/*  Add submenu hyperlink based on the value of arbitrary parameter
 *  in the request's query string.









 */
void style_submenu_parametric(
  const char *zName      /*  Query parameter name */

){





  const char *zV;   /* value of the corresponding parameter   */

  if( zName == 0 || zName[0] == 0 || !fossil_islower(zName[0]) ||


     !fossil_no_strange_characters(zName)) {
















    return;
  }
  zV = PD(zName,"");
  if( zV[0] && fossil_no_strange_characters( zV )){

    assert( nSubmenu < count(aSubmenu) );

    aSubmenu[nSubmenu].zLabel = mprintf("[ %s ]",zV);  /* memory leak? */




    aSubmenu[nSubmenu].zLink  = mprintf("%R/%s?%s",zV,PD("QUERY_STRING",""));



    nSubmenu++;
  }
}

/*
** Disable or enable the submenu
*/







|
|
>
>
>
>
>
>
>
>
>
|

|
>

>
>
>
>
>
|
>
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
<
|
>

>
|
>
>
>
>
|
>
>
>







328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379

380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
    aSubmenuCtrl[nSubmenuCtrl].azChoice = (const char *const *)az;
    aSubmenuCtrl[nSubmenuCtrl].eVisible = STYLE_NORMAL;
    aSubmenuCtrl[nSubmenuCtrl].eType = FF_MULTI;
    nSubmenuCtrl++;
  }
}

/* Add hyperlinks depending on the existence and values of special
** parameters in the request's query string. The names of these
** parameters that are investigated are obtainted by concatenation
** of zPrefix with suffix "smplX", where X is either nothing or
** a positive digit <= nMaxDigit. zPrefix must start with a lowercase
** letter, be short and have no strange characters. A value is
** well-formed if its first filepath segment (separated by '/')
** has no strange characters. The labels of the resulting submenu items
** are equal to the well-formed values that are prepended by "✧"
** unless a value starts with a lowercase letter.
** Malformed values are silently ignored.
*/
void style_submenu_parametric(
  const char *zPrefix,   /* common prefix of the query parameters names */
  const int  nMaxDigit   /* maximal digit on the end of param names     */
){
  const char *zQS;             /* QUERY_STRING */
  const char *suffix = "smpl"; /* common suffix for all parameters      */
  const short sfxlen =  4;     /* length of the above suffix            */
  char  zN[32];                /* short names => no dynamic allocations */
  short i,l;

  /* zPrefix must be tidy and short; also filter out ENV/CGI variables  */
  assert( zPrefix != 0 && fossil_islower(zPrefix[0]) );
  l = strnlen( zPrefix, sizeof(zN) );
  assert( l+sfxlen+2 <= sizeof(zN) );
  assert( fossil_no_strange_characters(zPrefix) );
  /* concatenate zPrefix and suffix */
  strcpy( zN, zPrefix );
  strcpy( zN + l, suffix );
  l += sfxlen;
  zN[l+1] = 0; /* nul-terminator after digit's placeholder (if any) */
  zQS = PD("QUERY_STRING","");
  for( i = 0; i <= 9 && i <= nMaxDigit; i++ ){
    const char *zV, *z;
    zN[l] = ( i == 0 ?  0 : '0' + i ); /* ...smpl instead of ...smpl0 */
    zV = PD(zN,"");
    if( zV[0] == 0 || zV[0] == '/' ){
      continue;
    }
    /* require the first path segment to be unfancy ASCII string */
    for( z = zV; z[0] && z[0] != '/' ;){
      if( fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-' ) z++;
      else break;
    }

    if( z[0] != 0 && z[0] != '/' )
      continue;
    assert( nSubmenu < count(aSubmenu) );
    if(fossil_islower(zV[0])){
      aSubmenu[nSubmenu].zLabel = mprintf( "%s",zV); /* memory leak?  */
    }else{
      aSubmenu[nSubmenu].zLabel = mprintf("✧%s",zV); /* maybe: ◦✧⸰⸎ ✨ */
    }
    if( zQS[0] ){
      aSubmenu[nSubmenu].zLink  = mprintf("%R/%s?%s",zV,zQS);
    }else{
      aSubmenu[nSubmenu].zLink  = mprintf("%R/%s",zV);
    }
    nSubmenu++;
  }
}

/*
** Disable or enable the submenu
*/
Changes to src/wiki.c.
591
592
593
594
595
596
597

598
599
600
601
602
603
604
    ){
      style_submenu_element("Edit", "%R/wikiedit?name=%T", zPageName);
    }else if( rid && g.perm.ApndWiki ){
      style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName);
    }
    if( g.perm.Hyperlink ){
      style_submenu_element("History", "%R/whistory?name=%T", zPageName);

    }
  }
  if( !isPopup ){
    style_set_current_page("%T?name=%T", g.zPath, zPageName);
    wiki_page_header(WIKITYPE_UNKNOWN, zPageName, "");
    if( !noSubmenu ){
      wiki_standard_submenu(submenuFlags);







>







591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
    ){
      style_submenu_element("Edit", "%R/wikiedit?name=%T", zPageName);
    }else if( rid && g.perm.ApndWiki ){
      style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName);
    }
    if( g.perm.Hyperlink ){
      style_submenu_element("History", "%R/whistory?name=%T", zPageName);
      style_submenu_parametric("wiki",7);
    }
  }
  if( !isPopup ){
    style_set_current_page("%T?name=%T", g.zPath, zPageName);
    wiki_page_header(WIKITYPE_UNKNOWN, zPageName, "");
    if( !noSubmenu ){
      wiki_standard_submenu(submenuFlags);