All: Resolves #846: Set resource path to correct relative path so that for example images show up in Markdown viewers

pull/998/head
Laurent Cozic 2018-11-21 00:36:23 +00:00
parent 45cd8b7e3c
commit 897f53b13e
5 changed files with 80 additions and 26 deletions

View File

@ -21,6 +21,7 @@ describe('pathUtils', function() {
['con', '___'], ['con', '___'],
['no space at the end ', 'no space at the end'], ['no space at the end ', 'no space at the end'],
['nor dots...', 'nor dots'], ['nor dots...', 'nor dots'],
[' no space before either', 'no space before either'],
['thatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylong', 'thatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylong'], ['thatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylong', 'thatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylong'],
]; ];

View File

@ -86,6 +86,15 @@ function friendlySafeFilename(e, maxLength = null) {
} }
} }
while (output.length) {
const c = output[0];
if (c === ' ') {
output = output.substr(1, output.length - 1);
} else {
break;
}
}
if (!output) return _('Untitled'); if (!output) return _('Untitled');
return output.substr(0, maxLength); return output.substr(0, maxLength);

View File

@ -236,32 +236,46 @@ class InteropService {
const exporter = this.newModule_('exporter', exportFormat); const exporter = this.newModule_('exporter', exportFormat);
await exporter.init(exportPath); await exporter.init(exportPath);
for (let i = 0; i < itemsToExport.length; i++) { const typeOrder = [BaseModel.TYPE_FOLDER, BaseModel.TYPE_RESOURCE, BaseModel.TYPE_NOTE, BaseModel.TYPE_TAG, BaseModel.TYPE_NOTE_TAG];
const itemType = itemsToExport[i].type; const context = {
const ItemClass = BaseItem.getClassByItemType(itemType); resourcePaths: {},
const itemOrId = itemsToExport[i].itemOrId; };
const item = typeof itemOrId === 'object' ? itemOrId : await ItemClass.load(itemOrId);
if (!item) { for (let typeOrderIndex = 0; typeOrderIndex < typeOrder.length; typeOrderIndex++) {
if (itemType === BaseModel.TYPE_RESOURCE) { const type = typeOrder[typeOrderIndex];
result.warnings.push(sprintf('A resource that does not exist is referenced in a note. The resource was skipped. Resource ID: %s', itemOrId));
} else {
result.warnings.push(sprintf('Cannot find item with type "%s" and ID %s. Item was skipped.', ItemClass.tableName(), JSON.stringify(itemOrId)));
}
continue;
}
if (item.encryption_applied || item.encryption_blob_encrypted) throw new Error(_('This item is currently encrypted: %s "%s". Please wait for all items to be decrypted and try again.', BaseModel.modelTypeToName(itemType), item.title ? item.title : item.id)); for (let i = 0; i < itemsToExport.length; i++) {
const itemType = itemsToExport[i].type;
try { if (itemType !== type) continue;
if (itemType == BaseModel.TYPE_RESOURCE) {
const resourcePath = Resource.fullPath(item); const ItemClass = BaseItem.getClassByItemType(itemType);
await exporter.processResource(item, resourcePath); const itemOrId = itemsToExport[i].itemOrId;
const item = typeof itemOrId === 'object' ? itemOrId : await ItemClass.load(itemOrId);
if (!item) {
if (itemType === BaseModel.TYPE_RESOURCE) {
result.warnings.push(sprintf('A resource that does not exist is referenced in a note. The resource was skipped. Resource ID: %s', itemOrId));
} else {
result.warnings.push(sprintf('Cannot find item with type "%s" and ID %s. Item was skipped.', ItemClass.tableName(), JSON.stringify(itemOrId)));
}
continue;
} }
await exporter.processItem(ItemClass, item); if (item.encryption_applied || item.encryption_blob_encrypted) throw new Error(_('This item is currently encrypted: %s "%s". Please wait for all items to be decrypted and try again.', BaseModel.modelTypeToName(itemType), item.title ? item.title : item.id));
} catch (error) {
result.warnings.push(error.message); try {
if (itemType == BaseModel.TYPE_RESOURCE) {
const resourcePath = Resource.fullPath(item);
context.resourcePaths[item.id] = resourcePath;
exporter.updateContext(context);
await exporter.processResource(item, resourcePath);
}
await exporter.processItem(ItemClass, item);
} catch (error) {
result.warnings.push(error.message);
}
} }
} }

View File

@ -13,6 +13,14 @@ class InteropService_Exporter_Base {
return this.metadata_; return this.metadata_;
} }
updateContext(context) {
this.context_ = context;
}
context() {
return this.context_;
}
async temporaryDirectory_(createIt) { async temporaryDirectory_(createIt) {
const md5 = require('md5'); const md5 = require('md5');
const tempDir = require('os').tmpdir() + '/' + md5(Math.random() + Date.now()); const tempDir = require('os').tmpdir() + '/' + md5(Math.random() + Date.now());

View File

@ -1,5 +1,5 @@
const InteropService_Exporter_Base = require('lib/services/InteropService_Exporter_Base'); const InteropService_Exporter_Base = require('lib/services/InteropService_Exporter_Base');
const { basename, filename, friendlySafeFilename } = require('lib/path-utils.js'); const { basename, filename, friendlySafeFilename, rtrimSlashes } = require('lib/path-utils.js');
const BaseModel = require('lib/BaseModel'); const BaseModel = require('lib/BaseModel');
const Folder = require('lib/models/Folder'); const Folder = require('lib/models/Folder');
const Note = require('lib/models/Note'); const Note = require('lib/models/Note');
@ -16,12 +16,16 @@ class InteropService_Exporter_Md extends InteropService_Exporter_Base {
await shim.fsDriver().mkdir(this.resourceDir_); await shim.fsDriver().mkdir(this.resourceDir_);
} }
async makeDirPath_(item) { async makeDirPath_(item, pathPart = null) {
let output = ''; let output = '';
while (true) { while (true) {
if (item.type_ === BaseModel.TYPE_FOLDER) { if (item.type_ === BaseModel.TYPE_FOLDER) {
output = friendlySafeFilename(item.title, null, true) + '/' + output; if (pathPart) {
output = await shim.fsDriver().findUniqueFilename(output); output = pathPart + '/' + output;
} else {
output = friendlySafeFilename(item.title, null, true) + '/' + output;
output = await shim.fsDriver().findUniqueFilename(output);
}
} }
if (!item.parent_id) return output; if (!item.parent_id) return output;
item = await Folder.load(item.parent_id); item = await Folder.load(item.parent_id);
@ -29,6 +33,22 @@ class InteropService_Exporter_Md extends InteropService_Exporter_Base {
return output; return output;
} }
async replaceResourceIdsByRelativePaths_(item) {
const linkedResourceIds = await Note.linkedResourceIds(item.body);
const relativePath = rtrimSlashes(await this.makeDirPath_(item, '..'));
const resourcePaths = this.context() && this.context().resourcePaths ? this.context().resourcePaths : {};
let newBody = item.body;
for (let i = 0; i < linkedResourceIds.length; i++) {
const id = linkedResourceIds[i];
const resourcePath = relativePath + '/_resources/' + basename(resourcePaths[id]);
newBody = newBody.replace(new RegExp(':/' + id, 'g'), resourcePath);
}
return newBody;
}
async processItem(ItemClass, item) { async processItem(ItemClass, item) {
if ([BaseModel.TYPE_NOTE, BaseModel.TYPE_FOLDER].indexOf(item.type_) < 0) return; if ([BaseModel.TYPE_NOTE, BaseModel.TYPE_FOLDER].indexOf(item.type_) < 0) return;
@ -42,7 +62,9 @@ class InteropService_Exporter_Md extends InteropService_Exporter_Base {
if (item.type_ === BaseModel.TYPE_NOTE) { if (item.type_ === BaseModel.TYPE_NOTE) {
let noteFilePath = dirPath + '/' + friendlySafeFilename(item.title, null, true) + '.md'; let noteFilePath = dirPath + '/' + friendlySafeFilename(item.title, null, true) + '.md';
noteFilePath = await shim.fsDriver().findUniqueFilename(noteFilePath); noteFilePath = await shim.fsDriver().findUniqueFilename(noteFilePath);
const noteContent = await Note.serializeForEdit(item); const noteBody = await this.replaceResourceIdsByRelativePaths_(item);
const modNote = Object.assign({}, item, { body: noteBody });
const noteContent = await Note.serializeForEdit(modNote);
await shim.fsDriver().writeFile(noteFilePath, noteContent, 'utf-8'); await shim.fsDriver().writeFile(noteFilePath, noteContent, 'utf-8');
} }
} }