2018-02-26 21:25:54 +02:00
const InteropService _Importer _Base = require ( 'lib/services/InteropService_Importer_Base' ) ;
const BaseItem = require ( 'lib/models/BaseItem.js' ) ;
const BaseModel = require ( 'lib/BaseModel.js' ) ;
const Resource = require ( 'lib/models/Resource.js' ) ;
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 fs = require ( 'fs-extra' ) ;
const md5 = require ( 'md5' ) ;
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' ) ;
class InteropService _Importer _Raw extends InteropService _Importer _Base {
async exec ( result ) {
2018-05-03 14:11:45 +02:00
const itemIdMap = { } ;
2018-02-26 21:25:54 +02:00
const createdResources = { } ;
const noteTagsToCreate = [ ] ;
const destinationFolderId = this . options _ . destinationFolderId ;
2018-05-03 14:11:45 +02:00
const replaceLinkedItemIds = async ( noteBody ) => {
2018-02-26 21:25:54 +02:00
let output = noteBody ;
2018-05-03 14:11:45 +02:00
const itemIds = Note . linkedItemIds ( noteBody ) ;
2018-02-26 21:25:54 +02:00
2018-05-03 14:11:45 +02:00
for ( let i = 0 ; i < itemIds . length ; i ++ ) {
const id = itemIds [ i ] ;
if ( ! itemIdMap [ id ] ) itemIdMap [ id ] = uuid . create ( ) ;
output = output . replace ( new RegExp ( id , 'gi' ) , itemIdMap [ id ] ) ;
2018-02-26 21:25:54 +02:00
}
return output ;
}
const stats = await shim . fsDriver ( ) . readDirStats ( this . sourcePath _ ) ;
2018-03-01 20:35:17 +02:00
const folderExists = function ( stats , folderId ) {
folderId = folderId . toLowerCase ( ) ;
for ( let i = 0 ; i < stats . length ; i ++ ) {
const stat = stats [ i ] ;
const statId = BaseItem . pathToId ( stat . path ) ;
if ( statId . toLowerCase ( ) === folderId ) return true ;
}
return false ;
}
let defaultFolder _ = null ;
const defaultFolder = async ( ) => {
if ( defaultFolder _ ) return defaultFolder _ ;
2018-06-27 22:45:31 +02:00
const folderTitle = await Folder . findUniqueItemTitle ( this . options _ . defaultFolderTitle ? this . options _ . defaultFolderTitle : 'Imported' ) ;
2018-03-01 20:35:17 +02:00
defaultFolder _ = await Folder . save ( { title : folderTitle } ) ;
return defaultFolder _ ;
}
2018-06-10 20:15:40 +02:00
const setFolderToImportTo = async ( itemParentId ) => {
// Logic is a bit complex here:
// - If a destination folder was specified, move the note to it.
// - Otherwise, if the associated folder exists, use this.
// - If it doesn't exist, use the default folder. This is the case for example when importing JEX archives that contain only one or more notes, but no folder.
const itemParentExists = folderExists ( stats , itemParentId ) ;
if ( ! itemIdMap [ itemParentId ] ) {
if ( destinationFolderId ) {
itemIdMap [ itemParentId ] = destinationFolderId ;
} else if ( ! itemParentExists ) {
const parentFolder = await defaultFolder ( ) ;
itemIdMap [ itemParentId ] = parentFolder . id ;
} else {
itemIdMap [ itemParentId ] = uuid . create ( ) ;
}
}
}
2018-02-26 21:25:54 +02:00
for ( let i = 0 ; i < stats . length ; i ++ ) {
const stat = stats [ i ] ;
if ( stat . isDirectory ( ) ) continue ;
if ( fileExtension ( stat . path ) . toLowerCase ( ) !== 'md' ) continue ;
const content = await shim . fsDriver ( ) . readFile ( this . sourcePath _ + '/' + stat . path ) ;
let item = await BaseItem . unserialize ( content ) ;
const itemType = item . type _ ;
const ItemClass = BaseItem . itemClass ( item ) ;
delete item . type _ ;
if ( itemType === BaseModel . TYPE _NOTE ) {
2018-03-01 20:35:17 +02:00
2018-06-10 20:15:40 +02:00
await setFolderToImportTo ( item . parent _id ) ;
2018-03-01 20:35:17 +02:00
2018-05-03 14:11:45 +02:00
if ( ! itemIdMap [ item . id ] ) itemIdMap [ item . id ] = uuid . create ( ) ;
2018-06-10 20:15:40 +02:00
item . id = itemIdMap [ item . id ] ;
2018-05-03 14:11:45 +02:00
item . parent _id = itemIdMap [ item . parent _id ] ;
item . body = await replaceLinkedItemIds ( item . body ) ;
2018-02-26 21:25:54 +02:00
} else if ( itemType === BaseModel . TYPE _FOLDER ) {
if ( destinationFolderId ) continue ;
2018-05-03 14:11:45 +02:00
if ( ! itemIdMap [ item . id ] ) itemIdMap [ item . id ] = uuid . create ( ) ;
item . id = itemIdMap [ item . id ] ;
2018-06-27 22:45:31 +02:00
item . title = await Folder . findUniqueItemTitle ( item . title ) ;
2018-06-10 20:15:40 +02:00
if ( item . parent _id ) {
await setFolderToImportTo ( item . parent _id ) ;
item . parent _id = itemIdMap [ item . parent _id ] ;
}
2018-02-26 21:25:54 +02:00
} else if ( itemType === BaseModel . TYPE _RESOURCE ) {
2018-05-03 14:11:45 +02:00
if ( ! itemIdMap [ item . id ] ) itemIdMap [ item . id ] = uuid . create ( ) ;
item . id = itemIdMap [ item . id ] ;
2018-02-26 21:25:54 +02:00
createdResources [ item . id ] = item ;
} else if ( itemType === BaseModel . TYPE _TAG ) {
const tag = await Tag . loadByTitle ( item . title ) ;
if ( tag ) {
2018-05-03 14:11:45 +02:00
itemIdMap [ item . id ] = tag . id ;
2018-02-26 21:25:54 +02:00
continue ;
}
const tagId = uuid . create ( ) ;
2018-05-03 14:11:45 +02:00
itemIdMap [ item . id ] = tagId ;
2018-02-26 21:25:54 +02:00
item . id = tagId ;
} else if ( itemType === BaseModel . TYPE _NOTE _TAG ) {
noteTagsToCreate . push ( item ) ;
continue ;
}
await ItemClass . save ( item , { isNew : true , autoTimestamp : false } ) ;
}
for ( let i = 0 ; i < noteTagsToCreate . length ; i ++ ) {
const noteTag = noteTagsToCreate [ i ] ;
2018-05-03 14:11:45 +02:00
const newNoteId = itemIdMap [ noteTag . note _id ] ;
const newTagId = itemIdMap [ noteTag . tag _id ] ;
2018-02-26 21:25:54 +02:00
if ( ! newNoteId ) {
result . warnings . push ( sprintf ( 'Non-existent note %s referenced in tag %s' , noteTag . note _id , noteTag . tag _id ) ) ;
continue ;
}
if ( ! newTagId ) {
result . warnings . push ( sprintf ( 'Non-existent tag %s for note %s' , noteTag . tag _id , noteTag . note _id ) ) ;
continue ;
}
noteTag . id = uuid . create ( ) ;
noteTag . note _id = newNoteId ;
noteTag . tag _id = newTagId ;
await NoteTag . save ( noteTag , { isNew : true } ) ;
}
2018-02-27 22:04:38 +02:00
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 ) ;
2018-05-03 14:11:45 +02:00
const newId = itemIdMap [ oldId ] ;
2018-02-27 22:04:38 +02:00
if ( ! newId ) {
result . warnings . push ( sprintf ( 'Resource file is not referenced in any note and so was not imported: %s' , oldId ) ) ;
continue ;
}
2018-02-26 21:25:54 +02:00
2018-02-27 22:04:38 +02:00
const resource = createdResources [ newId ] ;
const destPath = Resource . fullPath ( resource ) ;
await shim . fsDriver ( ) . copy ( resourceFilePath , destPath ) ;
}
2018-02-26 21:25:54 +02:00
}
return result ;
}
}
module . exports = InteropService _Importer _Raw ;