diff --git a/boot/boot.js b/boot/boot.js index fbac37d77..6e99863f8 100644 --- a/boot/boot.js +++ b/boot/boot.js @@ -2133,6 +2133,7 @@ $tw.loadWikiTiddlers = function(wikiPath,options) { fileInfo = $tw.boot.files[title]; if(fileInfo.isEditableFile) { relativePath = path.relative($tw.boot.wikiTiddlersPath,fileInfo.filepath); + fileInfo.originalpath = relativePath; output[title] = path.sep === "/" ? relativePath : diff --git a/core/modules/commands/savewikifolder.js b/core/modules/commands/savewikifolder.js index f5cfb9cd7..cd4b5f26e 100644 --- a/core/modules/commands/savewikifolder.js +++ b/core/modules/commands/savewikifolder.js @@ -167,10 +167,10 @@ WikiFolderMaker.prototype.saveTiddler = function(directory,tiddler) { } var fileInfo = $tw.utils.generateTiddlerFileInfo(tiddler,{ directory: path.resolve(this.wikiFolderPath,directory), - wiki: this.wiki, pathFilters: pathFilters, extFilters: extFilters, - originalpath: this.wiki.extractTiddlerDataItem("$:/config/OriginalTiddlerPaths",title, "") + wiki: this.wiki, + fileInfo: {} }); try { $tw.utils.saveTiddlerToFileSync(tiddler,fileInfo); diff --git a/core/modules/utils/filesystem.js b/core/modules/utils/filesystem.js index b7fe2156c..230090840 100644 --- a/core/modules/utils/filesystem.js +++ b/core/modules/utils/filesystem.js @@ -213,13 +213,13 @@ Options include: extFilters: optional array of filters to be used to generate the base path wiki: optional wiki for evaluating the pathFilters, fileInfo: an existing fileInfo to check against - originalpath: a preferred filepath if no pathFilters match */ exports.generateTiddlerFileInfo = function(tiddler,options) { var fileInfo = {}, metaExt; // Propagate the isEditableFile flag - if(options.fileInfo) { - fileInfo.isEditableFile = options.fileInfo.isEditableFile || false; + if(options.fileInfo && !!options.fileInfo.isEditableFile) { + fileInfo.isEditableFile = true; + fileInfo.originalpath = options.fileInfo.originalpath; } // Check if the tiddler has any unsafe fields that can't be expressed in a .tid or .meta file: containing control characters, or leading/trailing whitespace var hasUnsafeFields = false; @@ -247,7 +247,7 @@ exports.generateTiddlerFileInfo = function(tiddler,options) { fileInfo.hasMetaFile = true; } if(options.extFilters) { - // Check for extension override + // Check for extension overrides metaExt = $tw.utils.generateTiddlerExtension(tiddler.fields.title,{ extFilters: options.extFilters, wiki: options.wiki @@ -279,8 +279,7 @@ exports.generateTiddlerFileInfo = function(tiddler,options) { directory: options.directory, pathFilters: options.pathFilters, wiki: options.wiki, - fileInfo: options.fileInfo, - originalpath: options.originalpath + fileInfo: options.fileInfo }); return fileInfo; }; @@ -292,8 +291,7 @@ Options include: wiki: optional wiki for evaluating the extFilters */ exports.generateTiddlerExtension = function(title,options) { - var self = this, - extension; + var extension; // Check if any of the extFilters applies if(options.extFilters && options.wiki) { $tw.utils.each(options.extFilters,function(filter) { @@ -319,11 +317,10 @@ Options include: fileInfo: an existing fileInfo object to check against */ exports.generateTiddlerFilepath = function(title,options) { - var self = this, - directory = options.directory || "", + var directory = options.directory || "", extension = options.extension || "", - originalpath = options.originalpath || "", - filepath; + originalpath = (options.fileInfo && options.fileInfo.originalpath) ? options.fileInfo.originalpath : "", + filepath; // Check if any of the pathFilters applies if(options.pathFilters && options.wiki) { $tw.utils.each(options.pathFilters,function(filter) { @@ -336,7 +333,7 @@ exports.generateTiddlerFilepath = function(title,options) { } }); } - if(!filepath && originalpath !== "") { + if(!filepath && !!originalpath) { //Use the originalpath without the extension var ext = path.extname(originalpath); filepath = originalpath.substring(0,originalpath.length - ext.length); @@ -345,27 +342,35 @@ exports.generateTiddlerFilepath = function(title,options) { // Remove any forward or backward slashes so we don't create directories filepath = filepath.replace(/\/|\\/g,"_"); } - //If the path does not start with "." or ".." and a path seperator, then + // Replace any Windows control codes + filepath = filepath.replace(/^(con|prn|aux|nul|com[0-9]|lpt[0-9])$/i,"_$1_"); + // Replace any leading spaces with the same number of underscores + filepath = filepath.replace(/^ +/,function (u) { return u.replace(/ /g, "_")}); + //If the path does not start with "." or ".." && a path seperator, then if(!/^\.{1,2}[/\\]/g.test(filepath)) { // Don't let the filename start with any dots because such files are invisible on *nix - filepath = filepath.replace(/^\.+/g,"_"); + filepath = filepath.replace(/^\.+/g,function (u) { return u.replace(/\./g, "_")}); + } + // Replace any Unicode control codes + filepath = filepath.replace(/[\x00-\x1f\x80-\x9f]/g,"_"); + // Replace any characters that can't be used in cross-platform filenames + filepath = $tw.utils.transliterate(filepath.replace(/<|>|~|\:|\"|\||\?|\*|\^/g,"_")); + // Replace any dots or spaces at the end of the extension with the same number of underscores + extension = extension.replace(/[\. ]+$/, function (u) { return u.replace(/[\. ]/g, "_")}); + // Truncate the extension if it is too long + if(extension.length > 32) { + extension = extension.substr(0,32); } // If the filepath already ends in the extension then remove it if(filepath.substring(filepath.length - extension.length) === extension) { filepath = filepath.substring(0,filepath.length - extension.length); } - // Remove any characters that can't be used in cross-platform filenames - filepath = $tw.utils.transliterate(filepath.replace(/<|>|~|\:|\"|\||\?|\*|\^/g,"_")); // Truncate the filename if it is too long if(filepath.length > 200) { filepath = filepath.substr(0,200); } - // Truncate the extension if it is too long - if(extension.length > 32) { - extension = extension.substr(0,32); - } - // If the resulting filename is blank (eg because the title is just punctuation characters) - if(!filepath) { + // If the resulting filename is blank (eg because the title is just punctuation) + if(!filepath || /^_+$/g.test(filepath)) { // ...then just use the character codes of the title filepath = ""; $tw.utils.each(title.split(""),function(char) { @@ -386,14 +391,15 @@ exports.generateTiddlerFilepath = function(title,options) { count++; } while(fs.existsSync(fullPath)); // 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 writePath = $tw.hooks.invokeHook("th-make-tiddler-path",fullPath), + // the resolved options.directory, the resolved wikiPath directory, the wikiTiddlersPath directory, + // or the 'originalpath' directory, then encodeURIComponent() and resolve to tiddler directory. + var writePath = $tw.hooks.invokeHook("th-make-tiddler-path",fullPath,fullPath), encode = (options.fileInfo || {writeError: false}).writeError == true; if(!encode) { - encode = !(fullPath.indexOf(path.resolve(directory)) == 0 || - fullPath.indexOf(path.resolve($tw.boot.wikiPath)) == 0 || - fullPath.indexOf($tw.boot.wikiTiddlersPath) == 0); + encode = !(writePath.indexOf($tw.boot.wikiTiddlersPath) == 0 || + writePath.indexOf(path.resolve(directory)) == 0 || + writePath.indexOf(path.resolve($tw.boot.wikiPath)) == 0 || + writePath.indexOf(path.resolve($tw.boot.wikiTiddlersPath,originalpath)) == 0 ); } if(encode) { writePath = path.resolve(directory,encodeURIComponent(fullPath)); diff --git a/plugins/tiddlywiki/filesystem/filesystemadaptor.js b/plugins/tiddlywiki/filesystem/filesystemadaptor.js index 5b2eda092..0ed686c84 100644 --- a/plugins/tiddlywiki/filesystem/filesystemadaptor.js +++ b/plugins/tiddlywiki/filesystem/filesystemadaptor.js @@ -53,7 +53,8 @@ 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; + var title = tiddler.fields.title, newInfo, pathFilters, extFilters, + fileInfo = this.boot.files[title]; if(this.wiki.tiddlerExists("$:/config/FileSystemPaths")) { pathFilters = this.wiki.getTiddlerText("$:/config/FileSystemPaths","").split("\n"); } @@ -65,8 +66,7 @@ FileSystemAdaptor.prototype.getTiddlerFileInfo = function(tiddler,callback) { pathFilters: pathFilters, extFilters: extFilters, wiki: this.wiki, - fileInfo: this.boot.files[title], - originalpath: this.wiki.extractTiddlerDataItem("$:/config/OriginalTiddlerPaths",title,"") + fileInfo: fileInfo }); callback(null,newInfo); };