mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-05 01:26:18 +00:00
Create new static index route with ability to create/update bags and recipes
Also introduces a new /.system/filename route for stylesheets, scripts etc.
This commit is contained in:
parent
1c64646393
commit
6063256439
@ -32,7 +32,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node ./tiddlywiki.js ./editions/multiwikiserver --listen",
|
||||
"test": "node ./tiddlywiki.js ./editions/test --verbose --version --build index && node ./tiddlywiki ./editions/multiwikiserver/ --listen debug-level=full --mws-test-server http://127.0.0.1:8080/ --quit",
|
||||
"test": "node ./tiddlywiki.js ./editions/test --verbose --version --build index && node ./tiddlywiki ./editions/multiwikiserver/ --mws-listen debug-level=full --mws-test-server http://127.0.0.1:8080/ --quit",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
|
@ -1,19 +0,0 @@
|
||||
title: $:/plugins/multiwikiserver/AdminLayout
|
||||
tags: $:/tags/Layout
|
||||
name: MultiWikiServer
|
||||
description: Admin Layout
|
||||
icon: $:/favicon.ico
|
||||
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
|
||||
<div class="mws-admin-layout">
|
||||
<!-- The next DIV is needed for the Jasmine test runner to know that the page has loaded -->
|
||||
<div class="tc-site-title">TiddlyWiki5</div>
|
||||
{{MultiWikiServer Administration}}
|
||||
<div class="mws-admin-layout-controls">
|
||||
<$button>
|
||||
<$action-setfield $tiddler="$:/layout" text="$:/core/ui/PageTemplate"/>
|
||||
Switch to TiddlyWiki default user interface
|
||||
</$button>
|
||||
</div>
|
||||
</div>
|
@ -1,2 +0,0 @@
|
||||
title: $:/DefaultTiddlers
|
||||
text: [[MultiWikiServer Administration]]
|
@ -1,231 +0,0 @@
|
||||
title: MultiWikiServer Administration
|
||||
|
||||
\procedure createBag(name,description,errorTiddler)
|
||||
\procedure completion-createBag()
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
<$action-log
|
||||
status=<<status>>
|
||||
statusText=<<statusText>>
|
||||
error=<<error>>
|
||||
data=<<data>>
|
||||
headers=<<headers>>
|
||||
/>
|
||||
<%if [<error>match[]] %>
|
||||
<$action-setfield $tiddler=<<errorTiddler>> text=""/>
|
||||
<$action-sendmessage $message="tm-server-refresh"/>
|
||||
<%else%>
|
||||
<$action-setfield $tiddler=<<errorTiddler>> text=<<data>>/>
|
||||
<%endif%>
|
||||
\end completion-createBag
|
||||
<$action-sendmessage
|
||||
$message="tm-http-request"
|
||||
url=`/wiki/${ [<name>encodeuricomponent[]] }$/bags/${ [<name>encodeuricomponent[]] }$`
|
||||
method="PUT"
|
||||
body=`{"description":"${ [<description>] }$"}`
|
||||
oncompletion=<<completion-createBag>>
|
||||
var-errorTiddler=<<errorTiddler>>
|
||||
/>
|
||||
\end createBag
|
||||
|
||||
\procedure createBagButton(name)
|
||||
\whitespace trim
|
||||
<form class="mws-form">
|
||||
<div class="mws-form-heading">
|
||||
<$text text="Create a new bag"/>
|
||||
</div>
|
||||
<div class="mws-form-fields">
|
||||
<div class="mws-form-field">
|
||||
<label class="mws-form-field-description">
|
||||
Bag name
|
||||
</label>
|
||||
<$edit-text tiddler="$:/temp/NewBagName" tag="input" placeholder="(bag name)" class="mws-form-field-input"/>
|
||||
</div>
|
||||
<div class="mws-form-field">
|
||||
<label class="mws-form-field-description">
|
||||
Bag description
|
||||
</label>
|
||||
<$edit-text tiddler="$:/temp/NewBagDescription" tag="input" placeholder="(description)" class="mws-form-field-input"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mws-form-status">
|
||||
<%if [[$:/temp/NewBagError]get[text]else[]!match[]] %>
|
||||
<div class="mws-form-error">
|
||||
<$text text={{$:/temp/NewBagError}}/>
|
||||
</div>
|
||||
<%endif%>
|
||||
</div>
|
||||
<div class="mws-form-buttons">
|
||||
<$button class="mws-form-button">
|
||||
<$transclude
|
||||
$variable="createBag"
|
||||
name={{$:/temp/NewBagName}}
|
||||
description={{$:/temp/NewBagDescription}}
|
||||
errorTiddler="$:/temp/NewBagError"
|
||||
/>
|
||||
Create Bag
|
||||
</$button>
|
||||
</div>
|
||||
</form>
|
||||
\end createBagButton
|
||||
|
||||
\procedure createRecipe(name,bag_names,description,errorTiddler)
|
||||
\procedure completion-createRecipe()
|
||||
\import [subfilter{$:/core/config/GlobalImportFilter}]
|
||||
<%if [<error>match[]] %>
|
||||
<$action-setfield $tiddler=<<errorTiddler>> text=""/>
|
||||
<$action-sendmessage $message="tm-server-refresh"/>
|
||||
<%else%>
|
||||
<$action-setfield $tiddler=<<errorTiddler>> text=<<data>>/>
|
||||
<%endif%>
|
||||
\end completion-createRecipe
|
||||
\procedure emptyArray() []
|
||||
\function createRecipeJson()
|
||||
[<bag_names>enlist-input[]] :reduce[<accumulator>!match[]else<emptyArray>jsonset<index>,<currentTiddler>]
|
||||
\end createRecipeJson
|
||||
<$action-sendmessage
|
||||
$message="tm-http-request"
|
||||
url=`/wiki/${ [<name>encodeuricomponent[]] }$/recipes/${ [<name>encodeuricomponent[]] }$`
|
||||
method="PUT"
|
||||
body=`{"bag_names":${ [<createRecipeJson>] }$,"description":"${ [<description>] }$"}`
|
||||
oncompletion=<<completion-createRecipe>>
|
||||
var-errorTiddler=<<errorTiddler>>
|
||||
/>
|
||||
\end createRecipe
|
||||
|
||||
\procedure createRecipeButton()
|
||||
\whitespace trim
|
||||
<form class="mws-form">
|
||||
<div class="mws-form-heading">
|
||||
<$text text="Create a new recipe"/>
|
||||
</div>
|
||||
<div class="mws-form-fields">
|
||||
<div class="mws-form-field">
|
||||
<label class="mws-form-field-description">
|
||||
Recipe name
|
||||
</label>
|
||||
<$edit-text tiddler="$:/temp/NewRecipeName" tag="input" placeholder="(recipe name)" class="mws-form-field-input"/>
|
||||
</div>
|
||||
<div class="mws-form-field">
|
||||
<label class="mws-form-field-description">
|
||||
Bag names
|
||||
</label>
|
||||
<$edit-text tiddler="$:/temp/NewRecipeBagNames" tag="input" placeholder="(space separated list of bags)"/>
|
||||
</div>
|
||||
<div class="mws-form-field">
|
||||
<label class="mws-form-field-description">
|
||||
Recipe description
|
||||
</label>
|
||||
<$edit-text tiddler="$:/temp/NewRecipeDescription" tag="input" placeholder="(description)" class="mws-form-field-input"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mws-form-status">
|
||||
<%if [[$:/temp/NewRecipeError]get[text]else[]!match[]] %>
|
||||
<div class="mws-form-error">
|
||||
<$text text={{$:/temp/NewRecipeError}}/>
|
||||
</div>
|
||||
<%endif%>
|
||||
</div>
|
||||
<div class="mws-form-buttons">
|
||||
<$button class="mws-form-button">
|
||||
<$transclude
|
||||
$variable="createRecipe"
|
||||
name={{$:/temp/NewRecipeName}}
|
||||
bag_names={{$:/temp/NewRecipeBagNames}}
|
||||
description={{$:/temp/NewRecipeDescription}}
|
||||
errorTiddler="$:/temp/NewRecipeError"
|
||||
/>
|
||||
Create Recipe
|
||||
</$button>
|
||||
</div>
|
||||
</form>
|
||||
\end createRecipeButton
|
||||
|
||||
<!-- Expects currentTiddler to be the title of a bag entity state tiddler -->
|
||||
\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[ ]] }}}>
|
||||
<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"
|
||||
>
|
||||
<$image
|
||||
source="$:/plugins/multiwikiserver/images/missing-favicon.png"
|
||||
class="mws-favicon-small"
|
||||
/>
|
||||
</$image>
|
||||
<span class="mws-bag-pill-label">
|
||||
<$text text={{!!bag-name}}/>
|
||||
</span>
|
||||
</a>
|
||||
</$genesis>
|
||||
\end
|
||||
|
||||
<!-- Expects currentTiddler to be the title of a recipe entity state tiddler -->
|
||||
\procedure wikiCard()
|
||||
\whitespace trim
|
||||
<a class="mws-wiki-card" href=`/wiki/${ [{!!recipe-name}encodeuricomponent[]] }$` rel="noopener noreferrer" target="_blank">
|
||||
<div class="mws-wiki-card-image">
|
||||
<$image
|
||||
source=`/wiki/${ [{!!recipe-name}encodeuricomponent[]] }$/recipes/${ [{!!recipe-name}encodeuricomponent[]] }$/tiddlers/%24%3A%2Ffavicon.ico`
|
||||
class="mws-favicon"
|
||||
>
|
||||
<$image
|
||||
source="$:/plugins/multiwikiserver/images/missing-favicon.png"
|
||||
class="mws-favicon"
|
||||
/>
|
||||
</$image>
|
||||
</div>
|
||||
<div class="mws-wiki-card-content">
|
||||
<div class="mws-wiki-card-header">
|
||||
<$text text={{!!recipe-name}}/>
|
||||
</div>
|
||||
<div class="mws-wiki-card-meta">
|
||||
<%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">
|
||||
<$text text={{!!text}}/>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
\end
|
||||
|
||||
<div class="mws-admin-container">
|
||||
<h1>Wikis</h1>
|
||||
<p>
|
||||
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/]]">
|
||||
<li>
|
||||
<<wikiCard>>
|
||||
</li>
|
||||
</$list>
|
||||
</ul>
|
||||
<div>
|
||||
<<createRecipeButton>>
|
||||
</div>
|
||||
<div>
|
||||
Higher numbered bags take priority if a tiddler with the same title is in more than one bag
|
||||
</div>
|
||||
<h1>Bags</h1>
|
||||
<ul class="mws-vertical-list">
|
||||
<$list filter="[prefix[$:/state/MultiWikiServer/bags/]]">
|
||||
<li>
|
||||
<<bagPill>>
|
||||
<$text text={{!!text}}/>
|
||||
</li>
|
||||
</$list>
|
||||
</ul>
|
||||
<div>
|
||||
<<createBagButton>>
|
||||
</div>
|
||||
</div>
|
@ -1,10 +0,0 @@
|
||||
title: $:/plugins/multiwikiserver/SideBarSegment
|
||||
tags: $:/tags/SideBarSegment
|
||||
list-before: $:/core/ui/SideBarSegments/page-controls
|
||||
|
||||
<div class="mws-admin-sidebar">
|
||||
<$button>
|
||||
<$action-setfield $tiddler="$:/layout" text="$:/plugins/multiwikiserver/AdminLayout"/>
|
||||
Switch back to ~MultiWikiServer administration user interface
|
||||
</$button>
|
||||
</div>
|
@ -1,2 +0,0 @@
|
||||
title: $:/favicon.ico
|
||||
type: image/png
|
@ -1,2 +0,0 @@
|
||||
title: $:/layout
|
||||
text: $:/plugins/multiwikiserver/AdminLayout
|
@ -1,2 +0,0 @@
|
||||
title: $:/plugins/multiwikiserver/images/missing-favicon.png
|
||||
type: image/png
|
@ -33,7 +33,8 @@ Command.prototype.execute = function() {
|
||||
}
|
||||
// Set up server
|
||||
this.server = $tw.mws.serverManager.createServer({
|
||||
wiki: $tw.wiki
|
||||
wiki: $tw.wiki,
|
||||
variables: self.params
|
||||
});
|
||||
this.server.listen(null,null,null,{
|
||||
callback: function() {
|
||||
|
@ -74,6 +74,7 @@ TestRunner.prototype.runTests = function(callback) {
|
||||
};
|
||||
|
||||
TestRunner.prototype.runTest = function(testSpec,callback) {
|
||||
const self = this;
|
||||
console.log(`Running Server Test: ${testSpec.description}`)
|
||||
if(testSpec.method === "GET" || testSpec.method === "POST") {
|
||||
const request = this.httpLibrary.request({
|
||||
@ -84,8 +85,8 @@ TestRunner.prototype.runTest = function(testSpec,callback) {
|
||||
method: testSpec.method,
|
||||
headers: testSpec.headers
|
||||
}, function(response) {
|
||||
if (response.statusCode < 200 || response.statusCode >= 300) {
|
||||
return callback(`Request failed to ${response.url} with status code ${response.statusCode} and ${JSON.stringify(response.headers)}`);
|
||||
if (response.statusCode < 200 || response.statusCode >= 400) {
|
||||
return callback(`Request failed to ${self.urlServerParsed.toString()} with status code ${response.statusCode} and ${JSON.stringify(response.headers)}`);
|
||||
}
|
||||
response.setEncoding("utf8");
|
||||
let buffer = "";
|
||||
@ -94,7 +95,7 @@ TestRunner.prototype.runTest = function(testSpec,callback) {
|
||||
});
|
||||
response.on("end", () => {
|
||||
const jsonData = $tw.utils.parseJSONSafe(buffer,function() {return undefined;});
|
||||
const testResult = testSpec.expectedResult(jsonData,buffer);
|
||||
const testResult = testSpec.expectedResult(jsonData,buffer,response.headers);
|
||||
callback(testResult ? null : "Test failed");
|
||||
});
|
||||
});
|
||||
@ -135,6 +136,20 @@ const testSpecs = [
|
||||
expectedResult: (jsonData,data) => {
|
||||
return jsonData["imported-tiddlers"] && $tw.utils.isArray(jsonData["imported-tiddlers"]) && jsonData["imported-tiddlers"][0] === "One White Pixel";
|
||||
}
|
||||
},
|
||||
{
|
||||
description: "Create a recipe",
|
||||
method: "POST",
|
||||
path: "/recipes",
|
||||
headers: {
|
||||
"Accept": '*/*',
|
||||
"Content-Type": 'application/x-www-form-urlencoded',
|
||||
"User-Agent": 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
|
||||
},
|
||||
data: "recipe_name=Elephants3214234&bag_names=one%20two%20three&description=A%20bag%20of%20elephants",
|
||||
expectedResult: (jsonData,data,headers) => {
|
||||
return headers.location === "/";
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -316,7 +316,8 @@ Server.prototype.findMatchingRoute = function(request,state) {
|
||||
} else {
|
||||
match = potentialRoute.path.exec(pathname);
|
||||
}
|
||||
if(match && request.method === potentialRoute.method) {
|
||||
// Allow POST as a synonym for PUT because HTML doesn't allow PUT forms
|
||||
if(match && (request.method === potentialRoute.method || (request.method === "POST" && potentialRoute.method === "PUT"))) {
|
||||
state.params = [];
|
||||
for(var p=1; p<match.length; p++) {
|
||||
state.params.push(match[p]);
|
||||
@ -346,6 +347,7 @@ Server.prototype.isAuthorized = function(authorizationType,username) {
|
||||
|
||||
Server.prototype.requestHandler = function(request,response,options) {
|
||||
options = options || {};
|
||||
const queryString = require("querystring");
|
||||
// Compose the state object
|
||||
var self = this;
|
||||
var state = {};
|
||||
@ -399,7 +401,7 @@ Server.prototype.requestHandler = function(request,response,options) {
|
||||
if(route.bodyFormat === "stream" || request.method === "GET" || request.method === "HEAD") {
|
||||
// Let the route handle the request stream itself
|
||||
route.handler(request,response,state);
|
||||
} else if(route.bodyFormat === "string" || !route.bodyFormat) {
|
||||
} else if(route.bodyFormat === "string" || route.bodyFormat === "www-form-urlencoded" || !route.bodyFormat) {
|
||||
// Set the encoding for the incoming request
|
||||
request.setEncoding("utf8");
|
||||
var data = "";
|
||||
@ -407,6 +409,9 @@ Server.prototype.requestHandler = function(request,response,options) {
|
||||
data += chunk.toString();
|
||||
});
|
||||
request.on("end",function() {
|
||||
if(route.bodyFormat === "www-form-urlencoded") {
|
||||
data = queryString.parse(data);
|
||||
}
|
||||
state.data = data;
|
||||
route.handler(request,response,state);
|
||||
});
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/multiwikiserver/routes/handlers/get-system.js
|
||||
type: application/javascript
|
||||
module-type: mws-route
|
||||
|
||||
Retrieves a system file. System files are stored in configuration tiddlers with the following fields:
|
||||
|
||||
* title: "$:/plugins/tiddlywiki/multiwikiserver/system-files/" suffixed with the name of the file
|
||||
* tags: tagged $:/tags/MWS/SystemFile or $:/tags/MWS/SystemFileWikified
|
||||
* system-file-type: optionally specify the MIME type that should be returned for the file
|
||||
|
||||
GET /.system/:filename
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "GET";
|
||||
|
||||
exports.path = /^\/\.system\/(.+)$/;
|
||||
|
||||
const SYSTEM_FILE_TITLE_PREFIX = "$:/plugins/tiddlywiki/multiwikiserver/system-files/";
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
// Get the parameters
|
||||
const filename = $tw.utils.decodeURIComponentSafe(state.params[0]),
|
||||
title = SYSTEM_FILE_TITLE_PREFIX + filename,
|
||||
tiddler = $tw.wiki.getTiddler(title),
|
||||
isSystemFile = tiddler && tiddler.hasTag("$:/tags/MWS/SystemFile"),
|
||||
isSystemFileWikified = tiddler && tiddler.hasTag("$:/tags/MWS/SystemFileWikified");
|
||||
if(tiddler && (isSystemFile || isSystemFileWikified)) {
|
||||
let text = tiddler.fields.text || "";
|
||||
const type = tiddler.fields["system-file-type"] || tiddler.fields.type || "text/plain",
|
||||
encoding = ($tw.config.contentTypeInfo[type] ||{encoding: "utf8"}).encoding;
|
||||
if(isSystemFileWikified) {
|
||||
text = $tw.wiki.renderTiddler("text/plain",title);
|
||||
}
|
||||
response.writeHead(200, "OK",{
|
||||
"Content-Type": type
|
||||
});
|
||||
response.write(text,encoding);
|
||||
response.end();
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
}
|
||||
};
|
||||
|
||||
}());
|
@ -0,0 +1,50 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/multiwikiserver/routes/handlers/post-bag.js
|
||||
type: application/javascript
|
||||
module-type: mws-route
|
||||
|
||||
POST /bags
|
||||
|
||||
Parameters:
|
||||
|
||||
bag_name
|
||||
description
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "POST";
|
||||
|
||||
exports.path = /^\/bags$/;
|
||||
|
||||
exports.bodyFormat = "www-form-urlencoded";
|
||||
|
||||
exports.csrfDisable = true;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
if(state.data.bag_name) {
|
||||
const result = $tw.mws.store.createBag(state.data.bag_name,state.data.description);
|
||||
if(!result) {
|
||||
state.sendResponse(302,{
|
||||
"Content-Type": "text/plain",
|
||||
"Location": "/"
|
||||
});
|
||||
} else {
|
||||
state.sendResponse(400,{
|
||||
"Content-Type": "text/plain"
|
||||
},
|
||||
result.message,
|
||||
"utf8");
|
||||
}
|
||||
} else {
|
||||
state.sendResponse(400,{
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
}());
|
@ -0,0 +1,51 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/multiwikiserver/routes/handlers/post-recipe.js
|
||||
type: application/javascript
|
||||
module-type: mws-route
|
||||
|
||||
POST /recipes
|
||||
|
||||
Parameters:
|
||||
|
||||
recipe_name
|
||||
description
|
||||
bag_names: space separated list of bags
|
||||
|
||||
\*/
|
||||
(function() {
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.method = "POST";
|
||||
|
||||
exports.path = /^\/recipes$/;
|
||||
|
||||
exports.bodyFormat = "www-form-urlencoded";
|
||||
|
||||
exports.csrfDisable = true;
|
||||
|
||||
exports.handler = function(request,response,state) {
|
||||
if(state.data.recipe_name && state.data.bag_names) {
|
||||
const result = $tw.mws.store.createRecipe(state.data.recipe_name,$tw.utils.parseStringArray(state.data.bag_names),state.data.description);
|
||||
if(!result) {
|
||||
state.sendResponse(302,{
|
||||
"Content-Type": "text/plain",
|
||||
"Location": "/"
|
||||
});
|
||||
} else {
|
||||
state.sendResponse(400,{
|
||||
"Content-Type": "text/plain"
|
||||
},
|
||||
result.message,
|
||||
"utf8");
|
||||
}
|
||||
} else {
|
||||
state.sendResponse(400,{
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
}());
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
@ -0,0 +1,3 @@
|
||||
title: $:/plugins/tiddlywiki/multiwikiserver/system-files/missing-favicon.png
|
||||
tags: $:/tags/MWS/SystemFile
|
||||
type: image/png
|
BIN
plugins/tiddlywiki/multiwikiserver/system-files/motovun-jack.jpg
Normal file
BIN
plugins/tiddlywiki/multiwikiserver/system-files/motovun-jack.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
@ -0,0 +1,3 @@
|
||||
title: $:/plugins/tiddlywiki/multiwikiserver/system-files/motovun-jack.jpg
|
||||
tags: $:/tags/MWS/SystemFile
|
||||
type: image/jpeg
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@ -0,0 +1,3 @@
|
||||
title: $:/plugins/tiddlywiki/multiwikiserver/system-files/mws-icon.png
|
||||
tags: $:/tags/MWS/SystemFile
|
||||
type: image/png
|
@ -1,23 +1,19 @@
|
||||
title: $:/plugins/multiwikiserver/Styles
|
||||
tags: $:/tags/Stylesheet
|
||||
title: $:/plugins/tiddlywiki/multiwikiserver/system-files/styles.css
|
||||
tags: $:/tags/MWS/SystemFileWikified
|
||||
system-file-type: text/css
|
||||
|
||||
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock
|
||||
|
||||
/*
|
||||
Styles specific to the full screen layout
|
||||
*/
|
||||
/* Import TiddlyWiki theme styles */
|
||||
|
||||
.mws-admin-layout {
|
||||
{{$:/core/ui/PageStylesheet}}
|
||||
|
||||
/* MWS Styles */
|
||||
|
||||
body {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.mws-form-error {
|
||||
border: 2px solid red;
|
||||
padding: 1em;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.mws-wiki-card {
|
||||
display: flex;
|
||||
margin: 1em 0;
|
||||
@ -62,6 +58,7 @@ Styles specific to the full screen layout
|
||||
.mws-vertical-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.mws-horizontal-list {
|
@ -1,62 +1,133 @@
|
||||
title: $:/plugins/tiddlywiki/multiwikiserver/templates/get-index
|
||||
|
||||
\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[ ]] }}}>
|
||||
<a class="mws-bag-pill-link" href=`/wiki/${ [<bag-name>encodeuricomponent[]] }$/bags/${ [<bag-name>encodeuricomponent[]] }$` rel="noopener noreferrer" target="_blank">
|
||||
<img
|
||||
src=`/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
|
||||
|
||||
! Wikis Available Here
|
||||
|
||||
<ul>
|
||||
<ul class="mws-vertical-list">
|
||||
<$list filter="[<recipe-list>jsonindexes[]] :sort[<currentTiddler>jsonget[recipe_name]]" variable="recipe-index">
|
||||
<li>
|
||||
<$let
|
||||
recipe-info={{{ [<recipe-list>jsonextract<recipe-index>] }}}
|
||||
recipe-name={{{ [<recipe-info>jsonget[recipe_name]] }}}
|
||||
>
|
||||
<div
|
||||
class="mws-wiki-card"
|
||||
>
|
||||
<div class="mws-wiki-card-image">
|
||||
<img
|
||||
src=`/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">
|
||||
<a
|
||||
href=`/wiki/${ [<recipe-name>encodeuricomponent[]] }$`
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<strong>
|
||||
<$text text={{{ [<recipe-info>jsonget[recipe_name]] }}}/>
|
||||
</strong>
|
||||
: <$text text={{{ [<recipe-info>jsonget[description]] }}}/>
|
||||
</a>
|
||||
<ul>
|
||||
<$list filter="[<recipe-info>jsonget[bag_names]]" variable="bag-name">
|
||||
<li>
|
||||
<a
|
||||
href=`/wiki/${ [<bag-name>encodeuricomponent[]] }$/bags/${ [<bag-name>encodeuricomponent[]] }$`
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<$text text=<<bag-name>>/>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
<div class="mws-wiki-card-meta">
|
||||
<%if true %>
|
||||
<ol class="mws-horizontal-list">
|
||||
<$list filter="[<recipe-info>jsonget[bag_names]]" variable="bag-name" counter="counter">
|
||||
<$transclude $variable="bagPill" is-topmost={{{ [<counter-last>match[yes]] }}} element-tag="li"/>
|
||||
</$list>
|
||||
</ul>
|
||||
</ol>
|
||||
<%else%>
|
||||
(no bags defined)
|
||||
<%endif%>
|
||||
</div>
|
||||
<div class="mws-wiki-card-description">
|
||||
<$text text={{{ [<recipe-info>jsonget[description]] }}}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</$let>
|
||||
</li>
|
||||
</$list>
|
||||
</ul>
|
||||
|
||||
<form action="/recipes" method="post" class="mws-form">
|
||||
<div class="mws-form-heading">
|
||||
Create a new recipe or modify and existing one
|
||||
</div>
|
||||
<div class="mws-form-fields">
|
||||
<div class="mws-form-field">
|
||||
<label class="mws-form-field-description">
|
||||
Recipe name
|
||||
</label>
|
||||
<input name="recipe_name" type="text"/>
|
||||
</div>
|
||||
<div class="mws-form-field">
|
||||
<label class="mws-form-field-description">
|
||||
Recipe description
|
||||
</label>
|
||||
<input name="description" type="text"/>
|
||||
</div>
|
||||
<div class="mws-form-field">
|
||||
<label class="mws-form-field-description">
|
||||
Bags in recipe (space separated)
|
||||
</label>
|
||||
<input name="bag_names" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mws-form-buttons">
|
||||
<input type="submit" value="Create or Update Recipe" formmethod="post"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
! Bags
|
||||
|
||||
<ul>
|
||||
<$list filter="[<bag-list>jsonindexes[]] :sort[<currentTiddler>jsonget[bag_name]]" variable="bag-index">
|
||||
<li>
|
||||
<ul class="mws-vertical-list">
|
||||
<$list filter="[<bag-list>jsonindexes[]] :sort[<currentTiddler>jsonget[bag_name]]" variable="bag-index" counter="counter">
|
||||
<li class="mws-wiki-card">
|
||||
<$let
|
||||
bag-info={{{ [<bag-list>jsonextract<bag-index>] }}}
|
||||
bag-name={{{ [<bag-info>jsonget[bag_name]] }}}
|
||||
>
|
||||
<a
|
||||
href=`/wiki/${ [<bag-name>encodeuricomponent[]] }$/bags/${ [<bag-name>encodeuricomponent[]] }$`
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<strong>
|
||||
<$text text={{{ [<bag-info>jsonget[bag_name]] }}}/>
|
||||
</strong>
|
||||
: <$text text={{{ [<bag-info>jsonget[description]] }}}/>
|
||||
</a>
|
||||
<$transclude $variable="bagPill"/>
|
||||
<$text text={{{ [<bag-info>jsonget[description]] }}}/>
|
||||
</$let>
|
||||
</li>
|
||||
</$list>
|
||||
</ul>
|
||||
|
||||
<form action="/bags" method="post" class="mws-form">
|
||||
<div class="mws-form-heading">
|
||||
Create a new bag or modify and existing one
|
||||
</div>
|
||||
<div class="mws-form-fields">
|
||||
<div class="mws-form-field">
|
||||
<label class="mws-form-field-description">
|
||||
Bag name
|
||||
</label>
|
||||
<input name="bag_name" type="text"/>
|
||||
</div>
|
||||
<div class="mws-form-field">
|
||||
<label class="mws-form-field-description">
|
||||
Bag description
|
||||
</label>
|
||||
<input name="description" type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mws-form-buttons">
|
||||
<input type="submit" value="Create or Update Bag" formmethod="post"/>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -9,11 +9,14 @@ page-content: title of tiddler containing the main page content
|
||||
<!doctype html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
<link rel="stylesheet" href="/.system/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="pageContainer">
|
||||
`
|
||||
<$view tiddler=<<page-content>> field="text" format="htmlwikified" />
|
||||
`
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
Loading…
Reference in New Issue
Block a user