Fossil

Check-in [7971343314]
Login

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

Overview
Comment:Enable interactive adjustment of the set of wiki pages that are listed on the [/wcontent] page. Corresponding submenu controls can be configured by the administrator to fulfill local needs and constraints.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | wcontent-subsets
Files: files | file ages | folders
SHA3-256: 797134331481cb9c0e89f72511e4355b05ec03ef37cccc3f2d6cca11b248795c
User & Date: george 2021-04-21 20:53:05.736
Context
2021-06-15
21:37
Merge from trunk check-in: cea36e6c86 user: george tags: wcontent-subsets
2021-04-21
20:53
Enable interactive adjustment of the set of wiki pages that are listed on the [/wcontent] page. Corresponding submenu controls can be configured by the administrator to fulfill local needs and constraints. check-in: 7971343314 user: george tags: wcontent-subsets
16:48
Remove extraneous and incomplete <span> tag from the /finfo output. check-in: d115594b2f user: drh tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/db.c.
4260
4261
4262
4263
4264
4265
4266








































4267
4268
4269
4270
4271
4272
4273
/*
** SETTING: web-browser      width=30 sensitive
** A shell command used to launch your preferred
** web browser when given a URL as an argument.
** Defaults to "start" on windows, "open" on Mac,
** and "firefox" on Unix.
*/









































/*
** Look up a control setting by its name.  Return a pointer to the Setting
** object, or NULL if there is no such setting.
**
** If allowPrefix is true, then the Setting returned is the first one for
** which zName is a prefix of the Setting name.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
/*
** SETTING: web-browser      width=30 sensitive
** A shell command used to launch your preferred
** web browser when given a URL as an argument.
** Defaults to "start" on windows, "open" on Mac,
** and "firefox" on Unix.
*/
/*
** SETTING: wiki-classes     width=40 block-text
** Defines classes for wiki pages that are recognized by the list
** of available wiki pages (displayed by /wcontent web page).
**
** Each line defines a single rule for a class using a triplet:
** Visibility   Label   Pattern
**
** Visibility is one of the following letters:
**
**     s  show on load, may be toggled afterwards
**     h  hide on load, may be toggled afterwards
**     d  hide permanently, checkbox disabled
**     x  exclude completely, no control in the submenu
**
** Label is a spaces-free name of the class shown in the submenu.
** This very same string is used as a value for HTML's class=""
** atributes on the corresponding rows (thus beware of mangling).
**
** Pattern is a GLOB pattern against which wiki page names are
** matched to distinguish a class. Case-sensitive pattern matching
** is performed if the Visibility is specified by a lowercase letter,
** otherwise pattern matching is case-insensitive (in which case
** the syntax of SQLite's LIKE operator applies).
** Pattern that consists of just a single * is a special case for
** catching all wiki pages that do not fall into any other class
** (this fallback does not depend on the case of the Visibility letter).
**
** If several consequtive lines share the same Label then this defines
** a single class that spans accross several patterns. In that case
** all Visibilities must also be equal (modulus upper/lower cases).
**
** Patterns are matched in the order of their appearance in the list.
** If a repository manages thousands of wiki pages and the /wcontent
** page is requested very frequently then server's CPU load may become
** a concern. In that case an administrator is advised to rearrange
** classes in the list in such a way that more patterns are matched
** earlier. In all cases it is advised to keep a special "catch-all"
** class in the bottom of the list.
*/

/*
** Look up a control setting by its name.  Return a pointer to the Setting
** object, or NULL if there is no such setting.
**
** If allowPrefix is true, then the Setting returned is the first one for
** which zName is a prefix of the Setting name.
Added src/fossil.page.wcontent.js.
































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* This script implements interactivity of checkboxes that
 * toggle visibilitiy of user-defined classes of wikipage.
 *
 * For the sake of compatibility with ascetic browsers the code tries
 * to avoid modern API and ECMAScript constructs. This makes it less
 * readable and may be reconsidered in the future.
*/
window.addEventListener( 'load', function() {

var tbody = document.querySelector(
            "body.wiki div.content table.sortable > tbody");
var prc = document.getElementById("page-reload-canary");
if( !tbody || !prc ) return;

var reloading = prc.checked;
// console.log("Reloading:",reloading);

var onChange = function(event){
  var display = event.target.checked ? "" : "none";
  var rows    = event.target.matchingRows;
  for(var i=0; i<rows.length; i++)
    rows[i].style.display = display;
}
var checkboxes = [];
document.querySelectorAll(
  "body.wiki .submenu > label.submenuckbox > input")
  .forEach(function(cbx){ checkboxes.push(cbx); });

for(var j=0; j<checkboxes.length; j++){
  var cbx = checkboxes[j];
  var ctrl = cbx.getAttribute("data-ctrl").toString();
  var cname = cbx.parentElement.innerText.toString();
  var hidden = ( ctrl == 'h' || ctrl == 'd' );
  if( reloading )
    hidden = !cbx.checked;
  else
    cbx.checked = !hidden;
  cbx.matchingRows = [];
  tbody.querySelectorAll("tr."+cname).forEach(function (tr){
    tr.style.display = ( hidden ? "none" : "" );
    cbx.matchingRows.push(tr);
  });
  cbx.addEventListener("change", onChange ); 
  // console.log( cbx.matchingRows.length, cname, ctrl );
}

prc.checked = true;
}); // window.addEventListener( 'load' ...
Changes to src/main.mk.
227
228
229
230
231
232
233

