From 39ddd934f697b95d1355edef492cfc4a639d7764 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sun, 25 Feb 2018 21:08:32 +0000 Subject: [PATCH] Changed export format extension to JEX and made it a non-compressed tar file --- CliClient/app/command-export.js | 4 +- CliClient/app/command-import.js | 39 +++++++++++++ ReactNativeClient/lib/fs-driver-base.js | 24 ++++++++ ReactNativeClient/lib/fs-driver-node.js | 24 ++++++-- ReactNativeClient/lib/fs-driver-rn.js | 10 +++- .../lib/services/InteropService.js | 57 +++++++++---------- 6 files changed, 119 insertions(+), 39 deletions(-) create mode 100644 CliClient/app/command-import.js create mode 100644 ReactNativeClient/lib/fs-driver-base.js diff --git a/CliClient/app/command-export.js b/CliClient/app/command-export.js index cd890ce35..97ae5d8a0 100644 --- a/CliClient/app/command-export.js +++ b/CliClient/app/command-export.js @@ -19,7 +19,7 @@ class Command extends BaseCommand { options() { return [ - ['--format ', 'jpz (compressed, default), raw (plain text files)'], + //['--format ', 'jex (default), raw'], ['--note ', _('Exports only the given note.')], ['--notebook ', _('Exports only the given notebook.')], ]; @@ -29,7 +29,7 @@ class Command extends BaseCommand { let exportOptions = {}; exportOptions.path = args.path; - exportOptions.format = args.options.format ? args.options.format : 'jpz'; + exportOptions.format = args.options.format ? args.options.format : 'jex'; if (args.options.note) { diff --git a/CliClient/app/command-import.js b/CliClient/app/command-import.js new file mode 100644 index 000000000..389ad3537 --- /dev/null +++ b/CliClient/app/command-import.js @@ -0,0 +1,39 @@ +const { BaseCommand } = require('./base-command.js'); +const InteropService = require('lib/services/InteropService.js'); +const BaseModel = require('lib/BaseModel.js'); +const Note = require('lib/models/Note.js'); +const { reg } = require('lib/registry.js'); +const { app } = require('./app.js'); +const { _ } = require('lib/locale.js'); +const fs = require('fs-extra'); + +class Command extends BaseCommand { + + usage() { + return 'import '; + } + + description() { + return _('Imports data into Joplin.'); + } + + options() { + return [ + //['--format ', 'jex, markdown'], + ]; + } + + async action(args) { + const importOptions = {}; + importOptions.path = args.path; + importOptions.format = args.options.format ? args.options.format : 'jex'; + + const service = new InteropService(); + const result = await service.import(importOptions); + + result.warnings.map((w) => this.stdout(w)); + } + +} + +module.exports = Command; \ No newline at end of file diff --git a/ReactNativeClient/lib/fs-driver-base.js b/ReactNativeClient/lib/fs-driver-base.js new file mode 100644 index 000000000..3f661bcf8 --- /dev/null +++ b/ReactNativeClient/lib/fs-driver-base.js @@ -0,0 +1,24 @@ +class FsDriverBase { + + async isDirectory(path) { + const stat = await this.stat(path); + return !stat ? false : stat.isDirectory(); + } + + async readDirStatsHandleRecursion_(basePath, stat, output, options) { + if (options.recursive && stat.isDirectory()) { + const subPath = basePath + '/' + stat.path; + const subStats = await this.readDirStats(subPath, options); + for (let j = 0; j < subStats.length; j++) { + const subStat = subStats[j]; + subStat.path = stat.path + '/' + subStat.path; + output.push(subStat); + } + } + + return output; + } + +} + +module.exports = FsDriverBase; \ No newline at end of file diff --git a/ReactNativeClient/lib/fs-driver-node.js b/ReactNativeClient/lib/fs-driver-node.js index 6b64cc931..381d70cfd 100644 --- a/ReactNativeClient/lib/fs-driver-node.js +++ b/ReactNativeClient/lib/fs-driver-node.js @@ -1,7 +1,8 @@ const fs = require('fs-extra'); const { time } = require('lib/time-utils.js'); +const FsDriverBase = require('lib/fs-driver-base'); -class FsDriverNode { +class FsDriverNode extends FsDriverBase { fsErrorToJsError_(error, path = null) { let msg = error.toString(); @@ -81,9 +82,14 @@ class FsDriverNode { async stat(path) { try { - const s = await fs.stat(path); - s.path = path; - return s; + const stat = await fs.stat(path); + return { + birthtime: stat.birthtime, + mtime: stat.mtime, + isDirectory: () => stat.isDirectory(), + path: path, + size: stat.size, + }; } catch (error) { if (error.code == 'ENOENT') return null; throw error; @@ -94,14 +100,20 @@ class FsDriverNode { return fs.utimes(path, timestampDate, timestampDate); } - async readDirStats(path) { + async readDirStats(path, options = null) { + if (!options) options = {}; + if (!('recursive' in options)) options.recursive = false; + let items = await fs.readdir(path); let output = []; for (let i = 0; i < items.length; i++) { - let stat = await this.stat(path + '/' + items[i]); + const item = items[i]; + let stat = await this.stat(path + '/' + item); if (!stat) continue; // Has been deleted between the readdir() call and now stat.path = stat.path.substr(path.length + 1); output.push(stat); + + output = await this.readDirStatsHandleRecursion_(path, stat, output, options); } return output; } diff --git a/ReactNativeClient/lib/fs-driver-rn.js b/ReactNativeClient/lib/fs-driver-rn.js index 2543e11b4..53b486fdc 100644 --- a/ReactNativeClient/lib/fs-driver-rn.js +++ b/ReactNativeClient/lib/fs-driver-rn.js @@ -1,6 +1,7 @@ const RNFS = require('react-native-fs'); +const FsDriverBase = require('lib/fs-driver-base'); -class FsDriverRN { +class FsDriverRN extends FsDriverBase { appendFileSync(path, string) { throw new Error('Not implemented'); @@ -34,13 +35,18 @@ class FsDriverRN { }; } - async readDirStats(path) { + async readDirStats(path, options = null) { + if (!options) options = {}; + if (!('recursive' in options)) options.recursive = false; + let items = await RNFS.readDir(path); let output = []; for (let i = 0; i < items.length; i++) { const item = items[i]; const relativePath = item.path.substr(path.length + 1); output.push(this.rnfsStatToStd_(item, relativePath)); + + output = await this.readDirStatsHandleRecursion_(path, item, output, options); } return output; } diff --git a/ReactNativeClient/lib/services/InteropService.js b/ReactNativeClient/lib/services/InteropService.js index 89b857ba0..c83cb2225 100644 --- a/ReactNativeClient/lib/services/InteropService.js +++ b/ReactNativeClient/lib/services/InteropService.js @@ -9,6 +9,7 @@ const { basename } = require('lib/path-utils.js'); const fs = require('fs-extra'); const md5 = require('md5'); const { sprintf } = require('sprintf-js'); +const { shim } = require('lib/shim'); class RawExporter { @@ -35,9 +36,11 @@ class RawExporter { } -class JpzExporter { +class JexExporter { async init(destPath) { + if (await shim.fsDriver().isDirectory(destPath)) throw new Error('Path is a directory: ' + destPath); + this.tempDir_ = require('os').tmpdir() + '/' + md5(Math.random() + Date.now()); this.destPath_ = destPath; this.rawExporter_ = new RawExporter(); @@ -53,27 +56,17 @@ class JpzExporter { } async close() { - const cwd = process.cwd(); - process.chdir(this.tempDir_); + const stats = await shim.fsDriver().readDirStats(this.tempDir_, { recursive: true }); + const filePaths = stats.map((a) => a.path); - const cleanUp = () => { - process.chdir(cwd); - } + await require('tar').create({ + strict: true, + portable: true, + file: this.destPath_, + cwd: this.tempDir_, + }, filePaths); - try { - await require('tar').c({ - strict: true, - gzip: true, - file: this.destPath_, - }, ['./']); - - await fs.remove(this.tempDir_); - } catch (error) { - cleanUp(); - throw error; - } - - cleanUp(); + await fs.remove(this.tempDir_); } } @@ -81,8 +74,18 @@ class JpzExporter { function newExporter(format) { if (format === 'raw') { return new RawExporter(); - } else if (format === 'jpz') { - return new JpzExporter(); + } else if (format === 'jex') { + return new JexExporter(); + } else { + throw new Error('Unknown format: ' + format); + } +} + +function newImporter(format) { + if (format === 'raw') { + return new RawImporter(); + } else if (format === 'jex') { + return new JexImporter(); } else { throw new Error('Unknown format: ' + format); } @@ -91,19 +94,15 @@ function newExporter(format) { class InteropService { async import(options) { - // format - // file/dir path + const importer = newImporter(options.format); + await importer.init(options.path); } async export(options) { const exportPath = options.path ? options.path : null; const sourceFolderIds = options.sourceFolderIds ? options.sourceFolderIds : []; const sourceNoteIds = options.sourceNoteIds ? options.sourceNoteIds : []; - - const result = { - warnings: [], - } - + const result = { warnings: [] } const itemsToExport = []; const queueExportItem = (itemType, itemOrId) => {