1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-02 22:49:09 +02:00

Electron: Handle import export

This commit is contained in:
Laurent Cozic
2018-02-27 20:04:38 +00:00
parent 45845f645d
commit 4046a51472
24 changed files with 562 additions and 338 deletions

View File

@@ -7,34 +7,111 @@ const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const { basename, filename } = require('lib/path-utils.js');
const fs = require('fs-extra');
const md5 = require('md5');
const ArrayUtils = require('lib/ArrayUtils');
const { sprintf } = require('sprintf-js');
const { shim } = require('lib/shim');
const { _ } = require('lib/locale');
const { fileExtension } = require('lib/path-utils');
const { uuid } = require('lib/uuid.js');
const { importEnex } = require('lib/import-enex');
const { toTitleCase } = require('lib/string-utils');
class InteropService {
newImportExportModule_(format, className) {
try {
const FormatClass = require('lib/services/' + className);
return new FormatClass();
} catch (error) {
error.message = _('Cannot load module for format "%s": %s', format, error.message);
throw error;
constructor() {
this.modules_ = null;
}
modules() {
if (this.modules_) return this.modules_;
let importModules = [
{
format: 'jex',
fileExtension: 'jex',
sources: ['file'],
description: _('Joplin Export File'),
}, {
format: 'md',
fileExtension: 'md',
sources: ['file', 'directory'],
isNoteArchive: false, // Tells whether the file can contain multiple notes (eg. Enex or Jex format)
description: _('Markdown'),
}, {
format: 'raw',
sources: ['directory'],
description: _('Joplin Export Directory'),
}, {
format: 'enex',
fileExtension: 'enex',
sources: ['file'],
description: _('Evernote Export File'),
},
];
let exportModules = [
{
format: 'jex',
fileExtension: 'jex',
target: 'file',
description: _('Joplin Export File'),
}, {
format: 'raw',
target: 'directory',
description: _('Joplin Export Directory'),
},
];
importModules = importModules.map((a) => {
const className = 'InteropService_Importer_' + toTitleCase(a.format);
const output = Object.assign({}, {
type: 'importer',
path: 'lib/services/' + className,
}, a);
if (!('isNoteArchive' in output)) output.isNoteArchive = true;
return output;
});
exportModules = exportModules.map((a) => {
const className = 'InteropService_Exporter_' + toTitleCase(a.format);
return Object.assign({}, {
type: 'exporter',
path: 'lib/services/' + className,
}, a);
});
this.modules_ = importModules.concat(exportModules);
return this.modules_;
}
moduleByFormat_(type, format) {
const modules = this.modules();
for (let i = 0; i < modules.length; i++) {
const m = modules[i];
if (m.format === format && m.type === type) return modules[i];
}
return null;
}
newExporter_(format) {
return this.newImportExportModule_(format, 'InteropService_Exporter_' + toTitleCase(format));
newModule_(type, format) {
const module = this.moduleByFormat_(type, format);
if (!module) throw new Error(_('Cannot load "%s" module for format "%s"', type, format));
const ModuleClass = require(module.path);
return new ModuleClass();
}
newImporter_(format) {
return this.newImportExportModule_(format, 'InteropService_Importer_' + toTitleCase(format));
moduleByFileExtension_(type, ext) {
ext = ext.toLowerCase();
const modules = this.modules();
for (let i = 0; i < modules.length; i++) {
const m = modules[i];
if (type !== m.type) continue;
if (m.fileExtension === ext) return m;
}
return null;
}
async import(options) {
@@ -47,25 +124,20 @@ class InteropService {
}, options);
if (options.format === 'auto') {
const ext = fileExtension(options.path).toLowerCase();
if (ext === 'jex') {
options.format = 'jex';
} else if (ext === 'enex') {
options.format = 'enex';
} else {
throw new Error('Cannot automatically detect source format from path: ' + options.path);
}
const module = this.moduleByFileExtension_('importer', fileExtension(options.path));
if (!module) throw new Error(_('Please specify import format for %s', options.path));
options.format = module.format;
}
if (options.destinationFolderId) {
const folder = await Folder.load(options.destinationFolderId);
if (!folder) throw new Error('Notebook not found: ' + options.destinationFolderId);
if (!folder) throw new Error(_('Cannot find "%s".', options.destinationFolderId));
options.destinationFolder = folder;
}
let result = { warnings: [] }
const importer = this.newImporter_(options.format);
const importer = this.newModule_('importer', options.format);
await importer.init(options.path, options);
result = await importer.exec(result);
@@ -132,7 +204,7 @@ class InteropService {
await queueExportItem(BaseModel.TYPE_TAG, exportedTagIds[i]);
}
const exporter = this.newExporter_(exportFormat);
const exporter = this.newModule_('exporter', exportFormat);
await exporter.init(exportPath);
for (let i = 0; i < itemsToExport.length; i++) {

View File

@@ -54,11 +54,4 @@ class InteropService_Exporter_Jex extends InteropService_Exporter_Base {
}
InteropService_Exporter_Jex.metadata = function() {
return {
format: 'jex',
fileExtension: 'jex',
};
}
module.exports = InteropService_Exporter_Jex;

View File

@@ -27,10 +27,4 @@ class InteropService_Exporter_Raw extends InteropService_Exporter_Base {
}
InteropService_Exporter_Raw.metadata = function() {
return {
format: 'raw',
};
}
module.exports = InteropService_Exporter_Raw;

View File

@@ -14,11 +14,12 @@ const { shim } = require('lib/shim');
const { _ } = require('lib/locale');
const { fileExtension } = require('lib/path-utils');
const { uuid } = require('lib/uuid.js');
const { importEnex } = require('lib/import-enex');
class InteropService_Importer_Enex extends InteropService_Importer_Base {
async exec(result) {
const { importEnex } = require('lib/import-enex');
let folder = this.options_.destinationFolder;
if (!folder) {

View File

@@ -47,11 +47,4 @@ class InteropService_Importer_Jex extends InteropService_Importer_Base {
}
InteropService_Importer_Jex.metadata = function() {
return {
format: 'jex',
fileExtension: 'jex',
};
}
module.exports = InteropService_Importer_Jex;

View File

@@ -6,7 +6,7 @@ const Folder = require('lib/models/Folder.js');
const NoteTag = require('lib/models/NoteTag.js');
const Note = require('lib/models/Note.js');
const Tag = require('lib/models/Tag.js');
const { basename, filename } = require('lib/path-utils.js');
const { basename, filename, rtrimSlashes } = require('lib/path-utils.js');
const fs = require('fs-extra');
const md5 = require('md5');
const { sprintf } = require('sprintf-js');
@@ -19,9 +19,7 @@ const { importEnex } = require('lib/import-enex');
class InteropService_Importer_Md extends InteropService_Importer_Base {
async exec(result) {
if (!this.options_.destinationFolder) throw new Error(_('Please specify the notebook where the notes should be imported to.'));
const parentFolderId = this.options_.destinationFolder.id;
let parentFolderId = null;
const filePaths = [];
if (await shim.fsDriver().isDirectory(this.sourcePath_)) {
@@ -32,7 +30,17 @@ class InteropService_Importer_Md extends InteropService_Importer_Base {
filePaths.push(this.sourcePath_ + '/' + stat.path);
}
}
if (!this.options_.destinationFolder) {
const folderTitle = await Folder.findUniqueFolderTitle(basename(rtrimSlashes(this.sourcePath_)));
const folder = await Folder.save({ title: folderTitle });
parentFolderId = folder.id;
} else {
parentFolderId = this.options_.destinationFolder.id;
}
} else {
if (!this.options_.destinationFolder) throw new Error(_('Please specify the notebook where the notes should be imported to.'));
parentFolderId = this.options_.destinationFolder.id
filePaths.push(this.sourcePath_);
}
@@ -59,11 +67,4 @@ class InteropService_Importer_Md extends InteropService_Importer_Base {
}
InteropService_Importer_Md.metadata = function() {
return {
format: 'md',
fileExtension: 'md',
};
}
module.exports = InteropService_Importer_Md;

View File

@@ -110,30 +110,27 @@ class InteropService_Importer_Raw extends InteropService_Importer_Base {
await NoteTag.save(noteTag, { isNew: true });
}
const resourceStats = await shim.fsDriver().readDirStats(this.sourcePath_ + '/resources');
if (await shim.fsDriver().isDirectory(this.sourcePath_ + '/resources')) {
const resourceStats = await shim.fsDriver().readDirStats(this.sourcePath_ + '/resources');
for (let i = 0; i < resourceStats.length; i++) {
const resourceFilePath = this.sourcePath_ + '/resources/' + resourceStats[i].path;
const oldId = Resource.pathToId(resourceFilePath);
const newId = resourceIdMap[oldId];
if (!newId) {
result.warnings.push(sprintf('Resource file is not referenced in any note and so was not imported: %s', oldId));
continue;
for (let i = 0; i < resourceStats.length; i++) {
const resourceFilePath = this.sourcePath_ + '/resources/' + resourceStats[i].path;
const oldId = Resource.pathToId(resourceFilePath);
const newId = resourceIdMap[oldId];
if (!newId) {
result.warnings.push(sprintf('Resource file is not referenced in any note and so was not imported: %s', oldId));
continue;
}
const resource = createdResources[newId];
const destPath = Resource.fullPath(resource);
await shim.fsDriver().copy(resourceFilePath, destPath);
}
const resource = createdResources[newId];
const destPath = Resource.fullPath(resource);
await shim.fsDriver().copy(resourceFilePath, destPath);
}
return result;
}
}
InteropService_Importer_Raw.metadata = function() {
return {
format: 'raw',
};
}
module.exports = InteropService_Importer_Raw;