2014-02-23 22:58:17 +00:00
/ * \
title : $ : / c o r e / m o d u l e s / u t i l s / f i l e s y s t e m . j s
type : application / javascript
module - type : utils - node
File system utilities
\ * /
( function ( ) {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict" ;
var fs = require ( "fs" ) ,
path = require ( "path" ) ;
2020-04-20 10:47:54 +00:00
/ *
Return the subdirectories of a path
* /
exports . getSubdirectories = function ( dirPath ) {
if ( ! $tw . utils . isDirectory ( dirPath ) ) {
return null ;
}
var subdirs = [ ] ;
2020-04-21 20:21:38 +00:00
$tw . utils . each ( fs . readdirSync ( dirPath ) , function ( item ) {
if ( $tw . utils . isDirectory ( path . resolve ( dirPath , item ) ) ) {
subdirs . push ( item ) ;
2020-04-20 10:47:54 +00:00
}
} ) ;
return subdirs ;
}
2014-02-23 22:58:17 +00:00
/ *
Recursively ( and synchronously ) copy a directory and all its content
* /
exports . copyDirectory = function ( srcPath , dstPath ) {
// Remove any trailing path separators
2020-04-27 14:00:06 +00:00
srcPath = path . resolve ( $tw . utils . removeTrailingSeparator ( srcPath ) ) ;
dstPath = path . resolve ( $tw . utils . removeTrailingSeparator ( dstPath ) ) ;
// Check that neither director is within the other
if ( srcPath . substring ( 0 , dstPath . length ) === dstPath || dstPath . substring ( 0 , srcPath . length ) === srcPath ) {
return "Cannot copy nested directories" ;
}
2014-02-23 22:58:17 +00:00
// Create the destination directory
var err = $tw . utils . createDirectory ( dstPath ) ;
if ( err ) {
return err ;
}
// Function to copy a folder full of files
var copy = function ( srcPath , dstPath ) {
var srcStats = fs . lstatSync ( srcPath ) ,
dstExists = fs . existsSync ( dstPath ) ;
if ( srcStats . isFile ( ) ) {
$tw . utils . copyFile ( srcPath , dstPath ) ;
} else if ( srcStats . isDirectory ( ) ) {
var items = fs . readdirSync ( srcPath ) ;
for ( var t = 0 ; t < items . length ; t ++ ) {
var item = items [ t ] ,
err = copy ( srcPath + path . sep + item , dstPath + path . sep + item ) ;
if ( err ) {
return err ;
}
2014-08-30 19:44:26 +00:00
}
2014-02-23 22:58:17 +00:00
}
} ;
copy ( srcPath , dstPath ) ;
return null ;
} ;
/ *
Copy a file
* /
var FILE _BUFFER _LENGTH = 64 * 1024 ,
2014-12-18 21:08:14 +00:00
fileBuffer ;
2014-02-23 22:58:17 +00:00
exports . copyFile = function ( srcPath , dstPath ) {
2014-12-18 21:08:14 +00:00
// Create buffer if required
if ( ! fileBuffer ) {
2018-06-13 10:22:17 +00:00
fileBuffer = Buffer . alloc ( FILE _BUFFER _LENGTH ) ;
2014-12-18 21:08:14 +00:00
}
2014-02-23 22:58:17 +00:00
// Create any directories in the destination
$tw . utils . createDirectory ( path . dirname ( dstPath ) ) ;
// Copy the file
var srcFile = fs . openSync ( srcPath , "r" ) ,
dstFile = fs . openSync ( dstPath , "w" ) ,
bytesRead = 1 ,
pos = 0 ;
while ( bytesRead > 0 ) {
bytesRead = fs . readSync ( srcFile , fileBuffer , 0 , FILE _BUFFER _LENGTH , pos ) ;
fs . writeSync ( dstFile , fileBuffer , 0 , bytesRead ) ;
pos += bytesRead ;
}
fs . closeSync ( srcFile ) ;
fs . closeSync ( dstFile ) ;
return null ;
2014-08-30 19:44:26 +00:00
} ;
2014-02-23 22:58:17 +00:00
/ *
Remove trailing path separator
* /
exports . removeTrailingSeparator = function ( dirPath ) {
var len = dirPath . length ;
if ( dirPath . charAt ( len - 1 ) === path . sep ) {
dirPath = dirPath . substr ( 0 , len - 1 ) ;
}
return dirPath ;
} ;
/ *
Recursively create a directory
* /
exports . createDirectory = function ( dirPath ) {
2014-04-25 21:41:59 +00:00
if ( dirPath . substr ( dirPath . length - 1 , 1 ) !== path . sep ) {
dirPath = dirPath + path . sep ;
}
var pos = 1 ;
pos = dirPath . indexOf ( path . sep , pos ) ;
while ( pos !== - 1 ) {
var subDirPath = dirPath . substr ( 0 , pos ) ;
2014-02-23 22:58:17 +00:00
if ( ! $tw . utils . isDirectory ( subDirPath ) ) {
try {
fs . mkdirSync ( subDirPath ) ;
} catch ( e ) {
return "Error creating directory '" + subDirPath + "'" ;
}
}
2014-04-25 21:41:59 +00:00
pos = dirPath . indexOf ( path . sep , pos + 1 ) ;
}
return null ;
} ;
/ *
Recursively create directories needed to contain a specified file
* /
exports . createFileDirectories = function ( filePath ) {
return $tw . utils . createDirectory ( path . dirname ( filePath ) ) ;
} ;
/ *
Recursively delete a directory
* /
exports . deleteDirectory = function ( dirPath ) {
if ( fs . existsSync ( dirPath ) ) {
var entries = fs . readdirSync ( dirPath ) ;
for ( var entryIndex = 0 ; entryIndex < entries . length ; entryIndex ++ ) {
var currPath = dirPath + path . sep + entries [ entryIndex ] ;
if ( fs . lstatSync ( currPath ) . isDirectory ( ) ) {
$tw . utils . deleteDirectory ( currPath ) ;
} else {
fs . unlinkSync ( currPath ) ;
}
}
fs . rmdirSync ( dirPath ) ;
2014-02-23 22:58:17 +00:00
}
return null ;
} ;
/ *
Check if a path identifies a directory
* /
exports . isDirectory = function ( dirPath ) {
return fs . existsSync ( dirPath ) && fs . statSync ( dirPath ) . isDirectory ( ) ;
} ;
2014-12-10 22:14:27 +00:00
/ *
Check if a path identifies a directory that is empty
* /
exports . isDirectoryEmpty = function ( dirPath ) {
if ( ! $tw . utils . isDirectory ( dirPath ) ) {
return false ;
}
var files = fs . readdirSync ( dirPath ) ,
empty = true ;
$tw . utils . each ( files , function ( file , index ) {
if ( file . charAt ( 0 ) !== "." ) {
empty = false ;
}
} ) ;
return empty ;
} ;
Make tiddler file paths configurable (#2379)
When saving new tiddlers on node.js, allow the user to override the path of the
generated .tid file. This is done by creating a tiddler
$:/config/FileSystemPaths which contains one or more filter expressions, one
per line. These filters are applied in turn to the tiddler to be saved, and
the first output produced is taken as a logical path relative to the wiki's
tiddlers directory. Any occurences of "/" in the logical path are replaced with
the platform's path separator, the extension ".tid" is appended, illegal
characters are replaced by "_" and the path is disambiguated (if necessary) in
order to arrive at the final tiddler file path. If none of the filters matches,
or the configuration tiddler does not exist, fall back to the previous file
naming scheme (i.e. replacing "/" by "_").
This implies we will now, for tiddlers matching the user-specified filters,
create directory trees below the tiddlers directory. In order to avoid
cluttering it with empty directory trees when renaming or removing tiddlers, any
directories that become empty by deleting a tiddler file are removed
(recursively).
Benefits of this configuration option include the ability to organize git
repositories of TiddlyWikis running on node.js, ability to replace characters
that cause trouble with particular operating systems or workflows (e.g. '$' on
unix) and the ability to replicate tiddler "paths" in the filesystem (by
including a filter like "[!has[draft.of]]") without forcing such a (potentially
problematic) change on all users.
2016-04-25 07:36:32 +00:00
/ *
Recursively delete a tree of empty directories
* /
exports . deleteEmptyDirs = function ( dirpath , callback ) {
var self = this ;
fs . readdir ( dirpath , function ( err , files ) {
if ( err ) {
return callback ( err ) ;
}
if ( files . length > 0 ) {
return callback ( null ) ;
}
fs . rmdir ( dirpath , function ( err ) {
if ( err ) {
return callback ( err ) ;
}
self . deleteEmptyDirs ( path . dirname ( dirpath ) , callback ) ;
} ) ;
} ) ;
} ;
2019-04-13 13:59:44 +00:00
/ *
Create a fileInfo object for saving a tiddler :
filepath : the absolute path to the file containing the tiddler
2020-11-30 22:31:48 +00:00
type : the type of the tiddler file on disk ( NOT the type of the tiddler )
2019-04-13 13:59:44 +00:00
hasMetaFile : true if the file also has a companion . meta file
2020-12-02 09:47:51 +00:00
isEditableFile : true if the tiddler was loaded via non - standard options & marked editable
2019-04-13 13:59:44 +00:00
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
2020-11-30 22:31:48 +00:00
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
2019-04-13 13:59:44 +00:00
* /
exports . generateTiddlerFileInfo = function ( tiddler , options ) {
2020-11-30 22:31:48 +00:00
var fileInfo = { } , metaExt ;
2020-12-06 22:59:48 +00:00
// Propagate the isEditableFile flag
2021-03-26 08:39:32 +00:00
if ( options . fileInfo && ! ! options . fileInfo . isEditableFile ) {
fileInfo . isEditableFile = true ;
fileInfo . originalpath = options . fileInfo . originalpath ;
2020-12-06 09:41:03 +00:00
}
2019-04-13 13:59:44 +00:00
// 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 ;
$tw . utils . each ( tiddler . getFieldStrings ( ) , function ( value , fieldName ) {
if ( fieldName !== "text" ) {
hasUnsafeFields = hasUnsafeFields || /[\x00-\x1F]/mg . test ( value ) ;
hasUnsafeFields = hasUnsafeFields || ( $tw . utils . trim ( value ) !== value ) ;
}
2023-01-19 21:16:46 +00:00
hasUnsafeFields = hasUnsafeFields || /:|#/mg . test ( fieldName ) ;
2019-04-13 13:59:44 +00:00
} ) ;
// Check for field values
if ( hasUnsafeFields ) {
// Save as a JSON file
fileInfo . type = "application/json" ;
fileInfo . hasMetaFile = false ;
} else {
// Save as a .tid or a text/binary file plus a .meta file
var tiddlerType = tiddler . fields . type || "text/vnd.tiddlywiki" ;
2023-02-03 19:22:43 +00:00
if ( tiddlerType === "text/vnd.tiddlywiki" || tiddler . hasField ( "_canonical_uri" ) ) {
2019-04-13 13:59:44 +00:00
// Save as a .tid file
fileInfo . type = "application/x-tiddler" ;
fileInfo . hasMetaFile = false ;
} else {
// Save as a text/binary file and a .meta file
fileInfo . type = tiddlerType ;
fileInfo . hasMetaFile = true ;
}
2020-11-30 22:31:48 +00:00
if ( options . extFilters ) {
2021-03-26 08:39:32 +00:00
// Check for extension overrides
2020-11-30 22:31:48 +00:00
metaExt = $tw . utils . generateTiddlerExtension ( tiddler . fields . title , {
extFilters : options . extFilters ,
wiki : options . wiki
} ) ;
2021-02-04 16:11:07 +00:00
if ( metaExt ) {
2020-12-06 09:41:03 +00:00
if ( metaExt === ".tid" ) {
// Overriding to the .tid extension needs special handling
fileInfo . type = "application/x-tiddler" ;
fileInfo . hasMetaFile = false ;
} else if ( metaExt === ".json" ) {
// Overriding to the .json extension needs special handling
fileInfo . type = "application/json" ;
fileInfo . hasMetaFile = false ;
} else {
//If the new type matches a known extention, use that MIME type's encoding
var extInfo = $tw . utils . getFileExtensionInfo ( metaExt ) ;
fileInfo . type = extInfo ? extInfo . type : null ;
fileInfo . encoding = $tw . utils . getTypeEncoding ( metaExt ) ;
fileInfo . hasMetaFile = true ;
}
2020-11-30 22:31:48 +00:00
}
}
2019-04-13 13:59:44 +00:00
}
2020-11-30 22:31:48 +00:00
// Take the file extension from the tiddler content type or metaExt
2019-04-13 13:59:44 +00:00
var contentTypeInfo = $tw . config . contentTypeInfo [ fileInfo . type ] || { extension : "" } ;
// Generate the filepath
2019-04-14 11:04:00 +00:00
fileInfo . filepath = $tw . utils . generateTiddlerFilepath ( tiddler . fields . title , {
2020-11-30 22:31:48 +00:00
extension : metaExt || contentTypeInfo . extension ,
2019-04-13 13:59:44 +00:00
directory : options . directory ,
2019-07-03 10:11:10 +00:00
pathFilters : options . pathFilters ,
2020-11-30 22:31:48 +00:00
wiki : options . wiki ,
2021-03-26 08:39:32 +00:00
fileInfo : options . fileInfo
2019-04-13 13:59:44 +00:00
} ) ;
return fileInfo ;
} ;
2020-11-30 22:31:48 +00:00
/ *
Generate the file extension for saving a tiddler
Options include :
extFilters : optional array of filters to be used to generate the extention
wiki : optional wiki for evaluating the extFilters
* /
exports . generateTiddlerExtension = function ( title , options ) {
2021-03-26 08:39:32 +00:00
var extension ;
2020-11-30 22:31:48 +00:00
// Check if any of the extFilters applies
if ( options . extFilters && options . wiki ) {
$tw . utils . each ( options . extFilters , function ( filter ) {
if ( ! extension ) {
var source = options . wiki . makeTiddlerIterator ( [ title ] ) ,
result = options . wiki . filterTiddlers ( filter , null , source ) ;
if ( result . length > 0 ) {
extension = result [ 0 ] ;
}
}
} ) ;
}
return extension ;
} ;
2019-04-13 13:59:44 +00:00
/ *
Generate the filepath for saving a tiddler
Options include :
extension : file extension to be added the finished filepath
directory : absolute path of root directory to which we are saving
pathFilters : optional array of filters to be used to generate the base path
wiki : optional wiki for evaluating the pathFilters
2020-11-30 22:31:48 +00:00
fileInfo : an existing fileInfo object to check against
2024-04-11 20:54:46 +00:00
fileInfo . overwrite : if true , turns off filename clash numbers ( defaults to false )
2019-04-13 13:59:44 +00:00
* /
2019-04-14 11:04:00 +00:00
exports . generateTiddlerFilepath = function ( title , options ) {
2021-03-26 08:39:32 +00:00
var directory = options . directory || "" ,
2019-04-13 13:59:44 +00:00
extension = options . extension || "" ,
2021-03-26 08:39:32 +00:00
originalpath = ( options . fileInfo && options . fileInfo . originalpath ) ? options . fileInfo . originalpath : "" ,
2024-04-11 20:54:46 +00:00
overwrite = options . fileInfo && options . fileInfo . overwrite || false ,
2021-03-26 08:39:32 +00:00
filepath ;
2019-04-13 13:59:44 +00:00
// Check if any of the pathFilters applies
if ( options . pathFilters && options . wiki ) {
$tw . utils . each ( options . pathFilters , function ( filter ) {
if ( ! filepath ) {
2019-04-14 11:04:00 +00:00
var source = options . wiki . makeTiddlerIterator ( [ title ] ) ,
2019-04-13 13:59:44 +00:00
result = options . wiki . filterTiddlers ( filter , null , source ) ;
if ( result . length > 0 ) {
filepath = result [ 0 ] ;
}
}
} ) ;
}
2021-03-26 08:39:32 +00:00
if ( ! filepath && ! ! originalpath ) {
2020-12-02 09:47:51 +00:00
//Use the originalpath without the extension
var ext = path . extname ( originalpath ) ;
2020-12-14 09:50:53 +00:00
filepath = originalpath . substring ( 0 , originalpath . length - ext . length ) ;
2020-12-02 09:47:51 +00:00
} else if ( ! filepath ) {
2019-04-14 11:04:00 +00:00
filepath = title ;
2019-04-13 13:59:44 +00:00
// Remove any forward or backward slashes so we don't create directories
filepath = filepath . replace ( /\/|\\/g , "_" ) ;
}
2021-03-26 08:39:32 +00:00
// 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
2020-11-30 22:31:48 +00:00
if ( ! /^\.{1,2}[/\\]/g . test ( filepath ) ) {
// Don't let the filename start with any dots because such files are invisible on *nix
2021-03-26 08:39:32 +00:00
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 ) ;
2020-11-30 22:31:48 +00:00
}
2021-01-03 11:50:14 +00:00
// 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 ) ;
}
2019-04-13 13:59:44 +00:00
// Truncate the filename if it is too long
if ( filepath . length > 200 ) {
filepath = filepath . substr ( 0 , 200 ) ;
}
2021-03-26 08:39:32 +00:00
// If the resulting filename is blank (eg because the title is just punctuation)
if ( ! filepath || /^_+$/g . test ( filepath ) ) {
2019-04-13 13:59:44 +00:00
// ...then just use the character codes of the title
2021-05-30 18:20:17 +00:00
filepath = "" ;
2019-04-14 11:04:00 +00:00
$tw . utils . each ( title . split ( "" ) , function ( char ) {
2019-04-13 13:59:44 +00:00
if ( filepath ) {
filepath += "-" ;
}
filepath += char . charCodeAt ( 0 ) . toString ( ) ;
} ) ;
}
2024-04-11 20:54:46 +00:00
// Add a uniquifier if the file already exists (default)
var fullPath = path . resolve ( directory , filepath + extension ) ;
if ( ! overwrite ) {
var oldPath = ( options . fileInfo ) ? options . fileInfo . filepath : undefined ,
2019-04-13 13:59:44 +00:00
count = 0 ;
2024-04-11 20:54:46 +00:00
do {
fullPath = path . resolve ( directory , filepath + ( count ? "_" + count : "" ) + extension ) ;
if ( oldPath && oldPath == fullPath ) break ;
count ++ ;
} while ( fs . existsSync ( fullPath ) ) ;
}
2020-12-14 09:50:53 +00:00
// If the last write failed with an error, or if path does not start with:
2021-03-26 08:39:32 +00:00
// the resolved options.directory, the resolved wikiPath directory, the wikiTiddlersPath directory,
2024-04-11 20:54:46 +00:00
// or the 'originalpath' directory, then $tw.utils.encodeURIComponentExtended() and resolve to options.directory.
2021-03-26 08:39:32 +00:00
var writePath = $tw . hooks . invokeHook ( "th-make-tiddler-path" , fullPath , fullPath ) ,
2020-12-14 09:50:53 +00:00
encode = ( options . fileInfo || { writeError : false } ) . writeError == true ;
2021-02-04 16:11:07 +00:00
if ( ! encode ) {
2021-03-26 08:39:32 +00:00
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 ) ;
2020-12-14 09:50:53 +00:00
}
2021-02-04 16:11:07 +00:00
if ( encode ) {
2023-01-19 17:45:54 +00:00
writePath = path . resolve ( directory , $tw . utils . encodeURIComponentExtended ( fullPath ) ) ;
2020-11-30 22:31:48 +00:00
}
2019-04-13 13:59:44 +00:00
// Return the full path to the file
2021-02-04 16:11:07 +00:00
return writePath ;
2019-04-13 13:59:44 +00:00
} ;
/ *
Save a tiddler to a file described by the fileInfo :
filepath : the absolute path to the file containing the tiddler
type : the type of the tiddler file ( NOT the type of the tiddler )
hasMetaFile : true if the file also has a companion . meta file
* /
exports . saveTiddlerToFile = function ( tiddler , fileInfo , callback ) {
$tw . utils . createDirectory ( path . dirname ( fileInfo . filepath ) ) ;
if ( fileInfo . hasMetaFile ) {
// Save the tiddler as a separate body and meta file
var typeInfo = $tw . config . contentTypeInfo [ tiddler . fields . type || "text/plain" ] || { encoding : "utf8" } ;
2021-03-26 08:42:31 +00:00
fs . writeFile ( fileInfo . filepath , tiddler . fields . text || "" , typeInfo . encoding , function ( err ) {
2019-04-13 13:59:44 +00:00
if ( err ) {
return callback ( err ) ;
}
2021-02-04 16:11:07 +00:00
fs . writeFile ( fileInfo . filepath + ".meta" , tiddler . getFieldStringBlock ( { exclude : [ "text" , "bag" ] } ) , "utf8" , function ( err ) {
if ( err ) {
return callback ( err ) ;
}
return callback ( null , fileInfo ) ;
} ) ;
2019-04-13 13:59:44 +00:00
} ) ;
} else {
// Save the tiddler as a self contained templated file
if ( fileInfo . type === "application/x-tiddler" ) {
2021-02-04 16:11:07 +00:00
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 ) ;
} ) ;
2019-04-13 13:59:44 +00:00
} else {
2021-02-04 16:11:07 +00:00
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 ) ;
} ) ;
2019-04-14 11:04:00 +00:00
}
}
} ;
/ *
Save a tiddler to a file described by the fileInfo :
filepath : the absolute path to the file containing the tiddler
type : the type of the tiddler file ( NOT the type of the tiddler )
hasMetaFile : true if the file also has a companion . meta file
* /
exports . saveTiddlerToFileSync = function ( tiddler , fileInfo ) {
$tw . utils . createDirectory ( path . dirname ( fileInfo . filepath ) ) ;
if ( fileInfo . hasMetaFile ) {
// Save the tiddler as a separate body and meta file
var typeInfo = $tw . config . contentTypeInfo [ tiddler . fields . type || "text/plain" ] || { encoding : "utf8" } ;
2021-03-26 08:42:31 +00:00
fs . writeFileSync ( fileInfo . filepath , tiddler . fields . text || "" , typeInfo . encoding ) ;
2019-06-10 16:52:26 +00:00
fs . writeFileSync ( fileInfo . filepath + ".meta" , tiddler . getFieldStringBlock ( { exclude : [ "text" , "bag" ] } ) , "utf8" ) ;
2019-04-14 11:04:00 +00:00
} else {
// Save the tiddler as a self contained templated file
if ( fileInfo . type === "application/x-tiddler" ) {
2019-06-10 16:52:26 +00:00
fs . writeFileSync ( fileInfo . filepath , tiddler . getFieldStringBlock ( { exclude : [ "text" , "bag" ] } ) + ( ! ! tiddler . fields . text ? "\n\n" + tiddler . fields . text : "" ) , "utf8" ) ;
2019-04-14 11:04:00 +00:00
} else {
2019-06-10 16:52:26 +00:00
fs . writeFileSync ( fileInfo . filepath , JSON . stringify ( [ tiddler . getFieldStrings ( { exclude : [ "bag" ] } ) ] , null , $tw . config . preferences . jsonSpaces ) , "utf8" ) ;
2019-04-13 13:59:44 +00:00
}
}
2021-03-26 08:42:31 +00:00
return fileInfo ;
2019-04-13 13:59:44 +00:00
} ;
2020-11-30 22:31:48 +00:00
/ *
Delete a file described by the fileInfo if it exits
* /
2021-02-04 16:11:07 +00:00
exports . deleteTiddlerFile = function ( fileInfo , callback ) {
2020-11-30 22:31:48 +00:00
//Only attempt to delete files that exist on disk
if ( ! fileInfo . filepath || ! fs . existsSync ( fileInfo . filepath ) ) {
2021-02-04 16:11:07 +00:00
//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 ) ;
2020-11-30 22:31:48 +00:00
}
// Delete the file
fs . unlink ( fileInfo . filepath , function ( err ) {
if ( err ) {
return callback ( err ) ;
2021-05-30 18:20:17 +00:00
}
2020-11-30 22:31:48 +00:00
// Delete the metafile if present
if ( fileInfo . hasMetaFile && fs . existsSync ( fileInfo . filepath + ".meta" ) ) {
fs . unlink ( fileInfo . filepath + ".meta" , function ( err ) {
if ( err ) {
return callback ( err ) ;
}
2021-02-04 16:11:07 +00:00
return $tw . utils . deleteEmptyDirs ( path . dirname ( fileInfo . filepath ) , function ( err ) {
if ( err ) {
return callback ( err ) ;
}
return callback ( null , fileInfo ) ;
} ) ;
2020-11-30 22:31:48 +00:00
} ) ;
} else {
2021-02-04 16:11:07 +00:00
return $tw . utils . deleteEmptyDirs ( path . dirname ( fileInfo . filepath ) , function ( err ) {
if ( err ) {
return callback ( err ) ;
}
return callback ( null , fileInfo ) ;
} ) ;
2020-11-30 22:31:48 +00:00
}
} ) ;
} ;
/ *
Cleanup old files on disk , by comparing the options values :
adaptorInfo from $tw . syncer . tiddlerInfo
bootInfo from $tw . boot . files
* /
2021-02-04 16:11:07 +00:00
exports . cleanupTiddlerFiles = function ( options , callback ) {
2020-11-30 22:31:48 +00:00
var adaptorInfo = options . adaptorInfo || { } ,
bootInfo = options . bootInfo || { } ,
title = options . title || "undefined" ;
if ( adaptorInfo . filepath && bootInfo . filepath && adaptorInfo . filepath !== bootInfo . filepath ) {
2021-02-04 16:11:07 +00:00
$tw . utils . deleteTiddlerFile ( adaptorInfo , function ( err ) {
2020-11-30 22:31:48 +00:00
if ( err ) {
if ( ( err . code == "EPERM" || err . code == "EACCES" ) && err . syscall == "unlink" ) {
// Error deleting the previous file on disk, should fail gracefully
2021-02-04 16:11:07 +00:00
$tw . syncer . displayError ( "Server desynchronized. Error cleaning up previous file for tiddler: \"" + title + "\"" , err ) ;
return callback ( null , bootInfo ) ;
2020-11-30 22:31:48 +00:00
} else {
return callback ( err ) ;
}
}
2021-02-04 16:11:07 +00:00
return callback ( null , bootInfo ) ;
2020-11-30 22:31:48 +00:00
} ) ;
} else {
2021-02-04 16:11:07 +00:00
return callback ( null , bootInfo ) ;
2020-11-30 22:31:48 +00:00
}
} ;
2014-02-23 22:58:17 +00:00
} ) ( ) ;