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

@ -388,7 +388,7 @@ 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) {
encode = !(fullPath.indexOf(path.resolve(directory)) == 0 ||
@ -396,12 +396,10 @@ exports.generateTiddlerFilepath = function(title,options) {
fullPath.indexOf($tw.boot.wikiTiddlersPath) == 0);
}
if(encode) {
fullPath = path.resolve(directory, encodeURIComponent(fullPath));
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);
});
}
}
};
@ -460,7 +473,9 @@ Delete a file described by the fileInfo if it exits
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);
});
}
});
};
@ -491,20 +516,20 @@ exports.cleanupTiddlerFiles = function(options, callback) {
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

@ -68,7 +68,6 @@ FileSystemAdaptor.prototype.getTiddlerFileInfo = function(tiddler,callback) {
fileInfo: this.boot.files[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) {

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),
@ -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);
}
});
};