Add support for recipe descriptions

This commit is contained in:
Jeremy Ruston 2024-01-23 14:29:50 +00:00
parent 138c7f2665
commit f6d6478944
6 changed files with 76 additions and 48 deletions

View File

@ -50,10 +50,12 @@ title: MultiWikiServer Administration
\procedure bagPill(element-tag:"span",is-topmost:"no")
\whitespace trim
<$genesis $type=<<element-tag>> class={{{ mws-bag-pill [<is-topmost>match[yes]then[mws-bag-pill-topmost]] +[join[ ]] }}}>
<$image source=`/wiki/${ [{!!bag-name}encodeuricomponent[]] }$/bags/${ [{!!bag-name}encodeuricomponent[]] }$/tiddlers/%24%3A%2Ffavicon.ico` class="mws-favicon-small"/>
<span class="mws-bag-pill-label">
<$text text={{!!bag-name}}/>
</span>
<a class="mws-bag-pill-link" href=`/wiki/${ [{!!bag-name}encodeuricomponent[]] }$/bags/${ [{!!bag-name}encodeuricomponent[]] }$` rel="noopener noreferrer" target="_blank">
<$image source=`/wiki/${ [{!!bag-name}encodeuricomponent[]] }$/bags/${ [{!!bag-name}encodeuricomponent[]] }$/tiddlers/%24%3A%2Ffavicon.ico` class="mws-favicon-small"/>
<span class="mws-bag-pill-label">
<$text text={{!!bag-name}}/>
</span>
</a>
</$genesis>
\end
@ -62,24 +64,25 @@ title: MultiWikiServer Administration
\whitespace trim
<a class="mws-wiki-card" href=`/wiki/${ [{!!recipe-name}encodeuricomponent[]] }$` rel="noopener noreferrer" target="_blank">
<div class="mws-wiki-card-image">
<img src=`/wiki/${ [{!!recipe-name}encodeuricomponent[]] }$/recipes/${ [{!!recipe-name}encodeuricomponent[]] }$/tiddlers/%24%3A%2Ffavicon.ico` class="mws-favicon"/>
<$image source=`/wiki/${ [{!!recipe-name}encodeuricomponent[]] }$/recipes/${ [{!!recipe-name}encodeuricomponent[]] }$/tiddlers/%24%3A%2Ffavicon.ico` class="mws-favicon"/>
</div>
<div class="mws-wiki-card-content">
<div class="mws-wiki-card-header">
<$text text={{!!recipe-name}}/>
</div>
<div class="mws-wiki-card-meta">
<ol class="mws-horizontal-list">
<$list filter="[list<currentTiddler>]" counter="counter">
<$transclude $variable="bagPill" is-topmost={{{ [<counter-last>match[yes]] }}} element-tag="li"/>
</$list>
</ol>
<%if [list<currentTiddler>] %>
<ol class="mws-horizontal-list">
<$list filter="[list<currentTiddler>]" counter="counter">
<$transclude $variable="bagPill" is-topmost={{{ [<counter-last>match[yes]] }}} element-tag="li"/>
</$list>
</ol>
<%else%>
(no bags defined)
<%endif%>
</div>
<div class="mws-wiki-card-description">
DDDDDD
</div>
<div class="mws-wiki-card-extra">
Additional Details
<$transclude $tiddler=<<currentTiddler>> $mode="inline"/>
</div>
</div>
</a>
@ -91,7 +94,7 @@ title: MultiWikiServer Administration
These are the wikis available on this server. Click on a wiki to visit it in a new browser tab.
</p>
<ul class="mws-vertical-list">
<$list filter="[prefix[$:/state/multiwikiserver/recipes/]]">
<$list filter="[prefix[_multiwikiserver/recipes/]]">
<li>
<<wikiCard>>
</li>
@ -105,7 +108,7 @@ title: MultiWikiServer Administration
</div>
<h1>Bags</h1>
<ul class="mws-vertical-list">
<$list filter="[prefix[$:/state/multiwikiserver/bags/]]">
<$list filter="[prefix[_multiwikiserver/bags/]]">
<li>
<<bagPill>>
</li>

View File

