2023-12-15 20:18:11 +02:00
import { ModuleType , FileSystemItem , ImportModuleOutputFormat , ImportOptions , ExportOptions , ImportExportResult , ExportProgressState , ExportModuleOutputFormat } from './types' ;
2020-11-05 18:58:23 +02:00
import shim from '../../shim' ;
import { _ } from '../../locale' ;
2021-01-22 19:41:11 +02:00
import BaseItem from '../../models/BaseItem' ;
2021-07-25 12:51:50 +02:00
import BaseModel , { ModelType } from '../../BaseModel' ;
2021-01-22 19:41:11 +02:00
import Resource from '../../models/Resource' ;
import Folder from '../../models/Folder' ;
import NoteTag from '../../models/NoteTag' ;
import Note from '../../models/Note' ;
2022-05-26 16:57:44 +02:00
import * as ArrayUtils from '../../ArrayUtils' ;
2023-07-12 11:30:38 +02:00
import InteropService_Importer_Jex from './InteropService_Importer_Jex' ;
import InteropService_Importer_Md from './InteropService_Importer_Md' ;
import InteropService_Importer_Md_frontmatter from './InteropService_Importer_Md_frontmatter' ;
import InteropService_Importer_Raw from './InteropService_Importer_Raw' ;
import InteropService_Exporter_Jex from './InteropService_Exporter_Jex' ;
import InteropService_Exporter_Raw from './InteropService_Exporter_Raw' ;
import InteropService_Exporter_Md from './InteropService_Exporter_Md' ;
import InteropService_Exporter_Md_frontmatter from './InteropService_Exporter_Md_frontmatter' ;
import InteropService_Importer_Base from './InteropService_Importer_Base' ;
import InteropService_Exporter_Base from './InteropService_Exporter_Base' ;
2023-07-18 15:58:06 +02:00
import Module , { dynamicRequireModuleFactory , makeExportModule , makeImportModule } from './Module' ;
2018-03-09 22:59:12 +02:00
const { sprintf } = require ( 'sprintf-js' ) ;
2020-11-05 18:58:23 +02:00
const { fileExtension } = require ( '../../path-utils' ) ;
2020-10-09 19:35:46 +02:00
const EventEmitter = require ( 'events' ) ;
export default class InteropService {
2020-11-12 21:13:28 +02:00
private defaultModules_ : Module [ ] ;
private userModules_ : Module [ ] = [ ] ;
private eventEmitter_ : any = null ;
private static instance_ : InteropService ;
2020-10-09 19:35:46 +02:00
2020-11-12 21:13:28 +02:00
public static instance ( ) : InteropService {
2020-10-09 19:35:46 +02:00
if ( ! this . instance_ ) this . instance_ = new InteropService ( ) ;
return this . instance_ ;
}
2018-02-25 19:01:16 +02:00
2022-02-12 19:46:32 +02:00
public constructor ( ) {
2020-10-09 19:35:46 +02:00
this . eventEmitter_ = new EventEmitter ( ) ;
}
2023-06-30 11:30:29 +02:00
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
2022-02-12 19:46:32 +02:00
public on ( eventName : string , callback : Function ) {
2020-10-09 19:35:46 +02:00
return this . eventEmitter_ . on ( eventName , callback ) ;
}
2023-06-30 11:30:29 +02:00
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
2022-02-12 19:46:32 +02:00
public off ( eventName : string , callback : Function ) {
2020-10-09 19:35:46 +02:00
return this . eventEmitter_ . removeListener ( eventName , callback ) ;
2019-09-30 00:11:36 +02:00
}
2018-02-27 22:04:38 +02:00
2022-02-12 19:46:32 +02:00
public module s ( ) {
2020-10-09 19:35:46 +02:00
if ( ! this . defaultModules_ ) {
2023-07-12 11:30:38 +02:00
const importModules = [
makeImportModule ( {
2020-10-09 19:35:46 +02:00
format : 'jex' ,
fileExtensions : [ 'jex' ] ,
sources : [ FileSystemItem . File ] ,
description : _ ( 'Joplin Export File' ) ,
2023-07-12 11:30:38 +02:00
} , ( ) = > new InteropService_Importer_Jex ( ) ) ,
makeImportModule ( {
2020-10-09 19:35:46 +02:00
format : 'raw' ,
sources : [ FileSystemItem . Directory ] ,
description : _ ( 'Joplin Export Directory' ) ,
2023-09-11 18:25:12 +02:00
separatorAfter : true ,
2023-07-12 11:30:38 +02:00
} , ( ) = > new InteropService_Importer_Raw ( ) ) ,
makeImportModule ( {
2020-10-09 19:35:46 +02:00
format : 'enex' ,
fileExtensions : [ 'enex' ] ,
sources : [ FileSystemItem . File ] ,
2023-09-11 18:25:12 +02:00
description : _ ( 'Evernote Export File (as HTML)' ) ,
2023-07-18 15:58:06 +02:00
supportsMobile : false ,
2023-09-11 18:25:12 +02:00
outputFormat : ImportModuleOutputFormat.Html ,
} , dynamicRequireModuleFactory ( './InteropService_Importer_EnexToHtml' ) ) ,
2023-07-12 11:30:38 +02:00
makeImportModule ( {
2020-10-09 19:35:46 +02:00
format : 'enex' ,
fileExtensions : [ 'enex' ] ,
sources : [ FileSystemItem . File ] ,
2023-09-11 18:25:12 +02:00
description : _ ( 'Evernote Export File (as Markdown)' ) ,
2023-07-18 15:58:06 +02:00
supportsMobile : false ,
2023-09-11 18:25:12 +02:00
isDefault : true ,
} , dynamicRequireModuleFactory ( './InteropService_Importer_EnexToMd' ) ) ,
2024-01-10 00:03:34 +02:00
makeImportModule ( {
format : 'enex' ,
fileExtensions : [ 'enex' ] ,
sources : [ FileSystemItem . Directory ] ,
description : _ ( 'Evernote Export Files (Directory, as HTML)' ) ,
supportsMobile : false ,
outputFormat : ImportModuleOutputFormat.Html ,
} , dynamicRequireModuleFactory ( './InteropService_Importer_EnexToHtml' ) ) ,
makeImportModule ( {
format : 'enex' ,
fileExtensions : [ 'enex' ] ,
sources : [ FileSystemItem . Directory ] ,
description : _ ( 'Evernote Export Files (Directory, as Markdown)' ) ,
supportsMobile : false ,
} , dynamicRequireModuleFactory ( './InteropService_Importer_EnexToMd' ) ) ,
2023-09-11 18:25:12 +02:00
makeImportModule ( {
format : 'html' ,
fileExtensions : [ 'html' ] ,
sources : [ FileSystemItem . File , FileSystemItem . Directory ] ,
isNoteArchive : false , // Tells whether the file can contain multiple notes (eg. Enex or Jex format)
description : _ ( 'HTML document' ) ,
} , ( ) = > new InteropService_Importer_Md ( ) ) ,
makeImportModule ( {
format : 'md' ,
fileExtensions : [ 'md' , 'markdown' , 'txt' , 'html' ] ,
sources : [ FileSystemItem . File , FileSystemItem . Directory ] ,
isNoteArchive : false , // Tells whether the file can contain multiple notes (eg. Enex or Jex format)
description : _ ( 'Markdown' ) ,
} , ( ) = > new InteropService_Importer_Md ( ) ) ,
makeImportModule ( {
format : 'md_frontmatter' ,
fileExtensions : [ 'md' , 'markdown' , 'txt' , 'html' ] ,
sources : [ FileSystemItem . File , FileSystemItem . Directory ] ,
isNoteArchive : false , // Tells whether the file can contain multiple notes (eg. Enex or Jex format)
description : _ ( 'Markdown + Front Matter' ) ,
} , ( ) = > new InteropService_Importer_Md_frontmatter ( ) ) ,
makeImportModule ( {
format : 'txt' ,
fileExtensions : [ 'txt' ] ,
sources : [ FileSystemItem . File , FileSystemItem . Directory ] ,
isNoteArchive : false , // Tells whether the file can contain multiple notes (eg. Enex or Jex format)
description : _ ( 'Text document' ) ,
} , ( ) = > new InteropService_Importer_Md ( ) ) ,
2020-10-09 19:35:46 +02:00
] ;
2018-02-27 22:04:38 +02:00
2023-07-12 11:30:38 +02:00
const exportModules = [
makeExportModule ( {
2023-12-15 20:18:11 +02:00
format : ExportModuleOutputFormat.Jex ,
2020-10-09 19:35:46 +02:00
fileExtensions : [ 'jex' ] ,
target : FileSystemItem.File ,
description : _ ( 'Joplin Export File' ) ,
2023-07-12 11:30:38 +02:00
} , ( ) = > new InteropService_Exporter_Jex ( ) ) ,
makeExportModule ( {
2023-12-15 20:18:11 +02:00
format : ExportModuleOutputFormat.Raw ,
2020-10-09 19:35:46 +02:00
target : FileSystemItem.Directory ,
description : _ ( 'Joplin Export Directory' ) ,
2023-07-12 11:30:38 +02:00
} , ( ) = > new InteropService_Exporter_Raw ( ) ) ,
makeExportModule ( {
2023-12-15 20:18:11 +02:00
format : ExportModuleOutputFormat.Markdown ,
2020-10-09 19:35:46 +02:00
target : FileSystemItem.Directory ,
description : _ ( 'Markdown' ) ,
2023-07-12 11:30:38 +02:00
} , ( ) = > new InteropService_Exporter_Md ( ) ) ,
makeExportModule ( {
2023-12-15 20:18:11 +02:00
format : ExportModuleOutputFormat.MarkdownFrontMatter ,
2021-10-16 10:59:37 +02:00
target : FileSystemItem.Directory ,
description : _ ( 'Markdown + Front Matter' ) ,
2023-07-12 11:30:38 +02:00
} , ( ) = > new InteropService_Exporter_Md_frontmatter ( ) ) ,
makeExportModule ( {
2023-12-15 20:18:11 +02:00
format : ExportModuleOutputFormat.Html ,
2020-10-09 19:35:46 +02:00
fileExtensions : [ 'html' , 'htm' ] ,
target : FileSystemItem.File ,
isNoteArchive : false ,
description : _ ( 'HTML File' ) ,
2023-07-18 15:58:06 +02:00
supportsMobile : false ,
} , dynamicRequireModuleFactory ( './InteropService_Exporter_Html' ) ) ,
2023-07-12 11:30:38 +02:00
makeExportModule ( {
2023-12-15 20:18:11 +02:00
format : ExportModuleOutputFormat.Html ,
2020-10-09 19:35:46 +02:00
target : FileSystemItem.Directory ,
description : _ ( 'HTML Directory' ) ,
2023-07-18 15:58:06 +02:00
supportsMobile : false ,
} , dynamicRequireModuleFactory ( './InteropService_Exporter_Html' ) ) ,
2020-10-09 19:35:46 +02:00
] ;
2018-03-12 20:01:47 +02:00
2023-07-12 11:30:38 +02:00
this . defaultModules_ = ( importModules as Module [ ] ) . concat ( exportModules ) ;
2020-10-09 19:35:46 +02:00
}
2018-02-27 22:04:38 +02:00
2020-10-09 19:35:46 +02:00
return this . defaultModules_ . concat ( this . userModules_ ) ;
}
2020-11-12 21:13:28 +02:00
public registerModule ( module : Module ) {
2020-10-09 19:35:46 +02:00
this . userModules_ . push ( module ) ;
this . eventEmitter_ . emit ( 'modulesChanged' ) ;
2018-02-27 22:04:38 +02:00
}
2019-09-23 23:18:30 +02:00
// Find the module that matches the given type ("importer" or "exporter")
// and the given format. Some formats can have multiple assocated importers
// or exporters, such as ENEX. In this case, the one marked as "isDefault"
// is returned. This is useful to auto-detect the module based on the format.
// For more precise matching, newModuleFromPath_ should be used.
2022-02-12 19:46:32 +02:00
private findModuleByFormat_ ( type : ModuleType , format : string , target : FileSystemItem = null , outputFormat : ImportModuleOutputFormat = null ) {
2018-02-27 22:04:38 +02:00
const module s = this . module s ( ) ;
2019-09-23 23:18:30 +02:00
const matches = [ ] ;
2023-07-18 15:58:06 +02:00
const isMobile = shim . mobilePlatform ( ) !== '' ;
2018-02-27 22:04:38 +02:00
for ( let i = 0 ; i < module s.length ; i ++ ) {
const m = module s [ i ] ;
2023-07-18 15:58:06 +02:00
if ( ! m . supportsMobile && isMobile ) {
continue ;
}
2019-12-15 20:41:13 +02:00
if ( m . format === format && m . type === type ) {
2020-06-14 18:45:17 +02:00
if ( ! target && ! outputFormat ) {
2019-12-15 20:41:13 +02:00
matches . push ( m ) ;
2023-07-12 11:30:38 +02:00
} else if (
m . type === ModuleType . Exporter && target && target === m . target
) {
2020-06-14 18:45:17 +02:00
matches . push ( m ) ;
2023-07-12 11:30:38 +02:00
} else if (
m . type === ModuleType . Importer && outputFormat && outputFormat === m . outputFormat
) {
2019-12-15 20:41:13 +02:00
matches . push ( m ) ;
}
}
2018-02-26 21:16:01 +02:00
}
2019-09-23 23:18:30 +02:00
2020-05-21 10:14:33 +02:00
const output = matches . find ( m = > ! ! m . isDefault ) ;
2019-09-23 23:18:30 +02:00
if ( output ) return output ;
return matches . length ? matches [ 0 ] : null ;
2018-02-25 19:01:16 +02:00
}
2023-06-30 10:55:56 +02:00
// NOTE TO FUTURE SELF: It might make sense to simply move all the existing
// formatters to the `newModuleFromPath_` approach, so that there's only one way
// to do this mapping. This isn't a priority right now (per the convo in:
// https://github.com/laurent22/joplin/pull/1795#discussion_r322379121) but
// we can do it if it ever becomes necessary.
2022-02-12 19:46:32 +02:00
private newModuleByFormat_ ( type : ModuleType , format : string , outputFormat : ImportModuleOutputFormat = ImportModuleOutputFormat . Markdown ) {
2020-06-14 18:45:17 +02:00
const module Metadata = this . findModuleByFormat_ ( type , format , null , outputFormat ) ;
if ( ! module Metadata ) throw new Error ( _ ( 'Cannot load "%s" module for format "%s" and output "%s"' , type , format , outputFormat ) ) ;
2020-10-09 19:35:46 +02:00
2023-07-12 11:30:38 +02:00
return module Metadata.factory ( ) ;
2019-09-23 23:18:30 +02:00
}
2023-06-30 10:55:56 +02:00
// The existing `newModuleByFormat_` fn would load by the input format. This
// was fine when there was a 1-1 mapping of input formats to output formats,
// but now that we have 2 possible outputs for an `enex` input, we need to be
// explicit with which importer we want to use.
//
// https://github.com/laurent22/joplin/pull/1795#pullrequestreview-281574417
2023-07-18 15:58:06 +02:00
private newModuleFromPath_ ( type : ModuleType , options : ExportOptions & ImportOptions ) {
2019-12-15 20:41:13 +02:00
const module Metadata = this . findModuleByFormat_ ( type , options . format , options . target ) ;
2020-11-17 16:50:28 +02:00
if ( ! module Metadata ) throw new Error ( _ ( 'Cannot load "%s" module for format "%s" and target "%s"' , type , options . format , options . target ) ) ;
2020-10-09 19:35:46 +02:00
2023-07-12 11:30:38 +02:00
return module Metadata.factory ( options ) ;
2018-02-26 20:43:50 +02:00
}
2022-02-12 19:46:32 +02:00
private module ByFileExtension_ ( type : ModuleType , ext : string ) {
2018-02-27 22:04:38 +02:00
ext = ext . toLowerCase ( ) ;
const module s = this . module s ( ) ;
for ( let i = 0 ; i < module s.length ; i ++ ) {
const m = module s [ i ] ;
if ( type !== m . type ) continue ;
2023-07-12 11:30:38 +02:00
if ( m . fileExtensions . includes ( ext ) ) return m ;
2018-02-27 22:04:38 +02:00
}
return null ;
2018-02-26 21:16:01 +02:00
}
2021-07-25 12:51:50 +02:00
public async import ( options : ImportOptions ) : Promise < ImportExportResult > {
2019-07-29 15:43:53 +02:00
if ( ! ( await shim . fsDriver ( ) . exists ( options . path ) ) ) throw new Error ( _ ( 'Cannot find "%s".' , options . path ) ) ;
2018-02-26 21:16:01 +02:00
2020-10-09 19:35:46 +02:00
options = {
format : 'auto' ,
destinationFolderId : null ,
destinationFolder : null ,
. . . options ,
} ;
2018-02-25 19:01:16 +02:00
2018-03-09 22:59:12 +02:00
if ( options . format === 'auto' ) {
2020-10-09 19:35:46 +02:00
const module = this . module ByFileExtension_ ( ModuleType . Importer , fileExtension ( options . path ) ) ;
2018-03-09 22:59:12 +02:00
if ( ! module ) throw new Error ( _ ( 'Please specify import format for %s' , options . path ) ) ;
2018-02-27 22:04:38 +02:00
options . format = module .format ;
2018-02-25 19:01:16 +02:00
}
if ( options . destinationFolderId ) {
const folder = await Folder . load ( options . destinationFolderId ) ;
2018-02-27 22:04:38 +02:00
if ( ! folder ) throw new Error ( _ ( 'Cannot find "%s".' , options . destinationFolderId ) ) ;
2018-02-26 20:43:50 +02:00
options . destinationFolder = folder ;
2018-02-25 19:01:16 +02:00
}
2020-11-12 21:13:28 +02:00
let result : ImportExportResult = { warnings : [ ] } ;
2018-02-25 19:01:16 +02:00
2020-11-17 16:50:28 +02:00
const importer = this . newModuleByFormat_ ( ModuleType . Importer , options . format , options . outputFormat ) ;
2019-09-23 23:18:30 +02:00
2023-07-12 11:30:38 +02:00
if ( ! ( importer instanceof InteropService_Importer_Base ) ) {
throw new Error ( 'Resolved importer is not an importer' ) ;
}
2018-02-26 20:43:50 +02:00
await importer . init ( options . path , options ) ;
result = await importer . exec ( result ) ;
2018-02-25 19:01:16 +02:00
return result ;
2018-02-23 21:32:19 +02:00
}
2022-02-12 19:44:07 +02:00
private normalizeItemForExport ( _itemType : ModelType , item : any ) : any {
const override : any = { } ;
if ( 'is_shared' in item ) override . is_shared = 0 ;
if ( 'share_id' in item ) override . share_id = '' ;
if ( Object . keys ( override ) . length ) {
return {
. . . item ,
. . . override ,
} ;
2021-07-25 12:51:50 +02:00
} else {
return item ;
}
}
public async export ( options : ExportOptions ) : Promise < ImportExportResult > {
2020-10-09 19:35:46 +02:00
options = {
2023-12-15 20:18:11 +02:00
format : ExportModuleOutputFormat.Jex ,
2020-10-09 19:35:46 +02:00
. . . options ,
} ;
2019-12-15 20:41:13 +02:00
2018-02-23 21:32:19 +02:00
const exportPath = options . path ? options.path : null ;
2018-06-10 20:15:40 +02:00
let sourceFolderIds = options . sourceFolderIds ? options . sourceFolderIds : [ ] ;
2018-02-23 21:32:19 +02:00
const sourceNoteIds = options . sourceNoteIds ? options . sourceNoteIds : [ ] ;
2020-11-12 21:13:28 +02:00
const result : ImportExportResult = { warnings : [ ] } ;
const itemsToExport : any [ ] = [ ] ;
2018-02-23 21:32:19 +02:00
2023-07-18 15:58:06 +02:00
options . onProgress ? . ( ExportProgressState . QueuingItems , null ) ;
let totalItemsToProcess = 0 ;
2020-11-12 21:13:28 +02:00
const queueExportItem = ( itemType : number , itemOrId : any ) = > {
2023-07-18 15:58:06 +02:00
totalItemsToProcess ++ ;
2018-02-23 21:32:19 +02:00
itemsToExport . push ( {
type : itemType ,
2019-07-29 15:43:53 +02:00
itemOrId : itemOrId ,
2018-02-23 21:32:19 +02:00
} ) ;
2019-07-29 15:43:53 +02:00
} ;
2018-02-23 21:32:19 +02:00
2020-03-14 01:46:14 +02:00
const exportedNoteIds = [ ] ;
2020-11-12 21:13:28 +02:00
let resourceIds : string [ ] = [ ] ;
2020-09-07 23:12:51 +02:00
// Recursively get all the folders that have valid parents
2021-01-22 19:41:11 +02:00
const folderIds = await Folder . childrenIds ( '' ) ;
2018-02-23 21:32:19 +02:00
2020-11-17 13:50:46 +02:00
if ( options . includeConflicts ) folderIds . push ( Folder . conflictFolderId ( ) ) ;
2018-06-10 20:15:40 +02:00
let fullSourceFolderIds = sourceFolderIds . slice ( ) ;
for ( let i = 0 ; i < sourceFolderIds . length ; i ++ ) {
const id = sourceFolderIds [ i ] ;
const childrenIds = await Folder . childrenIds ( id ) ;
fullSourceFolderIds = fullSourceFolderIds . concat ( childrenIds ) ;
}
sourceFolderIds = fullSourceFolderIds ;
2018-02-23 21:32:19 +02:00
for ( let folderIndex = 0 ; folderIndex < folderIds . length ; folderIndex ++ ) {
const folderId = folderIds [ folderIndex ] ;
if ( sourceFolderIds . length && sourceFolderIds . indexOf ( folderId ) < 0 ) continue ;
if ( ! sourceNoteIds . length ) await queueExportItem ( BaseModel . TYPE_FOLDER , folderId ) ;
2020-11-17 13:50:46 +02:00
const noteIds = await Folder . noteIds ( folderId , { includeConflicts : ! ! options . includeConflicts } ) ;
2018-02-23 21:32:19 +02:00
for ( let noteIndex = 0 ; noteIndex < noteIds . length ; noteIndex ++ ) {
const noteId = noteIds [ noteIndex ] ;
if ( sourceNoteIds . length && sourceNoteIds . indexOf ( noteId ) < 0 ) continue ;
const note = await Note . load ( noteId ) ;
await queueExportItem ( BaseModel . TYPE_NOTE , note ) ;
exportedNoteIds . push ( noteId ) ;
2018-05-03 14:11:45 +02:00
const rids = await Note . linkedResourceIds ( note . body ) ;
2018-02-23 21:32:19 +02:00
resourceIds = resourceIds . concat ( rids ) ;
}
}
2018-02-26 21:25:54 +02:00
resourceIds = ArrayUtils . unique ( resourceIds ) ;
2018-02-23 21:32:19 +02:00
for ( let i = 0 ; i < resourceIds . length ; i ++ ) {
await queueExportItem ( BaseModel . TYPE_RESOURCE , resourceIds [ i ] ) ;
}
const noteTags = await NoteTag . all ( ) ;
2020-03-14 01:46:14 +02:00
const exportedTagIds = [ ] ;
2018-02-23 21:32:19 +02:00
for ( let i = 0 ; i < noteTags . length ; i ++ ) {
const noteTag = noteTags [ i ] ;
if ( exportedNoteIds . indexOf ( noteTag . note_id ) < 0 ) continue ;
await queueExportItem ( BaseModel . TYPE_NOTE_TAG , noteTag . id ) ;
exportedTagIds . push ( noteTag . tag_id ) ;
}
for ( let i = 0 ; i < exportedTagIds . length ; i ++ ) {
await queueExportItem ( BaseModel . TYPE_TAG , exportedTagIds [ i ] ) ;
}
2020-10-09 19:35:46 +02:00
const exporter = this . newModuleFromPath_ ( ModuleType . Exporter , options ) ;
2023-07-12 11:30:38 +02:00
if ( ! ( exporter instanceof InteropService_Exporter_Base ) ) {
throw new Error ( 'Resolved exporter is not an exporter' ) ;
}
2020-01-24 23:46:48 +02:00
await exporter . init ( exportPath , options ) ;
2018-02-23 21:32:19 +02:00
2018-11-21 02:36:23 +02:00
const typeOrder = [ BaseModel . TYPE_FOLDER , BaseModel . TYPE_RESOURCE , BaseModel . TYPE_NOTE , BaseModel . TYPE_TAG , BaseModel . TYPE_NOTE_TAG ] ;
2020-11-12 21:13:28 +02:00
const context : any = {
2018-11-21 02:36:23 +02:00
resourcePaths : { } ,
} ;
2021-08-23 01:35:45 +02:00
// Prepare to process each type before starting any
// This will allow exporters to operate on the full context
2018-11-21 02:36:23 +02:00
for ( let typeOrderIndex = 0 ; typeOrderIndex < typeOrder . length ; typeOrderIndex ++ ) {
const type = typeOrder [ typeOrderIndex ] ;
2020-01-18 15:16:14 +02:00
await exporter . prepareForProcessingItemType ( type , itemsToExport ) ;
2021-08-23 01:35:45 +02:00
}
2023-07-18 15:58:06 +02:00
let itemsProcessed = 0 ;
2021-08-23 01:35:45 +02:00
for ( let typeOrderIndex = 0 ; typeOrderIndex < typeOrder . length ; typeOrderIndex ++ ) {
const type = typeOrder [ typeOrderIndex ] ;
2020-01-18 15:16:14 +02:00
2018-11-21 02:36:23 +02:00
for ( let i = 0 ; i < itemsToExport . length ; i ++ ) {
const itemType = itemsToExport [ i ] . type ;
if ( itemType !== type ) continue ;
const ItemClass = BaseItem . getClassByItemType ( itemType ) ;
const itemOrId = itemsToExport [ i ] . itemOrId ;
2022-02-27 12:49:18 +02:00
const rawItem = typeof itemOrId === 'object' ? itemOrId : await ItemClass . load ( itemOrId ) ;
2018-11-21 02:36:23 +02:00
2022-02-27 12:49:18 +02:00
if ( ! rawItem ) {
2018-11-21 02:36:23 +02:00
if ( itemType === BaseModel . TYPE_RESOURCE ) {
result . warnings . push ( sprintf ( 'A resource that does not exist is referenced in a note. The resource was skipped. Resource ID: %s' , itemOrId ) ) ;
} else {
result . warnings . push ( sprintf ( 'Cannot find item with type "%s" and ID %s. Item was skipped.' , ItemClass . tableName ( ) , JSON . stringify ( itemOrId ) ) ) ;
}
continue ;
2018-02-23 21:32:19 +02:00
}
2022-02-27 12:49:18 +02:00
const item = this . normalizeItemForExport ( itemType , rawItem ) ;
2020-09-17 11:00:13 +02:00
if ( item . encryption_applied || item . encryption_blob_encrypted ) {
result . warnings . push ( sprintf ( 'This item is currently encrypted: %s "%s" (%s) and was not exported. You may wait for it to be decrypted and try again.' , BaseModel . modelTypeToName ( itemType ) , item . title ? item.title : item.id , item . id ) ) ;
continue ;
}
2018-02-27 22:51:07 +02:00
2018-11-21 02:36:23 +02:00
try {
2022-07-23 11:33:12 +02:00
if ( itemType === BaseModel . TYPE_RESOURCE ) {
2018-11-21 02:36:23 +02:00
const resourcePath = Resource . fullPath ( item ) ;
context . resourcePaths [ item . id ] = resourcePath ;
exporter . updateContext ( context ) ;
await exporter . processResource ( item , resourcePath ) ;
}
2018-02-23 21:32:19 +02:00
2020-10-09 19:35:46 +02:00
await exporter . processItem ( itemType , item ) ;
2018-11-21 02:36:23 +02:00
} catch ( error ) {
2019-12-15 20:41:13 +02:00
console . error ( error ) ;
2018-11-21 02:36:23 +02:00
result . warnings . push ( error . message ) ;
}
2023-07-18 15:58:06 +02:00
itemsProcessed ++ ;
options . onProgress ? . ( ExportProgressState . Exporting , itemsProcessed / totalItemsToProcess ) ;
2018-02-23 21:32:19 +02:00
}
}
2023-07-18 15:58:06 +02:00
options . onProgress ? . ( ExportProgressState . Closing , null ) ;
2018-02-23 21:32:19 +02:00
await exporter . close ( ) ;
return result ;
}
}