2018-03-09 22:59:12 +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' ) ;
2018-02-26 21:25:54 +02:00
class InteropService _Importer _Raw extends InteropService _Importer _Base {
2018-03-09 22:59:12 +02:00
2018-02-26 21:25:54 +02:00
async exec ( result ) {
const noteIdMap = { } ;
const folderIdMap = { } ;
const resourceIdMap = { } ;
const tagIdMap = { } ;
const createdResources = { } ;
const noteTagsToCreate = [ ] ;
const destinationFolderId = this . options _ . destinationFolderId ;
2018-03-09 22:59:12 +02:00
const replaceResourceNoteIds = ( noteBody ) => {
2018-02-26 21:25:54 +02:00
let output = noteBody ;
const resourceIds = Note . linkedResourceIds ( noteBody ) ;
for ( let i = 0 ; i < resourceIds . length ; i ++ ) {
const id = resourceIds [ i ] ;
if ( ! resourceIdMap [ id ] ) resourceIdMap [ id ] = uuid . create ( ) ;
2018-03-09 22:59:12 +02:00
output = output . replace ( new RegExp ( id , 'gi' ) , resourceIdMap [ id ] ) ;
2018-02-26 21:25:54 +02:00
}
return output ;
2018-03-09 22:59:12 +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 ;
2018-03-09 22:59:12 +02:00
}
2018-03-01 20:35:17 +02:00
let defaultFolder _ = null ;
const defaultFolder = async ( ) => {
if ( defaultFolder _ ) return defaultFolder _ ;
2018-03-09 22:59:12 +02:00
const folderTitle = await Folder . findUniqueFolderTitle ( this . options _ . defaultFolderTitle ? this . options _ . defaultFolderTitle : 'Imported' ) ;
2018-03-01 20:35:17 +02:00
defaultFolder _ = await Folder . save ( { title : folderTitle } ) ;
return defaultFolder _ ;
2018-03-09 22:59:12 +02:00
}
2018-03-01 20:35:17 +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 ;
2018-03-09 22:59:12 +02:00
if ( fileExtension ( stat . path ) . toLowerCase ( ) !== 'md' ) continue ;
2018-02-26 21:25:54 +02:00
2018-03-09 22:59:12 +02:00
const content = await shim . fsDriver ( ) . readFile ( this . sourcePath _ + '/' + stat . path ) ;
2018-02-26 21:25:54 +02:00
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-09 22:59:12 +02:00
2018-03-01 20:35:17 +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.
if ( ! folderIdMap [ item . parent _id ] ) {
if ( destinationFolderId ) {
folderIdMap [ item . parent _id ] = destinationFolderId ;
} else if ( ! folderExists ( stats , item . parent _id ) ) {
const parentFolder = await defaultFolder ( ) ;
folderIdMap [ item . parent _id ] = parentFolder . id ;
} else {
folderIdMap [ item . parent _id ] = uuid . create ( ) ;
}
}
2018-02-26 21:25:54 +02:00
const noteId = uuid . create ( ) ;
noteIdMap [ item . id ] = noteId ;
item . id = noteId ;
item . parent _id = folderIdMap [ item . parent _id ] ;
item . body = replaceResourceNoteIds ( item . body ) ;
} else if ( itemType === BaseModel . TYPE _FOLDER ) {
if ( destinationFolderId ) continue ;
if ( ! folderIdMap [ item . id ] ) folderIdMap [ item . id ] = uuid . create ( ) ;
item . id = folderIdMap [ item . id ] ;
item . title = await Folder . findUniqueFolderTitle ( item . title ) ;
} else if ( itemType === BaseModel . TYPE _RESOURCE ) {
if ( ! resourceIdMap [ item . id ] ) resourceIdMap [ item . id ] = uuid . create ( ) ;
item . id = resourceIdMap [ item . id ] ;
createdResources [ item . id ] = item ;
} else if ( itemType === BaseModel . TYPE _TAG ) {
2018-03-09 22:59:12 +02:00
const tag = await Tag . loadByTitle ( item . title ) ;
2018-02-26 21:25:54 +02:00
if ( tag ) {
tagIdMap [ item . id ] = tag . id ;
continue ;
}
const tagId = uuid . create ( ) ;
tagIdMap [ item . id ] = tagId ;
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 ] ;
const newNoteId = noteIdMap [ noteTag . note _id ] ;
const newTagId = tagIdMap [ noteTag . tag _id ] ;
if ( ! newNoteId ) {
2018-03-09 22:59:12 +02:00
result . warnings . push ( sprintf ( 'Non-existent note %s referenced in tag %s' , noteTag . note _id , noteTag . tag _id ) ) ;
2018-02-26 21:25:54 +02:00
continue ;
}
if ( ! newTagId ) {
2018-03-09 22:59:12 +02:00
result . warnings . push ( sprintf ( 'Non-existent tag %s for note %s' , noteTag . tag _id , noteTag . note _id ) ) ;
2018-02-26 21:25:54 +02:00
continue ;
}
noteTag . id = uuid . create ( ) ;
noteTag . note _id = newNoteId ;
noteTag . tag _id = newTagId ;
await NoteTag . save ( noteTag , { isNew : true } ) ;
}
2018-03-09 22:59:12 +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 ++ ) {
2018-03-09 22:59:12 +02:00
const resourceFilePath = this . sourcePath _ + '/resources/' + resourceStats [ i ] . path ;
2018-02-27 22:04:38 +02:00
const oldId = Resource . pathToId ( resourceFilePath ) ;
const newId = resourceIdMap [ oldId ] ;
if ( ! newId ) {
2018-03-09 22:59:12 +02:00
result . warnings . push ( sprintf ( 'Resource file is not referenced in any note and so was not imported: %s' , oldId ) ) ;
2018-02-27 22:04:38 +02:00
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 ;
}
2018-03-09 22:59:12 +02:00
2018-02-26 21:25:54 +02:00
}
2018-03-09 22:59:12 +02:00
module . exports = InteropService _Importer _Raw ;