mirror of https://github.com/laurent22/joplin.git
All: Add support for application plugins (#3257)
parent
833fb1264f
commit
fe41d37f8f
|
@ -59,15 +59,25 @@ Tools/node_modules
|
|||
Tools/PortableAppsLauncher
|
||||
Modules/TinyMCE/IconPack/postinstall.js
|
||||
Modules/TinyMCE/langs/
|
||||
CliClient/build/
|
||||
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
CliClient/app/LinkSelector.js
|
||||
CliClient/build/LinkSelector.js
|
||||
CliClient/app/services/plugins/PluginRunner.js
|
||||
CliClient/tests/models_Setting.js
|
||||
CliClient/tests/services_CommandService.js
|
||||
CliClient/tests/services_InteropService.js
|
||||
CliClient/tests/services_PluginService.js
|
||||
CliClient/tests/services/plugins/sandboxProxy.js
|
||||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.js
|
||||
ElectronClient/app.js
|
||||
ElectronClient/bridge.js
|
||||
ElectronClient/commands/copyDevCommand.js
|
||||
ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/ElectronAppWrapper.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/Button/Button.js
|
||||
ElectronClient/gui/ConfigScreen/ButtonBar.js
|
||||
|
@ -103,6 +113,7 @@ ElectronClient/gui/MainScreen/commands/toggleNoteList.js
|
|||
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
|
@ -138,7 +149,7 @@ ElectronClient/gui/NoteEditor/utils/useFormNote.js
|
|||
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteToolbarButtons.js
|
||||
ElectronClient/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
|
||||
|
@ -154,6 +165,7 @@ ElectronClient/gui/ResizableLayout/hooks/useWindowResizeEvent.js
|
|||
ElectronClient/gui/ResizableLayout/ResizableLayout.js
|
||||
ElectronClient/gui/ResourceScreen.js
|
||||
ElectronClient/gui/Root_UpgradeSyncTarget.js
|
||||
ElectronClient/gui/Root.js
|
||||
ElectronClient/gui/SearchBar/hooks/useSearch.js
|
||||
ElectronClient/gui/SearchBar/SearchBar.js
|
||||
ElectronClient/gui/SearchBar/styles/index.js
|
||||
|
@ -169,12 +181,25 @@ ElectronClient/gui/ToggleEditorsButton/ToggleEditorsButton.js
|
|||
ElectronClient/gui/ToolbarBase.js
|
||||
ElectronClient/gui/ToolbarButton/styles/index.js
|
||||
ElectronClient/gui/ToolbarButton/ToolbarButton.js
|
||||
ElectronClient/gui/utils/NoteListUtils.js
|
||||
ElectronClient/InteropServiceHelper.js
|
||||
ElectronClient/services/bridge.js
|
||||
ElectronClient/services/plugins/hooks/useThemeCss.js
|
||||
ElectronClient/services/plugins/hooks/useViewIsReady.js
|
||||
ElectronClient/services/plugins/PlatformImplementation.js
|
||||
ElectronClient/services/plugins/PluginRunner.js
|
||||
ElectronClient/services/plugins/UserWebview.js
|
||||
ElectronClient/services/plugins/UserWebviewDialog.js
|
||||
ElectronClient/services/plugins/UserWebviewDialogButtonBar.js
|
||||
ReactNativeClient/lib/AsyncActionQueue.js
|
||||
ReactNativeClient/lib/BaseApplication.js
|
||||
ReactNativeClient/lib/checkPermissions.js
|
||||
ReactNativeClient/lib/commands/historyBackward.js
|
||||
ReactNativeClient/lib/commands/historyForward.js
|
||||
ReactNativeClient/lib/commands/synchronize.js
|
||||
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
|
||||
ReactNativeClient/lib/errorUtils.js
|
||||
ReactNativeClient/lib/eventManager.js
|
||||
ReactNativeClient/lib/hooks/useEffectDebugger.js
|
||||
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
|
||||
ReactNativeClient/lib/hooks/usePrevious.js
|
||||
|
@ -184,18 +209,79 @@ ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
|||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||
ReactNativeClient/lib/JoplinServerApi.js
|
||||
ReactNativeClient/lib/locale.js
|
||||
ReactNativeClient/lib/Logger.js
|
||||
ReactNativeClient/lib/markdownUtils.js
|
||||
ReactNativeClient/lib/models/Alarm.js
|
||||
ReactNativeClient/lib/models/Setting.js
|
||||
ReactNativeClient/lib/ntpDate.js
|
||||
ReactNativeClient/lib/reducer.js
|
||||
ReactNativeClient/lib/services/AlarmService.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriver.android.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriver.ios.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriverNode.js
|
||||
ReactNativeClient/lib/services/BaseService.js
|
||||
ReactNativeClient/lib/services/BooleanExpression.js
|
||||
ReactNativeClient/lib/services/commands/MenuUtils.js
|
||||
ReactNativeClient/lib/services/commands/propsHaveChanged.js
|
||||
ReactNativeClient/lib/services/commands/ToolbarButtonUtils.js
|
||||
ReactNativeClient/lib/services/CommandService.js
|
||||
ReactNativeClient/lib/services/contextkey/contextkey.js
|
||||
ReactNativeClient/lib/services/debug/populateDatabase.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Base.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Custom.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Html.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Jex.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Md.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Raw.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Base.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Custom.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToHtml.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToMd.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Jex.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Md.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Raw.js
|
||||
ReactNativeClient/lib/services/interop/InteropService.js
|
||||
ReactNativeClient/lib/services/interop/types.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainService.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.node.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriverBase.js
|
||||
ReactNativeClient/lib/services/KeymapService.js
|
||||
ReactNativeClient/lib/services/plugins/api/Global.js
|
||||
ReactNativeClient/lib/services/plugins/api/Joplin.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinCommands.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinData.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinFilters.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinInterop.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinPlugins.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinSettings.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViews.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsDialogs.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsMenuItems.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsPanels.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsToolbarButtons.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinWorkspace.js
|
||||
ReactNativeClient/lib/services/plugins/api/types.js
|
||||
ReactNativeClient/lib/services/plugins/BasePluginRunner.js
|
||||
ReactNativeClient/lib/services/plugins/MenuItemController.js
|
||||
ReactNativeClient/lib/services/plugins/Plugin.js
|
||||
ReactNativeClient/lib/services/plugins/PluginService.js
|
||||
ReactNativeClient/lib/services/plugins/reducer.js
|
||||
ReactNativeClient/lib/services/plugins/sandboxProxy.js
|
||||
ReactNativeClient/lib/services/plugins/ToolbarButtonController.js
|
||||
ReactNativeClient/lib/services/plugins/utils/createViewHandle.js
|
||||
ReactNativeClient/lib/services/plugins/utils/executeSandboxCall.js
|
||||
ReactNativeClient/lib/services/plugins/utils/manifestFromObject.js
|
||||
ReactNativeClient/lib/services/plugins/utils/mapEventHandlersToIds.js
|
||||
ReactNativeClient/lib/services/plugins/utils/types.js
|
||||
ReactNativeClient/lib/services/plugins/ViewController.js
|
||||
ReactNativeClient/lib/services/plugins/WebviewController.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/index.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
|
||||
ReactNativeClient/lib/services/rest/actionApi.desktop.js
|
||||
ReactNativeClient/lib/services/rest/Api.js
|
||||
ReactNativeClient/lib/services/rest/errors.js
|
||||
ReactNativeClient/lib/services/searchengine/filterParser.js
|
||||
ReactNativeClient/lib/services/searchengine/queryBuilder.js
|
||||
|
@ -209,6 +295,8 @@ ReactNativeClient/lib/services/synchronizer/utils/types.js
|
|||
ReactNativeClient/lib/services/UndoRedoService.js
|
||||
ReactNativeClient/lib/ShareExtension.js
|
||||
ReactNativeClient/lib/shareHandler.js
|
||||
ReactNativeClient/lib/shim.js
|
||||
ReactNativeClient/lib/Synchronizer.js
|
||||
ReactNativeClient/lib/theme.js
|
||||
ReactNativeClient/lib/themes/aritimDark.js
|
||||
ReactNativeClient/lib/themes/dark.js
|
||||
|
@ -219,6 +307,7 @@ ReactNativeClient/lib/themes/oledDark.js
|
|||
ReactNativeClient/lib/themes/solarizedDark.js
|
||||
ReactNativeClient/lib/themes/solarizedLight.js
|
||||
ReactNativeClient/lib/themes/type.js
|
||||
ReactNativeClient/lib/uuid.js
|
||||
ReactNativeClient/lib/versionInfo.js
|
||||
ReactNativeClient/PluginAssetsLoader.js
|
||||
ReactNativeClient/setUpQuickActions.js
|
||||
|
|
16
.eslintrc.js
16
.eslintrc.js
|
@ -48,8 +48,9 @@ module.exports = {
|
|||
// -------------------------------
|
||||
'react/jsx-uses-react': 'error',
|
||||
'react/jsx-uses-vars': 'error',
|
||||
'no-unused-vars': 'error',
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'no-unused-vars': ['error', { 'argsIgnorePattern': '^_' }],
|
||||
'@typescript-eslint/no-unused-vars': ['error', { 'argsIgnorePattern': '^_' }],
|
||||
'@typescript-eslint/explicit-member-accessibility': 'off',
|
||||
'no-constant-condition': 0,
|
||||
'no-prototype-builtins': 0,
|
||||
// This error is always a false positive so far since it detects
|
||||
|
@ -121,4 +122,15 @@ module.exports = {
|
|||
'react-hooks',
|
||||
'import',
|
||||
],
|
||||
'overrides': [
|
||||
{
|
||||
// enable the rule specifically for TypeScript files
|
||||
'files': ['*.ts', '*.tsx'],
|
||||
'rules': {
|
||||
// Warn only because it would make it difficult to convert JS classes to TypeScript, unless we
|
||||
// make everything public which is not great. New code however should specify member accessibility.
|
||||
'@typescript-eslint/explicit-member-accessibility': ['warn'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -52,15 +52,26 @@ Tools/commit_hook.txt
|
|||
*.map
|
||||
ReactNativeClient/lib/sql-extensions/spellfix.so
|
||||
ReactNativeClient/lib/sql-extensions/spellfix.dylib
|
||||
CliClient/build/
|
||||
plugin_types/
|
||||
|
||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||
CliClient/app/LinkSelector.js
|
||||
CliClient/build/LinkSelector.js
|
||||
CliClient/app/services/plugins/PluginRunner.js
|
||||
CliClient/tests/models_Setting.js
|
||||
CliClient/tests/services_CommandService.js
|
||||
CliClient/tests/services_InteropService.js
|
||||
CliClient/tests/services_PluginService.js
|
||||
CliClient/tests/services/plugins/sandboxProxy.js
|
||||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.js
|
||||
ElectronClient/app.js
|
||||
ElectronClient/bridge.js
|
||||
ElectronClient/commands/copyDevCommand.js
|
||||
ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/ElectronAppWrapper.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/Button/Button.js
|
||||
ElectronClient/gui/ConfigScreen/ButtonBar.js
|
||||
|
@ -96,6 +107,7 @@ ElectronClient/gui/MainScreen/commands/toggleNoteList.js
|
|||
ElectronClient/gui/MainScreen/commands/toggleSidebar.js
|
||||
ElectronClient/gui/MainScreen/commands/toggleVisiblePanes.js
|
||||
ElectronClient/gui/MainScreen/MainScreen.js
|
||||
ElectronClient/gui/MenuBar.js
|
||||
ElectronClient/gui/MultiNoteActions.js
|
||||
ElectronClient/gui/NoteContentPropertiesDialog.js
|
||||
ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.js
|
||||
|
@ -131,7 +143,7 @@ ElectronClient/gui/NoteEditor/utils/useFormNote.js
|
|||
ElectronClient/gui/NoteEditor/utils/useMarkupToHtml.js
|
||||
ElectronClient/gui/NoteEditor/utils/useMessageHandler.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||
ElectronClient/gui/NoteEditor/utils/useNoteToolbarButtons.js
|
||||
ElectronClient/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||
ElectronClient/gui/NoteEditor/utils/useSearchMarkers.js
|
||||
ElectronClient/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||
ElectronClient/gui/NoteList/commands/focusElementNoteList.js
|
||||
|
@ -147,6 +159,7 @@ ElectronClient/gui/ResizableLayout/hooks/useWindowResizeEvent.js
|
|||
ElectronClient/gui/ResizableLayout/ResizableLayout.js
|
||||
ElectronClient/gui/ResourceScreen.js
|
||||
ElectronClient/gui/Root_UpgradeSyncTarget.js
|
||||
ElectronClient/gui/Root.js
|
||||
ElectronClient/gui/SearchBar/hooks/useSearch.js
|
||||
ElectronClient/gui/SearchBar/SearchBar.js
|
||||
ElectronClient/gui/SearchBar/styles/index.js
|
||||
|
@ -162,12 +175,25 @@ ElectronClient/gui/ToggleEditorsButton/ToggleEditorsButton.js
|
|||
ElectronClient/gui/ToolbarBase.js
|
||||
ElectronClient/gui/ToolbarButton/styles/index.js
|
||||
ElectronClient/gui/ToolbarButton/ToolbarButton.js
|
||||
ElectronClient/gui/utils/NoteListUtils.js
|
||||
ElectronClient/InteropServiceHelper.js
|
||||
ElectronClient/services/bridge.js
|
||||
ElectronClient/services/plugins/hooks/useThemeCss.js
|
||||
ElectronClient/services/plugins/hooks/useViewIsReady.js
|
||||
ElectronClient/services/plugins/PlatformImplementation.js
|
||||
ElectronClient/services/plugins/PluginRunner.js
|
||||
ElectronClient/services/plugins/UserWebview.js
|
||||
ElectronClient/services/plugins/UserWebviewDialog.js
|
||||
ElectronClient/services/plugins/UserWebviewDialogButtonBar.js
|
||||
ReactNativeClient/lib/AsyncActionQueue.js
|
||||
ReactNativeClient/lib/BaseApplication.js
|
||||
ReactNativeClient/lib/checkPermissions.js
|
||||
ReactNativeClient/lib/commands/historyBackward.js
|
||||
ReactNativeClient/lib/commands/historyForward.js
|
||||
ReactNativeClient/lib/commands/synchronize.js
|
||||
ReactNativeClient/lib/components/screens/UpgradeSyncTargetScreen.js
|
||||
ReactNativeClient/lib/errorUtils.js
|
||||
ReactNativeClient/lib/eventManager.js
|
||||
ReactNativeClient/lib/hooks/useEffectDebugger.js
|
||||
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
|
||||
ReactNativeClient/lib/hooks/usePrevious.js
|
||||
|
@ -177,18 +203,79 @@ ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
|||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||
ReactNativeClient/lib/JoplinServerApi.js
|
||||
ReactNativeClient/lib/locale.js
|
||||
ReactNativeClient/lib/Logger.js
|
||||
ReactNativeClient/lib/markdownUtils.js
|
||||
ReactNativeClient/lib/models/Alarm.js
|
||||
ReactNativeClient/lib/models/Setting.js
|
||||
ReactNativeClient/lib/ntpDate.js
|
||||
ReactNativeClient/lib/reducer.js
|
||||
ReactNativeClient/lib/services/AlarmService.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriver.android.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriver.ios.js
|
||||
ReactNativeClient/lib/services/AlarmServiceDriverNode.js
|
||||
ReactNativeClient/lib/services/BaseService.js
|
||||
ReactNativeClient/lib/services/BooleanExpression.js
|
||||
ReactNativeClient/lib/services/commands/MenuUtils.js
|
||||
ReactNativeClient/lib/services/commands/propsHaveChanged.js
|
||||
ReactNativeClient/lib/services/commands/ToolbarButtonUtils.js
|
||||
ReactNativeClient/lib/services/CommandService.js
|
||||
ReactNativeClient/lib/services/contextkey/contextkey.js
|
||||
ReactNativeClient/lib/services/debug/populateDatabase.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Base.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Custom.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Html.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Jex.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Md.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Exporter_Raw.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Base.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Custom.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToHtml.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_EnexToMd.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Jex.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Md.js
|
||||
ReactNativeClient/lib/services/interop/InteropService_Importer_Raw.js
|
||||
ReactNativeClient/lib/services/interop/InteropService.js
|
||||
ReactNativeClient/lib/services/interop/types.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainService.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.dummy.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.mobile.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriver.node.js
|
||||
ReactNativeClient/lib/services/keychain/KeychainServiceDriverBase.js
|
||||
ReactNativeClient/lib/services/KeymapService.js
|
||||
ReactNativeClient/lib/services/plugins/api/Global.js
|
||||
ReactNativeClient/lib/services/plugins/api/Joplin.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinCommands.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinData.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinFilters.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinInterop.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinPlugins.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinSettings.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViews.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsDialogs.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsMenuItems.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsPanels.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinViewsToolbarButtons.js
|
||||
ReactNativeClient/lib/services/plugins/api/JoplinWorkspace.js
|
||||
ReactNativeClient/lib/services/plugins/api/types.js
|
||||
ReactNativeClient/lib/services/plugins/BasePluginRunner.js
|
||||
ReactNativeClient/lib/services/plugins/MenuItemController.js
|
||||
ReactNativeClient/lib/services/plugins/Plugin.js
|
||||
ReactNativeClient/lib/services/plugins/PluginService.js
|
||||
ReactNativeClient/lib/services/plugins/reducer.js
|
||||
ReactNativeClient/lib/services/plugins/sandboxProxy.js
|
||||
ReactNativeClient/lib/services/plugins/ToolbarButtonController.js
|
||||
ReactNativeClient/lib/services/plugins/utils/createViewHandle.js
|
||||
ReactNativeClient/lib/services/plugins/utils/executeSandboxCall.js
|
||||
ReactNativeClient/lib/services/plugins/utils/manifestFromObject.js
|
||||
ReactNativeClient/lib/services/plugins/utils/mapEventHandlersToIds.js
|
||||
ReactNativeClient/lib/services/plugins/utils/types.js
|
||||
ReactNativeClient/lib/services/plugins/ViewController.js
|
||||
ReactNativeClient/lib/services/plugins/WebviewController.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/index.js
|
||||
ReactNativeClient/lib/services/ResourceEditWatcher/reducer.js
|
||||
ReactNativeClient/lib/services/rest/actionApi.desktop.js
|
||||
ReactNativeClient/lib/services/rest/Api.js
|
||||
ReactNativeClient/lib/services/rest/errors.js
|
||||
ReactNativeClient/lib/services/searchengine/filterParser.js
|
||||
ReactNativeClient/lib/services/searchengine/queryBuilder.js
|
||||
|
@ -202,6 +289,8 @@ ReactNativeClient/lib/services/synchronizer/utils/types.js
|
|||
ReactNativeClient/lib/services/UndoRedoService.js
|
||||
ReactNativeClient/lib/ShareExtension.js
|
||||
ReactNativeClient/lib/shareHandler.js
|
||||
ReactNativeClient/lib/shim.js
|
||||
ReactNativeClient/lib/Synchronizer.js
|
||||
ReactNativeClient/lib/theme.js
|
||||
ReactNativeClient/lib/themes/aritimDark.js
|
||||
ReactNativeClient/lib/themes/dark.js
|
||||
|
@ -212,6 +301,7 @@ ReactNativeClient/lib/themes/oledDark.js
|
|||
ReactNativeClient/lib/themes/solarizedDark.js
|
||||
ReactNativeClient/lib/themes/solarizedLight.js
|
||||
ReactNativeClient/lib/themes/type.js
|
||||
ReactNativeClient/lib/uuid.js
|
||||
ReactNativeClient/lib/versionInfo.js
|
||||
ReactNativeClient/PluginAssetsLoader.js
|
||||
ReactNativeClient/setUpQuickActions.js
|
||||
|
|
2
BUILD.md
2
BUILD.md
|
@ -96,4 +96,4 @@ The Markdown renderer is located under ReactNativeClient/lib/joplin-renderer. Wh
|
|||
|
||||
# Troubleshooting
|
||||
|
||||
Please read for the [Build Troubleshooting Document](https://github.com/laurent22/joplin/blob/master/readme/build_troubleshooting.md) for various tips on how to get the build working.
|
||||
Please read for the [Build Troubleshooting Document](https://github.com/laurent22/joplin/blob/dev/readme/build_troubleshooting.md) for various tips on how to get the build working.
|
|
@ -1,4 +1,4 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { netUtils } = require('lib/net-utils.js');
|
||||
|
||||
const http = require('http');
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { reducer, defaultState } = require('lib/reducer.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const reducer = require('lib/reducer').default;
|
||||
const { defaultState } = require('lib/reducer');
|
||||
const { splitCommandString } = require('lib/string-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const shim = require('lib/shim').default;
|
||||
const Entities = require('html-entities').AllHtmlEntities;
|
||||
const htmlentities = new Entities().encode;
|
||||
|
||||
|
@ -477,7 +479,7 @@ class AppGui {
|
|||
this.linkSelector_.noteX + cursorOffsetX,
|
||||
this.linkSelector_.noteY + cursorOffsetY
|
||||
);
|
||||
setTimeout(() => this.term_.term().inverse(this.linkSelector_.link), 50);
|
||||
shim.setTimeout(() => this.term_.term().inverse(this.linkSelector_.link), 50);
|
||||
}
|
||||
} else if (cmd === 'open_link') {
|
||||
if (this.widget('noteText').hasFocus) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { BaseApplication } = require('lib/BaseApplication');
|
||||
const BaseApplication = require('lib/BaseApplication').default;
|
||||
const { FoldersScreenUtils } = require('lib/folders-screen-utils.js');
|
||||
const ResourceService = require('lib/services/ResourceService');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
|
@ -6,14 +6,15 @@ const Folder = require('lib/models/Folder.js');
|
|||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { fileExtension } = require('lib/path-utils.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const fs = require('fs-extra');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const Cache = require('lib/Cache');
|
||||
const RevisionService = require('lib/services/RevisionService');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class Application extends BaseApplication {
|
||||
constructor() {
|
||||
|
@ -161,7 +162,7 @@ class Application extends BaseApplication {
|
|||
};
|
||||
|
||||
// Give it a few seconds to cancel otherwise exit anyway
|
||||
setTimeout(async () => {
|
||||
shim.setTimeout(async () => {
|
||||
await doExit();
|
||||
}, 5000);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { reg } = require('lib/registry.js');
|
||||
|
||||
class BaseCommand {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const fs = require('fs-extra');
|
||||
const { fileExtension, dirname } = require('lib/path-utils.js');
|
||||
const wrap_ = require('word-wrap');
|
||||
const { languageCode } = require('lib/locale.js');
|
||||
const { languageCode } = require('lib/locale');
|
||||
|
||||
const rootDir = dirname(dirname(__dirname));
|
||||
const MAX_WIDTH = 78;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
'use strict';
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { dirname } = require('lib/path-utils.js');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const exec = require('child_process').exec;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const yargParser = require('yargs-parser');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const stringPadding = require('string-padding');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
|
||||
const cliUtils = {};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ const BaseItem = require('lib/models/BaseItem');
|
|||
const BaseModel = require('lib/BaseModel');
|
||||
const { toTitleCase } = require('lib/string-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const markdownUtils = require('lib/markdownUtils');
|
||||
const markdownUtils = require('lib/markdownUtils').default;
|
||||
const { Database } = require('lib/database.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
@ -53,9 +53,9 @@ class Command extends BaseCommand {
|
|||
|
||||
const lines = [];
|
||||
|
||||
lines.push('# Joplin API');
|
||||
lines.push('# Joplin Data API');
|
||||
lines.push('');
|
||||
|
||||
lines.push('This API is available when the clipper server is running. It provides access to the notes, notebooks, tags and other Joplin object via a REST API. Plugins can also access this API even when the clipper server is not running.');
|
||||
lines.push('');
|
||||
lines.push('In order to use it, you\'ll first need to find on which port the service is running. To do so, open the Web Clipper Options in Joplin and if the service is running it should tell you on which port. Normally it runs on port **41184**. If you want to find it programmatically, you may follow this kind of algorithm:');
|
||||
lines.push('');
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _, setLocale } = require('lib/locale.js');
|
||||
const { _, setLocale } = require('lib/locale');
|
||||
const { app } = require('./app.js');
|
||||
const fs = require('fs-extra');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
const BaseItem = require('lib/models/BaseItem');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const shim = require('lib/shim').default;
|
||||
const pathUtils = require('lib/path-utils.js');
|
||||
const imageType = require('image-type');
|
||||
const readChunk = require('read-chunk');
|
||||
|
@ -38,7 +38,7 @@ class Command extends BaseCommand {
|
|||
this.stdout(_('Operation cancelled'));
|
||||
return false;
|
||||
}
|
||||
Setting.setObjectKey('encryption.passwordCache', masterKeyId, password);
|
||||
Setting.setObjectValue('encryption.passwordCache', masterKeyId, password);
|
||||
await EncryptionService.instance().loadMasterKeysFromSettings();
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
const fs = require('fs-extra');
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { splitCommandString } = require('lib/string-utils.js');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const uuid = require('lib/uuid').default;
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const InteropService = require('lib/services/InteropService.js');
|
||||
const InteropService = require('lib/services/interop/InteropService').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
@ -14,7 +14,7 @@ class Command extends BaseCommand {
|
|||
}
|
||||
|
||||
options() {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const formats = service
|
||||
.modules()
|
||||
.filter(m => m.type === 'exporter' && m.format !== 'html')
|
||||
|
@ -41,7 +41,7 @@ class Command extends BaseCommand {
|
|||
exportOptions.sourceFolderIds = folders.map(n => n.id);
|
||||
}
|
||||
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const result = await service.export(exportOptions);
|
||||
|
||||
result.warnings.map(w => this.stdout(w));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { renderCommandHelp } = require('./help-utils.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const InteropService = require('lib/services/InteropService.js');
|
||||
const InteropService = require('lib/services/interop/InteropService').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
@ -15,7 +15,7 @@ class Command extends BaseCommand {
|
|||
}
|
||||
|
||||
options() {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const formats = service
|
||||
.modules()
|
||||
.filter(m => m.type === 'importer')
|
||||
|
@ -63,7 +63,7 @@ class Command extends BaseCommand {
|
|||
|
||||
app().gui().showConsole();
|
||||
this.stdout(_('Importing notes...'));
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const result = await service.import(importOptions);
|
||||
result.warnings.map(w => this.stdout(w));
|
||||
cliUtils.redrawDone();
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const uuid = require('lib/uuid').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const { _ } = require('lib/locale');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Logger = require('lib/Logger').default;
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { _ } = require('lib/locale');
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { OneDriveApiNodeUtils } = require('lib/onedrive-api-node-utils.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const Synchronizer = require('lib/Synchronizer').default;
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const md5 = require('md5');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
const CommandDone = require('./command-done.js');
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { BaseCommand } = require('./base-command.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const { dirname } = require('lib/path-utils.js');
|
||||
const { FsDriverNode } = require('./fs-driver-node.js');
|
||||
|
|
|
@ -2,7 +2,7 @@ const Folder = require('lib/models/Folder.js');
|
|||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||
const _ = require('lib/locale.js')._;
|
||||
const _ = require('lib/locale')._;
|
||||
|
||||
class FolderListWidget extends ListWidget {
|
||||
constructor() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const Note = require('lib/models/Note.js');
|
||||
const TextWidget = require('tkwidgets/TextWidget.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
class NoteWidget extends TextWidget {
|
||||
constructor() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { wrap } = require('lib/string-utils.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const { _ } = require('lib/locale');
|
||||
|
||||
const MAX_WIDTH = 78;
|
||||
const INDENT = ' ';
|
||||
|
|
|
@ -21,12 +21,12 @@ const Note = require('lib/models/Note.js');
|
|||
const Tag = require('lib/models/Tag.js');
|
||||
const NoteTag = require('lib/models/NoteTag.js');
|
||||
const MasterKey = require('lib/models/MasterKey');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Revision = require('lib/models/Revision.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const Logger = require('lib/Logger').default;
|
||||
const { FsDriverNode } = require('lib/fs-driver-node.js');
|
||||
const { shimInit } = require('lib/shim-init-node.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const envFromArgs = require('lib/envFromArgs');
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import * as vm from 'vm';
|
||||
import Plugin from 'lib/services/plugins/Plugin';
|
||||
import sandboxProxy from 'lib/services/plugins/sandboxProxy';
|
||||
import BasePluginRunner from 'lib/services/plugins/BasePluginRunner';
|
||||
import executeSandboxCall from 'lib/services/plugins/utils/executeSandboxCall';
|
||||
import Global from 'lib/services/plugins/api/Global';
|
||||
import mapEventHandlersToIds, { EventHandlers } from 'lib/services/plugins/utils/mapEventHandlersToIds';
|
||||
|
||||
function createConsoleWrapper(pluginId:string) {
|
||||
const wrapper:any = {};
|
||||
|
||||
for (const n in console) {
|
||||
if (!console.hasOwnProperty(n)) continue;
|
||||
wrapper[n] = (...args:any[]) => {
|
||||
const newArgs = args.slice();
|
||||
newArgs.splice(0, 0, `Plugin "${pluginId}":`);
|
||||
return (console as any)[n](...newArgs);
|
||||
};
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// The CLI plugin runner is more complex than it needs to be because it more or less emulates
|
||||
// how it would work in a multi-process architecture, as in the desktop app (and probably how
|
||||
// it would work in the mobile app too). This is mainly to allow doing integration testing.
|
||||
//
|
||||
// For example, all plugin calls go through a proxy, however they could made directly since
|
||||
// the plugin script is running within the same process as the main app.
|
||||
|
||||
export default class PluginRunner extends BasePluginRunner {
|
||||
|
||||
private eventHandlers_:EventHandlers = {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.eventHandler = this.eventHandler.bind(this);
|
||||
}
|
||||
|
||||
private async eventHandler(eventHandlerId:string, args:any[]) {
|
||||
const cb = this.eventHandlers_[eventHandlerId];
|
||||
return cb(...args);
|
||||
}
|
||||
|
||||
private newSandboxProxy(pluginId:string, sandbox:Global) {
|
||||
const target = async (path:string, args:any[]) => {
|
||||
return executeSandboxCall(pluginId, sandbox, `joplin.${path}`, mapEventHandlersToIds(args, this.eventHandlers_), this.eventHandler);
|
||||
};
|
||||
|
||||
return {
|
||||
joplin: sandboxProxy(target),
|
||||
console: createConsoleWrapper(pluginId),
|
||||
};
|
||||
}
|
||||
|
||||
async run(plugin:Plugin, sandbox:Global) {
|
||||
const vmSandbox = vm.createContext(this.newSandboxProxy(plugin.id, sandbox));
|
||||
|
||||
try {
|
||||
vm.runInContext(plugin.scriptText, vmSandbox);
|
||||
} catch (error) {
|
||||
this.logger().error(`In plugin ${plugin.id}:`, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -46,8 +46,14 @@ tasks.prepareTestBuild = {
|
|||
],
|
||||
});
|
||||
|
||||
await utils.copyDir(`${__dirname}/../ReactNativeClient/lib`, `${testBuildDir}/lib`);
|
||||
await utils.copyDir(`${__dirname}/../ReactNativeClient/locales`, `${testBuildDir}/locales`);
|
||||
const rootDir = utils.rootDir();
|
||||
|
||||
await utils.copyDir(`${rootDir}/ReactNativeClient/lib`, `${testBuildDir}/lib`, {
|
||||
excluded: [
|
||||
`${rootDir}/ReactNativeClient/lib/joplin-renderer/node_modules`,
|
||||
],
|
||||
});
|
||||
await utils.copyDir(`${rootDir}/ReactNativeClient/locales`, `${testBuildDir}/locales`);
|
||||
await fs.mkdirp(`${testBuildDir}/data`);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "joplin",
|
||||
"version": "1.2.3",
|
||||
"version": "1.3.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -606,6 +606,11 @@
|
|||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||
"dev": true
|
||||
},
|
||||
"builtin-modules": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
|
||||
"integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw=="
|
||||
},
|
||||
"cache-base": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
|
||||
|
@ -4344,6 +4349,11 @@
|
|||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
|
||||
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.1.12",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz",
|
||||
"integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A=="
|
||||
},
|
||||
"nanomatch": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
|
||||
|
@ -5250,6 +5260,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"re-reselect": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/re-reselect/-/re-reselect-4.0.0.tgz",
|
||||
"integrity": "sha512-wuygyq8TXUlSdVXv2kigXxQNOgdb9m7LbIjwfTNGSpaY1riLd5e+VeQjlQMyUtrk0oiyhi1AqIVynworl3qxHA=="
|
||||
},
|
||||
"read-chunk": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-2.1.0.tgz",
|
||||
|
@ -5610,6 +5625,11 @@
|
|||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
||||
},
|
||||
"reselect": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz",
|
||||
"integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "1.2.3",
|
||||
"version": "1.3.0",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
|
@ -41,6 +41,7 @@
|
|||
"aws-sdk": "^2.588.0",
|
||||
"base-64": "^0.1.0",
|
||||
"base64-stream": "^1.0.0",
|
||||
"builtin-modules": "^3.1.0",
|
||||
"clean-html": "^1.5.0",
|
||||
"compare-version": "^0.1.2",
|
||||
"diacritics": "^1.3.0",
|
||||
|
@ -83,6 +84,7 @@
|
|||
"mime": "^2.0.3",
|
||||
"moment": "^2.24.0",
|
||||
"multiparty": "^4.2.1",
|
||||
"nanoid": "^3.1.12",
|
||||
"node-emoji": "^1.8.1",
|
||||
"node-fetch": "^1.7.1",
|
||||
"node-persist": "^2.1.0",
|
||||
|
@ -91,13 +93,16 @@
|
|||
"promise": "^7.1.1",
|
||||
"proper-lockfile": "^2.0.1",
|
||||
"query-string": "4.3.4",
|
||||
"re-reselect": "^4.0.0",
|
||||
"read-chunk": "^2.1.0",
|
||||
"redux": "^3.7.2",
|
||||
"relative": "^3.0.2",
|
||||
"request": "^2.88.0",
|
||||
"reselect": "^4.0.0",
|
||||
"sax": "^1.2.4",
|
||||
"server-destroy": "^1.0.1",
|
||||
"sharp": "^0.23.2",
|
||||
"slug": "^3.3.4",
|
||||
"sprintf-js": "^1.1.1",
|
||||
"sqlite3": "^4.1.1",
|
||||
"string-padding": "^1.0.2",
|
||||
|
@ -109,7 +114,6 @@
|
|||
"terminal-kit": "^1.30.0",
|
||||
"tkwidgets": "^0.5.26",
|
||||
"url-parse": "^1.4.7",
|
||||
"slug": "^3.3.4",
|
||||
"uuid": "^3.0.1",
|
||||
"valid-url": "^1.0.9",
|
||||
"word-wrap": "^1.2.3",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const { enexXmlToHtml } = require('lib/import-enex-html-gen.js');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
|
|
@ -9,7 +9,7 @@ const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer
|
|||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
|
|
@ -9,7 +9,7 @@ const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer
|
|||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const HtmlToHtml = require('lib/joplin-renderer/HtmlToHtml');
|
||||
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer
|
|||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const HtmlToMd = require('lib/HtmlToMd');
|
||||
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer
|
|||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const MdToHtml = require('lib/joplin-renderer/MdToHtml');
|
||||
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
||||
const { themeStyle } = require('lib/theme');
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const mdImporterService = require('lib/services/InteropService_Importer_Md');
|
||||
const mdImporterService = require('lib/services/interop/InteropService_Importer_Md').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ const { time } = require('lib/time-utils.js');
|
|||
const { sortedIds, createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
'use strict';
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { asyncTest,checkThrow } = require('test-utils.js');
|
||||
const eventManager = require('lib/eventManager').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
describe('eventManager', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
eventManager.reset();
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(async (done) => {
|
||||
eventManager.reset();
|
||||
done();
|
||||
});
|
||||
|
||||
it('should watch state props', asyncTest(async () => {
|
||||
let localStateName = '';
|
||||
let callCount = 0;
|
||||
|
||||
function nameWatch(event) {
|
||||
callCount++;
|
||||
localStateName = event.value;
|
||||
}
|
||||
|
||||
const globalState = {
|
||||
name: 'john',
|
||||
};
|
||||
|
||||
eventManager.appStateOn('name', nameWatch);
|
||||
eventManager.appStateEmit(globalState);
|
||||
|
||||
expect(localStateName).toBe('john');
|
||||
|
||||
globalState.name = 'paul';
|
||||
|
||||
eventManager.appStateEmit(globalState);
|
||||
|
||||
expect(localStateName).toBe('paul');
|
||||
|
||||
expect(callCount).toBe(2);
|
||||
|
||||
eventManager.appStateEmit(globalState);
|
||||
|
||||
expect(callCount).toBe(2);
|
||||
}));
|
||||
|
||||
it('should unwatch state props', asyncTest(async () => {
|
||||
let localStateName = '';
|
||||
|
||||
function nameWatch(event) {
|
||||
localStateName = event.value;
|
||||
}
|
||||
|
||||
const globalState = {
|
||||
name: 'john',
|
||||
};
|
||||
|
||||
eventManager.appStateOn('name', nameWatch);
|
||||
eventManager.appStateOff('name', nameWatch);
|
||||
eventManager.appStateEmit(globalState);
|
||||
|
||||
expect(localStateName).toBe('');
|
||||
}));
|
||||
|
||||
it('should watch nested props', asyncTest(async () => {
|
||||
let localStateName = '';
|
||||
|
||||
function nameWatch(event) {
|
||||
localStateName = event.value;
|
||||
}
|
||||
|
||||
const globalState = {
|
||||
user: {
|
||||
name: 'john',
|
||||
},
|
||||
};
|
||||
|
||||
eventManager.appStateOn('user.name', nameWatch);
|
||||
eventManager.appStateEmit(globalState);
|
||||
|
||||
expect(localStateName).toBe('john');
|
||||
|
||||
globalState.user.name = 'paul';
|
||||
|
||||
eventManager.appStateEmit(globalState);
|
||||
|
||||
expect(localStateName).toBe('paul');
|
||||
}));
|
||||
|
||||
it('should not be possible to modify state props', asyncTest(async () => {
|
||||
let localUser = {};
|
||||
|
||||
function userWatch(event) {
|
||||
// Normally, the user should not keep a reference to the whole object
|
||||
// but make a copy. However, if they do keep a reference and try to
|
||||
// modify it, it should throw an exception as that would be an attempt
|
||||
// to directly modify the Redux state.
|
||||
localUser = event.value;
|
||||
}
|
||||
|
||||
const globalState = {
|
||||
user: {
|
||||
name: 'john',
|
||||
},
|
||||
};
|
||||
|
||||
eventManager.appStateOn('user', userWatch);
|
||||
eventManager.appStateEmit(globalState);
|
||||
|
||||
expect(checkThrow(() => localUser.name = 'paul')).toBe(true);
|
||||
}));
|
||||
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
require('app-module-path').addPath(__dirname);
|
||||
const { asyncTest, id, ids, createNTestFolders, sortedIds, createNTestNotes, TestApp } = require('test-utils.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const uuid = require('lib/uuid').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
require('app-module-path').addPath(__dirname);
|
||||
const { setupDatabaseAndSynchronizer, switchClient, asyncTest, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('test-utils.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
require('app-module-path').addPath(__dirname);
|
||||
const { setupDatabaseAndSynchronizer, switchClient, asyncTest, id, ids, sortedIds, at, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('test-utils.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
require('app-module-path').addPath(__dirname);
|
||||
const { setupDatabaseAndSynchronizer, switchClient, asyncTest, createNTestFolders, createNTestNotes, createNTestTags, TestApp } = require('test-utils.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const uuid = require('lib/uuid').default;
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { asyncTest, sleep, fileApi, fileContentEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const fs = require('fs-extra');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { asyncTest } = require('test-utils.js');
|
||||
const markdownUtils = require('lib/markdownUtils.js');
|
||||
const markdownUtils = require('lib/markdownUtils').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -9,7 +9,7 @@ const Note = require('lib/models/Note.js');
|
|||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -7,7 +7,7 @@ const { createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatab
|
|||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -8,7 +8,7 @@ const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
|||
const ResourceService = require('lib/services/ResourceService');
|
||||
const ItemChangeUtils = require('lib/services/ItemChangeUtils');
|
||||
const Note = require('lib/models/Note');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const ItemChange = require('lib/models/ItemChange');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
|
|
@ -6,10 +6,10 @@ const { time } = require('lib/time-utils.js');
|
|||
const { sortedIds, createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -6,10 +6,10 @@ const { time } = require('lib/time-utils.js');
|
|||
const { sortedIds, createNTestNotes, asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -8,7 +8,7 @@ const Folder = require('lib/models/Folder.js');
|
|||
const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -10,7 +10,7 @@ const NoteTag = require('lib/models/NoteTag.js');
|
|||
const Tag = require('lib/models/Tag.js');
|
||||
const Revision = require('lib/models/Revision.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
describe('models_Setting', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should return only sub-values', asyncTest(async () => {
|
||||
const settings = {
|
||||
'sync.5.path': 'http://example.com',
|
||||
'sync.5.username': 'testing',
|
||||
};
|
||||
|
||||
let output = Setting.subValues('sync.5', settings);
|
||||
expect(output['path']).toBe('http://example.com');
|
||||
expect(output['username']).toBe('testing');
|
||||
|
||||
output = Setting.subValues('sync.4', settings);
|
||||
expect('path' in output).toBe(false);
|
||||
expect('username' in output).toBe(false);
|
||||
}));
|
||||
|
||||
});
|
|
@ -0,0 +1,135 @@
|
|||
import Setting from 'lib/models/Setting';
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('test-utils.js');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
describe('models_Setting', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should return only sub-values', asyncTest(async () => {
|
||||
const settings = {
|
||||
'sync.5.path': 'http://example.com',
|
||||
'sync.5.username': 'testing',
|
||||
};
|
||||
|
||||
let output = Setting.subValues('sync.5', settings);
|
||||
expect(output['path']).toBe('http://example.com');
|
||||
expect(output['username']).toBe('testing');
|
||||
|
||||
output = Setting.subValues('sync.4', settings);
|
||||
expect('path' in output).toBe(false);
|
||||
expect('username' in output).toBe(false);
|
||||
}));
|
||||
|
||||
it('should allow registering new settings dynamically', asyncTest(async () => {
|
||||
await expectThrow(async () => Setting.setValue('myCustom', '123'));
|
||||
|
||||
await Setting.registerSetting('myCustom', {
|
||||
public: true,
|
||||
value: 'default',
|
||||
type: Setting.TYPE_STRING,
|
||||
});
|
||||
|
||||
await expectNotThrow(async () => Setting.setValue('myCustom', '123'));
|
||||
|
||||
expect(Setting.value('myCustom')).toBe('123');
|
||||
}));
|
||||
|
||||
it('should not clear old custom settings', asyncTest(async () => {
|
||||
// In general the following should work:
|
||||
//
|
||||
// - Plugin register a new setting
|
||||
// - User set new value for setting
|
||||
// - Settings are saved
|
||||
// - => App restart
|
||||
// - Plugin does not register setting again
|
||||
// - Settings are loaded
|
||||
// - Settings are saved
|
||||
// - Plugin register setting again
|
||||
// - Previous value set by user should still be there.
|
||||
//
|
||||
// In other words, once a custom setting has been set we don't clear it
|
||||
// even if registration doesn't happen immediately. That allows for example
|
||||
// to delay setting registration without a risk for the custom setting to be deleted.
|
||||
|
||||
await Setting.registerSetting('myCustom', {
|
||||
public: true,
|
||||
value: 'default',
|
||||
type: Setting.TYPE_STRING,
|
||||
});
|
||||
|
||||
Setting.setValue('myCustom', '123');
|
||||
|
||||
await Setting.saveAll();
|
||||
|
||||
await Setting.reset();
|
||||
|
||||
await Setting.load();
|
||||
|
||||
await Setting.registerSetting('myCustom', {
|
||||
public: true,
|
||||
value: 'default',
|
||||
type: Setting.TYPE_STRING,
|
||||
});
|
||||
|
||||
await Setting.saveAll();
|
||||
|
||||
expect(Setting.value('myCustom')).toBe('123');
|
||||
}));
|
||||
|
||||
it('should return values with correct type for custom settings', asyncTest(async () => {
|
||||
await Setting.registerSetting('myCustom', {
|
||||
public: true,
|
||||
value: 123,
|
||||
type: Setting.TYPE_INT,
|
||||
});
|
||||
|
||||
Setting.setValue('myCustom', 456);
|
||||
|
||||
await Setting.saveAll();
|
||||
await Setting.reset();
|
||||
await Setting.load();
|
||||
|
||||
await Setting.registerSetting('myCustom', {
|
||||
public: true,
|
||||
value: 123,
|
||||
type: Setting.TYPE_INT,
|
||||
});
|
||||
|
||||
expect(typeof Setting.value('myCustom')).toBe('number');
|
||||
expect(Setting.value('myCustom')).toBe(456);
|
||||
}));
|
||||
|
||||
it('should validate registered keys', asyncTest(async () => {
|
||||
const md = {
|
||||
public: true,
|
||||
value: 'default',
|
||||
type: Setting.TYPE_STRING,
|
||||
};
|
||||
|
||||
await expectThrow(async () => await Setting.registerSetting('', md));
|
||||
await expectThrow(async () => await Setting.registerSetting('no spaces', md));
|
||||
await expectThrow(async () => await Setting.registerSetting('nospecialcharacters!!!', md));
|
||||
await expectThrow(async () => await Setting.registerSetting('Robert\'); DROP TABLE Students;--', md));
|
||||
|
||||
await expectNotThrow(async () => await Setting.registerSetting('numbersareok123', md));
|
||||
await expectNotThrow(async () => await Setting.registerSetting('so-ARE-dashes_123', md));
|
||||
}));
|
||||
|
||||
it('should register new sections', asyncTest(async () => {
|
||||
await Setting.registerSection('mySection', {
|
||||
label: 'My section',
|
||||
});
|
||||
|
||||
expect(Setting.sectionNameToLabel('mySection')).toBe('My section');
|
||||
}));
|
||||
});
|
|
@ -9,7 +9,7 @@ const Note = require('lib/models/Note.js');
|
|||
const NoteTag = require('lib/models/NoteTag.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -5,7 +5,8 @@ const { setupDatabaseAndSynchronizer, switchClient, asyncTest, createNTestNotes,
|
|||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { reducer, defaultState, stateUtils, MAX_HISTORY } = require('lib/reducer.js');
|
||||
const reducer = require('lib/reducer').default;
|
||||
const { defaultState, stateUtils, MAX_HISTORY } = require('lib/reducer');
|
||||
|
||||
function initTestState(folders, selectedFolderIndex, notes, selectedNoteIndexes, tags = null, selectedTagIndex = null) {
|
||||
let state = defaultState;
|
||||
|
@ -87,7 +88,7 @@ function getIds(items, indexes = null) {
|
|||
|
||||
let insideBeforeEach = false;
|
||||
|
||||
describe('Reducer', function() {
|
||||
describe('reducer', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
insideBeforeEach = true;
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import sandboxProxy, { Target } from 'lib/services/plugins/sandboxProxy';
|
||||
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
|
||||
|
||||
describe('services_plugins_sandboxProxy', function() {
|
||||
|
||||
beforeEach(async (done:Function) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should create a new sandbox proxy', asyncTest(async () => {
|
||||
interface Result {
|
||||
path: string,
|
||||
args: any[],
|
||||
}
|
||||
|
||||
const results:Result[] = [];
|
||||
|
||||
const target:Target = (path:string, args:any[]) => {
|
||||
results.push({ path, args });
|
||||
};
|
||||
|
||||
const proxy = sandboxProxy(target);
|
||||
|
||||
proxy.testing.bla.test('hello', '123');
|
||||
proxy.testing.test2();
|
||||
|
||||
expect(results[0].path).toBe('testing.bla.test');
|
||||
expect(results[0].args.join('_')).toBe('hello_123');
|
||||
expect(results[1].path).toBe('testing.test2');
|
||||
expect(results[1].args.join('_')).toBe('');
|
||||
}));
|
||||
|
||||
it('should allow importing a namespace', asyncTest(async () => {
|
||||
interface Result {
|
||||
path: string,
|
||||
args: any[],
|
||||
}
|
||||
|
||||
const results:Result[] = [];
|
||||
|
||||
const target:Target = (path:string, args:any[]) => {
|
||||
results.push({ path, args });
|
||||
};
|
||||
|
||||
const proxy = sandboxProxy(target);
|
||||
|
||||
const ns = proxy.testing.blabla;
|
||||
ns.test();
|
||||
ns.test2();
|
||||
|
||||
expect(results[0].path).toBe('testing.blabla.test');
|
||||
expect(results[1].path).toBe('testing.blabla.test2');
|
||||
}));
|
||||
|
||||
});
|
|
@ -0,0 +1,278 @@
|
|||
import MenuUtils from 'lib/services/commands/MenuUtils';
|
||||
import ToolbarButtonUtils from 'lib/services/commands/ToolbarButtonUtils';
|
||||
import CommandService, { CommandDeclaration, CommandRuntime } from 'lib/services/CommandService';
|
||||
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
|
||||
|
||||
interface TestCommand {
|
||||
declaration: CommandDeclaration,
|
||||
runtime: CommandRuntime,
|
||||
}
|
||||
|
||||
function newService():CommandService {
|
||||
const service = new CommandService();
|
||||
service.initialize({});
|
||||
return service;
|
||||
}
|
||||
|
||||
function createCommand(name:string, options:any):TestCommand {
|
||||
const declaration:CommandDeclaration = {
|
||||
name: name,
|
||||
};
|
||||
|
||||
const runtime:CommandRuntime = {
|
||||
execute: options.execute,
|
||||
};
|
||||
|
||||
if (options.mapStateToProps) runtime.mapStateToProps = options.mapStateToProps;
|
||||
if (options.isEnabled) runtime.isEnabled = options.isEnabled;
|
||||
|
||||
return { declaration, runtime };
|
||||
}
|
||||
|
||||
function registerCommand(service:CommandService, cmd:TestCommand) {
|
||||
service.registerDeclaration(cmd.declaration);
|
||||
service.registerRuntime(cmd.declaration.name, cmd.runtime);
|
||||
}
|
||||
|
||||
describe('services_CommandService', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should create toolbar button infos from commands', asyncTest(async () => {
|
||||
const service = newService();
|
||||
const toolbarButtonUtils = new ToolbarButtonUtils(service);
|
||||
|
||||
const executedCommands:string[] = [];
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
execute: () => {
|
||||
executedCommands.push('test1');
|
||||
},
|
||||
}));
|
||||
|
||||
registerCommand(service, createCommand('test2', {
|
||||
execute: () => {
|
||||
executedCommands.push('test2');
|
||||
},
|
||||
}));
|
||||
|
||||
const toolbarInfos = toolbarButtonUtils.commandsToToolbarButtons({}, ['test1', 'test2']);
|
||||
|
||||
await toolbarInfos[0].onClick();
|
||||
await toolbarInfos[1].onClick();
|
||||
|
||||
expect(executedCommands.join('_')).toBe('test1_test2');
|
||||
expect(toolbarInfos[0].enabled).toBe(true);
|
||||
expect(toolbarInfos[1].enabled).toBe(true);
|
||||
}));
|
||||
|
||||
it('should enable and disable toolbar buttons depending on state', asyncTest(async () => {
|
||||
const service = newService();
|
||||
const toolbarButtonUtils = new ToolbarButtonUtils(service);
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
execute: () => {},
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
selectedNoteId: state.selectedNoteId,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
};
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return props.selectedNoteId === 'abc';
|
||||
},
|
||||
}));
|
||||
|
||||
registerCommand(service, createCommand('test2', {
|
||||
execute: () => {},
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
selectedNoteId: state.selectedNoteId,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
};
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return props.selectedNoteId === '123';
|
||||
},
|
||||
}));
|
||||
|
||||
const toolbarInfos = toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: '123',
|
||||
selectedFolderId: 'aaa',
|
||||
}, ['test1', 'test2']);
|
||||
|
||||
expect(toolbarInfos[0].enabled).toBe(false);
|
||||
expect(toolbarInfos[1].enabled).toBe(true);
|
||||
}));
|
||||
|
||||
it('should return the same toolbarButtons array if nothing has changed', asyncTest(async () => {
|
||||
const service = newService();
|
||||
const toolbarButtonUtils = new ToolbarButtonUtils(service);
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
execute: () => {},
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
selectedNoteId: state.selectedNoteId,
|
||||
};
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return props.selectedNoteId === 'ok';
|
||||
},
|
||||
}));
|
||||
|
||||
registerCommand(service, createCommand('test2', {
|
||||
execute: () => {},
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
};
|
||||
},
|
||||
isEnabled: (props:any) => {
|
||||
return props.selectedFolderId === 'ok';
|
||||
},
|
||||
}));
|
||||
|
||||
const toolbarInfos1 = toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: 'ok',
|
||||
selectedFolderId: 'notok',
|
||||
}, ['test1', 'test2']);
|
||||
|
||||
const toolbarInfos2 = toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: 'ok',
|
||||
selectedFolderId: 'notok',
|
||||
}, ['test1', 'test2']);
|
||||
|
||||
expect(toolbarInfos1).toBe(toolbarInfos2);
|
||||
expect(toolbarInfos1[0] === toolbarInfos2[0]).toBe(true);
|
||||
expect(toolbarInfos1[1] === toolbarInfos2[1]).toBe(true);
|
||||
|
||||
const toolbarInfos3 = toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: 'ok',
|
||||
selectedFolderId: 'ok',
|
||||
}, ['test1', 'test2']);
|
||||
|
||||
expect(toolbarInfos2 === toolbarInfos3).toBe(false);
|
||||
expect(toolbarInfos2[0] === toolbarInfos3[0]).toBe(true);
|
||||
expect(toolbarInfos2[1] === toolbarInfos3[1]).toBe(false);
|
||||
|
||||
{
|
||||
expect(toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: 'ok',
|
||||
selectedFolderId: 'notok',
|
||||
}, ['test1', '-', 'test2'])).toBe(toolbarButtonUtils.commandsToToolbarButtons({
|
||||
selectedNoteId: 'ok',
|
||||
selectedFolderId: 'notok',
|
||||
}, ['test1', '-', 'test2']));
|
||||
}
|
||||
}));
|
||||
|
||||
it('should create menu items from commands', asyncTest(async () => {
|
||||
const service = newService();
|
||||
const utils = new MenuUtils(service);
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
execute: () => {},
|
||||
}));
|
||||
|
||||
registerCommand(service, createCommand('test2', {
|
||||
execute: () => {},
|
||||
}));
|
||||
|
||||
const clickedCommands:string[] = [];
|
||||
|
||||
const onClick = (commandName:string) => {
|
||||
clickedCommands.push(commandName);
|
||||
};
|
||||
|
||||
const menuItems = utils.commandsToMenuItems(['test1', 'test2'], onClick);
|
||||
|
||||
menuItems.test1.click();
|
||||
menuItems.test2.click();
|
||||
|
||||
expect(clickedCommands.join('_')).toBe('test1_test2');
|
||||
|
||||
// Also check that the same commands always return strictly the same menu
|
||||
expect(utils.commandsToMenuItems(['test1', 'test2'], onClick)).toBe(utils.commandsToMenuItems(['test1', 'test2'], onClick));
|
||||
}));
|
||||
|
||||
it('should give menu item props from state', asyncTest(async () => {
|
||||
const service = newService();
|
||||
const utils = new MenuUtils(service);
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
isOk: state.test1 === 'ok',
|
||||
};
|
||||
},
|
||||
execute: () => {},
|
||||
}));
|
||||
|
||||
registerCommand(service, createCommand('test2', {
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
isOk: state.test2 === 'ok',
|
||||
};
|
||||
},
|
||||
execute: () => {},
|
||||
}));
|
||||
|
||||
{
|
||||
const menuItemProps = utils.commandsToMenuItemProps({
|
||||
test1: 'ok',
|
||||
test2: 'notok',
|
||||
}, ['test1', 'test2']);
|
||||
|
||||
expect(menuItemProps.test1.isOk).toBe(true);
|
||||
expect(menuItemProps.test2.isOk).toBe(false);
|
||||
}
|
||||
|
||||
{
|
||||
const menuItemProps = utils.commandsToMenuItemProps({
|
||||
test1: 'ok',
|
||||
test2: 'ok',
|
||||
}, ['test1', 'test2']);
|
||||
|
||||
expect(menuItemProps.test1.isOk).toBe(true);
|
||||
expect(menuItemProps.test2.isOk).toBe(true);
|
||||
}
|
||||
|
||||
expect(utils.commandsToMenuItemProps({
|
||||
test1: 'ok',
|
||||
test2: 'ok',
|
||||
}, ['test1', 'test2'])).toBe(utils.commandsToMenuItemProps({
|
||||
test1: 'ok',
|
||||
test2: 'ok',
|
||||
}, ['test1', 'test2']));
|
||||
}));
|
||||
|
||||
it('should create stateful menu items', asyncTest(async () => {
|
||||
const service = newService();
|
||||
const utils = new MenuUtils(service);
|
||||
|
||||
let propValue = null;
|
||||
|
||||
registerCommand(service, createCommand('test1', {
|
||||
mapStateToProps: (state:any) => {
|
||||
return {
|
||||
isOk: state.test1 === 'ok',
|
||||
};
|
||||
},
|
||||
execute: (props:any) => {
|
||||
propValue = props.isOk;
|
||||
},
|
||||
}));
|
||||
|
||||
const menuItem = utils.commandToStatefulMenuItem('test1', { isOk: 'hello' });
|
||||
menuItem.click();
|
||||
|
||||
expect(propValue).toBe('hello');
|
||||
}));
|
||||
|
||||
});
|
|
@ -8,7 +8,7 @@ const Folder = require('lib/models/Folder.js');
|
|||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const MasterKey = require('lib/models/MasterKey');
|
||||
|
@ -289,35 +289,4 @@ describe('services_EncryptionService', function() {
|
|||
const plainText = await service.decryptString(cipherText);
|
||||
expect(plainText).toBe('🐶🐶🐶'.substr(0,5));
|
||||
}));
|
||||
|
||||
// it('should upgrade master key encryption mode', asyncTest(async () => {
|
||||
// let masterKey = await service.generateMasterKey('123456', {
|
||||
// encryptionMethod: EncryptionService.METHOD_SJCL_2,
|
||||
// });
|
||||
// masterKey = await MasterKey.save(masterKey);
|
||||
// Setting.setObjectKey('encryption.passwordCache', masterKey.id, '123456');
|
||||
// Setting.setValue('encryption.activeMasterKeyId', masterKey.id);
|
||||
|
||||
// await sleep(0.01);
|
||||
|
||||
// await service.loadMasterKeysFromSettings();
|
||||
|
||||
// masterKeyNew = await MasterKey.load(masterKey.id);
|
||||
|
||||
// // Check that the master key has been upgraded
|
||||
|
||||
// expect(masterKeyNew.created_time).toBe(masterKey.created_time);
|
||||
// expect(masterKeyNew.updated_time === masterKey.updated_time).toBe(false);
|
||||
// expect(masterKeyNew.content === masterKey.content).toBe(false);
|
||||
// expect(masterKeyNew.encryption_method === masterKey.encryption_method).toBe(false);
|
||||
// expect(masterKeyNew.checksum === masterKey.checksum).toBe(false);
|
||||
// expect(masterKeyNew.encryption_method).toBe(service.defaultMasterKeyEncryptionMethod_);
|
||||
|
||||
// // Check that encryption still works
|
||||
|
||||
// const cipherText = await service.encryptString('some secret');
|
||||
// const plainText = await service.decryptString(cipherText);
|
||||
// expect(plainText).toBe('some secret');
|
||||
// }));
|
||||
|
||||
});
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
import InteropService from 'lib/services/interop/InteropService';
|
||||
import { CustomExportContext, CustomImportContext, Module, ModuleType } from 'lib/services/interop/types';
|
||||
import shim from 'lib/shim';
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { asyncTest, fileContentEqual, expectNotThrow, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const InteropService = require('lib/services/InteropService.js');
|
||||
const { asyncTest, fileContentEqual, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const NoteTag = require('lib/models/NoteTag.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const fs = require('fs-extra');
|
||||
const ArrayUtils = require('lib/ArrayUtils');
|
||||
const ObjectUtils = require('lib/ObjectUtils');
|
||||
const { shim } = require('lib/shim.js');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
@ -23,7 +20,7 @@ function exportDir() {
|
|||
return `${__dirname}/export`;
|
||||
}
|
||||
|
||||
function fieldsEqual(model1, model2, fieldNames) {
|
||||
function fieldsEqual(model1:any, model2:any, fieldNames:string[]) {
|
||||
for (let i = 0; i < fieldNames.length; i++) {
|
||||
const f = fieldNames[i];
|
||||
expect(model1[f]).toBe(model2[f], `For key ${f}`);
|
||||
|
@ -43,7 +40,7 @@ describe('services_InteropService', function() {
|
|||
});
|
||||
|
||||
it('should export and import folders', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
let folder1 = await Folder.save({ title: 'folder1' });
|
||||
folder1 = await Folder.load(folder1.id);
|
||||
const filePath = `${exportDir()}/test.jex`;
|
||||
|
@ -78,7 +75,7 @@ describe('services_InteropService', function() {
|
|||
}));
|
||||
|
||||
it('should import folders and de-duplicate titles when needed', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const folder1 = await Folder.save({ title: 'folder' });
|
||||
const folder2 = await Folder.save({ title: 'folder' });
|
||||
const filePath = `${exportDir()}/test.jex`;
|
||||
|
@ -90,15 +87,15 @@ describe('services_InteropService', function() {
|
|||
await service.import({ path: filePath });
|
||||
|
||||
const allFolders = await Folder.all();
|
||||
expect(allFolders.map(f => f.title).sort().join(' - ')).toBe('folder - folder (1)');
|
||||
expect(allFolders.map((f:any) => f.title).sort().join(' - ')).toBe('folder - folder (1)');
|
||||
}));
|
||||
|
||||
it('should import folders, and only de-duplicate titles when needed', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
const folder2 = await Folder.save({ title: 'folder2' });
|
||||
const sub1 = await Folder.save({ title: 'Sub', parent_id: folder1.id });
|
||||
const sub2 = await Folder.save({ title: 'Sub', parent_id: folder2.id });
|
||||
await Folder.save({ title: 'Sub', parent_id: folder1.id });
|
||||
await Folder.save({ title: 'Sub', parent_id: folder2.id });
|
||||
const filePath = `${exportDir()}/test.jex`;
|
||||
await service.export({ path: filePath });
|
||||
|
||||
|
@ -117,7 +114,7 @@ describe('services_InteropService', function() {
|
|||
}));
|
||||
|
||||
it('should export and import folders and notes', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
note1 = await Note.load(note1.id);
|
||||
|
@ -156,7 +153,7 @@ describe('services_InteropService', function() {
|
|||
}));
|
||||
|
||||
it('should export and import notes to specific folder', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
note1 = await Note.load(note1.id);
|
||||
|
@ -175,7 +172,7 @@ describe('services_InteropService', function() {
|
|||
}));
|
||||
|
||||
it('should export and import tags', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const filePath = `${exportDir()}/test.jex`;
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
|
@ -215,7 +212,7 @@ describe('services_InteropService', function() {
|
|||
}));
|
||||
|
||||
it('should export and import resources', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const filePath = `${exportDir()}/test.jex`;
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
|
@ -251,7 +248,7 @@ describe('services_InteropService', function() {
|
|||
}));
|
||||
|
||||
it('should export and import single notes', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const filePath = `${exportDir()}/test.jex`;
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
|
@ -271,7 +268,7 @@ describe('services_InteropService', function() {
|
|||
}));
|
||||
|
||||
it('should export and import single folders', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const filePath = `${exportDir()}/test.jex`;
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
|
@ -292,7 +289,7 @@ describe('services_InteropService', function() {
|
|||
|
||||
it('should export and import folder and its sub-folders', asyncTest(async () => {
|
||||
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const filePath = `${exportDir()}/test.jex`;
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
const folder2 = await Folder.save({ title: 'folder2', parent_id: folder1.id });
|
||||
|
@ -326,7 +323,7 @@ describe('services_InteropService', function() {
|
|||
}));
|
||||
|
||||
it('should export and import links to notes', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const filePath = `${exportDir()}/test.jex`;
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
const note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
|
@ -349,43 +346,21 @@ describe('services_InteropService', function() {
|
|||
expect(note2_2.body.indexOf(note1_2.id) >= 0).toBe(true);
|
||||
}));
|
||||
|
||||
it('should export into json format', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
note1 = await Note.load(note1.id);
|
||||
const filePath = exportDir();
|
||||
|
||||
await service.export({ path: filePath, format: 'json' });
|
||||
|
||||
// verify that the json files exist and can be parsed
|
||||
const items = [folder1, note1];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const jsonFile = `${filePath}/${items[i].id}.json`;
|
||||
const json = await fs.readFile(jsonFile, 'utf-8');
|
||||
const obj = JSON.parse(json);
|
||||
expect(obj.id).toBe(items[i].id);
|
||||
expect(obj.type_).toBe(items[i].type_);
|
||||
expect(obj.title).toBe(items[i].title);
|
||||
expect(obj.body).toBe(items[i].body);
|
||||
}
|
||||
}));
|
||||
|
||||
it('should export selected notes in md format', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
let note11 = await Note.save({ title: 'title note11', parent_id: folder1.id });
|
||||
note11 = await Note.load(note11.id);
|
||||
let note12 = await Note.save({ title: 'title note12', parent_id: folder1.id });
|
||||
note12 = await Note.load(note12.id);
|
||||
const note12 = await Note.save({ title: 'title note12', parent_id: folder1.id });
|
||||
await Note.load(note12.id);
|
||||
|
||||
let folder2 = await Folder.save({ title: 'folder2', parent_id: folder1.id });
|
||||
folder2 = await Folder.load(folder2.id);
|
||||
let note21 = await Note.save({ title: 'title note21', parent_id: folder2.id });
|
||||
note21 = await Note.load(note21.id);
|
||||
|
||||
let folder3 = await Folder.save({ title: 'folder3', parent_id: folder1.id });
|
||||
folder3 = await Folder.load(folder2.id);
|
||||
await Folder.save({ title: 'folder3', parent_id: folder1.id });
|
||||
await Folder.load(folder2.id);
|
||||
|
||||
const outDir = exportDir();
|
||||
|
||||
|
@ -401,16 +376,16 @@ describe('services_InteropService', function() {
|
|||
}));
|
||||
|
||||
it('should export MD with unicode filenames', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
const folder2 = await Folder.save({ title: 'ジョプリン' });
|
||||
const note1 = await Note.save({ title: '生活', parent_id: folder1.id });
|
||||
const note2 = await Note.save({ title: '生活', parent_id: folder1.id });
|
||||
const note2b = await Note.save({ title: '生活', parent_id: folder1.id });
|
||||
const note3 = await Note.save({ title: '', parent_id: folder1.id });
|
||||
const note4 = await Note.save({ title: '', parent_id: folder1.id });
|
||||
const note5 = await Note.save({ title: 'salut, ça roule ?', parent_id: folder1.id });
|
||||
const note6 = await Note.save({ title: 'ジョプリン', parent_id: folder2.id });
|
||||
await Note.save({ title: '生活', parent_id: folder1.id });
|
||||
await Note.save({ title: '生活', parent_id: folder1.id });
|
||||
await Note.save({ title: '生活', parent_id: folder1.id });
|
||||
await Note.save({ title: '', parent_id: folder1.id });
|
||||
await Note.save({ title: '', parent_id: folder1.id });
|
||||
await Note.save({ title: 'salut, ça roule ?', parent_id: folder1.id });
|
||||
await Note.save({ title: 'ジョプリン', parent_id: folder2.id });
|
||||
|
||||
const outDir = exportDir();
|
||||
|
||||
|
@ -430,7 +405,7 @@ describe('services_InteropService', function() {
|
|||
await Note.save({ title: 'textexportnote1', parent_id: folder1.id });
|
||||
await Note.save({ title: 'textexportnote2', parent_id: folder1.id });
|
||||
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
|
||||
await service.export({
|
||||
path: exportDir(),
|
||||
|
@ -454,7 +429,7 @@ describe('services_InteropService', function() {
|
|||
|
||||
await Folder.save({ title: 'orphan', parent_id: '0c5bbd8a1b5a48189484a412a7e534cc' });
|
||||
|
||||
const service = new InteropService();
|
||||
const service = InteropService.instance();
|
||||
|
||||
const result = await service.export({
|
||||
path: exportDir(),
|
||||
|
@ -464,4 +439,99 @@ describe('services_InteropService', function() {
|
|||
expect(result.warnings.length).toBe(0);
|
||||
}));
|
||||
|
||||
it('should allow registering new import modules', asyncTest(async () => {
|
||||
const testImportFilePath = `${exportDir()}/testImport${Math.random()}.test`;
|
||||
await shim.fsDriver().writeFile(testImportFilePath, 'test', 'utf8');
|
||||
|
||||
const result = {
|
||||
hasBeenExecuted: false,
|
||||
sourcePath: '',
|
||||
};
|
||||
|
||||
const module:Module = {
|
||||
type: ModuleType.Importer,
|
||||
description: 'Test Import Module',
|
||||
format: 'testing',
|
||||
fileExtensions: ['test'],
|
||||
isCustom: true,
|
||||
|
||||
onExec: async (context:CustomImportContext) => {
|
||||
result.hasBeenExecuted = true;
|
||||
result.sourcePath = context.sourcePath;
|
||||
},
|
||||
};
|
||||
|
||||
const service = InteropService.instance();
|
||||
service.registerModule(module);
|
||||
await service.import({
|
||||
format: 'testing',
|
||||
path: testImportFilePath,
|
||||
});
|
||||
|
||||
expect(result.hasBeenExecuted).toBe(true);
|
||||
expect(result.sourcePath).toBe(testImportFilePath);
|
||||
}));
|
||||
|
||||
it('should allow registering new export modules', asyncTest(async () => {
|
||||
const folder1 = await Folder.save({ title: 'folder1' });
|
||||
const note1 = await Note.save({ title: 'note1', parent_id: folder1.id });
|
||||
await Note.save({ title: 'note2', parent_id: folder1.id });
|
||||
await shim.attachFileToNote(note1, `${__dirname}/../tests/support/photo.jpg`);
|
||||
|
||||
const filePath = `${exportDir()}/example.test`;
|
||||
|
||||
const result:any = {
|
||||
destPath: '',
|
||||
itemTypes: [],
|
||||
items: [],
|
||||
resources: [],
|
||||
filePaths: [],
|
||||
closeCalled: false,
|
||||
};
|
||||
|
||||
const module:Module = {
|
||||
type: ModuleType.Exporter,
|
||||
description: 'Test Export Module',
|
||||
format: 'testing',
|
||||
fileExtensions: ['test'],
|
||||
isCustom: true,
|
||||
|
||||
onInit: async (context:CustomExportContext) => {
|
||||
result.destPath = context.destPath;
|
||||
},
|
||||
|
||||
onProcessItem: async (_context:CustomExportContext, itemType:number, item:any) => {
|
||||
result.itemTypes.push(itemType);
|
||||
result.items.push(item);
|
||||
},
|
||||
|
||||
onProcessResource: async (_context:CustomExportContext, resource:any, filePath:string) => {
|
||||
result.resources.push(resource);
|
||||
result.filePaths.push(filePath);
|
||||
},
|
||||
|
||||
onClose: async (_context:CustomExportContext) => {
|
||||
result.closeCalled = true;
|
||||
},
|
||||
};
|
||||
|
||||
const service = InteropService.instance();
|
||||
service.registerModule(module);
|
||||
await service.export({
|
||||
format: 'testing',
|
||||
path: filePath,
|
||||
});
|
||||
|
||||
expect(result.destPath).toBe(filePath);
|
||||
expect(result.itemTypes.sort().join('_')).toBe('1_1_2_4');
|
||||
expect(result.items.length).toBe(4);
|
||||
expect(result.items.map((o:any) => o.title).sort().join('_')).toBe('folder1_note1_note2_photo.jpg');
|
||||
expect(result.resources.length).toBe(1);
|
||||
expect(result.resources[0].title).toBe('photo.jpg');
|
||||
expect(result.filePaths.length).toBe(1);
|
||||
expect(!!result.filePaths[0]).toBe(true);
|
||||
expect(result.closeCalled).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
});
|
|
@ -4,12 +4,12 @@ require('app-module-path').addPath(__dirname);
|
|||
|
||||
const fs = require('fs-extra');
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
|
||||
const InteropService_Exporter_Md = require('lib/services/InteropService_Exporter_Md.js');
|
||||
const InteropService_Exporter_Md = require('lib/services/interop/InteropService_Exporter_Md').default;
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
const exportDir = `${__dirname}/export`;
|
||||
|
||||
|
@ -67,8 +67,8 @@ describe('services_InteropService_Exporter_Md', function() {
|
|||
|
||||
expect(!exporter.context() && !(exporter.context().notePaths || Object.keys(exporter.context().notePaths).length)).toBe(false, 'Context should be empty before processing.');
|
||||
|
||||
await exporter.processItem(Folder, folder1);
|
||||
await exporter.processItem(Folder, folder2);
|
||||
await exporter.processItem(Folder.modelType(), folder1);
|
||||
await exporter.processItem(Folder.modelType(), folder2);
|
||||
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
|
||||
|
||||
expect(Object.keys(exporter.context().notePaths).length).toBe(3, 'There should be 3 note paths in the context.');
|
||||
|
@ -96,7 +96,7 @@ describe('services_InteropService_Exporter_Md', function() {
|
|||
queueExportItem(BaseModel.TYPE_NOTE, note1);
|
||||
queueExportItem(BaseModel.TYPE_NOTE, note1_2);
|
||||
|
||||
await exporter.processItem(Folder, folder1);
|
||||
await exporter.processItem(Folder.modelType(), folder1);
|
||||
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
|
||||
|
||||
expect(Object.keys(exporter.context().notePaths).length).toBe(2, 'There should be 2 note paths in the context.');
|
||||
|
@ -121,7 +121,7 @@ describe('services_InteropService_Exporter_Md', function() {
|
|||
queueExportItem(BaseModel.TYPE_FOLDER, folder1.id);
|
||||
queueExportItem(BaseModel.TYPE_NOTE, note1);
|
||||
|
||||
await exporter.processItem(Folder, folder1);
|
||||
await exporter.processItem(Folder.modelType(), folder1);
|
||||
// Create a file with the path of note1 before processing note1
|
||||
await shim.fsDriver().writeFile(`${exportDir}/folder1/note1.md`, 'Note content', 'utf-8');
|
||||
|
||||
|
@ -189,10 +189,10 @@ describe('services_InteropService_Exporter_Md', function() {
|
|||
const folder3 = await Folder.save({ title: 'folder3', parent_id: folder1.id });
|
||||
queueExportItem(BaseModel.TYPE_FOLDER, folder3.id);
|
||||
|
||||
await exporter.processItem(Folder, folder2);
|
||||
await exporter.processItem(Folder, folder3);
|
||||
await exporter.processItem(Folder.modelType(), folder2);
|
||||
await exporter.processItem(Folder.modelType(), folder3);
|
||||
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
|
||||
await exporter.processItem(Note, note2);
|
||||
await exporter.processItem(Note.modelType(), note2);
|
||||
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/folder1`)).toBe(true, 'Folder should be created in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/folder1/folder2`)).toBe(true, 'Folder should be created in filesystem.');
|
||||
|
@ -227,9 +227,9 @@ describe('services_InteropService_Exporter_Md', function() {
|
|||
queueExportItem(BaseModel.TYPE_NOTE, note3);
|
||||
|
||||
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
|
||||
await exporter.processItem(Note, note1);
|
||||
await exporter.processItem(Note, note2);
|
||||
await exporter.processItem(Note, note3);
|
||||
await exporter.processItem(Note.modelType(), note1);
|
||||
await exporter.processItem(Note.modelType(), note2);
|
||||
await exporter.processItem(Note.modelType(), note3);
|
||||
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/${exporter.context().notePaths[note1.id]}`)).toBe(true, 'File should be saved in filesystem.');
|
||||
expect(await shim.fsDriver().exists(`${exportDir}/${exporter.context().notePaths[note2.id]}`)).toBe(true, 'File should be saved in filesystem.');
|
||||
|
@ -262,8 +262,8 @@ describe('services_InteropService_Exporter_Md', function() {
|
|||
queueExportItem(BaseModel.TYPE_NOTE, note2);
|
||||
const resource2 = await Resource.load((await Note.linkedResourceIds(note2.body))[0]);
|
||||
|
||||
await exporter.processItem(Folder, folder1);
|
||||
await exporter.processItem(Folder, folder2);
|
||||
await exporter.processItem(Folder.modelType(), folder1);
|
||||
await exporter.processItem(Folder.modelType(), folder2);
|
||||
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
|
||||
const context = {
|
||||
resourcePaths: {},
|
||||
|
@ -271,8 +271,8 @@ describe('services_InteropService_Exporter_Md', function() {
|
|||
context.resourcePaths[resource1.id] = 'resource1.jpg';
|
||||
context.resourcePaths[resource2.id] = 'resource2.jpg';
|
||||
exporter.updateContext(context);
|
||||
await exporter.processItem(Note, note1);
|
||||
await exporter.processItem(Note, note2);
|
||||
await exporter.processItem(Note.modelType(), note1);
|
||||
await exporter.processItem(Note.modelType(), note2);
|
||||
|
||||
const note1_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note1.id]}`);
|
||||
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
|
||||
|
@ -315,13 +315,13 @@ describe('services_InteropService_Exporter_Md', function() {
|
|||
queueExportItem(BaseModel.TYPE_NOTE, note2);
|
||||
queueExportItem(BaseModel.TYPE_NOTE, note3);
|
||||
|
||||
await exporter.processItem(Folder, folder1);
|
||||
await exporter.processItem(Folder, folder2);
|
||||
await exporter.processItem(Folder, folder3);
|
||||
await exporter.processItem(Folder.modelType(), folder1);
|
||||
await exporter.processItem(Folder.modelType(), folder2);
|
||||
await exporter.processItem(Folder.modelType(), folder3);
|
||||
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
|
||||
await exporter.processItem(Note, note1);
|
||||
await exporter.processItem(Note, note2);
|
||||
await exporter.processItem(Note, note3);
|
||||
await exporter.processItem(Note.modelType(), note1);
|
||||
await exporter.processItem(Note.modelType(), note2);
|
||||
await exporter.processItem(Note.modelType(), note3);
|
||||
|
||||
const note1_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note1.id]}`);
|
||||
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
|
||||
|
@ -351,10 +351,10 @@ describe('services_InteropService_Exporter_Md', function() {
|
|||
queueExportItem(BaseModel.TYPE_NOTE, note1);
|
||||
queueExportItem(BaseModel.TYPE_NOTE, note2);
|
||||
|
||||
await exporter.processItem(Folder, folder1);
|
||||
await exporter.processItem(Folder.modelType(), folder1);
|
||||
await exporter.prepareForProcessingItemType(BaseModel.TYPE_NOTE, itemsToExport);
|
||||
await exporter.processItem(Note, note1);
|
||||
await exporter.processItem(Note, note2);
|
||||
await exporter.processItem(Note.modelType(), note1);
|
||||
await exporter.processItem(Note.modelType(), note2);
|
||||
|
||||
const note2_body = await shim.fsDriver().readFile(`${exportDir}/${exporter.context().notePaths[note2.id]}`);
|
||||
expect(note2_body).toContain('[link](../folder%20with%20space1/note1%20name%20with%20space.md)', 'Whitespace in URL should be encoded');
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { tempFilePath } = require('test-utils.js');
|
||||
const KeymapService = require('lib/services/KeymapService').default;
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
|
@ -76,6 +77,31 @@ describe('services_KeymapService', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('registerCommandAccelerator', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
|
||||
it('should allow registering new commands', async () => {
|
||||
keymapService.initialize('linux');
|
||||
keymapService.registerCommandAccelerator('myCustomCommand', 'Ctrl+Shift+Alt+B');
|
||||
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Ctrl+Shift+Alt+B');
|
||||
|
||||
// Check that macOS key conversion is working
|
||||
keymapService.initialize('darwin');
|
||||
keymapService.registerCommandAccelerator('myCustomCommand', 'CmdOrCtrl+Shift+Alt+B');
|
||||
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+B');
|
||||
keymapService.setAccelerator('myCustomCommand', 'Cmd+Shift+Option+X');
|
||||
|
||||
// Check that the new custom shortcut is being saved and loaded
|
||||
const keymapFilePath = tempFilePath('json');
|
||||
await keymapService.saveCustomKeymap(keymapFilePath);
|
||||
|
||||
keymapService.initialize('darwin');
|
||||
await keymapService.loadCustomKeymap(keymapFilePath);
|
||||
|
||||
expect(keymapService.getAccelerator('myCustomCommand')).toEqual('Cmd+Shift+Option+X');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAccelerator', () => {
|
||||
beforeEach(() => keymapService.initialize());
|
||||
|
||||
|
@ -254,15 +280,15 @@ describe('services_KeymapService', () => {
|
|||
expect(() => keymapService.overrideKeymap(customKeymapItems)).toThrow();
|
||||
});
|
||||
|
||||
it('should throw when the provided commands are invalid', () => {
|
||||
const customKeymapItems = [
|
||||
{ command: 'totallyInvalidCommand', accelerator: 'Ctrl+Shift+G' },
|
||||
{ command: 'print', accelerator: 'Alt+P' },
|
||||
{ command: 'focusElementNoteTitle', accelerator: 'Ctrl+Alt+Shift+J' },
|
||||
];
|
||||
// it('should throw when the provided commands are invalid', () => {
|
||||
// const customKeymapItems = [
|
||||
// { command: 'totallyInvalidCommand', accelerator: 'Ctrl+Shift+G' },
|
||||
// { command: 'print', accelerator: 'Alt+P' },
|
||||
// { command: 'focusElementNoteTitle', accelerator: 'Ctrl+Alt+Shift+J' },
|
||||
// ];
|
||||
|
||||
expect(() => keymapService.overrideKeymap(customKeymapItems)).toThrow();
|
||||
});
|
||||
// expect(() => keymapService.overrideKeymap(customKeymapItems)).toThrow();
|
||||
// });
|
||||
|
||||
it('should throw when duplicate accelerators are provided', () => {
|
||||
const customKeymaps_Darwin = [
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import PluginRunner from '../app/services/plugins/PluginRunner';
|
||||
import PluginService from 'lib/services/plugins/PluginService';
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
|
||||
const Note = require('lib/models/Note');
|
||||
const Folder = require('lib/models/Folder');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
const testPluginDir = `${__dirname}/../tests/support/plugins`;
|
||||
|
||||
function newPluginService() {
|
||||
const runner = new PluginRunner();
|
||||
const service = new PluginService();
|
||||
service.initialize(
|
||||
{
|
||||
joplin: {
|
||||
workspace: {},
|
||||
},
|
||||
},
|
||||
runner,
|
||||
{
|
||||
dispatch: () => {},
|
||||
getState: () => {},
|
||||
}
|
||||
);
|
||||
return service;
|
||||
}
|
||||
|
||||
describe('services_PluginService', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should load and run a simple plugin', asyncTest(async () => {
|
||||
const service = newPluginService();
|
||||
const plugin = await service.loadPlugin(`${testPluginDir}/simple`);
|
||||
await service.runPlugin(plugin);
|
||||
|
||||
const allFolders = await Folder.all();
|
||||
expect(allFolders.length).toBe(1);
|
||||
expect(allFolders[0].title).toBe('my plugin folder');
|
||||
|
||||
const allNotes = await Note.all();
|
||||
expect(allNotes.length).toBe(1);
|
||||
expect(allNotes[0].title).toBe('testing plugin!');
|
||||
expect(allNotes[0].parent_id).toBe(allFolders[0].id);
|
||||
}));
|
||||
|
||||
it('should load and run a plugin that uses external packages', asyncTest(async () => {
|
||||
const service = newPluginService();
|
||||
const plugin = await service.loadPlugin(`${testPluginDir}/withExternalModules`);
|
||||
expect(plugin.id).toBe('withexternalmodules');
|
||||
await service.runPlugin(plugin);
|
||||
|
||||
const allFolders = await Folder.all();
|
||||
expect(allFolders.length).toBe(1);
|
||||
expect(allFolders[0].title).toBe(' foo');
|
||||
}));
|
||||
|
||||
it('should load multiple plugins from a directory', asyncTest(async () => {
|
||||
const service = newPluginService();
|
||||
await service.loadAndRunPlugins(`${testPluginDir}/multi_plugins`);
|
||||
|
||||
const plugin1 = service.pluginById('simple1');
|
||||
const plugin2 = service.pluginById('simple2');
|
||||
expect(!!plugin1).toBe(true);
|
||||
expect(!!plugin2).toBe(true);
|
||||
|
||||
const allFolders = await Folder.all();
|
||||
expect(allFolders.length).toBe(2);
|
||||
expect(allFolders.map((f:any) => f.title).sort().join(', ')).toBe('multi - simple1, multi - simple2');
|
||||
}));
|
||||
|
||||
});
|
|
@ -4,7 +4,7 @@ require('app-module-path').addPath(__dirname);
|
|||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { asyncTest, resourceService, decryptionWorker, encryptionService, loadEncryptionMasterKey, allSyncTargetItemsEncrypted, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const InteropService = require('lib/services/InteropService.js');
|
||||
const InteropService = require('lib/services/interop/InteropService').default;
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
|
@ -16,7 +16,7 @@ const ResourceService = require('lib/services/ResourceService.js');
|
|||
const fs = require('fs-extra');
|
||||
const ArrayUtils = require('lib/ArrayUtils');
|
||||
const ObjectUtils = require('lib/ObjectUtils');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
|
|
@ -5,7 +5,7 @@ require('app-module-path').addPath(__dirname);
|
|||
const { time } = require('lib/time-utils.js');
|
||||
const { asyncTest, fileContentEqual, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Note = require('lib/models/Note.js');
|
||||
const NoteTag = require('lib/models/NoteTag.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
|
@ -13,7 +13,7 @@ const Tag = require('lib/models/Tag.js');
|
|||
const Revision = require('lib/models/Revision.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const RevisionService = require('lib/services/RevisionService.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -8,7 +8,7 @@ const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest
|
|||
const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
const Note = require('lib/models/Note');
|
||||
const ItemChange = require('lib/models/ItemChange');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -10,9 +10,9 @@ const Note = require('lib/models/Note');
|
|||
const Folder = require('lib/models/Folder');
|
||||
const Tag = require('lib/models/Tag');
|
||||
const ItemChange = require('lib/models/ItemChange');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
const ResourceService = require('lib/services/ResourceService.js');
|
||||
|
||||
|
||||
|
|
|
@ -12,10 +12,9 @@
|
|||
// const ItemChange = require('lib/models/ItemChange');
|
||||
// const Setting = require('lib/models/Setting');
|
||||
// const Resource = require('lib/models/Resource.js');
|
||||
// const { shim } = require('lib/shim');
|
||||
// const shim = require('lib/shim').default;
|
||||
// const ResourceService = require('lib/services/ResourceService.js');
|
||||
|
||||
|
||||
// process.on('unhandledRejection', (reason, p) => {
|
||||
// console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
// });
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
// /* eslint-disable no-unused-vars */
|
||||
|
||||
// require('app-module-path').addPath(__dirname);
|
||||
|
||||
// const { asyncTest, fileContentEqual, setupDatabase, checkThrow, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
// const KvStore = require('lib/services/KvStore.js');
|
||||
// const UndoRedoService = require('lib/services/UndoRedoService.js').default;
|
||||
|
||||
// process.on('unhandledRejection', (reason, p) => {
|
||||
// console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
// });
|
||||
|
||||
// describe('services_UndoRedoService', function() {
|
||||
|
||||
// beforeEach(async (done) => {
|
||||
// await setupDatabaseAndSynchronizer(1);
|
||||
// await switchClient(1);
|
||||
// done();
|
||||
// });
|
||||
|
||||
// it('should undo and redo', asyncTest(async () => {
|
||||
// const service = new UndoRedoService();
|
||||
|
||||
// expect(service.canUndo).toBe(false);
|
||||
// expect(service.canRedo).toBe(false);
|
||||
|
||||
// service.push('test');
|
||||
// expect(service.canUndo).toBe(true);
|
||||
// expect(service.canRedo).toBe(false);
|
||||
// service.push('test 2');
|
||||
// service.push('test 3');
|
||||
|
||||
// expect(service.undo()).toBe('test 3');
|
||||
// expect(service.canRedo).toBe(true);
|
||||
// expect(service.undo()).toBe('test 2');
|
||||
// expect(service.undo()).toBe('test');
|
||||
|
||||
// expect(checkThrow(() => service.undo())).toBe(true);
|
||||
|
||||
// expect(service.canUndo).toBe(false);
|
||||
// expect(service.canRedo).toBe(true);
|
||||
|
||||
// expect(service.redo()).toBe('test');
|
||||
// expect(service.canUndo).toBe(true);
|
||||
// expect(service.redo()).toBe('test 2');
|
||||
// expect(service.redo()).toBe('test 3');
|
||||
|
||||
// expect(service.canRedo).toBe(false);
|
||||
|
||||
// expect(checkThrow(() => service.redo())).toBe(true);
|
||||
// }));
|
||||
|
||||
// it('should clear the redo stack when undoing', asyncTest(async () => {
|
||||
// const service = new UndoRedoService();
|
||||
|
||||
// service.push('test');
|
||||
// service.push('test 2');
|
||||
// service.push('test 3');
|
||||
|
||||
// service.undo();
|
||||
// expect(service.canRedo).toBe(true);
|
||||
|
||||
// service.push('test 4');
|
||||
// expect(service.canRedo).toBe(false);
|
||||
|
||||
// expect(service.undo()).toBe('test 4');
|
||||
// expect(service.undo()).toBe('test 2');
|
||||
// }));
|
||||
|
||||
// it('should limit the size of the undo stack', asyncTest(async () => {
|
||||
// const service = new UndoRedoService();
|
||||
|
||||
// for (let i = 0; i < 30; i++) {
|
||||
// service.push(`test${i}`);
|
||||
// }
|
||||
|
||||
// for (let i = 0; i < 20; i++) {
|
||||
// service.undo();
|
||||
// }
|
||||
|
||||
// expect(service.canUndo).toBe(false);
|
||||
// }));
|
||||
|
||||
// });
|
|
@ -3,8 +3,8 @@
|
|||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const { shim } = require('lib/shim');
|
||||
const Setting = require('lib/models/Setting');
|
||||
const shim = require('lib/shim').default;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const KeychainService = require('lib/services/keychain/KeychainService').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
|
@ -50,7 +50,7 @@ describeIfCompatible('services_KeychainService', function() {
|
|||
}));
|
||||
|
||||
it('should save and load secure settings', asyncTest(async () => {
|
||||
Setting.setObjectKey('encryption.passwordCache', 'testing', '123456');
|
||||
Setting.setObjectValue('encryption.passwordCache', 'testing', '123456');
|
||||
await Setting.saveAll();
|
||||
await Setting.load();
|
||||
const passwords = Setting.value('encryption.passwordCache');
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('test-utils.js');
|
||||
const Api = require('lib/services/rest/Api');
|
||||
const Api = require('lib/services/rest/Api').default;
|
||||
const Folder = require('lib/models/Folder');
|
||||
const Resource = require('lib/models/Resource');
|
||||
const Note = require('lib/models/Note');
|
||||
const Tag = require('lib/models/Tag');
|
||||
const NoteTag = require('lib/models/NoteTag');
|
||||
const { shim } = require('lib/shim');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"spec_dir": "tests-build",
|
||||
"spec_files": [
|
||||
"*.js",
|
||||
"services/plugins/*.js",
|
||||
"!test-utils.js"
|
||||
],
|
||||
"stopSpecOnExpectationFailure": false,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
_temp
|
|
@ -0,0 +1,2 @@
|
|||
dist/*
|
||||
node_modules/
|
|
@ -0,0 +1,14 @@
|
|||
# Joplin Plugin
|
||||
|
||||
This is a template to create a new Joplin plugin.
|
||||
|
||||
The main two files you will want to look at are:
|
||||
|
||||
- `/src/index.ts`, which contains the entry point for the plugin source code.
|
||||
- `/src/manifest.json`, which is the plugin manifest. It contains information such as the plugin a name, version, etc.
|
||||
|
||||
The plugin is built using webpack, which create the compiled code in `/dist`. The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
|
||||
|
||||
## Building the plugin
|
||||
|
||||
To build the plugin, simply run `npm run dist`.
|
|
@ -0,0 +1,15 @@
|
|||
import Plugin from '../Plugin';
|
||||
import Joplin from './Joplin';
|
||||
import Logger from 'lib/Logger';
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export default class Global {
|
||||
private joplin_;
|
||||
private requireWhiteList_;
|
||||
constructor(logger: Logger, implementation: any, plugin: Plugin, store: any);
|
||||
get joplin(): Joplin;
|
||||
private requireWhiteList;
|
||||
require(filePath: string): any;
|
||||
get process(): any;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import Plugin from '../Plugin';
|
||||
import JoplinData from './JoplinData';
|
||||
import JoplinPlugins from './JoplinPlugins';
|
||||
import JoplinWorkspace from './JoplinWorkspace';
|
||||
import JoplinFilters from './JoplinFilters';
|
||||
import JoplinCommands from './JoplinCommands';
|
||||
import JoplinViews from './JoplinViews';
|
||||
import JoplinInterop from './JoplinInterop';
|
||||
import JoplinSettings from './JoplinSettings';
|
||||
import Logger from 'lib/Logger';
|
||||
/**
|
||||
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
|
||||
*/
|
||||
export default class Joplin {
|
||||
private data_;
|
||||
private plugins_;
|
||||
private workspace_;
|
||||
private filters_;
|
||||
private commands_;
|
||||
private views_;
|
||||
private interop_;
|
||||
private settings_;
|
||||
constructor(logger: Logger, implementation: any, plugin: Plugin, store: any);
|
||||
get data(): JoplinData;
|
||||
get plugins(): JoplinPlugins;
|
||||
get workspace(): JoplinWorkspace;
|
||||
/**
|
||||
* @ignore
|
||||
*
|
||||
* Not sure if it's the best way to hook into the app
|
||||
* so for now disable filters.
|
||||
*/
|
||||
get filters(): JoplinFilters;
|
||||
get commands(): JoplinCommands;
|
||||
get views(): JoplinViews;
|
||||
get interop(): JoplinInterop;
|
||||
get settings(): JoplinSettings;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
import { Command } from './types';
|
||||
/**
|
||||
* This class allows executing or registering new Joplin commands. Commands can be executed or associated with
|
||||
* {@link JoplinViewsToolbarButtons | toolbar buttons} or {@link JoplinViewsMenuItems | menu items}.
|
||||
*
|
||||
* [View the demo plugin](https://github.com/laurent22/joplin/CliClient/tests/support/plugins/register_command)
|
||||
*
|
||||
* ## Executing Joplin's internal commands
|
||||
*
|
||||
* It is also possible to execute internal Joplin's commands which, as of now, are not well documented.
|
||||
* You can find the list directly on GitHub though at the following locations:
|
||||
*
|
||||
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/gui/MainScreen/commands
|
||||
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/commands
|
||||
* https://github.com/laurent22/joplin/tree/dev/ElectronClient/gui/NoteEditor/commands/editorCommandDeclarations.ts
|
||||
*
|
||||
* To view what arguments are supported, you can open any of these files and look at the `execute()` command.
|
||||
*/
|
||||
export default class JoplinCommands {
|
||||
/**
|
||||
* <span class="platform-desktop">desktop</span> Executes the given command.
|
||||
* The `props` are the arguments passed to the command, and they vary based on the command
|
||||
*
|
||||
* ```typescript
|
||||
* // Create a new note in the current notebook:
|
||||
* await joplin.commands.execute('newNote');
|
||||
*
|
||||
* // Create a new sub-notebook under the provided notebook
|
||||
* // Note: internally, notebooks are called "folders".
|
||||
* await joplin.commands.execute('newFolder', { parent_id: "SOME_FOLDER_ID" });
|
||||
* ```
|
||||
*/
|
||||
execute(commandName: string, props?: any): Promise<any>;
|
||||
/**
|
||||
* <span class="platform-desktop">desktop</span> Registers a new command.
|
||||
*
|
||||
* ```typescript
|
||||
* // Register a new commmand called "testCommand1"
|
||||
*
|
||||
* await joplin.commands.register({
|
||||
* name: 'testCommand1',
|
||||
* label: 'My Test Command 1',
|
||||
* iconName: 'fas fa-music',
|
||||
* execute: () => {
|
||||
* alert('Testing plugin command 1');
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
register(command: Command): Promise<void>;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { Path } from './types';
|
||||
/**
|
||||
* This module provides access to the Joplin data API: https://joplinapp.org/api/
|
||||
* This is the main way to retrieve data, such as notes, notebooks, tags, etc.
|
||||
* or to update them or delete them.
|
||||
*
|
||||
* This is also what you would use to search notes, via the `search` endpoint.
|
||||
*
|
||||
* [View the demo plugin](https://github.com/laurent22/joplin/CliClient/tests/support/plugins/simple)
|
||||
*
|
||||
* In general you would use the methods in this class as if you were using a REST API. There are four methods that map to GET, POST, PUT and DELETE calls.
|
||||
* And each method takes these parameters:
|
||||
*
|
||||
* * `path`: This is an array that represents the path to the resource in the form `["resouceName", "resourceId", "resourceLink"]` (eg. ["tags", ":id", "notes"]). The "resources" segment is the name of the resources you want to access (eg. "notes", "folders", etc.). If not followed by anything, it will refer to all the resources in that collection. The optional "resourceId" points to a particular resources within the collection. Finally, an optional "link" can be present, which links the resource to a collection of resources. This can be used in the API for example to retrieve all the notes associated with a tag.
|
||||
* * `query`: (Optional) The query parameters. In a URL, this is the part after the question mark "?". In this case, it should be an object with key/value pairs.
|
||||
* * `data`: (Optional) Applies to PUT and POST calls only. The request body contains the data you want to create or modify, for example the content of a note or folder.
|
||||
* * `files`: (Optional) Used to create new resources and associate them with files.
|
||||
*
|
||||
* Please refer to the [Joplin API documentation](https://joplinapp.org/api/) for complete details about each call. As the plugin runs within the Joplin application **you do not need an authorisation token** to use this API.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```typescript
|
||||
* // Get a note ID, title and body
|
||||
* const noteId = 'some_note_id';
|
||||
* const note = await joplin.data.get(['notes', noteId], { fields: ['id', 'title', 'body'] });
|
||||
*
|
||||
* // Get all folders
|
||||
* const folders = await joplin.data.get(['folders']);
|
||||
*
|
||||
* // Set the note body
|
||||
* await joplin.data.put(['notes', noteId], null, { body: "New note body" });
|
||||
*
|
||||
* // Create a new note under one of the folders
|
||||
* await joplin.data.post(['notes'], null, { body: "my new note", title: "some title", parent_id: folders[0].id });
|
||||
* ```
|
||||
*/
|
||||
export default class JoplinData {
|
||||
private api_;
|
||||
private pathSegmentRegex_;
|
||||
private serializeApiBody;
|
||||
private pathToString;
|
||||
get(path: Path, query?: any): Promise<any>;
|
||||
post(path: Path, query?: any, body?: any, files?: any[]): Promise<any>;
|
||||
put(path: Path, query?: any, body?: any, files?: any[]): Promise<any>;
|
||||
delete(path: Path, query?: any): Promise<any>;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue