From 9ba4556250d62d811426f1c389992b78f49b5d43 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 14 Apr 2024 18:41:34 +0100 Subject: [PATCH] Add support for the plugin library We create a system bag to contain each plugin/theme/language. It seems wasteful because it results in lots of bags, but the semantics are exactly right and so it seems like the right approach --- .../modules/routes/handlers/get-index.js | 3 +- .../multiwikiserver/modules/startup.js | 96 +++++++++++++++++-- .../modules/store/sql-tiddler-store.js | 41 +++++--- .../multiwikiserver/templates/get-index.tid | 27 +++++- 4 files changed, 141 insertions(+), 26 deletions(-) diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-index.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-index.js index 21eced396..8aca75fa1 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-index.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-index.js @@ -3,7 +3,7 @@ title: $:/plugins/tiddlywiki/multiwikiserver/routes/handlers/get-index.js type: application/javascript module-type: mws-route -GET / +GET /?show_system=true \*/ (function() { @@ -31,6 +31,7 @@ exports.handler = function(request,response,state) { // Render the html var html = $tw.mws.store.adminWiki.renderTiddler("text/plain","$:/plugins/tiddlywiki/multiwikiserver/templates/page",{ variables: { + "show-system": state.queryParameters.show_system || "off", "page-content": "$:/plugins/tiddlywiki/multiwikiserver/templates/get-index", "bag-list": JSON.stringify(bagList), "recipe-list": JSON.stringify(recipeList) diff --git a/plugins/tiddlywiki/multiwikiserver/modules/startup.js b/plugins/tiddlywiki/multiwikiserver/modules/startup.js index 8a9c97921..9b8e63d36 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/startup.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/startup.js @@ -46,30 +46,112 @@ function setupStore() { } function loadStore(store) { - const path = require("path"); + const path = require("path"), + fs = require("fs"); // Performance timing console.time("mws-initial-load"); + // Copy plugins + var makePluginBagName = function(type,publisher,name) { + return "$:/" + type + "/" + (publisher ? publisher + "/" : "") + name; + }, + savePlugin = function(pluginFields,type,publisher,name) { + const bagName = makePluginBagName(type,publisher,name); + const result = store.createBag(bagName,pluginFields.description || "(no description)",{allowPrivilegedCharacters: true}); + store.saveBagTiddler(pluginFields,bagName); + }, + collectPlugins = function(folder,type,publisher) { + var pluginFolders = $tw.utils.getSubdirectories(folder) || []; + for(var p=0; p.,\/\\\?]+$/g.test(name))) { - return "Invalid character(s)"; + if(allowPrivilegedCharacters) { + if(!(/^[^\s\u00A0\x00-\x1F\x7F`!@#%^&*()+={}\[\];\'\"<>,\\\?]+$/g.test(name))) { + return "Invalid character(s)"; + } + } else { + if(!(/^[^\s\u00A0\x00-\x1F\x7F`!@#$%^&*()+={}\[\];:\'\"<>.,\/\\\?]+$/g.test(name))) { + return "Invalid character(s)"; + } } return null; }; @@ -92,14 +98,14 @@ SqlTiddlerStore.prototype.validateItemName = function(name) { /* Returns null if the argument is an array of valid bag/recipe names, or a string error message if not */ -SqlTiddlerStore.prototype.validateItemNames = function(names) { +SqlTiddlerStore.prototype.validateItemNames = function(names,allowPrivilegedCharacters) { if(!$tw.utils.isArray(names)) { return "Not a valid array"; } var errors = []; for(const name of names) { - const result = this.validateItemName(name); - if(result) { + const result = this.validateItemName(name,allowPrivilegedCharacters); + if(result && errors.indexOf(result) === -1) { errors.push(result); } } @@ -184,10 +190,16 @@ SqlTiddlerStore.prototype.listBags = function() { return this.sqlTiddlerDatabase.listBags(); }; -SqlTiddlerStore.prototype.createBag = function(bag_name,description) { +/* +Options include: + +allowPrivilegedCharacters - allows "$", ":" and "/" to appear in recipe name +*/ +SqlTiddlerStore.prototype.createBag = function(bag_name,description,options) { + options = options || {}; var self = this; return this.sqlTiddlerDatabase.transaction(function() { - const validationBagName = self.validateItemName(bag_name); + const validationBagName = self.validateItemName(bag_name,options.allowPrivilegedCharacters); if(validationBagName) { return {message: validationBagName}; } @@ -203,18 +215,19 @@ SqlTiddlerStore.prototype.listRecipes = function() { /* Returns null on success, or {message:} on error + +Options include: + +allowPrivilegedCharacters - allows "$", ":" and "/" to appear in recipe name */ -SqlTiddlerStore.prototype.createRecipe = function(recipe_name,bag_names,description) { +SqlTiddlerStore.prototype.createRecipe = function(recipe_name,bag_names,description,options) { bag_names = bag_names || []; description = description || ""; - const validationRecipeName = this.validateItemName(recipe_name); + options = options || {}; + const validationRecipeName = this.validateItemName(recipe_name,options.allowPrivilegedCharacters); if(validationRecipeName) { return {message: validationRecipeName}; } - const validationBagNames = this.validateItemNames(bag_names); - if(validationBagNames) { - return {message: validationBagNames}; - } if(bag_names.length === 0) { return {message: "Recipes must contain at least one bag"}; } diff --git a/plugins/tiddlywiki/multiwikiserver/templates/get-index.tid b/plugins/tiddlywiki/multiwikiserver/templates/get-index.tid index 8fd7f1743..9b64a1ea7 100644 --- a/plugins/tiddlywiki/multiwikiserver/templates/get-index.tid +++ b/plugins/tiddlywiki/multiwikiserver/templates/get-index.tid @@ -1,5 +1,11 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/get-index + +\function .hide.system() +[match[on]] +[all[]!prefix[$:/]] +\end + \procedure bagPill(element-tag:"span",is-topmost:"yes") \whitespace trim <$genesis $type=<> class={{{ mws-bag-pill [match[yes]then[mws-bag-pill-topmost]] +[join[ ]] }}}> @@ -45,9 +51,9 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/get-index
<%if true %> -
    - <$list filter="[jsonget[bag_names]]" variable="bag-name" counter="counter"> - <$transclude $variable="bagPill" is-topmost={{{ [match[yes]] }}} element-tag="li"/> +
      + <$list filter="[jsonget[bag_names]reverse[]] :filter[.hide.system[]]" variable="bag-name" counter="counter"> + <$transclude $variable="bagPill" is-topmost={{{ [match[yes]] }}} element-tag="li"/>
    <%else%> @@ -96,7 +102,7 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/get-index ! Bags
      - <$list filter="[jsonindexes[]] :sort[jsonget[bag_name]]" variable="bag-index" counter="counter"> + <$list filter="[jsonindexes[]] :filter[jsonget,[bag_name].hide.system[]] :sort[jsonget,[bag_name]]" variable="bag-index" counter="counter">
    • <$let bag-info={{{ [jsonextract] }}} @@ -131,3 +137,16 @@ title: $:/plugins/tiddlywiki/multiwikiserver/templates/get-index
+ +! Advanced + + +
+<%if [match[on]] %> + +<%else%> + +<%endif%> + + + \ No newline at end of file