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 { sprintf } = require ( 'sprintf-js' ) ;
const { shim } = require ( 'lib/shim' ) ;
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 ;
2019-07-29 15:43:53 +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 ;
2019-07-29 15:43:53 +02:00
} ;
2018-02-26 21:25:54 +02:00
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 ;
2019-07-29 15:43:53 +02:00
} ;
2018-03-01 20:35:17 +02:00
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' ) ;
2019-07-30 09:35:42 +02:00
// eslint-disable-next-line require-atomic-updates
2018-03-01 20:35:17 +02:00
defaultFolder _ = await Folder . save ( { title : folderTitle } ) ;
return defaultFolder _ ;
2019-07-29 15:43:53 +02:00
} ;
2018-03-01 20:35:17 +02:00
2019-07-29 15:43:53 +02:00
const setFolderToImportTo = async itemParentId => {
2018-06-10 20:15:40 +02:00
// 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 ( ) ;
2019-07-30 09:35:42 +02:00
// eslint-disable-next-line require-atomic-updates
2018-06-10 20:15:40 +02:00
itemIdMap [ itemParentId ] = parentFolder . id ;
} else {
itemIdMap [ itemParentId ] = uuid . create ( ) ;
}
}
2019-07-29 15:43:53 +02:00
} ;
2018-06-10 20:15:40 +02:00
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 ;
2019-09-19 23:51:18 +02:00
const content = await shim . fsDriver ( ) . readFile ( ` ${ this . sourcePath _ } / ${ stat . path } ` ) ;
2020-03-14 01:46:14 +02:00
const item = await BaseItem . unserialize ( content ) ;
2018-02-26 21:25:54 +02:00
const itemType = item . type _ ;
const ItemClass = BaseItem . itemClass ( item ) ;
delete item . type _ ;
if ( itemType === BaseModel . TYPE _NOTE ) {
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 ) {
2019-07-29 15:43:53 +02:00
const tag = await Tag . loadByTitle ( item . title ) ;
2018-02-26 21:25:54 +02:00
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 } ) ;
}
2019-09-19 23:51:18 +02:00
if ( await shim . fsDriver ( ) . isDirectory ( ` ${ this . sourcePath _ } /resources ` ) ) {
const resourceStats = await shim . fsDriver ( ) . readDirStats ( ` ${ this . sourcePath _ } /resources ` ) ;
2018-02-27 22:04:38 +02:00
for ( let i = 0 ; i < resourceStats . length ; i ++ ) {
2019-09-19 23:51:18 +02:00
const resourceFilePath = ` ${ this . sourcePath _ } /resources/ ${ resourceStats [ i ] . path } ` ;
2018-02-27 22:04:38 +02:00
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 ;
}
}
2019-07-29 15:43:53 +02:00
module . exports = InteropService _Importer _Raw ;