diff --git a/boot/boot.js b/boot/boot.js index f6341dab0..c065cfb61 100644 --- a/boot/boot.js +++ b/boot/boot.js @@ -1894,7 +1894,7 @@ $tw.loadTiddlersFromSpecification = function(filepath,excludeRegExp) { }); }); if(isEditableFile) { - tiddlers.push({filepath: pathname, hasMetaFile: !!metadata && !isTiddlerFile, tiddlers: fileTiddlers}); + tiddlers.push({filepath: pathname, hasMetaFile: !!metadata && !isTiddlerFile, isEditableFile: true, tiddlers: fileTiddlers}); } else { tiddlers.push({tiddlers: fileTiddlers}); } @@ -2074,6 +2074,11 @@ $tw.loadWikiTiddlers = function(wikiPath,options) { } else { return null; } + // Save the path to the tiddlers folder for the filesystemadaptor + var config = wikiInfo.config || {}; + if($tw.boot.wikiPath == wikiPath) { + $tw.boot.wikiTiddlersPath = path.resolve($tw.boot.wikiPath,config["default-tiddler-location"] || $tw.config.wikiTiddlersSubDir); + } // Load any parent wikis if(wikiInfo.includeWikis) { parentPaths = parentPaths.slice(0); @@ -2107,27 +2112,30 @@ $tw.loadWikiTiddlers = function(wikiPath,options) { $tw.boot.files[tiddler.title] = { filepath: tiddlerFile.filepath, type: tiddlerFile.type, - hasMetaFile: tiddlerFile.hasMetaFile + hasMetaFile: tiddlerFile.hasMetaFile, + isEditableFile: config["retain-original-tiddler-path"] || tiddlerFile.isEditableFile || tiddlerFile.filepath.indexOf($tw.boot.wikiTiddlersPath) !== 0 }; }); } $tw.wiki.addTiddlers(tiddlerFile.tiddlers); }); - // Save the original tiddler file locations if requested - var config = wikiInfo.config || {}; - if(config["retain-original-tiddler-path"]) { - var output = {}, relativePath; + if ($tw.boot.wikiPath == wikiPath) { + // Save the original tiddler file locations if requested + var output = {}, relativePath, fileInfo; for(var title in $tw.boot.files) { - relativePath = path.relative(resolvedWikiPath,$tw.boot.files[title].filepath); - output[title] = - path.sep === "/" ? - relativePath : - relativePath.split(path.sep).join("/"); + fileInfo = $tw.boot.files[title]; + if(fileInfo.isEditableFile) { + relativePath = path.relative($tw.boot.wikiTiddlersPath,fileInfo.filepath); + output[title] = + path.sep === "/" ? + relativePath : + relativePath.split(path.sep).join("/"); + } + } + if(Object.keys(output).length > 0){ + $tw.wiki.addTiddler({title: "$:/config/OriginalTiddlerPaths", type: "application/json", text: JSON.stringify(output)}); } - $tw.wiki.addTiddler({title: "$:/config/OriginalTiddlerPaths", type: "application/json", text: JSON.stringify(output)}); } - // Save the path to the tiddlers folder for the filesystemadaptor - $tw.boot.wikiTiddlersPath = path.resolve($tw.boot.wikiPath,config["default-tiddler-location"] || $tw.config.wikiTiddlersSubDir); // Load any plugins within the wiki folder var wikiPluginsPath = path.resolve(wikiPath,$tw.config.wikiPluginsSubDir); if(fs.existsSync(wikiPluginsPath)) { @@ -2174,7 +2182,7 @@ $tw.loadTiddlersNode = function() { // Load any extra plugins $tw.utils.each($tw.boot.extraPlugins,function(name) { if(name.charAt(0) === "+") { // Relative path to plugin - var pluginFields = $tw.loadPluginFolder(name.substring(1));; + var pluginFields = $tw.loadPluginFolder(name.substring(1)); if(pluginFields) { $tw.wiki.addTiddler(pluginFields); } diff --git a/core/modules/utils/filesystem.js b/core/modules/utils/filesystem.js index 7c77eb0d7..9ee2e8acd 100644 --- a/core/modules/utils/filesystem.js +++ b/core/modules/utils/filesystem.js @@ -206,12 +206,14 @@ Create a fileInfo object for saving a tiddler: filepath: the absolute path to the file containing the tiddler type: the type of the tiddler file on disk (NOT the type of the tiddler) hasMetaFile: true if the file also has a companion .meta file + isEditableFile: true if the tiddler was loaded via non-standard options & marked editable Options include: directory: absolute path of root directory to which we are saving pathFilters: optional array of filters to be used to generate the base path 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; @@ -271,8 +273,13 @@ exports.generateTiddlerFileInfo = function(tiddler,options) { directory: options.directory, pathFilters: options.pathFilters, wiki: options.wiki, - fileInfo: options.fileInfo + fileInfo: options.fileInfo, + originalpath: options.originalpath }); + // Propigate the isEditableFile flag + if(options.fileInfo) { + fileInfo.isEditableFile = options.fileInfo.isEditableFile || false; + } return fileInfo; }; @@ -313,6 +320,7 @@ exports.generateTiddlerFilepath = function(title,options) { var self = this, directory = options.directory || "", extension = options.extension || "", + originalpath = options.originalpath || "", filepath; // Check if any of the pathFilters applies if(options.pathFilters && options.wiki) { @@ -326,7 +334,11 @@ exports.generateTiddlerFilepath = function(title,options) { } }); } - if(!filepath) { + if(!filepath && originalpath !== "") { + //Use the originalpath without the extension + var ext = path.extname(originalpath); + filepath = originalpath.substring(0,originalpath.length - ext.length);; + } else if(!filepath) { filepath = title; // If the filepath already ends in the extension then remove it if(filepath.substring(filepath.length - extension.length) === extension) { @@ -367,8 +379,8 @@ exports.generateTiddlerFilepath = function(title,options) { } count++; } while(fs.existsSync(fullPath)); - //If the path does not start with the wiki directory, or if the last write failed - var encode = fullPath.indexOf($tw.boot.wikiPath) !== 0 || ((options.fileInfo || {writeError: false}).writeError == true); + //If the path does not start with the wikiPath directory or the wikiTiddlersPath directory, or if the last write failed + var encode = !(fullPath.indexOf($tw.boot.wikiPath) == 0 || fullPath.indexOf($tw.boot.wikiTiddlersPath) == 0) || ((options.fileInfo || {writeError: false}).writeError == true); if(encode){ //encodeURIComponent() and then resolve to tiddler directory fullPath = path.resolve(directory, encodeURIComponent(fullPath)); diff --git a/editions/tw5.com/tiddlers/nodejs/Customising Tiddler File Naming.tid b/editions/tw5.com/tiddlers/nodejs/Customising Tiddler File Naming.tid index d13a6ce3b..3aef1351f 100644 --- a/editions/tw5.com/tiddlers/nodejs/Customising Tiddler File Naming.tid +++ b/editions/tw5.com/tiddlers/nodejs/Customising Tiddler File Naming.tid @@ -4,13 +4,15 @@ tags: [[TiddlyWiki on Node.js]] title: Customising Tiddler File Naming type: text/vnd.tiddlywiki -By default, a [[TiddlyWiki on Node.js]] instance using a [[wiki folder|TiddlyWikiFolders]] will create new tiddler files by using the sanitised and disambiguated title as filename and write them to the wiki folder's `tiddlers/` directory. The default file extension of `.tid` is used for tiddlers that are missing the `type` field, or for tiddlers of type "text/vnd.tiddlywiki". Tidders of other types are saved according to their IMIE types (defined at boot startup). Both the logical path (directory and file name) and the file extension can be customized independently by creating optional tiddlers: [[$:/config/FileSystemPaths]] and [[$:/config/FileSystemExtensions]]. +By default, a [[TiddlyWiki on Node.js]] instance using a [[wiki folder|TiddlyWikiFolders]] will create new tiddler files by using the sanitised and disambiguated title as filename and write them to the wiki folder's `tiddlers/` directory. This can be overridden by mapping a path in the wiki's tiddlywiki.info file, using a `default-tiddler-location` property in the `config` object. All filepath operations are relative to this `$tw.boot.wikiTiddlersPath` internal javacript variable. + +The default file extension of `.tid` is used for tiddlers that are missing the `type` field, or for tiddlers of type "text/vnd.tiddlywiki". Tidders of other types are saved according to their IMIE types (defined at boot startup). Both the logical path (directory and file name) and the file extension can be customized independently by creating optional tiddlers: [[$:/config/FileSystemPaths]] and [[$:/config/FileSystemExtensions]]. ! File System Paths The logical path can be customised by creating a tiddler [[$:/config/FileSystemPaths]] containing one or more [[filter expressions|Filter Syntax]], each on a line of its own. Newly created tiddlers are matched to each filter in turn, and the first output of the first filter to produce any output is taken as a logical path to be used for the tiddler file. Tiddlers are also tested against the [[$:/config/FileSystemPaths]] on every save to disk, and if the logical path has changed a new file is created and the old file deleted. -Tiddlers are limited to being written to the [[wiki folder|TiddlyWikiFolders]]. Any error saving a tiddler to disk, with a logical path that does not start with the wiki folder's path the most common error, causes the filepath to be encoded via Javascript's `encodeURIComponent()` method and the tiddler is saved as this file in the wiki folder's `tiddlers/` directory. +Tiddlers are limited to being written to the [[wiki folder|TiddlyWikiFolders]]. Any error saving a tiddler to disk, with a logical path that does not start with the wiki folder's path the most common error, causes the filepath to be encoded via Javascript's `encodeURIComponent()` method and the tiddler is saved as this file in the wiki folder's `$tw.boot.wikiTiddlersPath` directory. Logical paths do not include the file-on-disk's extension (see below), and they can use `/` or `\` as directory separator (when generating the physical path, this is replaced by the correct separator for the platform ~TiddlyWiki is running on). If none of the filters matches, the logical path is simply the title with all occurences of `/` replaced by `_` (for backwards compatibility). @@ -22,13 +24,12 @@ In both cases, the characters `<>~:"\|?*^` are replaced by `_` in order to guara [is[system]!has[draft.of]removeprefix[$:/]addprefix[_system/]] [is[draft]search-replace:g:regexp[/|\\],[_]addprefix[drafts/]] [tag[task]addprefix[mytasks/]] -[tag[externalnote]addprefix[../externalnotes/]] -[addprefix[wiki/]] +[!tag[externalnote]addprefix[wiki/]] ``` -This will store newly created system tiddlers that are not drafts of other tiddlers in `tiddlers/_system` (after stripping the `$:/` prefix). Next, all drafts have the path seperator characters in their titles replaced by "_" and are stored in `tiddlers/drafts/`. Then tiddlers tagged [[task]] are stored in a subdirectory `tiddlers/mytasks/`. Next, all tiddlers tagged "externalnote" will be written to `/tiddlers/../externalnotes/`, which resolves to `/externalnotes/` and places these tiddlers outside of the tiddler folder. Use [[tiddlywiki.files Files|tiddlywiki.files_Files]] to reimport these tiddlers when the wiki server is restarted. Finally, all tidders will match the final `[addprefix[wiki/]]` storing these in `/tiddlers/wiki/`. +This will store newly created system tiddlers that are not drafts of other tiddlers in `tiddlers/_system` (after stripping the `$:/` prefix). Next, all drafts have the path seperator characters in their titles replaced by "_" and are stored in `tiddlers/drafts/`. Then tiddlers tagged [[task]] are stored in a subdirectory `tiddlers/mytasks/`. Finally, all tidders not tagged with "externalnote" will match the final `[!tag[externalnote]addprefix[wiki/]]` storing these in `/wiki/`. In this example, tiddlers tagged with "externalnote" have been imported using [[tiddlywiki.files Files|tiddlywiki.files_Files]] with an "isEditableFile" flag set to true, causing the server to remember their original file path. -Because there was a filter match, any `/` or `\` in the tiddler title is mapped to a path separator. With the above filters, the non-system, non-draft tiddler `some/thing/entirely/new` (with no tags) will be saved to `/tiddlers/wiki/some/thing/entirely/new.tid` (ie, the file `new.tid` in a directory called `entirely/`). Thus, $:/config/FileSystemPaths itself will end up in `tiddlers/_system/config/FileSystemPaths.tid` or `tiddlers\_system\config\FileSystemPaths.tid`, depending on the platform. +Whenever a tiddler generates a $:/config/FileSystemPaths filter match, any `/` or `\` in the tiddler title is mapped to a path separator. With the above filters, the non-system, non-draft tiddler `some/thing/entirely/new` (with no tags) will be saved to `/tiddlers/wiki/some/thing/entirely/new.tid` (ie, the file `new.tid` in a directory called `entirely/`). Thus, $:/config/FileSystemPaths itself will end up in `tiddlers/_system/config/FileSystemPaths.tid` or `tiddlers\_system\config\FileSystemPaths.tid`, depending on the platform. ! File System Extensions diff --git a/editions/tw5.com/tiddlers/nodejs/tiddlywiki.files_Files.tid b/editions/tw5.com/tiddlers/nodejs/tiddlywiki.files_Files.tid index 389643175..b519b6bf7 100644 --- a/editions/tw5.com/tiddlers/nodejs/tiddlywiki.files_Files.tid +++ b/editions/tw5.com/tiddlers/nodejs/tiddlywiki.files_Files.tid @@ -51,7 +51,7 @@ Directory specifications in the `directories` array may take the following forms ** ''path'' - (required) the absolute or relative path to the directory containing the tiddler files (relative paths are interpreted relative to the path of the `tiddlywiki.files` file). Note that the directory is not recursively searched; sub-directories are ignored ** ''filesRegExp'' - (optional) a [[regular expression|https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions]] that matches the filenames of the files that should be processed within the directory ** ''isTiddlerFile'' - (required) if `true`, the file will be treated as a [[tiddler file|TiddlerFiles]] and deserialised to extract the tiddlers. Otherwise, the raw content of the file is assigned to the `text` field without any parsing -** ''isEditableFile'' - (optional) if `true`, changes to the tiddler be saved back to the original file. The ''path'' of the current directory being read must be within the wiki's base directory, and a $:/config/FileSystemPath filter is required to add the correct logical path to the tiddler's title (see second **Example**). <<.from-version "5.1.23">> +** ''isEditableFile'' - (optional) if `true`, changes to the tiddler be saved back to the original file. The tiddler will be saved back to the original filepath as long as it does not generate a result from the $:/config/FileSystemPath filters, which will override the final filepath generated if a result is returned from a filter. <<.from-version "5.1.23">> ** ''fields'' - (required) an object containing values that override or customise the fields provided in the tiddler file (see above) Fields can be overridden for particular files by creating a file with the same name plus the suffix `.meta` -- see TiddlerFiles. @@ -102,7 +102,7 @@ This example retrieves all the files with the extension `.txt` from a folder spe * ''created'' - set to the creation date/time of the text file * ''modified'' - set to the modification date/time of the text file * ''type'' - set to `text/plain` -* ''tags'' - set to `[[note]] [[externalnote]]` (using array notation) +* ''tags'' - set to `[[note]] [[externalnote]] [[.txt]]` (using array notation) * ''text'' - not set, thus the content of the file is loaded as the text field ``` @@ -125,8 +125,8 @@ This example retrieves all the files with the extension `.txt` from a folder spe } ``` -This will load all text files in the `/externalnotes/` directory into the wiki as individual tiddlers. These can be a collection of snippets in various markup-languages. Then, the `type` field of each of these tiddlers can be changed to match their languages For example, "text/vnd.tiddlywiki" for wikitext, or "text/markdown" for markdown files. Then, using $:/config/FileSystemPaths and $:/config/FileSystemExtentions tiddlers with the following lines we can cause any changes to these tiddlers to be saved back to the directory they started from, and as "*.txt" files with accompanying "*.txt.meta" files. These meta files will then over-ride any fields generated from the config `tiddlywiki.files` file (such as the tiddler's `type` field) when the server is restarted. +This will load all text files in the `../../externalnotes/` directory into the wiki as individual tiddlers. These can be a collection of snippets in various markup-languages. Then, the `type` field of each of these tiddlers can be changed to match their languages For example, "text/vnd.tiddlywiki" for wikitext, or "text/markdown" for markdown files. Then, using $:/config/FileSystemPaths and $:/config/FileSystemExtentions tiddlers with the following lines we can cause any changes to these tiddlers to be saved back to the directory they started from, and also as "*.txt" files with accompanying "*.txt.meta" files. These meta files will be generated as required, and will then over-ride any fields generated from the config `tiddlywiki.files` file (such as the tiddler's `type` field) when the server is restarted. -From the examples in [[Customising Tiddler File Naming]] we see that the `[tag[externalnote]addprefix[../externalnotes/]]` filter in the $:/config/FileSystemPaths tiddler catches all tiddlers tagged with `externalnotes` (that have not matched an earlier filter). These tiddlers have "../externalnotes/" appended to their titles to render the final logical path. As this path starts in the wiki's "tiddlers/" folder by default (one folder above the folder holding the above config file) it differes by one set of "../". +From the examples in [[Customising Tiddler File Naming]] we see that the final `[!tag[externalnote]addprefix[wiki/]]` filter in the $:/config/FileSystemPaths tiddler excludes all tiddlers tagged with `externalnotes` (that have not matched an earlier filter). These tiddlers have their filepath retrieved from the $:/config/OriginalTiddlerPaths generated upon boot startup. -Then, the `[tag[.txt]then[.txt]]` filter in the $:/config/FileSystemExtensions tiddler forces all these text files (which start with tag ".txt") to be saved back to disk as *.txt and accompanying *.txt.meta files (overriding the normal tiddler-type to file-type mapping). In this case, allowing the snippets of Tiddlywiki wikitext or markdown-text to be saved back to "text" *.txt files. \ No newline at end of file +Then, the `[tag[.txt]then[.txt]]` filter in the $:/config/FileSystemExtensions tiddler forces all these tiddlers to be saved back to disk as *.txt and accompanying *.txt.meta files (overriding the normal tiddler-type to file-type mapping). In this case, allowing the snippets of Tiddlywiki wikitext or markdown-text to be saved back to "text" *.txt files. \ No newline at end of file diff --git a/plugins/tiddlywiki/filesystem/filesystemadaptor.js b/plugins/tiddlywiki/filesystem/filesystemadaptor.js index fce2eaeaa..5cef917bf 100644 --- a/plugins/tiddlywiki/filesystem/filesystemadaptor.js +++ b/plugins/tiddlywiki/filesystem/filesystemadaptor.js @@ -52,16 +52,15 @@ The type is found by looking up the extension in $tw.config.fileExtensionInfo (e It is the responsibility of the filesystem adaptor to update this.boot.files for new files that are created. */ FileSystemAdaptor.prototype.getTiddlerFileInfo = function(tiddler,callback) { - // See if we've already got information about this file - var title = tiddler.fields.title, - newInfo, fileInfo = this.boot.files[title]; // Always generate a fileInfo object when this fuction is called + var title = tiddler.fields.title, newInfo; newInfo = $tw.utils.generateTiddlerFileInfo(tiddler,{ directory: this.boot.wikiTiddlersPath, pathFilters: this.wiki.getTiddlerText("$:/config/FileSystemPaths","").split("\n"), extFilters: this.wiki.getTiddlerText("$:/config/FileSystemExtensions","").split("\n"), wiki: this.wiki, - fileInfo: fileInfo + fileInfo: this.boot.files[title], + originalpath: this.wiki.extractTiddlerDataItem("$:/config/OriginalTiddlerPaths",title, "") }); this.boot.files[title] = newInfo; callback(null,newInfo);