@ -53,10 +53,6 @@ Styles specific to the full screen layout
}
.mws-wiki-card-extra {
}
.mws-vertical-list {
list-style: none;
padding: 0;
@ -79,6 +75,11 @@ Styles specific to the full screen layout
padding: 0 0.25em;
}
.mws-bag-pill:hover {
background: <<colour foreground>>;
foreground: <<colour background>>;
}
.mws-bag-pill-topmost {
background: <<colour very-muted-foreground>>;
}
@ -87,17 +88,23 @@ Styles specific to the full screen layout
margin-left: 0.5em;
}
.mws-bag-pill-link {
text-decoration: none;
color: currentcolor;
}
.mws-favicon.tc-image-loading, .mws-favicon-small.tc-image-loading,
.mws-favicon.tc-image-error, .mws-favicon-small.tc-image-error {
visibility: hidden;
}
.mws-favicon {
max-width: 4em;
width: 4em;
max-height: 4em;
}
.mws-favicon-small {
vertical-align: text-bottom;
max-width: 1em;
width: 1em;
max-height: 1em;
}

View File

@ -45,10 +45,10 @@ exports.startup = function() {
$tw.sqlTiddlerStore.createBag("bag-alpha");
$tw.sqlTiddlerStore.createBag("bag-beta");
$tw.sqlTiddlerStore.createBag("bag-gamma");
$tw.sqlTiddlerStore.createRecipe("recipe-rho",["bag-alpha","bag-beta"]);
$tw.sqlTiddlerStore.createRecipe("recipe-sigma",["bag-alpha","bag-gamma"]);
$tw.sqlTiddlerStore.createRecipe("recipe-tau",["bag-alpha"]);
$tw.sqlTiddlerStore.createRecipe("recipe-upsilon",["bag-alpha","bag-gamma","bag-beta"]);
$tw.sqlTiddlerStore.createRecipe("recipe-rho",["bag-alpha","bag-beta"],"First wiki");
$tw.sqlTiddlerStore.createRecipe("recipe-sigma",["bag-alpha","bag-gamma"],"Second Wiki");
$tw.sqlTiddlerStore.createRecipe("recipe-tau",["bag-alpha"],"Third Wiki");
$tw.sqlTiddlerStore.createRecipe("recipe-upsilon",["bag-alpha","bag-gamma","bag-beta"],"Fourth Wiki");
// Save tiddlers
$tw.sqlTiddlerStore.saveBagTiddler({title: "$:/SiteTitle",text: "Bag Alpha"},"bag-alpha");
$tw.sqlTiddlerStore.saveBagTiddler({title: "$:/SiteTitle",text: "Bag Beta"},"bag-beta");

View File

@ -40,10 +40,14 @@ exports.handler = function(request,response,state) {
// Require the recipe names to match
if(recipe_name === recipe_name_2) {
var result = $tw.sqlTiddlerStore.saveRecipeTiddler(fields,recipe_name);
response.writeHead(204, "OK",{
Etag: "\"" + result.bag_name + "/" + encodeURIComponent(title) + "/" + result.tiddler_id + ":\"",
"Content-Type": "text/plain"
});
if(result) {
response.writeHead(204, "OK",{
Etag: "\"" + result.bag_name + "/" + encodeURIComponent(title) + "/" + result.tiddler_id + ":\"",
"Content-Type": "text/plain"
});
} else {
response.writeHead(400);
}
response.end();
return;
}

View File

@ -64,7 +64,8 @@ SqlTiddlerDatabase.prototype.createTables = function() {
-- Recipes have names...
CREATE TABLE IF NOT EXISTS recipes (
recipe_id INTEGER PRIMARY KEY AUTOINCREMENT,
recipe_name TEXT UNIQUE
recipe_name TEXT UNIQUE,
description TEXT
)
`,`
-- ...and recipes also have an ordered list of bags
@ -139,23 +140,27 @@ SqlTiddlerDatabase.prototype.createBag = function(bagname) {
});
};
/*
Returns array of {recipe_name:,description:}
*/
SqlTiddlerDatabase.prototype.listRecipes = function() {
const rows = this.runStatementGetAll(`
SELECT recipe_name
SELECT recipe_name, description
FROM recipes
ORDER BY recipe_name
`);
return rows;
};
SqlTiddlerDatabase.prototype.createRecipe = function(recipename,bagnames) {
SqlTiddlerDatabase.prototype.createRecipe = function(recipename,bagnames,description) {
// Run the queries
this.runStatement(`
-- Create the entry in the recipes table if required
INSERT OR IGNORE INTO recipes (recipe_name)
VALUES ($recipe_name)
INSERT OR IGNORE INTO recipes (recipe_name, description)
VALUES ($recipe_name, $description)
`,{
recipe_name: recipename
recipe_name: recipename,
description: description
});
this.runStatement(`
-- Delete existing recipe_bags entries for this recipe
@ -219,7 +224,7 @@ SqlTiddlerDatabase.prototype.saveBagTiddler = function(tiddlerFields,bagname) {
};
/*
Returns {tiddler_id:,bag_name:}
Returns {tiddler_id:,bag_name:} or null if the recipe is empty
*/
SqlTiddlerDatabase.prototype.saveRecipeTiddler = function(tiddlerFields,recipename) {
// Find the topmost bag in the recipe
@ -241,6 +246,9 @@ SqlTiddlerDatabase.prototype.saveRecipeTiddler = function(tiddlerFields,recipena
`,{
recipe_name: recipename
});
if(!row) {
return null;
}
// Save the tiddler to the topmost bag
var info = this.saveBagTiddler(tiddlerFields,row.bag_name);
return {

View File

@ -23,7 +23,7 @@ adminWiki - reference to $tw.Wiki object into which entity state tiddlers should
function SqlTiddlerStore(options) {
options = options || {};
this.adminWiki = options.adminWiki || $tw.wiki;
this.entityStateTiddlerPrefix = "$:/state/multiwikiserver/";
this.entityStateTiddlerPrefix = "_multiwikiserver/";
// Create the database
this.databasePath = options.databasePath || ":memory:";
var SqlTiddlerDatabase = require("$:/plugins/tiddlywiki/multiwikiserver/sql-tiddler-database.js").SqlTiddlerDatabase;
@ -57,7 +57,7 @@ SqlTiddlerStore.prototype.updateAdminWiki = function() {
this.saveEntityStateTiddler({
title: "recipes/" + recipeInfo.recipe_name,
"recipe-name": recipeInfo.recipe_name,
text: "",
text: recipeInfo.description,
list: $tw.utils.stringifyList(this.getRecipeBags(recipeInfo.recipe_name).map(bag_name => {
return this.entityStateTiddlerPrefix + "bags/" + bag_name;
}))
@ -110,12 +110,14 @@ SqlTiddlerStore.prototype.listRecipes = function() {
return this.sqlTiddlerDatabase.listRecipes();
};
SqlTiddlerStore.prototype.createRecipe = function(recipename,bagnames) {
this.sqlTiddlerDatabase.createRecipe(recipename,bagnames);
SqlTiddlerStore.prototype.createRecipe = function(recipename,bagnames,description) {
bagnames = bagnames || [];
description = description || "";
this.sqlTiddlerDatabase.createRecipe(recipename,bagnames,description);
this.saveEntityStateTiddler({
title: "recipes/" + recipename,
"recipe-name": recipename,
text: "",
text: description,
list: $tw.utils.stringifyList(bagnames.map(bag_name => {
return this.entityStateTiddlerPrefix + "bags/" + bag_name;
}))
@ -162,12 +164,16 @@ Returns {bag_name:, tiddler: {fields}, tiddler_id:}
*/
SqlTiddlerStore.prototype.getRecipeTiddler = function(title,recipename) {
var tiddlerInfo = this.sqlTiddlerDatabase.getRecipeTiddler(title,recipename);
return Object.assign(
{},
tiddlerInfo,
{
tiddler: this.processOutgoingTiddler(tiddlerInfo.tiddler,tiddlerInfo.tiddler_id,tiddlerInfo.bag_name,recipename)
});
if(tiddlerInfo) {
return Object.assign(
{},
tiddlerInfo,
{
tiddler: this.processOutgoingTiddler(tiddlerInfo.tiddler,tiddlerInfo.tiddler_id,tiddlerInfo.bag_name,recipename)
});
} else {
return null;
}
};
/*