234
235
236
237
238
239
240
  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.info-diff.js \
  $(SRCDIR)/fossil.numbered-lines.js \
  $(SRCDIR)/fossil.page.fileedit.js \
  $(SRCDIR)/fossil.page.forumpost.js \
  $(SRCDIR)/fossil.page.pikchrshow.js \

  $(SRCDIR)/fossil.page.whistory.js \
  $(SRCDIR)/fossil.page.wikiedit.js \
  $(SRCDIR)/fossil.pikchr.js \
  $(SRCDIR)/fossil.popupwidget.js \
  $(SRCDIR)/fossil.storage.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/fossil.wikiedit-wysiwyg.js \







>







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.info-diff.js \
  $(SRCDIR)/fossil.numbered-lines.js \
  $(SRCDIR)/fossil.page.fileedit.js \
  $(SRCDIR)/fossil.page.forumpost.js \
  $(SRCDIR)/fossil.page.pikchrshow.js \
  $(SRCDIR)/fossil.page.wcontent.js \
  $(SRCDIR)/fossil.page.whistory.js \
  $(SRCDIR)/fossil.page.wikiedit.js \
  $(SRCDIR)/fossil.pikchr.js \
  $(SRCDIR)/fossil.popupwidget.js \
  $(SRCDIR)/fossil.storage.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/fossil.wikiedit-wysiwyg.js \
Changes to src/wiki.c.
19
20
21
22
23
24
25


















26
27
28
29
30
31
32
** This file contains code to do formatting of wiki text.
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include "wiki.h"



















/*
** Return true if the input string is a well-formed wiki page name.
**
** Well-formed wiki page names do not begin or end with whitespace,
** and do not contain tabs or other control characters and do not
** contain more than a single space character in a row.  Well-formed
** names must be between 1 and 100 characters in length, inclusive.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
** This file contains code to do formatting of wiki text.
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include "wiki.h"

#if INTERFACE

/*
** WikiClass struct holds information for matching a wiki page name.
** It is constructed by load_wiki_classes() function and represents a
** single well-formed line obtained from the 'wiki-classes' setting.
*/
struct WikiClass {
  const char * zPattern; /* pattern to match against                    */
  int   isCaseIns;       /* syntax flag: 0 for GLOB, 1 for LIKE         */
  short isAltPat;        /* indicates an additional pattern for a class */

  const char * zVisblty; /* visibility flag, one of: "s" "h" "d" "x"    */
  const char * zLabel;   /* user-visible class name, same in HTML attrs */
  const char * zParam;   /* checkbox attr, like in <input name="..." /> */
};
#endif

