This commit is contained in:
Jeremy Ruston 2024-04-29 14:25:38 +08:00 committed by GitHub
commit a767db95f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 314 additions and 0 deletions

100
core/modules/library.js Normal file
View File

@ -0,0 +1,100 @@
/*\
title: $:/core/modules/library.js
type: application/javascript
module-type: global
Library handling utilities
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
function Library(options) {
}
/*
*/
Library.prototype.getLibraryItems = function() {
if(!this.items) {
this.loadLibraryItems();
}
};
/*
*/
Library.prototype.loadLibraryItems = function() {
var self = this,
fs = require("fs"),
path = require("path");
// Collect the library items from disk
this.items = {};
var collectPlugins = function(folder) {
var pluginFolders = $tw.utils.getSubdirectories(folder) || [];
for(var p=0; p<pluginFolders.length; p++) {
if(!$tw.boot.excludeRegExp.test(pluginFolders[p])) {
var pluginFields = $tw.loadPluginFolder(path.resolve(folder,"./" + pluginFolders[p]));
if(pluginFields && pluginFields.title) {
self.items[pluginFields.title] = pluginFields;
}
}
}
},
collectPublisherPlugins = function(folder) {
var publisherFolders = $tw.utils.getSubdirectories(folder) || [];
for(var t=0; t<publisherFolders.length; t++) {
if(!$tw.boot.excludeRegExp.test(publisherFolders[t])) {
collectPlugins(path.resolve(folder,"./" + publisherFolders[t]));
}
}
};
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.pluginsPath,$tw.config.pluginsEnvVar),collectPublisherPlugins);
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.themesPath,$tw.config.themesEnvVar),collectPublisherPlugins);
$tw.utils.each($tw.getLibraryItemSearchPaths($tw.config.languagesPath,$tw.config.languagesEnvVar),collectPlugins);
// Compile the metadata
this.itemMetadata = [];
$tw.utils.each(Object.keys(self.items),function(title) {
var tiddler = self.items[title];
// Collect the skinny list data
var pluginTiddlers = $tw.utils.parseJSONSafe(tiddler.text),
readmeContent = (pluginTiddlers.tiddlers[title + "/readme"] || {}).text,
doesRequireReload = !!$tw.wiki.doesPluginInfoRequireReload(pluginTiddlers),
iconTiddler = pluginTiddlers.tiddlers[title + "/icon"] || {},
iconType = iconTiddler.type,
iconText = iconTiddler.text,
iconContent;
if(iconType && iconText) {
iconContent = $tw.utils.makeDataUri(iconText,iconType);
}
self.itemMetadata.push($tw.utils.extend({},tiddler,{
text: undefined,
readme: readmeContent,
"requires-reload": doesRequireReload ? "yes" : "no",
icon: iconContent
}));
});
};
/*
*/
Library.prototype.getMetadata = function() {
this.loadLibraryItems();
return this.itemMetadata;
};
/*
*/
Library.prototype.getItem = function(title) {
this.loadLibraryItems();
if($tw.utils.hop(this.items,title)) {
return this.items[title];
} else {
return null;
}
};
exports.Library = Library;
})();

View File

@ -0,0 +1,24 @@
/*\
title: $:/core/modules/server/routes/get-library-html.js
type: application/javascript
module-type: route
GET /library/{:title}
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "GET";
exports.path = /^\/library$/;
exports.handler = function(request,response,state) {
var text = state.wiki.getTiddlerText("$:/core/templates/library.template.html","");
state.sendResponse(200,{"Content-Type": "text/html"},text,"utf8");
};
}());

View File

@ -0,0 +1,31 @@
/*\
title: $:/core/modules/server/routes/get-library-item.js
type: application/javascript
module-type: route
GET /library
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "GET";
exports.path = /^\/library\/(.+)$/;
exports.handler = function(request,response,state) {
var title = $tw.utils.decodeURIComponentSafe($tw.utils.decodeURIComponentSafe(state.params[0])),
item = $tw.library.getItem(title);
if(item) {
var text = JSON.stringify(item);
state.sendResponse(200,{"Content-Type": "application/json"},text,"utf8");
} else {
response.writeHead(404);
response.end();
}
};
}());

View File

@ -0,0 +1,24 @@
/*\
title: $:/core/modules/server/routes/get-library.js
type: application/javascript
module-type: route
GET /library
\*/
(function() {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
exports.method = "GET";
exports.path = /^\/library\/$/;
exports.handler = function(request,response,state) {
var text = JSON.stringify($tw.library.getMetadata());
state.sendResponse(200,{"Content-Type": "application/json"},text,"utf8");
};
}());

