mirror of
https://github.com/laurent22/joplin.git
synced 2025-03-29 21:21:15 +02:00
All: Resolves #734: Allow exporting to a hierarchy of Markdown files, and fixed a few issues related to exporting notebooks
This commit is contained in:
parent
81ec8eaf83
commit
eaf3eef2d3
@ -11,7 +11,7 @@ class InteropServiceHelper {
|
|||||||
|
|
||||||
if (module.target === 'file') {
|
if (module.target === 'file') {
|
||||||
path = bridge().showSaveDialog({
|
path = bridge().showSaveDialog({
|
||||||
filters: [{ name: module.description, extensions: module.fileExtension}]
|
filters: [{ name: module.description, extensions: module.fileExtensions}]
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
path = bridge().showOpenDialog({
|
path = bridge().showOpenDialog({
|
||||||
|
@ -238,14 +238,22 @@ class SideBarComponent extends React.Component {
|
|||||||
|
|
||||||
const InteropService = require("lib/services/InteropService.js");
|
const InteropService = require("lib/services/InteropService.js");
|
||||||
|
|
||||||
|
const exportMenu = new Menu();
|
||||||
|
const ioService = new InteropService();
|
||||||
|
const ioModules = ioService.modules();
|
||||||
|
for (let i = 0; i < ioModules.length; i++) {
|
||||||
|
const module = ioModules[i];
|
||||||
|
if (module.type !== 'exporter') continue;
|
||||||
|
|
||||||
|
exportMenu.append(new MenuItem({ label: module.fullLabel() , click: async () => {
|
||||||
|
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId] });
|
||||||
|
}}));
|
||||||
|
}
|
||||||
|
|
||||||
menu.append(
|
menu.append(
|
||||||
new MenuItem({
|
new MenuItem({
|
||||||
label: _("Export"),
|
label: _("Export"),
|
||||||
click: async () => {
|
submenu: exportMenu,
|
||||||
const ioService = new InteropService();
|
|
||||||
const module = ioService.moduleByFormat_("exporter", "jex");
|
|
||||||
await InteropServiceHelper.export(this.props.dispatch.bind(this), module, { sourceFolderIds: [itemId] });
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,11 @@ function safeFileExtension(e) {
|
|||||||
return e.replace(/[^a-zA-Z0-9]/g, '')
|
return e.replace(/[^a-zA-Z0-9]/g, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
function safeFilename(e, maxLength = 32) {
|
function safeFilename(e, maxLength = null, allowSpaces = false) {
|
||||||
|
if (maxLength === null) maxLength = 32;
|
||||||
if (!e || !e.replace) return '';
|
if (!e || !e.replace) return '';
|
||||||
let output = e.replace(/[^a-zA-Z0-9\-_\(\)\.]/g, '_')
|
const regex = allowSpaces ? /[^a-zA-Z0-9\-_\(\)\. ]/g : /[^a-zA-Z0-9\-_\(\)\.]/g
|
||||||
|
let output = e.replace(regex, '_')
|
||||||
return output.substr(0, maxLength);
|
return output.substr(0, maxLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,10 @@ class InteropService {
|
|||||||
format: 'raw',
|
format: 'raw',
|
||||||
target: 'directory',
|
target: 'directory',
|
||||||
description: _('Joplin Export Directory'),
|
description: _('Joplin Export Directory'),
|
||||||
|
}, {
|
||||||
|
format: 'md',
|
||||||
|
target: 'directory',
|
||||||
|
description: _('Markdown'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
58
ReactNativeClient/lib/services/InteropService_Exporter_Md.js
Normal file
58
ReactNativeClient/lib/services/InteropService_Exporter_Md.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
const InteropService_Exporter_Base = require('lib/services/InteropService_Exporter_Base');
|
||||||
|
const { basename, filename, safeFilename } = require('lib/path-utils.js');
|
||||||
|
const BaseModel = require('lib/BaseModel');
|
||||||
|
const Folder = require('lib/models/Folder');
|
||||||
|
const Note = require('lib/models/Note');
|
||||||
|
const { shim } = require('lib/shim');
|
||||||
|
|
||||||
|
class InteropService_Exporter_Md extends InteropService_Exporter_Base {
|
||||||
|
|
||||||
|
async init(destDir) {
|
||||||
|
this.destDir_ = destDir;
|
||||||
|
this.resourceDir_ = destDir ? destDir + '/_resources' : null;
|
||||||
|
this.createdDirs_ = [];
|
||||||
|
|
||||||
|
await shim.fsDriver().mkdir(this.destDir_);
|
||||||
|
await shim.fsDriver().mkdir(this.resourceDir_);
|
||||||
|
}
|
||||||
|
|
||||||
|
async makeDirPath_(item) {
|
||||||
|
let output = '';
|
||||||
|
while (true) {
|
||||||
|
if (item.type_ === BaseModel.TYPE_FOLDER) {
|
||||||
|
output = safeFilename(item.title, null, true) + '/' + output;
|
||||||
|
}
|
||||||
|
if (!item.parent_id) return output;
|
||||||
|
item = await Folder.load(item.parent_id);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
async processItem(ItemClass, item) {
|
||||||
|
if ([BaseModel.TYPE_NOTE, BaseModel.TYPE_FOLDER].indexOf(item.type_) < 0) return;
|
||||||
|
|
||||||
|
const filename = safeFilename(item.title, null, true);
|
||||||
|
const dirPath = this.destDir_ + '/' + (await this.makeDirPath_(item));
|
||||||
|
|
||||||
|
if (this.createdDirs_.indexOf(dirPath) < 0) {
|
||||||
|
await shim.fsDriver().mkdir(dirPath);
|
||||||
|
this.createdDirs_.push(dirPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type_ === BaseModel.TYPE_NOTE) {
|
||||||
|
const noteFilePath = dirPath + '/' + safeFilename(item.title, null, true) + '.md';
|
||||||
|
const noteContent = await Note.serializeForEdit(item);
|
||||||
|
await shim.fsDriver().writeFile(noteFilePath, noteContent, 'utf-8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async processResource(resource, filePath) {
|
||||||
|
const destResourcePath = this.resourceDir_ + '/' + basename(filePath);
|
||||||
|
await shim.fsDriver().copy(filePath, destResourcePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async close() {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = InteropService_Exporter_Md;
|
Loading…
x
Reference in New Issue
Block a user