/*
** Return true if the input string is a well-formed wiki page name.
**
** Well-formed wiki page names do not begin or end with whitespace,
** and do not contain tabs or other control characters and do not
** contain more than a single space character in a row.  Well-formed
** names must be between 1 and 100 characters in length, inclusive.
437
438
439
440
441
442
443




















































































































444
445
446
447
448
449
450
    case WIKITYPE_CHECKIN: return "checkin";
    case WIKITYPE_BRANCH: return "branch";
    case WIKITYPE_TAG: return "tag";
    case WIKITYPE_NORMAL:
    default: return "normal";
  }
}





















































































































/*
** Add an appropriate style_header() for either the /wiki or /wikiedit page
** for zPageName.  zExtra is an empty string for /wiki but has the text
** "Edit: " for /wikiedit.
**
** If the page is /wiki and the page is one of the special times (check-in,







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
    case WIKITYPE_CHECKIN: return "checkin";
    case WIKITYPE_BRANCH: return "branch";
    case WIKITYPE_TAG: return "tag";
    case WIKITYPE_NORMAL:
    default: return "normal";
  }
}

/*
** load_wiki_classes() loads and parses the value of the 'wiki-classes'
** setting. Retrurns either NULL or a dynamically allocated array of
** WikiClasses (thus fossil_free() for the returned value is advised).
*/
WikiClass* load_wiki_classes(
  int *nWC,  /* number of well-formed elements in the returned array    */
  Blob *wcs  /* holds a buffer for strings in the returned WikiClass'es */
){

  static const char *zPN[] = { /* no malloc() for the common cases */
     "wc0", "wc1", "wc2", "wc3", "wc4", "wc5", "wc6", "wc7",
     "wc8", "wc9","wc10","wc11","wc12","wc13","wc14","wc15",
    "wc16","wc17","wc18","wc19","wc20","wc21","wc22","wc23"
  };
  WikiClass* aWC = 0;
  int n = 0;
  char *zWCS = db_get("wiki-classes",0);
  if( zWCS && strlen(zWCS) >= 5 ){
    Blob line  = empty_blob;
    int nAlloc = 0;
    blob_set_dynamic(wcs,zWCS);
    while( blob_line(wcs,&line) > 0 ){
      Blob vis = empty_blob;
      Blob lbl = empty_blob;
      Blob pat = empty_blob;
      WikiClass * wc;
      const char *z;
      int cins = 0;
      char v;
      if( blob_token(&line,&vis) != 1 ){
        continue;
      }
      v = vis.aData[0];
      switch( v ){
        case 'D':
        case 'H':
        case 'S':
        case 'X':
          cins = 1;
          vis.aData[0] = (char)tolower(v);
        case 'd':
        case 'h':
        case 's':
        case 'x':
          break;
        default:
          v = 0;
      }
      if( v == 0 ) continue;
      if( blob_token(&line,&lbl) <= 0 ){
        continue;
      }
      blob_tail(&line,&pat);
      /* blob_to_lf_only(&pre); <-- this is redundant, isn't it ? */
      blob_trim(&pat);
      z = blob_terminate(&pat);
      while(fossil_isspace(z[0])) z++;
      if( z[0] == 0 ){
        continue;
      }
      if( n >= nAlloc ){
        nAlloc += count(zPN);
        aWC = (WikiClass*)( aWC ?
             fossil_realloc(aWC,sizeof(WikiClass)*nAlloc)  :
             fossil_malloc_zero(sizeof(WikiClass)*nAlloc) );
      }
      wc = aWC + n;
      wc->zPattern  = z;
      wc->isCaseIns = cins;
      wc->zVisblty  = blob_terminate(&vis);
      wc->zLabel    = blob_terminate(&lbl);
      wc->zParam    = ( n < count(zPN) ? zPN[n] : mprintf("wc%d",n) );
      if( n > 0 && strcmp( wc->zLabel, wc[-1].zLabel ) == 0 ){
        if( wc->zVisblty[0] != wc[-1].zVisblty[0] ||
            strcmp( wc->zPattern, wc[-1].zPattern ) == 0 ){
          continue;
        }
        wc->isAltPat = 1;
      }else{
        wc->isAltPat = 0;
      }
      n++;
    }
  }
  if( nWC ) *nWC = n;
  return aWC;
}
/*
** Find and return a WikiClass that matches a name of a wiki page.
** Returns 0 if "fallback" pattern was not provisioned.
*/
const WikiClass* resolve_wiki_class(
  const char *zName,    /* name of a wiki page that should be classified */
  const WikiClass *aWC, /* pointer to the array of WikiClass'es          */
  int nWC               /* number of elements in the above array         */
){
  const WikiClass* fallback = 0;
  if( aWC && zName ){
    int i;
    for( i=0; i<nWC; i++ ){
      const WikiClass* wc = aWC + i;
      if( wc->zPattern[0] == '*' && wc->zPattern[1] == 0 ){
        if( !fallback ) fallback = wc;
      }else if( wc->isCaseIns ){
        if( sqlite3_strlike( wc->zPattern, zName, 0 ) == 0 )
          return wc;
      }else{
        if( sqlite3_strglob( wc->zPattern, zName ) == 0 )
          return wc;
      }
    }
  }
  return fallback;
}

