1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-27 03:57:21 +00:00

Fix filesystem (#5465)

This commit is contained in:
Joshua Fontany 2021-02-04 08:11:07 -08:00 committed by GitHub
parent 9f9ce6595b
commit bfa062f23d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 57 deletions

View File

@ -158,11 +158,25 @@ WikiFolderMaker.prototype.saveCustomPlugin = function(pluginTiddler) {
};
WikiFolderMaker.prototype.saveTiddler = function(directory,tiddler) {
var title = tiddler.fields.title, fileInfo, pathFilters, extFilters;
if(this.wiki.tiddlerExists("$:/config/FileSystemPaths")) {
pathFilters = this.wiki.getTiddlerText("$:/config/FileSystemPaths","").split("\n");
}
if(this.wiki.tiddlerExists("$:/config/FileSystemExtensions")) {
extFilters = this.wiki.getTiddlerText("$:/config/FileSystemExtensions","").split("\n");
}
var fileInfo = $tw.utils.generateTiddlerFileInfo(tiddler,{
directory: path.resolve(this.wikiFolderPath,directory),
wiki: this.wiki
wiki: this.wiki,
pathFilters: pathFilters,
extFilters: extFilters,
originalpath: this.wiki.extractTiddlerDataItem("$:/config/OriginalTiddlerPaths",title, "")
});
try {
$tw.utils.saveTiddlerToFileSync(tiddler,fileInfo);
} catch (err) {
console.log("SaveWikiFolder: Error saving file '" + fileInfo.filepath + "', tiddler: '" + tiddler.fields.title);
}
};
WikiFolderMaker.prototype.saveJSONFile = function(filename,json) {

View File

@ -640,10 +640,6 @@ DeleteTiddlerTask.prototype.run = function(callback) {
}
// Remove the info stored about this tiddler
delete self.syncer.tiddlerInfo[self.title];
if($tw.boot.files){
// Remove the tiddler from $tw.boot.files
delete $tw.boot.files[self.title];
}
// Invoke the callback
callback(null);
},{

View File

@ -252,7 +252,7 @@ exports.generateTiddlerFileInfo = function(tiddler,options) {
extFilters: options.extFilters,
wiki: options.wiki
});
if(metaExt){
if(metaExt) {
if(metaExt === ".tid") {
// Overriding to the .tid extension needs special handling
fileInfo.type = "application/x-tiddler";
@ -388,20 +388,18 @@ exports.generateTiddlerFilepath = function(title,options) {
// If the last write failed with an error, or if path does not start with:
// the resolved options.directory, the resolved wikiPath directory, or the wikiTiddlersPath directory,
// then encodeURIComponent() and resolve to tiddler directory
var newPath = fullPath,
var writePath = $tw.hooks.invokeHook("th-make-tiddler-path",fullPath),
encode = (options.fileInfo || {writeError: false}).writeError == true;
if(!encode){
if(!encode) {
encode = !(fullPath.indexOf(path.resolve(directory)) == 0 ||
fullPath.indexOf(path.resolve($tw.boot.wikiPath)) == 0 ||
fullPath.indexOf($tw.boot.wikiTiddlersPath) == 0);
}
if(encode){
fullPath = path.resolve(directory, encodeURIComponent(fullPath));
if(encode) {
writePath = path.resolve(directory,encodeURIComponent(fullPath));
}
// Call hook to allow plugins to modify the final path
fullPath = $tw.hooks.invokeHook("th-make-tiddler-path", newPath, fullPath);
// Return the full path to the file
return fullPath;
return writePath;
};
/*
@ -419,14 +417,29 @@ exports.saveTiddlerToFile = function(tiddler,fileInfo,callback) {
if(err) {
return callback(err);
}
fs.writeFile(fileInfo.filepath + ".meta",tiddler.getFieldStringBlock({exclude: ["text","bag"]}),"utf8",callback);
fs.writeFile(fileInfo.filepath + ".meta",tiddler.getFieldStringBlock({exclude: ["text","bag"]}),"utf8",function(err) {
if(err) {
return callback(err);
}
return callback(null,fileInfo);
});
});
} else {
// Save the tiddler as a self contained templated file
if(fileInfo.type === "application/x-tiddler") {
fs.writeFile(fileInfo.filepath,tiddler.getFieldStringBlock({exclude: ["text","bag"]}) + (!!tiddler.fields.text ? "\n\n" + tiddler.fields.text : ""),"utf8",callback);
fs.writeFile(fileInfo.filepath,tiddler.getFieldStringBlock({exclude: ["text","bag"]}) + (!!tiddler.fields.text ? "\n\n" + tiddler.fields.text : ""),"utf8",function(err) {
if(err) {
return callback(err);
}
return callback(null,fileInfo);
});
} else {
fs.writeFile(fileInfo.filepath,JSON.stringify([tiddler.getFieldStrings({exclude: ["bag"]})],null,$tw.config.preferences.jsonSpaces),"utf8",callback);
fs.writeFile(fileInfo.filepath,JSON.stringify([tiddler.getFieldStrings({exclude: ["bag"]})],null,$tw.config.preferences.jsonSpaces),"utf8",function(err) {
if(err) {
return callback(err);
}
return callback(null,fileInfo);
});
}
}
};
@ -457,10 +470,12 @@ exports.saveTiddlerToFileSync = function(tiddler,fileInfo) {
/*
Delete a file described by the fileInfo if it exits
*/
exports.deleteTiddlerFile = function(fileInfo, callback) {
exports.deleteTiddlerFile = function(fileInfo,callback) {
//Only attempt to delete files that exist on disk
if(!fileInfo.filepath || !fs.existsSync(fileInfo.filepath)) {
return callback(null);
//For some reason, the tiddler is only in memory or we can't modify the file at this path
$tw.syncer.displayError("Server deleteTiddlerFile task failed for filepath: "+fileInfo.filepath);
return callback(null,fileInfo);
}
// Delete the file
fs.unlink(fileInfo.filepath,function(err) {
@ -473,10 +488,20 @@ exports.deleteTiddlerFile = function(fileInfo, callback) {
if(err) {
return callback(err);
}
return $tw.utils.deleteEmptyDirs(path.dirname(fileInfo.filepath),callback);
return $tw.utils.deleteEmptyDirs(path.dirname(fileInfo.filepath),function(err) {
if(err) {
return callback(err);
}
return callback(null,fileInfo);
});
});
} else {
return $tw.utils.deleteEmptyDirs(path.dirname(fileInfo.filepath),callback);
return $tw.utils.deleteEmptyDirs(path.dirname(fileInfo.filepath),function(err) {
if(err) {
return callback(err);
}
return callback(null,fileInfo);
});
}
});
};
@ -486,25 +511,25 @@ Cleanup old files on disk, by comparing the options values:
adaptorInfo from $tw.syncer.tiddlerInfo
bootInfo from $tw.boot.files
*/
exports.cleanupTiddlerFiles = function(options, callback) {
exports.cleanupTiddlerFiles = function(options,callback) {
var adaptorInfo = options.adaptorInfo || {},
bootInfo = options.bootInfo || {},
title = options.title || "undefined";
if(adaptorInfo.filepath && bootInfo.filepath && adaptorInfo.filepath !== bootInfo.filepath) {
return $tw.utils.deleteTiddlerFile(adaptorInfo, function(err){
$tw.utils.deleteTiddlerFile(adaptorInfo,function(err) {
if(err) {
if ((err.code == "EPERM" || err.code == "EACCES") && err.syscall == "unlink") {
// Error deleting the previous file on disk, should fail gracefully
$tw.syncer.displayError("Server desynchronized. Error cleaning up previous file for tiddler: "+title, err);
return callback(null);
$tw.syncer.displayError("Server desynchronized. Error cleaning up previous file for tiddler: \""+title+"\"",err);
return callback(null,bootInfo);
} else {
return callback(err);
}
}
return callback(null);
return callback(null,bootInfo);
});
} else {
return callback(null);
return callback(null,bootInfo);
}
};

View File

@ -54,10 +54,10 @@ It is the responsibility of the filesystem adaptor to update this.boot.files for
FileSystemAdaptor.prototype.getTiddlerFileInfo = function(tiddler,callback) {
// Always generate a fileInfo object when this fuction is called
var title = tiddler.fields.title, newInfo, pathFilters, extFilters;
if(this.wiki.tiddlerExists("$:/config/FileSystemPaths")){
if(this.wiki.tiddlerExists("$:/config/FileSystemPaths")) {
pathFilters = this.wiki.getTiddlerText("$:/config/FileSystemPaths","").split("\n");
}
if(this.wiki.tiddlerExists("$:/config/FileSystemExtensions")){
if(this.wiki.tiddlerExists("$:/config/FileSystemExtensions")) {
extFilters = this.wiki.getTiddlerText("$:/config/FileSystemExtensions","").split("\n");
}
newInfo = $tw.utils.generateTiddlerFileInfo(tiddler,{
@ -66,9 +66,8 @@ FileSystemAdaptor.prototype.getTiddlerFileInfo = function(tiddler,callback) {
extFilters: extFilters,
wiki: this.wiki,
fileInfo: this.boot.files[title],
originalpath: this.wiki.extractTiddlerDataItem("$:/config/OriginalTiddlerPaths",title, "")
originalpath: this.wiki.extractTiddlerDataItem("$:/config/OriginalTiddlerPaths",title,"")
});
this.boot.files[title] = newInfo;
callback(null,newInfo);
};
@ -76,35 +75,38 @@ FileSystemAdaptor.prototype.getTiddlerFileInfo = function(tiddler,callback) {
/*
Save a tiddler and invoke the callback with (err,adaptorInfo,revision)
*/
FileSystemAdaptor.prototype.saveTiddler = function(tiddler,callback) {
FileSystemAdaptor.prototype.saveTiddler = function(tiddler,callback,options) {
var self = this;
var syncerInfo = options.tiddlerInfo || {};
this.getTiddlerFileInfo(tiddler,function(err,fileInfo) {
if(err) {
return callback(err);
}
$tw.utils.saveTiddlerToFile(tiddler,fileInfo,function(err) {
$tw.utils.saveTiddlerToFile(tiddler,fileInfo,function(err,fileInfo) {
if(err) {
if ((err.code == "EPERM" || err.code == "EACCES") && err.syscall == "open") {
var bootInfo = self.boot.files[tiddler.fields.title];
bootInfo.writeError = true;
self.boot.files[tiddler.fields.title] = bootInfo;
$tw.syncer.displayError("Sync for tiddler [["+tiddler.fields.title+"]] will be retried with encoded filepath", encodeURIComponent(bootInfo.filepath));
fileInfo = fileInfo || self.boot.files[tiddler.fields.title];
fileInfo.writeError = true;
self.boot.files[tiddler.fields.title] = fileInfo;
$tw.syncer.logger.log("Sync failed for \""+tiddler.fields.title+"\" and will be retried with encoded filepath",encodeURIComponent(fileInfo.filepath));
return callback(err);
} else {
return callback(err);
}
}
// Store new boot info only after successful writes
self.boot.files[tiddler.fields.title] = fileInfo;
// Cleanup duplicates if the file moved or changed extensions
var options = {
adaptorInfo: ($tw.syncer.tiddlerInfo[tiddler.fields.title] || {adaptorInfo: {} }).adaptorInfo,
bootInfo: self.boot.files[tiddler.fields.title] || {},
adaptorInfo: syncerInfo.adaptorInfo || {},
bootInfo: fileInfo || {},
title: tiddler.fields.title
};
$tw.utils.cleanupTiddlerFiles(options, function(err){
$tw.utils.cleanupTiddlerFiles(options,function(err,fileInfo) {
if(err) {
return callback(err);
}
return callback(null, self.boot.files[tiddler.fields.title]);
return callback(null,fileInfo);
});
});
});
@ -127,20 +129,22 @@ FileSystemAdaptor.prototype.deleteTiddler = function(title,callback,options) {
fileInfo = this.boot.files[title];
// Only delete the tiddler if we have writable information for the file
if(fileInfo) {
$tw.utils.deleteTiddlerFile(fileInfo, function(err){
$tw.utils.deleteTiddlerFile(fileInfo,function(err,fileInfo) {
if(err) {
if ((err.code == "EPERM" || err.code == "EACCES") && err.syscall == "unlink") {
// Error deleting the file on disk, should fail gracefully
$tw.syncer.displayError("Server desynchronized. Error deleting file for deleted tiddler: "+title, err);
return callback(null);
$tw.syncer.displayError("Server desynchronized. Error deleting file for deleted tiddler \"" + title + "\"",err);
return callback(null,fileInfo);
} else {
return callback(err);
}
}
return callback(null);
// Remove the tiddler from self.boot.files & return null adaptorInfo
delete self.boot.files[title];
return callback(null,null);
});
} else {
callback(null);
callback(null,null);
}
};

View File

@ -106,7 +106,7 @@ SaveTrailSyncAdaptor.prototype.saveTiddler = function(tiddler,callback) {
saveTiddlerFile(tiddler,{reason: "modified"});
}
}
callback(null);
callback(null,null);
};
/*
@ -120,7 +120,7 @@ SaveTrailSyncAdaptor.prototype.loadTiddler = function(title,callback) {
Delete a tiddler and invoke the callback with (err)
*/
SaveTrailSyncAdaptor.prototype.deleteTiddler = function(title,callback,options) {
callback(null);
callback(null,null);
};
function saveTiddlerFile(tiddler,options) {
@ -139,8 +139,8 @@ function saveTiddlerFile(tiddler,options) {
link.setAttribute("target","_blank");
link.setAttribute("rel","noopener noreferrer");
if(Blob !== undefined) {
var blob = new Blob([text], {type: "text/plain"});
link.setAttribute("href", URL.createObjectURL(blob));
var blob = new Blob([text],{type: "text/plain"});
link.setAttribute("href",URL.createObjectURL(blob));
} else {
link.setAttribute("href","data:text/plain," + encodeURIComponent(text));
}

View File

@ -182,10 +182,10 @@ TiddlyWebAdaptor.prototype.getSkinnyTiddlers = function(callback) {
/*
Save a tiddler and invoke the callback with (err,adaptorInfo,revision)
*/
TiddlyWebAdaptor.prototype.saveTiddler = function(tiddler,callback) {
TiddlyWebAdaptor.prototype.saveTiddler = function(tiddler,callback,options) {
var self = this;
if(this.isReadOnly) {
return callback(null);
return callback(null,options.tiddlerInfo.adaptorInfo);
}
$tw.utils.httpRequest({
url: this.host + "recipes/" + encodeURIComponent(this.recipe) + "/tiddlers/" + encodeURIComponent(tiddler.fields.title),
@ -207,7 +207,7 @@ TiddlyWebAdaptor.prototype.saveTiddler = function(tiddler,callback) {
// Invoke the callback
callback(null,{
bag: etagInfo.bag
}, etagInfo.revision);
},etagInfo.revision);
}
}
});
@ -238,12 +238,12 @@ tiddlerInfo: the syncer's tiddlerInfo for this tiddler
TiddlyWebAdaptor.prototype.deleteTiddler = function(title,callback,options) {
var self = this;
if(this.isReadOnly) {
return callback(null);
return callback(null,options.tiddlerInfo.adaptorInfo);
}
// If we don't have a bag it means that the tiddler hasn't been seen by the server, so we don't need to delete it
var bag = options.tiddlerInfo.adaptorInfo && options.tiddlerInfo.adaptorInfo.bag;
if(!bag) {
return callback(null);
return callback(null,options.tiddlerInfo.adaptorInfo);
}
// Issue HTTP request to delete the tiddler
$tw.utils.httpRequest({
@ -253,8 +253,8 @@ TiddlyWebAdaptor.prototype.deleteTiddler = function(title,callback,options) {
if(err) {
return callback(err);
}
// Invoke the callback
callback(null);
// Invoke the callback & return null adaptorInfo
callback(null,null);
}
});
};