View File

@ -124,6 +124,8 @@ exports.startup = function() {
$tw.syncadaptor = new module.adaptorClass({wiki: $tw.wiki});
}
});
// Kick off the library manager
$tw.library = new $tw.Library();
// Set up the syncer object if we've got a syncadaptor
if($tw.syncadaptor) {
$tw.syncer = new $tw.Syncer({

View File

@ -0,0 +1,127 @@
title: $:/core/templates/library.template.html
type: text/html
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="application-name" content="TiddlyWiki Plugin Library" />
<meta name="application-version" content="v0.0.0" />
<meta name="copyright" content="Copyright 2015 Jeremy Ruston" />
<link id="faviconLink" rel="shortcut icon" href="favicon.ico">
<title>Plugin Library</title>
<script>
/*
A simple HTTP-over-window.postMessage implementation of a standard TiddlyWeb-compatible server. It uses real HTTP to load the individual tiddler JSON files.
*/
// Listen for window messages
window.addEventListener("message",function listener(event){
console.log("plugin library: Received message from",event.origin);
console.log("plugin library: Message content",event.data);
switch(event.data.verb) {
case "GET":
if(event.data.url === "recipes/library/tiddlers.json") {
// Route for recipes/library/tiddlers.json
var url = "library/";
httpGet(url,function(err,responseText) {
if(err) {
event.source.postMessage({
verb: "GET-RESPONSE",
status: "404",
cookies: event.data.cookies,
url: event.data.url,
type: "text/plain",
body: "Not found"
},"*");
} else {
event.source.postMessage({
verb: "GET-RESPONSE",
status: "200",
cookies: event.data.cookies,
url: event.data.url,
type: "application/json",
body: responseText
},"*");
}
});
} else if(event.data.url.indexOf("recipes/library/tiddlers/") === 0) {
var url = "library/" + encodeURIComponent(removeSuffix(removePrefix(event.data.url,"recipes/library/tiddlers/"),".json"));
// Route for recipes/library/tiddlers/<uri-encoded-tiddler-title>.json
httpGet(url,function(err,responseText) {
if(err) {
event.source.postMessage({
verb: "GET-RESPONSE",
status: "404",
cookies: event.data.cookies,
url: event.data.url,
type: "text/plain",
body: "Not found"
},"*");
} else {
event.source.postMessage({
verb: "GET-RESPONSE",
status: "200",
cookies: event.data.cookies,
url: event.data.url,
type: "application/json",
body: responseText
},"*");
}
});
} else {
event.source.postMessage({
verb: "GET-RESPONSE",
status: "404",
cookies: event.data.cookies,
url: event.data.url,
type: "text/plain",
body: "Not found"
},"*");
}
break;
}
},false);
// Helper to remove string prefixes
function removePrefix(string,prefix) {
if(string.indexOf(prefix) === 0) {
return string.substr(prefix.length);
} else {
return string;
}
}
// Helper to remove string suffixes
function removeSuffix(string,suffix) {
if(string.indexOf(suffix) === string.length - suffix.length) {
return string.substr(0,string.length - suffix.length);
} else {
return string;
}
}
// Helper for HTTP GET
function httpGet(url,callback) {
var http = new XMLHttpRequest();
http.open("GET",url,true);
http.onreadystatechange = function() {
if(http.readyState == 4 && http.status == 200) {
callback(null,http.responseText);
}
};
http.send();
}
</script>
</head>
<body>
<h1>HelloThere</h1>
<p>This is the TiddlyWiki plugin library. It is not intended to be opened directly in the browser.</p>
<p>See <a href="https://tiddlywiki.com/" target="_blank">https://tiddlywiki.com/</a> for details of how to install plugins.</p>
</body>
</html>

View File

@ -0,0 +1,6 @@
title: $:/plugins/tiddlywiki/tiddlyweb/BuiltinLibrary
tags: $:/tags/PluginLibrary
url: /library
caption: TiddlyWiki Built-in Plugin Library
This is the TiddlyWiki Built-in Plugin Library