/*
** Add an appropriate style_header() for either the /wiki or /wikiedit page
** for zPageName.  zExtra is an empty string for /wiki but has the text
** "Edit: " for /wikiedit.
**
** If the page is /wiki and the page is one of the special times (check-in,
1771
1772
1773
1774
1775
1776
1777



1778
1779
1780
1781
1782
1783
1784
1785
1786
1787







1788
1789

1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809



1810

1811
1812
1813
1814
1815

1816
1817





1818
1819
1820





1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835

1836
1837


1838
1839
1840
1841
1842
1843
1844
** List all available wiki pages with date created and last modified.
*/
void wcontent_page(void){
  Stmt q;
  double rNow;
  int showAll = P("all")!=0;
  int showRid = P("showid")!=0;




  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  style_set_current_feature("wiki");
  style_header("Available Wiki Pages");
  if( showAll ){
    style_submenu_element("Active", "%R/wcontent");
  }else{
    style_submenu_element("All", "%R/wcontent?all=1");
  }







  wiki_standard_submenu(W_ALL_BUT(W_LIST));
  db_prepare(&q, listAllWikiPages/*works-like:""*/);

  @ <div class="brlist">
  @ <table class='sortable' data-column-types='tKN' data-init-sort='1'>
  @ <thead><tr>
  @ <th>Name</th>
  @ <th>Last Change</th>
  @ <th>Versions</th>
  if( showRid ){
    @ <th>RID</th>
  }
  @ </tr></thead><tbody>
  rNow = db_double(0.0, "SELECT julianday('now')");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zWName = db_column_text(&q, 0);
    const char *zSort = db_column_text(&q, 1);
    int wrid = db_column_int(&q, 2);
    double rWmtime = db_column_double(&q, 3);
    sqlite3_int64 iMtime = (sqlite3_int64)(rWmtime*86400.0);
    char *zAge;
    int wcnt = db_column_int(&q, 4);
    char *zWDisplayName;





    if( sqlite3_strglob("checkin/*", zWName)==0 ){
      zWDisplayName = mprintf("%.25s...", zWName);
    }else{
      zWDisplayName = mprintf("%s", zWName);
    }

    if( wrid==0 ){
      if( !showAll ) continue;





      @ <tr><td data-sortkey="%h(zSort)">\
      @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td>
    }else{





      @ <tr><td data-sortkey="%h(zSort)">\
      @ %z(href("%R/wiki?name=%T&p",zWName))%h(zWDisplayName)</a></td>
    }
    zAge = human_readable_age(rNow - rWmtime);
    @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td>
    fossil_free(zAge);
    @ <td>%z(href("%R/whistory?name=%T",zWName))%d(wcnt)</a></td>
    if( showRid ){
      @ <td>%d(wrid)</td>
    }
    @ </tr>
    fossil_free(zWDisplayName);
  }
  @ </tbody></table></div>
  db_finalize(&q);

  style_table_sorter();
  style_finish_page();


}

/*
** WEBPAGE: wfind
**
** URL: /wfind?title=TITLE
** List all wiki pages whose titles contain the search text







>
>
>










>
>
>
>
>
>
>


>




















>
>
>
|
>
|


|

>


>
>
>
>
>
|


>
>
>
>
>
|














>


>
>







1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
** List all available wiki pages with date created and last modified.
*/
void wcontent_page(void){
  Stmt q;
  double rNow;
  int showAll = P("all")!=0;
  int showRid = P("showid")!=0;
  Blob wcs = empty_blob;
  int i, nWC = 0;
  WikiClass* aWC;

  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  style_set_current_feature("wiki");
  style_header("Available Wiki Pages");
  if( showAll ){
    style_submenu_element("Active", "%R/wcontent");
  }else{
    style_submenu_element("All", "%R/wcontent?all=1");
  }
  aWC = load_wiki_classes(&nWC,&wcs);
  for( i=0; i<nWC; i++ ){
    const WikiClass * c = aWC + i;
    if( c->isAltPat || c->zVisblty[0] == 'x' ) continue;
    style_submenu_checkbox( c->zParam, c->zLabel,
      c->zVisblty[0]=='d' ? STYLE_DISABLED : STYLE_NORMAL, c->zVisblty);
  }
  wiki_standard_submenu(W_ALL_BUT(W_LIST));
  db_prepare(&q, listAllWikiPages/*works-like:""*/);
  @ <input hidden="hidden" id="page-reload-canary" type="checkbox"/>
  @ <div class="brlist">
  @ <table class='sortable' data-column-types='tKN' data-init-sort='1'>
  @ <thead><tr>
  @ <th>Name</th>
  @ <th>Last Change</th>
  @ <th>Versions</th>
  if( showRid ){
    @ <th>RID</th>
  }
  @ </tr></thead><tbody>
  rNow = db_double(0.0, "SELECT julianday('now')");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zWName = db_column_text(&q, 0);
    const char *zSort = db_column_text(&q, 1);
    int wrid = db_column_int(&q, 2);
    double rWmtime = db_column_double(&q, 3);
    sqlite3_int64 iMtime = (sqlite3_int64)(rWmtime*86400.0);
    char *zAge;
    int wcnt = db_column_int(&q, 4);
    char *zWDisplayName;
    const WikiClass * wc = resolve_wiki_class(zWName,aWC,nWC);
    if( wc && (wc->zVisblty[0] == 'x' || wc->zVisblty[0] == 'd') ){
      continue;
    }

    if( sqlite3_strglob("checkin/*", zWName)==0 ){ /* --?--> strncmp() */
      zWDisplayName = mprintf("%.25s...", zWName);
    }else{
      zWDisplayName = mprintf("%s", zWName); /* --?--> fossil_strdup() */
    }

    if( wrid==0 ){
      if( !showAll ) continue;
      if(wc){
        @ <tr class="%h(wc->zLabel)">
      }else{
        @ <tr>
      }
      @ <td data-sortkey="%h(zSort)">\
      @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td>
    }else{
      if(wc){
        @ <tr class="%h(wc->zLabel)">
      }else{
        @ <tr>
      }
      @ <td data-sortkey="%h(zSort)">\
      @ %z(href("%R/wiki?name=%T&p",zWName))%h(zWDisplayName)</a></td>
    }
    zAge = human_readable_age(rNow - rWmtime);
    @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td>
    fossil_free(zAge);
    @ <td>%z(href("%R/whistory?name=%T",zWName))%d(wcnt)</a></td>
    if( showRid ){
      @ <td>%d(wrid)</td>
    }
    @ </tr>
    fossil_free(zWDisplayName);
  }
  @ </tbody></table></div>
  db_finalize(&q);
  builtin_request_js("fossil.page.wcontent.js");
  style_table_sorter();
  style_finish_page();
  if(aWC) fossil_free(aWC);
  blob_reset(&wcs); /* FIXME: it's an analog of fossil_free(), isn't it? */
}

