1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-11-29 22:48:10 +02:00

All: Fixes #992: Allow non-ASCII chars when exporting MD and handle duplicate filenames

This commit is contained in:
Laurent Cozic
2018-11-20 00:42:21 +00:00
parent 7bfc3e1256
commit 07b724d65b
10 changed files with 135 additions and 20 deletions

View File

@@ -1,3 +1,5 @@
const { filename, fileExtension } = require('lib/path-utils');
class FsDriverBase {
async isDirectory(path) {
@@ -19,6 +21,23 @@ class FsDriverBase {
return output;
}
async findUniqueFilename(name) {
let counter = 1;
let nameNoExt = filename(name, true);
let extension = fileExtension(name);
if (extension) extension = '.' + extension;
let nameToTry = nameNoExt + extension;
while (true) {
const exists = await this.exists(nameToTry);
if (!exists) return nameToTry;
nameToTry = nameNoExt + ' (' + counter + ')' + extension;
counter++;
if (counter >= 1000) nameToTry = nameNoExt + ' (' + ((new Date()).getTime()) + ')' + extension;
if (counter >= 10000) throw new Error('Cannot find unique title');
}
}
}
module.exports = FsDriverBase;

View File

@@ -1,3 +1,5 @@
const { _ } = require('lib/locale');
function dirname(path) {
if (!path) throw new Error('Path is empty');
let s = path.split(/\/|\\/);
@@ -11,9 +13,9 @@ function basename(path) {
return s[s.length - 1];
}
function filename(path) {
function filename(path, includeDir = false) {
if (!path) throw new Error('Path is empty');
let output = basename(path);
let output = includeDir ? path : basename(path);
if (output.indexOf('.') < 0) return output;
output = output.split('.');
@@ -48,6 +50,47 @@ function safeFilename(e, maxLength = null, allowSpaces = false) {
return output.substr(0, maxLength);
}
let friendlySafeFilename_blackListChars = '/<>:\'"\\|?*';
for (let i = 0; i < 32; i++) {
friendlySafeFilename_blackListChars += String.fromCharCode(i);
}
const friendlySafeFilename_blackListNames = [".", "..", "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"];
function friendlySafeFilename(e, maxLength = null) {
if (maxLength === null) maxLength = 255;
if (!e || !e.replace) return _('Untitled');
let output = '';
for (let i = 0; i < e.length; i++) {
const c = e[i];
if (friendlySafeFilename_blackListChars.indexOf(c) >= 0) {
output += '_';
} else {
output += c;
}
}
if (output.length <= 4) {
if (friendlySafeFilename_blackListNames.indexOf(output.toUpperCase()) >= 0) {
output = '___';
}
}
while (output.length) {
const c = output[output.length - 1];
if (c === ' ' || c === '.') {
output = output.substr(0, output.length - 1);
} else {
break;
}
}
if (!output) return _('Untitled');
return output.substr(0, maxLength);
}
function toSystemSlashes(path, os = null) {
if (os === null) os = process.platform;
if (os === 'win32') return path.replace(/\//g, "\\");
@@ -62,4 +105,4 @@ function ltrimSlashes(path) {
return path.replace(/^\/+/, '');
}
module.exports = { basename, dirname, filename, isHidden, fileExtension, safeFilename, safeFileExtension, toSystemSlashes, rtrimSlashes, ltrimSlashes };
module.exports = { basename, dirname, filename, isHidden, fileExtension, safeFilename, friendlySafeFilename, safeFileExtension, toSystemSlashes, rtrimSlashes, ltrimSlashes };

View File

@@ -1,10 +1,9 @@
const InteropService_Exporter_Base = require('lib/services/InteropService_Exporter_Base');
const { basename, filename, safeFilename } = require('lib/path-utils.js');
const { basename, filename, friendlySafeFilename } = 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');
const unidecode = require('unidecode');
class InteropService_Exporter_Md extends InteropService_Exporter_Base {
@@ -21,7 +20,8 @@ class InteropService_Exporter_Md extends InteropService_Exporter_Base {
let output = '';
while (true) {
if (item.type_ === BaseModel.TYPE_FOLDER) {
output = safeFilename(item.title, null, true) + '/' + output;
output = friendlySafeFilename(item.title, null, true) + '/' + output;
output = await shim.fsDriver().findUniqueFilename(output);
}
if (!item.parent_id) return output;
item = await Folder.load(item.parent_id);
@@ -32,7 +32,6 @@ class InteropService_Exporter_Md extends InteropService_Exporter_Base {
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) {
@@ -41,7 +40,8 @@ class InteropService_Exporter_Md extends InteropService_Exporter_Base {
}
if (item.type_ === BaseModel.TYPE_NOTE) {
const noteFilePath = dirPath + '/' + safeFilename(unidecode(item.title), null, true) + '.md';
let noteFilePath = dirPath + '/' + friendlySafeFilename(item.title, null, true) + '.md';
noteFilePath = await shim.fsDriver().findUniqueFilename(noteFilePath);
const noteContent = await Note.serializeForEdit(item);
await shim.fsDriver().writeFile(noteFilePath, noteContent, 'utf-8');
}