mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-06-03 23:24:07 +00:00
Update TiddlyWeb support
Lots of changes: * Make the built-in server support recipes and bags, albeit there's just one of each, called "default" * Correctly parse returned Etag to get bag of freshly PUT tiddlers * URI encoding for tiddler titles, so that tiddlers with slashes and so on work OK
This commit is contained in:
parent
7e57c422dc
commit
71ecb022ef
@ -44,7 +44,7 @@ Command.prototype.execute = function() {
|
|||||||
data += chunk.toString();
|
data += chunk.toString();
|
||||||
});
|
});
|
||||||
request.on("end",function() {
|
request.on("end",function() {
|
||||||
var prefix = "/tiddlers/";
|
var prefix = "/recipes/default/tiddlers/";
|
||||||
if(requestPath.indexOf(prefix) === 0) {
|
if(requestPath.indexOf(prefix) === 0) {
|
||||||
var title = decodeURIComponent(requestPath.substr(prefix.length)),
|
var title = decodeURIComponent(requestPath.substr(prefix.length)),
|
||||||
fields = JSON.parse(data);
|
fields = JSON.parse(data);
|
||||||
@ -65,7 +65,10 @@ Command.prototype.execute = function() {
|
|||||||
}
|
}
|
||||||
console.log("PUT tiddler",title,fields)
|
console.log("PUT tiddler",title,fields)
|
||||||
// self.commander.wiki.addTiddler(new $tw.Tiddler(JSON.parse(data),{title: title}));
|
// self.commander.wiki.addTiddler(new $tw.Tiddler(JSON.parse(data),{title: title}));
|
||||||
response.writeHead(204, "OK");
|
var changeCount = self.commander.wiki.getChangeCount(title).toString();
|
||||||
|
response.writeHead(204, "OK",{
|
||||||
|
Etag: "\"default/" + title + "/" + changeCount + ":\""
|
||||||
|
});
|
||||||
response.end();
|
response.end();
|
||||||
} else {
|
} else {
|
||||||
response.writeHead(404);
|
response.writeHead(404);
|
||||||
@ -74,10 +77,11 @@ console.log("PUT tiddler",title,fields)
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "DELETE":
|
case "DELETE":
|
||||||
var prefix = "/tiddlers/";
|
var prefix = "/bags/default/tiddlers/";
|
||||||
if(requestPath.indexOf(prefix) === 0) {
|
if(requestPath.indexOf(prefix) === 0) {
|
||||||
console.log("DELETE tiddler",requestPath.substr(prefix.length))
|
var title = decodeURIComponent(requestPath.substr(prefix.length));
|
||||||
// self.commander.wiki.deleteTiddler(decodeURIComponent(requestPath.substr(prefix.length)));
|
console.log("DELETE tiddler",title)
|
||||||
|
// self.commander.wiki.deleteTiddler(decodeURIComponent(title));
|
||||||
response.writeHead(204, "OK");
|
response.writeHead(204, "OK");
|
||||||
response.end();
|
response.end();
|
||||||
} else {
|
} else {
|
||||||
@ -94,10 +98,13 @@ console.log("DELETE tiddler",requestPath.substr(prefix.length))
|
|||||||
response.writeHead(200, {"Content-Type": "application/json"});
|
response.writeHead(200, {"Content-Type": "application/json"});
|
||||||
text = JSON.stringify({
|
text = JSON.stringify({
|
||||||
username: "ANONYMOUS",
|
username: "ANONYMOUS",
|
||||||
|
space: {
|
||||||
|
recipe: "default"
|
||||||
|
},
|
||||||
tiddlywiki_version: $tw.version
|
tiddlywiki_version: $tw.version
|
||||||
});
|
});
|
||||||
response.end(text,"utf8");
|
response.end(text,"utf8");
|
||||||
} else if(requestPath === "/tiddlers.json") {
|
} else if(requestPath === "/recipes/default/tiddlers.json") {
|
||||||
response.writeHead(200, {"Content-Type": "application/json"});
|
response.writeHead(200, {"Content-Type": "application/json"});
|
||||||
var tiddlers = [];
|
var tiddlers = [];
|
||||||
$tw.wiki.forEachTiddler("title",function(title,tiddler) {
|
$tw.wiki.forEachTiddler("title",function(title,tiddler) {
|
||||||
|
@ -4,6 +4,6 @@ title: $:/core/templates/html-div-tiddler
|
|||||||
|
|
||||||
This template is used for saving tiddlers as an HTML DIV tag with attributes representing the tiddler fields. This version includes the tiddler changecount as the field `revision`.
|
This template is used for saving tiddlers as an HTML DIV tag with attributes representing the tiddler fields. This version includes the tiddler changecount as the field `revision`.
|
||||||
|
|
||||||
-->`<div`<$fields exclude='text revision' template=' $name$="$encoded_value$"'></$fields>` revision="`<$info type='changecount'/>`">
|
-->`<div`<$fields exclude='text revision bag' template=' $name$="$encoded_value$"'></$fields>` revision="`<$info type='changecount'/>`" bag="default">
|
||||||
<pre>`<$view field="text" format="htmlencoded" />`</pre>
|
<pre>`<$view field="text" format="htmlencoded" />`</pre>
|
||||||
</div>`
|
</div>`
|
||||||
|
@ -76,7 +76,7 @@ var TiddlyWebSyncer = function(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Tasks are {type: "load"/"save", title:, queueTime:, lastModificationTime:}
|
// Tasks are {type: "load"/"save"/"delete", title:, queueTime:, lastModificationTime:}
|
||||||
this.taskQueue = {}; // Hashmap of tasks to be performed
|
this.taskQueue = {}; // Hashmap of tasks to be performed
|
||||||
this.taskInProgress = {}; // Hash of tasks in progress
|
this.taskInProgress = {}; // Hash of tasks in progress
|
||||||
this.taskTimerId = null; // Sync timer
|
this.taskTimerId = null; // Sync timer
|
||||||
@ -157,9 +157,7 @@ TiddlyWebSyncer.prototype.getStatus = function(callback) {
|
|||||||
if(json) {
|
if(json) {
|
||||||
// Record the recipe
|
// Record the recipe
|
||||||
if(json.space) {
|
if(json.space) {
|
||||||
self.recipe = "recipes/" + json.space.recipe + "/";
|
self.recipe = json.space.recipe;
|
||||||
} else {
|
|
||||||
self.recipe = "";
|
|
||||||
}
|
}
|
||||||
// Check if we're logged in
|
// Check if we're logged in
|
||||||
isLoggedIn = json.username !== "GUEST";
|
isLoggedIn = json.username !== "GUEST";
|
||||||
@ -265,7 +263,7 @@ TiddlyWebSyncer.prototype.syncFromServer = function() {
|
|||||||
this.log("Retrieving skinny tiddler list");
|
this.log("Retrieving skinny tiddler list");
|
||||||
var self = this;
|
var self = this;
|
||||||
this.httpRequest({
|
this.httpRequest({
|
||||||
url: this.host + this.recipe + "tiddlers.json",
|
url: this.host + "recipes/" + this.recipe + "/tiddlers.json",
|
||||||
callback: function(err,data) {
|
callback: function(err,data) {
|
||||||
// Check for errors
|
// Check for errors
|
||||||
if(err) {
|
if(err) {
|
||||||
@ -328,9 +326,13 @@ TiddlyWebSyncer.prototype.enqueueSyncTask = function(task) {
|
|||||||
// Set the timestamps on this task
|
// Set the timestamps on this task
|
||||||
task.queueTime = now;
|
task.queueTime = now;
|
||||||
task.lastModificationTime = now;
|
task.lastModificationTime = now;
|
||||||
// Bail if it's not a tiddler we know about
|
// Fill in some tiddlerInfo if the tiddler is one we haven't seen before
|
||||||
if(!$tw.utils.hop(this.tiddlerInfo,task.title)) {
|
if(!$tw.utils.hop(this.tiddlerInfo,task.title)) {
|
||||||
return;
|
this.tiddlerInfo[task.title] = {
|
||||||
|
revision: "0",
|
||||||
|
bag: "bag-not-set",
|
||||||
|
changeCount: -1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Bail if this is a save and the tiddler is already at the changeCount that the server has
|
// Bail if this is a save and the tiddler is already at the changeCount that the server has
|
||||||
if(task.type === "save" && this.wiki.getChangeCount(task.title) <= this.tiddlerInfo[task.title].changeCount) {
|
if(task.type === "save" && this.wiki.getChangeCount(task.title) <= this.tiddlerInfo[task.title].changeCount) {
|
||||||
@ -449,7 +451,7 @@ TiddlyWebSyncer.prototype.dispatchTask = function(task,callback) {
|
|||||||
var changeCount = this.wiki.getChangeCount(task.title);
|
var changeCount = this.wiki.getChangeCount(task.title);
|
||||||
this.log("Dispatching 'save' task:",task.title);
|
this.log("Dispatching 'save' task:",task.title);
|
||||||
this.httpRequest({
|
this.httpRequest({
|
||||||
url: this.host + this.recipe + "tiddlers/" + task.title,
|
url: this.host + "recipes/" + encodeURIComponent(this.recipe) + "/tiddlers/" + encodeURIComponent(task.title),
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-type": "application/json"
|
"Content-type": "application/json"
|
||||||
@ -460,9 +462,11 @@ TiddlyWebSyncer.prototype.dispatchTask = function(task,callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
// Save the details of the new revision of the tiddler
|
// Save the details of the new revision of the tiddler
|
||||||
var tiddlerInfo = self.tiddlerInfo[task.title];
|
var etagInfo = self.parseEtag(request.getResponseHeader("Etag")),
|
||||||
|
tiddlerInfo = self.tiddlerInfo[task.title];
|
||||||
tiddlerInfo.changeCount = changeCount;
|
tiddlerInfo.changeCount = changeCount;
|
||||||
tiddlerInfo.revision = self.getRevisionFromEtag(request);
|
tiddlerInfo.bag = etagInfo.bag;
|
||||||
|
tiddlerInfo.revision = etagInfo.revision;
|
||||||
// Invoke the callback
|
// Invoke the callback
|
||||||
callback(null);
|
callback(null);
|
||||||
}
|
}
|
||||||
@ -471,7 +475,7 @@ TiddlyWebSyncer.prototype.dispatchTask = function(task,callback) {
|
|||||||
// Load the tiddler
|
// Load the tiddler
|
||||||
this.log("Dispatching 'load' task:",task.title);
|
this.log("Dispatching 'load' task:",task.title);
|
||||||
this.httpRequest({
|
this.httpRequest({
|
||||||
url: this.host + this.recipe + "tiddlers/" + task.title,
|
url: this.host + "recipes/" + encodeURIComponent(this.recipe) + "/tiddlers/" + encodeURIComponent(task.title),
|
||||||
callback: function(err,data,request) {
|
callback: function(err,data,request) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
@ -485,10 +489,9 @@ TiddlyWebSyncer.prototype.dispatchTask = function(task,callback) {
|
|||||||
} else if(task.type === "delete") {
|
} else if(task.type === "delete") {
|
||||||
// Delete the tiddler
|
// Delete the tiddler
|
||||||
this.log("Dispatching 'delete' task:",task.title);
|
this.log("Dispatching 'delete' task:",task.title);
|
||||||
var bag = this.tiddlerInfo[task.title].bag,
|
var bag = this.tiddlerInfo[task.title].bag;
|
||||||
bagFragment = bag ? "bags/" + bag + "/tiddlers/" : "tiddlers/";
|
|
||||||
this.httpRequest({
|
this.httpRequest({
|
||||||
url: this.host + bagFragment + task.title,
|
url: this.host + "bags/" + encodeURIComponent(bag) + "/tiddlers/" + encodeURIComponent(task.title),
|
||||||
type: "DELETE",
|
type: "DELETE",
|
||||||
callback: function(err,data,request) {
|
callback: function(err,data,request) {
|
||||||
if(err) {
|
if(err) {
|
||||||
@ -566,14 +569,32 @@ TiddlyWebSyncer.prototype.convertTiddlerToTiddlyWebFormat = function(title) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Extract the revision from the Etag header of a request
|
Split a TiddlyWeb Etag into its constituent parts. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
"system-images_public/unsyncedIcon/946151:9f11c278ccde3a3149f339f4a1db80dd4369fc04"
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the value includes the opening and closing double quotes.
|
||||||
|
|
||||||
|
The parts are:
|
||||||
|
|
||||||
|
```
|
||||||
|
<bag>/<title>/<revision>:<hash>
|
||||||
|
```
|
||||||
*/
|
*/
|
||||||
TiddlyWebSyncer.prototype.getRevisionFromEtag = function(request) {
|
TiddlyWebSyncer.prototype.parseEtag = function(etag) {
|
||||||
var etag = request.getResponseHeader("Etag");
|
var firstSlash = etag.indexOf("/"),
|
||||||
if(etag) {
|
lastSlash = etag.lastIndexOf("/"),
|
||||||
return etag.split("/")[2].split(":")[0]; // etags are like "system-images_public/unsyncedIcon/946151:9f11c278ccde3a3149f339f4a1db80dd4369fc04"
|
colon = etag.lastIndexOf(":");
|
||||||
|
if(firstSlash === -1 || lastSlash === -1 || colon === -1) {
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return {
|
||||||
|
bag: decodeURIComponent(etag.substring(1,firstSlash)),
|
||||||
|
title: decodeURIComponent(etag.substring(firstSlash + 1,lastSlash)),
|
||||||
|
revision: etag.substring(lastSlash + 1,colon)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -604,7 +625,7 @@ TiddlyWebSyncer.prototype.httpRequest = function(options) {
|
|||||||
// Set up the state change handler
|
// Set up the state change handler
|
||||||
request.onreadystatechange = function() {
|
request.onreadystatechange = function() {
|
||||||
if(this.readyState === 4) {
|
if(this.readyState === 4) {
|
||||||
if(this.status === 200) {
|
if(this.status === 200 || this.status === 204) {
|
||||||
// Success!
|
// Success!
|
||||||
options.callback(null,this.responseText,this);
|
options.callback(null,this.responseText,this);
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user