1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Desktop, Cli: Fixes #2331: Only de-duplicate imported notebook titles when needed

This commit is contained in:
Laurent Cozic 2020-06-28 18:00:51 +01:00
parent d601575549
commit eb8841379c
4 changed files with 68 additions and 4 deletions

View File

@ -79,6 +79,45 @@ describe('services_InteropService', function() {
fieldsEqual(folder3, folder1, fieldNames); fieldsEqual(folder3, folder1, fieldNames);
})); }));
it('should import folders and de-duplicate titles when needed', asyncTest(async () => {
const service = new InteropService();
const folder1 = await Folder.save({ title: 'folder' });
const folder2 = await Folder.save({ title: 'folder' });
const filePath = `${exportDir()}/test.jex`;
await service.export({ path: filePath });
await Folder.delete(folder1.id);
await Folder.delete(folder2.id);
await service.import({ path: filePath });
const allFolders = await Folder.all();
expect(allFolders.map(f => f.title).sort().join(' - ')).toBe('folder - folder (1)');
}));
it('should import folders, and only de-duplicate titles when needed', asyncTest(async () => {
const service = new InteropService();
const folder1 = await Folder.save({ title: 'folder1' });
const folder2 = await Folder.save({ title: 'folder2' });
const sub1 = await Folder.save({ title: 'Sub', parent_id: folder1.id });
const sub2 = await Folder.save({ title: 'Sub', parent_id: folder2.id });
const filePath = `${exportDir()}/test.jex`;
await service.export({ path: filePath });
await Folder.delete(folder1.id);
await Folder.delete(folder2.id);
await service.import({ path: filePath });
const importedFolder1 = await Folder.loadByTitle('folder1');
const importedFolder2 = await Folder.loadByTitle('folder2');
const importedSub1 = await Folder.load((await Folder.childrenIds(importedFolder1.id))[0]);
const importedSub2 = await Folder.load((await Folder.childrenIds(importedFolder2.id))[0]);
expect(importedSub1.title).toBe('Sub');
expect(importedSub2.title).toBe('Sub');
}));
it('should export and import folders and notes', asyncTest(async () => { it('should export and import folders and notes', asyncTest(async () => {
const service = new InteropService(); const service = new InteropService();
const folder1 = await Folder.save({ title: 'folder1' }); const folder1 = await Folder.save({ title: 'folder1' });

View File

@ -284,6 +284,21 @@ class BaseModel {
return this.modelSelectOne(sql, [fieldValue]); return this.modelSelectOne(sql, [fieldValue]);
} }
static loadByFields(fields, options = null) {
if (!options) options = {};
if (!('caseInsensitive' in options)) options.caseInsensitive = false;
if (!options.fields) options.fields = '*';
const whereSql = [];
const params = [];
for (const fieldName in fields) {
whereSql.push(`\`${fieldName}\` = ?`);
params.push(fields[fieldName]);
}
let sql = `SELECT ${this.db().escapeFields(options.fields)} FROM \`${this.tableName()}\` WHERE ${whereSql.join(' AND ')}`;
if (options.caseInsensitive) sql += ' COLLATE NOCASE';
return this.modelSelectOne(sql, params);
}
static loadByTitle(fieldValue) { static loadByTitle(fieldValue) {
return this.modelSelectOne(`SELECT * FROM \`${this.tableName()}\` WHERE \`title\` = ?`, [fieldValue]); return this.modelSelectOne(`SELECT * FROM \`${this.tableName()}\` WHERE \`title\` = ?`, [fieldValue]);
} }

View File

@ -29,11 +29,21 @@ class BaseItem extends BaseModel {
throw new Error(`Invalid class name: ${className}`); throw new Error(`Invalid class name: ${className}`);
} }
static async findUniqueItemTitle(title) { static async findUniqueItemTitle(title, parentId = null) {
let counter = 1; let counter = 1;
let titleToTry = title; let titleToTry = title;
while (true) { while (true) {
const item = await this.loadByField('title', titleToTry); let item = null;
if (parentId !== null) {
item = await this.loadByFields({
title: titleToTry,
parent_id: parentId,
});
} else {
item = await this.loadByField('title', titleToTry);
}
if (!item) return titleToTry; if (!item) return titleToTry;
titleToTry = `${title} (${counter})`; titleToTry = `${title} (${counter})`;
counter++; counter++;

View File

@ -46,7 +46,7 @@ class InteropService_Importer_Raw extends InteropService_Importer_Base {
let defaultFolder_ = null; let defaultFolder_ = null;
const defaultFolder = async () => { const defaultFolder = async () => {
if (defaultFolder_) return defaultFolder_; if (defaultFolder_) return defaultFolder_;
const folderTitle = await Folder.findUniqueItemTitle(this.options_.defaultFolderTitle ? this.options_.defaultFolderTitle : 'Imported'); const folderTitle = await Folder.findUniqueItemTitle(this.options_.defaultFolderTitle ? this.options_.defaultFolderTitle : 'Imported', '');
// eslint-disable-next-line require-atomic-updates // eslint-disable-next-line require-atomic-updates
defaultFolder_ = await Folder.save({ title: folderTitle }); defaultFolder_ = await Folder.save({ title: folderTitle });
return defaultFolder_; return defaultFolder_;
@ -96,7 +96,7 @@ class InteropService_Importer_Raw extends InteropService_Importer_Base {
if (!itemIdMap[item.id]) itemIdMap[item.id] = uuid.create(); if (!itemIdMap[item.id]) itemIdMap[item.id] = uuid.create();
item.id = itemIdMap[item.id]; item.id = itemIdMap[item.id];
item.title = await Folder.findUniqueItemTitle(item.title); item.title = await Folder.findUniqueItemTitle(item.title, item.parent_id);
if (item.parent_id) { if (item.parent_id) {
await setFolderToImportTo(item.parent_id); await setFolderToImportTo(item.parent_id);