2020-10-09 19:35:46 +02:00
import { ImportExportResult } from './types' ;
2021-01-22 19:41:11 +02:00
import InteropService_Importer_Base from './InteropService_Importer_Base' ;
import BaseItem from '../../models/BaseItem' ;
import BaseModel from '../../BaseModel' ;
import Resource from '../../models/Resource' ;
import Folder from '../../models/Folder' ;
import NoteTag from '../../models/NoteTag' ;
import Note from '../../models/Note' ;
import Tag from '../../models/Tag' ;
2018-02-26 21:25:54 +02:00
const { sprintf } = require ( 'sprintf-js' ) ;
2021-01-22 19:41:11 +02:00
import shim from '../../shim' ;
2020-11-05 18:58:23 +02:00
const { fileExtension } = require ( '../../path-utils' ) ;
2021-01-22 19:41:11 +02:00
import uuid from '../../uuid' ;
2018-02-26 21:25:54 +02:00
2020-10-09 19:35:46 +02:00
export default class InteropService_Importer_Raw extends InteropService_Importer_Base {
2023-03-06 16:22:01 +02:00
public async exec ( result : ImportExportResult ) {
2020-11-12 21:13:28 +02:00
const itemIdMap : any = { } ;
const createdResources : any = { } ;
2018-02-26 21:25:54 +02:00
const noteTagsToCreate = [ ] ;
const destinationFolderId = this . options_ . destinationFolderId ;
2020-11-12 21:13:28 +02:00
const replaceLinkedItemIds = async ( noteBody : string ) = > {
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
2020-11-12 21:13:28 +02:00
const folderExists = function ( stats : any [ ] , folderId : string ) {
2018-03-01 20:35:17 +02:00
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
2020-11-12 21:13:28 +02:00
let defaultFolder_ : any = null ;
2018-03-01 20:35:17 +02:00
const defaultFolder = async ( ) = > {
if ( defaultFolder_ ) return defaultFolder_ ;
2020-06-28 19:00:51 +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
2020-11-12 21:13:28 +02:00
const setFolderToImportTo = async ( itemParentId : string ) = > {
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-10 20:15:40 +02:00
if ( item . parent_id ) {
await setFolderToImportTo ( item . parent_id ) ;
item . parent_id = itemIdMap [ item . parent_id ] ;
}
2022-08-29 17:20:34 +02:00
item . title = await Folder . findUniqueItemTitle ( item . title , 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 ;
}
}