/*
** WEBPAGE: wfind
**
** URL: /wfind?title=TITLE
** List all wiki pages whose titles contain the search text
Changes to www/changes.wiki.
1
2









3
4
5
6
7
8
9
<title>Change Log</title>










<a name='v2_15'></a>
<h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)</h2>
  *  <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server.  <b>Upgrading to
     the patch is recommended.</b><p>
  *  The [./defcsp.md|default CSP] has been relaxed slightly to allow
     images to be loaded from any URL.  All other resources are still
     locked down by default.


>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<title>Change Log</title>

<a name='v2_16'></a>
<h2>Changes for Version 2.16 (pending)</h2>
  *  [/wcontent|Listing] of the available wiki pages
     [/timeline?r=wcontent-subsets|gained] the ability to
     [./javascript.md#wcontent|interactively] adjust a subset of
     wiki pages that are shown; this is based on the
     classification of wiki page names according to the
     [/help?cmd=wiki-classes|configurable] glob patterns.

<a name='v2_15'></a>
<h2>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)</h2>
  *  <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server.  <b>Upgrading to
     the patch is recommended.</b><p>
  *  The [./defcsp.md|default CSP] has been relaxed slightly to allow
     images to be loaded from any URL.  All other resources are still
     locked down by default.
Changes to www/javascript.md.
568
569
570
571
572
573
574









575
576
577
578
579
580
581

       SELECT xfrom, xmsg FROM chat WHERE msgid > 1234;

…would pull the messages submitted since the last poll. Making the
gateway bidirectional should be possible as well, as long as it properly
uses SQLite transactions.










----

## <a id="future"></a>Future Plans for JavaScript in Fossil

As of mid-2020, the informal provisional plan is to increase Fossil
UI's use of JavaScript considerably compared to its historically minimal
uses. To that end, a framework of Fossil-centric APIs is being developed







>
>
>
>
>
>
>
>
>







568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590

       SELECT xfrom, xmsg FROM chat WHERE msgid > 1234;

…would pull the messages submitted since the last poll. Making the
gateway bidirectional should be possible as well, as long as it properly
uses SQLite transactions.

### <a id="wcontent"></a>Wiki content listing

[Since](/timeline?r=wcontent-subsets) version 2.16 it is possible to
add [configurable](/help?cmd=wiki-classes) checkbox controls to the
submenu of [available wiki pages](/wcontent) for the interactive
adjustment of a subset of wiki pages that are shown.
Client-side script is used to toggle visibility of the corresponding
rows according to the state of these checkboxes.

----

## <a id="future"></a>Future Plans for JavaScript in Fossil

As of mid-2020, the informal provisional plan is to increase Fossil
UI's use of JavaScript considerably compared to its historically minimal
uses. To that end, a framework of Fossil-centric APIs is being developed