1
0
mirror of https://github.com/laurent22/joplin.git synced 2026-01-20 00:46:28 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Laurent Cozic
666707ec67 All: Resolves #932: Trying to fix orphaned resource bug, but not working 2020-11-08 16:50:40 +00:00
551 changed files with 9444 additions and 16920 deletions

View File

@@ -169,6 +169,9 @@ packages/app-cli/tests/fsDriver.js.map
packages/app-cli/tests/models_Setting.d.ts
packages/app-cli/tests/models_Setting.js
packages/app-cli/tests/models_Setting.js.map
packages/app-cli/tests/services/plugins/api/JoplinSetting.d.ts
packages/app-cli/tests/services/plugins/api/JoplinSetting.js
packages/app-cli/tests/services/plugins/api/JoplinSetting.js.map
packages/app-cli/tests/services/plugins/sandboxProxy.d.ts
packages/app-cli/tests/services/plugins/sandboxProxy.js
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
@@ -433,9 +436,6 @@ packages/app-desktop/gui/MainScreen/commands/showSpellCheckerMenu.js.map
packages/app-desktop/gui/MainScreen/commands/toggleEditors.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleEditors.js
packages/app-desktop/gui/MainScreen/commands/toggleEditors.js.map
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js.map
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js.map
@@ -583,9 +583,6 @@ packages/app-desktop/gui/NoteListControls/commands/focusSearch.js.map
packages/app-desktop/gui/NoteListItem.d.ts
packages/app-desktop/gui/NoteListItem.js
packages/app-desktop/gui/NoteListItem.js.map
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.d.ts
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js.map
packages/app-desktop/gui/NoteTextViewer.d.ts
packages/app-desktop/gui/NoteTextViewer.js
packages/app-desktop/gui/NoteTextViewer.js.map
@@ -595,60 +592,15 @@ packages/app-desktop/gui/NoteToolbar/NoteToolbar.js.map
packages/app-desktop/gui/OneDriveLoginScreen.d.ts
packages/app-desktop/gui/OneDriveLoginScreen.js
packages/app-desktop/gui/OneDriveLoginScreen.js.map
packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map
packages/app-desktop/gui/ResizableLayout/ResizableLayout.d.ts
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js.map
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.d.ts
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js.map
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.d.ts
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js.map
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.d.ts
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js.map
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.d.ts
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js.map
packages/app-desktop/gui/ResizableLayout/utils/movements.d.ts
packages/app-desktop/gui/ResizableLayout/utils/movements.js
packages/app-desktop/gui/ResizableLayout/utils/movements.js.map
packages/app-desktop/gui/ResizableLayout/utils/movements.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/movements.test.js
packages/app-desktop/gui/ResizableLayout/utils/movements.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/persist.d.ts
packages/app-desktop/gui/ResizableLayout/utils/persist.js
packages/app-desktop/gui/ResizableLayout/utils/persist.js.map
packages/app-desktop/gui/ResizableLayout/utils/persist.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/persist.test.js
packages/app-desktop/gui/ResizableLayout/utils/persist.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/removeItem.d.ts
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js.map
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.d.ts
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map
packages/app-desktop/gui/ResizableLayout/utils/style.d.ts
packages/app-desktop/gui/ResizableLayout/utils/style.js
packages/app-desktop/gui/ResizableLayout/utils/style.js.map
packages/app-desktop/gui/ResizableLayout/utils/types.d.ts
packages/app-desktop/gui/ResizableLayout/utils/types.js
packages/app-desktop/gui/ResizableLayout/utils/types.js.map
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js.map
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js.map
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.d.ts
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js.map
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.d.ts
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js.map
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.d.ts
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js.map
packages/app-desktop/gui/ResourceScreen.d.ts
packages/app-desktop/gui/ResourceScreen.js
packages/app-desktop/gui/ResourceScreen.js.map
@@ -661,6 +613,9 @@ packages/app-desktop/gui/Root_UpgradeSyncTarget.js.map
packages/app-desktop/gui/SearchBar/SearchBar.d.ts
packages/app-desktop/gui/SearchBar/SearchBar.js
packages/app-desktop/gui/SearchBar/SearchBar.js.map
packages/app-desktop/gui/SearchBar/hooks/useSearch.d.ts
packages/app-desktop/gui/SearchBar/hooks/useSearch.js
packages/app-desktop/gui/SearchBar/hooks/useSearch.js.map
packages/app-desktop/gui/SearchBar/styles/index.d.ts
packages/app-desktop/gui/SearchBar/styles/index.js
packages/app-desktop/gui/SearchBar/styles/index.js.map
@@ -727,9 +682,6 @@ packages/app-desktop/plugins/GotoAnything.js.map
packages/app-desktop/services/bridge.d.ts
packages/app-desktop/services/bridge.js
packages/app-desktop/services/bridge.js.map
packages/app-desktop/services/commands/stateToWhenClauseContext.d.ts
packages/app-desktop/services/commands/stateToWhenClauseContext.js
packages/app-desktop/services/commands/stateToWhenClauseContext.js.map
packages/app-desktop/services/commands/types.d.ts
packages/app-desktop/services/commands/types.js
packages/app-desktop/services/commands/types.js.map
@@ -907,9 +859,9 @@ packages/lib/models/NoteResource.js.map
packages/lib/models/Setting.d.ts
packages/lib/models/Setting.js
packages/lib/models/Setting.js.map
packages/lib/models/utils/paginatedFeed.d.ts
packages/lib/models/utils/paginatedFeed.js
packages/lib/models/utils/paginatedFeed.js.map
packages/lib/models/utils/modelFeed.d.ts
packages/lib/models/utils/modelFeed.js
packages/lib/models/utils/modelFeed.js.map
packages/lib/models/utils/paginationToSql.d.ts
packages/lib/models/utils/paginationToSql.js
packages/lib/models/utils/paginationToSql.js.map
@@ -979,9 +931,6 @@ packages/lib/services/commands/stateToWhenClauseContext.js.map
packages/lib/services/contextkey/contextkey.d.ts
packages/lib/services/contextkey/contextkey.js
packages/lib/services/contextkey/contextkey.js.map
packages/lib/services/database/types.d.ts
packages/lib/services/database/types.js
packages/lib/services/database/types.js.map
packages/lib/services/debug/populateDatabase.d.ts
packages/lib/services/debug/populateDatabase.js
packages/lib/services/debug/populateDatabase.js.map
@@ -1171,9 +1120,6 @@ packages/lib/services/rest/routes/search.js.map
packages/lib/services/rest/routes/tags.d.ts
packages/lib/services/rest/routes/tags.js
packages/lib/services/rest/routes/tags.js.map
packages/lib/services/rest/utils/collectionToPaginatedResults.d.ts
packages/lib/services/rest/utils/collectionToPaginatedResults.js
packages/lib/services/rest/utils/collectionToPaginatedResults.js.map
packages/lib/services/rest/utils/defaultAction.d.ts
packages/lib/services/rest/utils/defaultAction.js
packages/lib/services/rest/utils/defaultAction.js.map

View File

@@ -25,9 +25,6 @@ module.exports = {
'afterEach': 'readonly',
'jasmine': 'readonly',
// Jest variables
'test': 'readonly',
// React Native variables
'__DEV__': 'readonly',
@@ -133,28 +130,6 @@ module.exports = {
// 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'],
'@typescript-eslint/type-annotation-spacing': ['error', { 'before': false, 'after': true }],
'@typescript-eslint/comma-dangle': ['error', {
'arrays': 'always-multiline',
'objects': 'always-multiline',
'imports': 'always-multiline',
'exports': 'always-multiline',
'enums': 'always-multiline',
'generics': 'always-multiline',
'tuples': 'always-multiline',
'functions': 'never',
}],
'@typescript-eslint/semi': ['error', 'always'],
'@typescript-eslint/member-delimiter-style': ['error', {
'multiline': {
'delimiter': 'semi',
'requireLast': true,
},
'singleline': {
'delimiter': 'semi',
'requireLast': false,
},
}],
},
},
],

View File

@@ -0,0 +1,15 @@
---
name: "\U0001F914 Feature requests and support"
about: 'For non-bug issues we recommend using the forum, where you''ll be more likely
to get an answer: https://discourse.joplinapp.org/'
title: ''
labels: ''
assignees: ''
---
If this is a feature request or a support query, please note that you'll not get an answer here.
Instead we recommend using the forum where you'll are a lot more likely to get an answer: https://discourse.joplinapp.org/
The forum is also the right place to submit a feature request so that it can be discussed by other users.

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: "\U0001F914 Feature requests and support"
url: https://discourse.joplinapp.org/
about: I have a question or feature request …

84
.gitignore vendored
View File

@@ -161,6 +161,9 @@ packages/app-cli/tests/fsDriver.js.map
packages/app-cli/tests/models_Setting.d.ts
packages/app-cli/tests/models_Setting.js
packages/app-cli/tests/models_Setting.js.map
packages/app-cli/tests/services/plugins/api/JoplinSetting.d.ts
packages/app-cli/tests/services/plugins/api/JoplinSetting.js
packages/app-cli/tests/services/plugins/api/JoplinSetting.js.map
packages/app-cli/tests/services/plugins/sandboxProxy.d.ts
packages/app-cli/tests/services/plugins/sandboxProxy.js
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
@@ -425,9 +428,6 @@ packages/app-desktop/gui/MainScreen/commands/showSpellCheckerMenu.js.map
packages/app-desktop/gui/MainScreen/commands/toggleEditors.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleEditors.js
packages/app-desktop/gui/MainScreen/commands/toggleEditors.js.map
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js
packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.js.map
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.d.ts
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js
packages/app-desktop/gui/MainScreen/commands/toggleNoteList.js.map
@@ -575,9 +575,6 @@ packages/app-desktop/gui/NoteListControls/commands/focusSearch.js.map
packages/app-desktop/gui/NoteListItem.d.ts
packages/app-desktop/gui/NoteListItem.js
packages/app-desktop/gui/NoteListItem.js.map
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.d.ts
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js
packages/app-desktop/gui/NoteListWrapper/NoteListWrapper.js.map
packages/app-desktop/gui/NoteTextViewer.d.ts
packages/app-desktop/gui/NoteTextViewer.js
packages/app-desktop/gui/NoteTextViewer.js.map
@@ -587,60 +584,15 @@ packages/app-desktop/gui/NoteToolbar/NoteToolbar.js.map
packages/app-desktop/gui/OneDriveLoginScreen.d.ts
packages/app-desktop/gui/OneDriveLoginScreen.js
packages/app-desktop/gui/OneDriveLoginScreen.js.map
packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map
packages/app-desktop/gui/ResizableLayout/ResizableLayout.d.ts
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js
packages/app-desktop/gui/ResizableLayout/ResizableLayout.js.map
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.d.ts
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js
packages/app-desktop/gui/ResizableLayout/utils/findItemByKey.js.map
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.d.ts
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js
packages/app-desktop/gui/ResizableLayout/utils/isTempContainer.js.map
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.d.ts
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js
packages/app-desktop/gui/ResizableLayout/utils/iterateItems.js.map
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.d.ts
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js
packages/app-desktop/gui/ResizableLayout/utils/layoutItemProp.js.map
packages/app-desktop/gui/ResizableLayout/utils/movements.d.ts
packages/app-desktop/gui/ResizableLayout/utils/movements.js
packages/app-desktop/gui/ResizableLayout/utils/movements.js.map
packages/app-desktop/gui/ResizableLayout/utils/movements.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/movements.test.js
packages/app-desktop/gui/ResizableLayout/utils/movements.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/persist.d.ts
packages/app-desktop/gui/ResizableLayout/utils/persist.js
packages/app-desktop/gui/ResizableLayout/utils/persist.js.map
packages/app-desktop/gui/ResizableLayout/utils/persist.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/persist.test.js
packages/app-desktop/gui/ResizableLayout/utils/persist.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/removeItem.d.ts
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js
packages/app-desktop/gui/ResizableLayout/utils/removeItem.js.map
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.d.ts
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js
packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map
packages/app-desktop/gui/ResizableLayout/utils/style.d.ts
packages/app-desktop/gui/ResizableLayout/utils/style.js
packages/app-desktop/gui/ResizableLayout/utils/style.js.map
packages/app-desktop/gui/ResizableLayout/utils/types.d.ts
packages/app-desktop/gui/ResizableLayout/utils/types.js
packages/app-desktop/gui/ResizableLayout/utils/types.js.map
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.js.map
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js
packages/app-desktop/gui/ResizableLayout/utils/useLayoutItemSizes.test.js.map
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.d.ts
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js
packages/app-desktop/gui/ResizableLayout/utils/useWindowResizeEvent.js.map
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.d.ts
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js
packages/app-desktop/gui/ResizableLayout/utils/validateLayout.js.map
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.d.ts
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js
packages/app-desktop/gui/ResizableLayout/hooks/useLayoutItemSizes.js.map
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.d.ts
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js
packages/app-desktop/gui/ResizableLayout/hooks/useWindowResizeEvent.js.map
packages/app-desktop/gui/ResourceScreen.d.ts
packages/app-desktop/gui/ResourceScreen.js
packages/app-desktop/gui/ResourceScreen.js.map
@@ -653,6 +605,9 @@ packages/app-desktop/gui/Root_UpgradeSyncTarget.js.map
packages/app-desktop/gui/SearchBar/SearchBar.d.ts
packages/app-desktop/gui/SearchBar/SearchBar.js
packages/app-desktop/gui/SearchBar/SearchBar.js.map
packages/app-desktop/gui/SearchBar/hooks/useSearch.d.ts
packages/app-desktop/gui/SearchBar/hooks/useSearch.js
packages/app-desktop/gui/SearchBar/hooks/useSearch.js.map
packages/app-desktop/gui/SearchBar/styles/index.d.ts
packages/app-desktop/gui/SearchBar/styles/index.js
packages/app-desktop/gui/SearchBar/styles/index.js.map
@@ -719,9 +674,6 @@ packages/app-desktop/plugins/GotoAnything.js.map
packages/app-desktop/services/bridge.d.ts
packages/app-desktop/services/bridge.js
packages/app-desktop/services/bridge.js.map
packages/app-desktop/services/commands/stateToWhenClauseContext.d.ts
packages/app-desktop/services/commands/stateToWhenClauseContext.js
packages/app-desktop/services/commands/stateToWhenClauseContext.js.map
packages/app-desktop/services/commands/types.d.ts
packages/app-desktop/services/commands/types.js
packages/app-desktop/services/commands/types.js.map
@@ -899,9 +851,9 @@ packages/lib/models/NoteResource.js.map
packages/lib/models/Setting.d.ts
packages/lib/models/Setting.js
packages/lib/models/Setting.js.map
packages/lib/models/utils/paginatedFeed.d.ts
packages/lib/models/utils/paginatedFeed.js
packages/lib/models/utils/paginatedFeed.js.map
packages/lib/models/utils/modelFeed.d.ts
packages/lib/models/utils/modelFeed.js
packages/lib/models/utils/modelFeed.js.map
packages/lib/models/utils/paginationToSql.d.ts
packages/lib/models/utils/paginationToSql.js
packages/lib/models/utils/paginationToSql.js.map
@@ -971,9 +923,6 @@ packages/lib/services/commands/stateToWhenClauseContext.js.map
packages/lib/services/contextkey/contextkey.d.ts
packages/lib/services/contextkey/contextkey.js
packages/lib/services/contextkey/contextkey.js.map
packages/lib/services/database/types.d.ts
packages/lib/services/database/types.js
packages/lib/services/database/types.js.map
packages/lib/services/debug/populateDatabase.d.ts
packages/lib/services/debug/populateDatabase.js
packages/lib/services/debug/populateDatabase.js.map
@@ -1163,9 +1112,6 @@ packages/lib/services/rest/routes/search.js.map
packages/lib/services/rest/routes/tags.d.ts
packages/lib/services/rest/routes/tags.js
packages/lib/services/rest/routes/tags.js.map
packages/lib/services/rest/utils/collectionToPaginatedResults.d.ts
packages/lib/services/rest/utils/collectionToPaginatedResults.js
packages/lib/services/rest/utils/collectionToPaginatedResults.js.map
packages/lib/services/rest/utils/defaultAction.d.ts
packages/lib/services/rest/utils/defaultAction.js
packages/lib/services/rest/utils/defaultAction.js.map

1217
.ignore Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -66,11 +66,13 @@ script:
# Only do it for pull requests because Travis randomly fails to run them
# and that would break the desktop release.
if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
cd packages/app-cli
npm run test-ci
testResult=$?
if [ $testResult -ne 0 ]; then
exit $testResult
fi
cd ../..
fi
# Run linter for pull requests only - this is so that

View File

@@ -64,7 +64,7 @@ The Web Clipper is a browser extension that allows you to save web pages and scr
| :---: | :---: | :---: |
| <img width="50" src="https://avatars0.githubusercontent.com/u/6979755?s=96&v=4"/></br>[Devon Zuegel](https://github.com/devonzuegel) | <img width="50" src="https://avatars2.githubusercontent.com/u/24908652?s=96&v=4"/></br>[小西 孝宗](https://github.com/konishi-t) | <img width="50" src="https://avatars2.githubusercontent.com/u/215668?s=96&v=4"/></br>[Alexander van der Berg](https://github.com/avanderberg)
| <img width="50" src="https://avatars0.githubusercontent.com/u/1168659?s=96&v=4"/></br>[Nicholas Head](https://github.com/nicholashead) | <img width="50" src="https://avatars2.githubusercontent.com/u/1439535?s=96&v=4"/></br>[Frank Bloise](https://github.com/fbloise) | <img width="50" src="https://avatars2.githubusercontent.com/u/15859362?s=96&v=4"/></br>[Thomas Broussard](https://github.com/thomasbroussard)
| <img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br>[Brandon Johnson](https://github.com/dbrandonjohnson) | <img width="50" src="https://avatars1.githubusercontent.com/u/3061769?s=96&v=4"/></br>[@cnagy](https://github.com/c-nagy) |
| <img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br>[Brandon Johnson](https://github.com/dbrandonjohnson) | |
<!-- TOC -->
# Table of contents
@@ -318,8 +318,6 @@ The currently supported template variables are:
| `{{time}}` | Current time formatted based on the settings format | 13:00 |
| `{{datetime}}` | Current date and time formatted based on the settings format | 01/01/19 1:00 PM |
| `{{#custom_datetime}}` | Current date and/or time formatted based on a supplied string (using [moment.js](https://momentjs.com/) formatting) | `{{#custom_datetime}}M d{{/custom_datetime}}` |
| `{{bowm}}` | Date of the beginning of the week (when week starts on Monday) based on the settings format | |
| `{{bows}}` | Date of the beginning of the week (when week starts on Sunday) based on the settings format | |
# Searching
@@ -413,7 +411,7 @@ Please see the guide for information on how to contribute to the development of
Joplin is currently available in the languages below. If you would like to contribute a **new translation**, it is quite straightforward, please follow these steps:
- [Download Poedit](https://poedit.net/), the translation editor, and install it.
- [Download the file to be translated](https://raw.githubusercontent.com/laurent22/joplin/dev/packages/tools/locales/joplin.pot).
- [Download the file to be translated](https://raw.githubusercontent.com/laurent22/joplin/dev/packages/app-cli/locales/joplin.pot).
- In Poedit, open this .pot file, go into the Catalog menu and click Configuration. Change "Country" and "Language" to your own country and language.
- From then you can translate the file.
- Once it is done, please [open a pull request](https://github.com/laurent22/joplin/pulls) and add the file to it.

View File

@@ -433,39 +433,19 @@ for (let portToTest = 41184; portToTest &lt;= 41194; portToTest++) {
</code></pre>
<p>By default API results will contain the following fields: <strong>id</strong>, <strong>parent_id</strong>, <strong>title</strong></p>
<h1>Pagination<a name="pagination" href="#pagination" class="heading-anchor">🔗</a></h1>
<p>All API calls that return multiple results will be paginated and will have the following structure:</p>
<table>
<thead>
<tr>
<th>Key</th>
<th>Always present?</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>items</code></td>
<td>Yes</td>
<td>The array of items you have requested.</td>
</tr>
<tr>
<td><code>has_more</code></td>
<td>Yes</td>
<td>If <code>true</code>, there are more items after this page. If <code>false</code>, it means you have reached the end of the data set.</td>
</tr>
</tbody>
</table>
<p>All API calls that return multiple results will be paginated. The actual results will be under the <code>items</code> key, and if there are more results, there will also be a <code>cursor</code> key, which allows you to fetch the next results. If the <code>cursor</code> key is not present, it means you have reached the end of the data set.</p>
<p>You can specify how the results should be sorted using the <code>order_by</code> and <code>order_dir</code> query parameters, and you can specify the number of items to be returned using the <code>limit</code> parameter (the maximum being 100 items).</p>
<p>The following call for example will initiate a request to fetch all the notes, 10 at a time, and sorted by &quot;updated_time&quot; ascending:</p>
<pre><code>curl http://localhost:41184/notes?order_by=updated_time&amp;order_dir=ASC&amp;limit=10
</code></pre>
<p>This will return a result like this</p>
<pre><code>{ &quot;items&quot;: [ /* 10 notes */ ], &quot;has_more&quot;: true }
<pre><code>{ &quot;items&quot;: [ /* 10 notes */ ], &quot;cursor&quot;: &quot;somecursor&quot; }
</code></pre>
<p>Then you will resume fetching the results using this query:</p>
<pre><code>curl http://localhost:41184/notes?order_by=updated_time&amp;order_dir=ASC&amp;limit=10&amp;page=2
<pre><code>curl http://localhost:41184/notes?cursor=somecursor
</code></pre>
<p>Eventually you will get some results that do not contain an &quot;has_more&quot; paramater, at which point you will have retrieved all the results</p>
<p>Note that you only need to pass the cursor to the next request, as it will continue the fetching process using the same parameters you initially provided.</p>
<p>Eventually you will get some results that do not contain a &quot;cursor&quot; paramater, at which point you will have retrieved all the results</p>
<p>As an example the pseudo-code below could be used to fetch all the notes:</p>
<pre><code class="language-javascript">
async function fetchJson(url) {
@@ -473,11 +453,15 @@ async function fetchJson(url) {
}
async function fetchAllNotes() {
let pageNum = 1;
let query = '';
const url = 'http://localhost:41184/notes';
do {
const response = await fetchJson((http://localhost:41184/notes?page=' + pageNum++);
console.info('Printing notes:', response.items);
} while (response.has_more)
const response = await fetchJson(url + query);
console.info('Printing notes:');
console.info(response.items);
query = '?cursor' + response.cursor;
} while (response.cursor)
}
</code></pre>
<h1>Error handling<a name="error-handling" href="#error-handling" class="heading-anchor">🔗</a></h1>
@@ -932,8 +916,6 @@ async function fetchAllNotes() {
<p>Gets resource with ID :id</p>
<h2>GET /resources/:id/file<a name="get-resources-id-file" href="#get-resources-id-file" class="heading-anchor">🔗</a></h2>
<p>Gets the actual file associated with this resource.</p>
<h2>GET /resources/:id/notes<a name="get-resources-id-notes" href="#get-resources-id-notes" class="heading-anchor">🔗</a></h2>
<p>Gets the notes (IDs) associated with a resource.</p>
<h2>POST /resources<a name="post-resources" href="#post-resources" class="heading-anchor">🔗</a></h2>
<p>Creates a new resource</p>
<p>Creating a new resource is special because you also need to upload the file. Unlike other API calls, this one must have the &quot;multipart/form-data&quot; Content-Type. The file data must be passed to the &quot;data&quot; form field, and the other properties to the &quot;props&quot; form field. An example of a valid call with cURL would be:</p>

File diff suppressed because it is too large Load Diff

View File

@@ -512,7 +512,7 @@ https://github.com/laurent22/joplin/blob/dev/README.md
</tr>
<tr>
<td style="text-align:center"><img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br><a href="https://github.com/dbrandonjohnson">Brandon Johnson</a></td>
<td style="text-align:center"><img width="50" src="https://avatars1.githubusercontent.com/u/3061769?s=96&v=4"/></br><a href="https://github.com/c-nagy">@cnagy</a></td>
<td style="text-align:center"></td>
<td style="text-align:center"></td>
</tr>
</tbody>
@@ -854,7 +854,7 @@ Eg. <code>:search -- &quot;-tag:tag1&quot;</code>.</p>
<p>Joplin is currently available in the languages below. If you would like to contribute a <strong>new translation</strong>, it is quite straightforward, please follow these steps:</p>
<ul>
<li><a href="https://poedit.net/">Download Poedit</a>, the translation editor, and install it.</li>
<li><a href="https://raw.githubusercontent.com/laurent22/joplin/dev/packages/tools/locales/joplin.pot">Download the file to be translated</a>.</li>
<li><a href="https://raw.githubusercontent.com/laurent22/joplin/dev/packages/app-cli/locales/joplin.pot">Download the file to be translated</a>.</li>
<li>In Poedit, open this .pot file, go into the Catalog menu and click Configuration. Change &quot;Country&quot; and &quot;Language&quot; to your own country and language.</li>
<li>From then you can translate the file.</li>
<li>Once it is done, please <a href="https://github.com/laurent22/joplin/pulls">open a pull request</a> and add the file to it.</li>

View File

@@ -402,15 +402,15 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tbody>
<tr>
<td>Total Windows downloads</td>
<td>1,090,167</td>
<td>1,119,966</td>
</tr>
<tr>
<td>Total macOs downloads</td>
<td>422,857</td>
<td>436,003</td>
</tr>
<tr>
<td>Total Linux downloads</td>
<td>307,611</td>
<td>318,326</td>
</tr>
<tr>
<td>Windows %</td>
@@ -441,114 +441,370 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.18">v1.3.18</a></td>
<td>2020-11-06T12:07:02Z</td>
<td>8,566</td>
<td>3,609</td>
<td>2,837</td>
<td>15,012</td>
<td>806</td>
<td>317</td>
<td>320</td>
<td>1,443</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.17">v1.3.17</a></td>
<td>2020-11-06T11:35:15Z</td>
<td>22</td>
<td>5</td>
<td>9</td>
<td>36</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.6">v1.4.6</a></td>
<td>2020-11-05T22:44:12Z</td>
<td>196</td>
<td>60</td>
<td>36</td>
<td>292</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.15">v1.3.15</a></td>
<td>2020-11-04T12:22:50Z</td>
<td>2,121</td>
<td>1,251</td>
<td>820</td>
<td>4,192</td>
<td>2,065</td>
<td>1,224</td>
<td>799</td>
<td>4,088</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.11">v1.3.11</a></td>
<td>2020-10-31T13:22:20Z</td>
<td>680</td>
<td>166</td>
<td>466</td>
<td>1,312</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.10">v1.3.10</a></td>
<td>2020-10-29T13:27:14Z</td>
<td>331</td>
<td>99</td>
<td>301</td>
<td>731</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.9">v1.3.9</a></td>
<td>2020-10-23T16:04:26Z</td>
<td>795</td>
<td>221</td>
<td>611</td>
<td>1,627</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.8">v1.3.8</a></td>
<td>2020-10-21T18:46:29Z</td>
<td>468</td>
<td>91</td>
<td>315</td>
<td>874</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.7">v1.3.7</a></td>
<td>2020-10-20T11:35:55Z</td>
<td>252</td>
<td>63</td>
<td>328</td>
<td>643</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.5">v1.3.5</a></td>
<td>2020-10-17T14:26:35Z</td>
<td>427</td>
<td>114</td>
<td>393</td>
<td>934</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.3">v1.3.3</a></td>
<td>2020-10-17T10:56:57Z</td>
<td>96</td>
<td>23</td>
<td>21</td>
<td>140</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.2">v1.3.2</a></td>
<td>2020-10-11T20:39:49Z</td>
<td>642</td>
<td>161</td>
<td>550</td>
<td>1,353</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.3.1">v1.3.1</a></td>
<td>2020-10-11T15:10:18Z</td>
<td>62</td>
<td>30</td>
<td>31</td>
<td>123</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.2.6">v1.2.6</a></td>
<td>2020-10-09T13:56:59Z</td>
<td>43,259</td>
<td>17,626</td>
<td>13,986</td>
<td>74,871</td>
<td>43,075</td>
<td>17,608</td>
<td>13,970</td>
<td>74,653</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.2.4">v1.2.4</a></td>
<td>2020-09-30T07:34:29Z</td>
<td>771</td>
<td>229</td>
<td>785</td>
<td>1,785</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.2.3">v1.2.3</a></td>
<td>2020-09-29T15:13:02Z</td>
<td>177</td>
<td>50</td>
<td>68</td>
<td>295</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.2.2">v1.2.2</a></td>
<td>2020-09-22T20:31:55Z</td>
<td>736</td>
<td>188</td>
<td>628</td>
<td>1,552</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.1.4">v1.1.4</a></td>
<td>2020-09-21T11:20:09Z</td>
<td>27,391</td>
<td>13,455</td>
<td>7,691</td>
<td>48,537</td>
<td>27,381</td>
<td>13,453</td>
<td>7,690</td>
<td>48,524</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.1.3">v1.1.3</a></td>
<td>2020-09-17T10:30:37Z</td>
<td>543</td>
<td>137</td>
<td>454</td>
<td>1,134</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.1.2">v1.1.2</a></td>
<td>2020-09-15T12:58:38Z</td>
<td>343</td>
<td>102</td>
<td>241</td>
<td>686</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.1.1">v1.1.1</a></td>
<td>2020-09-11T23:32:47Z</td>
<td>480</td>
<td>182</td>
<td>336</td>
<td>998</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.245">v1.0.245</a></td>
<td>2020-09-09T12:56:10Z</td>
<td>20,762</td>
<td>9,957</td>
<td>5,617</td>
<td>36,336</td>
<td>20,756</td>
<td>9,956</td>
<td>5,615</td>
<td>36,327</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.242">v1.0.242</a></td>
<td>2020-09-04T22:00:34Z</td>
<td>12,299</td>
<td>6,393</td>
<td>12,297</td>
<td>6,392</td>
<td>3,003</td>
<td>21,695</td>
<td>21,692</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.241">v1.0.241</a></td>
<td>2020-09-04T18:06:00Z</td>
<td>23,064</td>
<td>5,684</td>
<td>23,061</td>
<td>5,683</td>
<td>4,959</td>
<td>33,707</td>
<td>33,703</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.239">v1.0.239</a></td>
<td>2020-09-01T21:56:36Z</td>
<td>562</td>
<td>213</td>
<td>394</td>
<td>1,169</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.237">v1.0.237</a></td>
<td>2020-08-29T15:38:04Z</td>
<td>574</td>
<td>898</td>
<td>333</td>
<td>1,805</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.236">v1.0.236</a></td>
<td>2020-08-28T09:16:54Z</td>
<td>300</td>
<td>98</td>
<td>100</td>
<td>498</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.235">v1.0.235</a></td>
<td>2020-08-18T22:08:01Z</td>
<td>1,624</td>
<td>477</td>
<td>916</td>
<td>3,017</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.234">v1.0.234</a></td>
<td>2020-08-17T23:13:02Z</td>
<td>515</td>
<td>113</td>
<td>96</td>
<td>724</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.233">v1.0.233</a></td>
<td>2020-08-01T14:51:15Z</td>
<td>42,160</td>
<td>18,145</td>
<td>12,344</td>
<td>72,649</td>
<td>42,141</td>
<td>18,142</td>
<td>12,342</td>
<td>72,625</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.232">v1.0.232</a></td>
<td>2020-07-28T22:34:40Z</td>
<td>637</td>
<td>210</td>
<td>174</td>
<td>1,021</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.227">v1.0.227</a></td>
<td>2020-07-07T20:44:54Z</td>
<td>40,128</td>
<td>15,234</td>
<td>40,124</td>
<td>15,233</td>
<td>9,610</td>
<td>64,972</td>
<td>64,967</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.226">v1.0.226</a></td>
<td>2020-07-04T10:21:26Z</td>
<td>4,862</td>
<td>2,228</td>
<td>684</td>
<td>7,774</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.224">v1.0.224</a></td>
<td>2020-06-20T22:26:08Z</td>
<td>24,702</td>
<td>10,975</td>
<td>24,700</td>
<td>10,974</td>
<td>5,999</td>
<td>41,676</td>
<td>41,673</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.223">v1.0.223</a></td>
<td>2020-06-20T11:51:27Z</td>
<td>172</td>
<td>100</td>
<td>74</td>
<td>346</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.221">v1.0.221</a></td>
<td>2020-06-20T01:44:20Z</td>
<td>840</td>
<td>191</td>
<td>204</td>
<td>1,235</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.220">v1.0.220</a></td>
<td>2020-06-13T18:26:22Z</td>
<td>31,505</td>
<td>9,887</td>
<td>31,501</td>
<td>9,886</td>
<td>6,407</td>
<td>47,799</td>
<td>47,794</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.218">v1.0.218</a></td>
<td>2020-06-07T10:43:34Z</td>
<td>14,480</td>
<td>14,477</td>
<td>6,946</td>
<td>2,950</td>
<td>24,376</td>
<td>24,373</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.217">v1.0.217</a></td>
<td>2020-06-06T15:17:27Z</td>
<td>216</td>
<td>84</td>
<td>52</td>
<td>352</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.216">v1.0.216</a></td>
<td>2020-05-24T14:21:01Z</td>
<td>36,322</td>
<td>14,233</td>
<td>36,286</td>
<td>14,228</td>
<td>10,169</td>
<td>60,724</td>
<td>60,683</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.214">v1.0.214</a></td>
<td>2020-05-21T17:15:15Z</td>
<td>6,416</td>
<td>3,450</td>
<td>757</td>
<td>10,623</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.212">v1.0.212</a></td>
<td>2020-05-21T07:48:39Z</td>
<td>203</td>
<td>62</td>
<td>44</td>
<td>309</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.211">v1.0.211</a></td>
<td>2020-05-20T08:59:16Z</td>
<td>294</td>
<td>127</td>
<td>83</td>
<td>504</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.209">v1.0.209</a></td>
<td>2020-05-17T18:32:51Z</td>
<td>1,387</td>
<td>833</td>
<td>145</td>
<td>2,365</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.207">v1.0.207</a></td>
<td>2020-05-10T16:37:35Z</td>
<td>1,181</td>
<td>259</td>
<td>1,011</td>
<td>2,451</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.201">v1.0.201</a></td>
<td>2020-04-15T22:55:13Z</td>
<td>52,335</td>
<td>20,032</td>
<td>18,167</td>
<td>90,534</td>
<td>52,297</td>
<td>20,030</td>
<td>18,166</td>
<td>90,493</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.200">v1.0.200</a></td>
@@ -561,90 +817,146 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.199">v1.0.199</a></td>
<td>2020-04-10T18:41:58Z</td>
<td>19,236</td>
<td>19,234</td>
<td>5,878</td>
<td>3,783</td>
<td>28,897</td>
<td>28,895</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.197">v1.0.197</a></td>
<td>2020-03-30T17:21:22Z</td>
<td>22,059</td>
<td>22,056</td>
<td>9,506</td>
<td>5,608</td>
<td>37,173</td>
<td>5,606</td>
<td>37,168</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.195">v1.0.195</a></td>
<td>2020-03-22T19:56:12Z</td>
<td>18,860</td>
<td>7,941</td>
<td>4,502</td>
<td>31,303</td>
<td>18,859</td>
<td>7,939</td>
<td>4,501</td>
<td>31,299</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.194">v1.0.194</a></td>
<td>2020-03-14T00:00:32Z</td>
<td>1,271</td>
<td>1,369</td>
<td>498</td>
<td>3,138</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.193">v1.0.193</a></td>
<td>2020-03-08T08:58:53Z</td>
<td>28,593</td>
<td>28,592</td>
<td>10,894</td>
<td>7,356</td>
<td>46,843</td>
<td>7,355</td>
<td>46,841</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.192">v1.0.192</a></td>
<td>2020-03-06T23:27:52Z</td>
<td>464</td>
<td>116</td>
<td>85</td>
<td>665</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.190">v1.0.190</a></td>
<td>2020-03-06T01:22:22Z</td>
<td>363</td>
<td>83</td>
<td>82</td>
<td>528</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.189">v1.0.189</a></td>
<td>2020-03-04T17:27:15Z</td>
<td>335</td>
<td>90</td>
<td>88</td>
<td>513</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.187">v1.0.187</a></td>
<td>2020-03-01T12:31:06Z</td>
<td>913</td>
<td>224</td>
<td>261</td>
<td>1,398</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.179">v1.0.179</a></td>
<td>2020-01-24T22:42:41Z</td>
<td>70,935</td>
<td>28,466</td>
<td>22,490</td>
<td>121,891</td>
<td>28,463</td>
<td>22,489</td>
<td>121,887</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.178">v1.0.178</a></td>
<td>2020-01-20T19:06:45Z</td>
<td>17,525</td>
<td>5,956</td>
<td>5,955</td>
<td>2,578</td>
<td>26,059</td>
<td>26,058</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.177">v1.0.177</a></td>
<td>2019-12-30T14:40:40Z</td>
<td>1,933</td>
<td>430</td>
<td>656</td>
<td>3,019</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.176">v1.0.176</a></td>
<td>2019-12-14T10:36:44Z</td>
<td>3,116</td>
<td>2,528</td>
<td>463</td>
<td>6,107</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.175">v1.0.175</a></td>
<td>2019-12-08T11:48:47Z</td>
<td>71,972</td>
<td>71,963</td>
<td>16,855</td>
<td>16,475</td>
<td>105,302</td>
<td>16,474</td>
<td>105,292</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.174">v1.0.174</a></td>
<td>2019-11-12T18:20:58Z</td>
<td>30,387</td>
<td>11,687</td>
<td>11,684</td>
<td>8,216</td>
<td>50,290</td>
<td>50,287</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.173">v1.0.173</a></td>
<td>2019-11-11T08:33:35Z</td>
<td>5,057</td>
<td>2,071</td>
<td>2,070</td>
<td>739</td>
<td>7,867</td>
<td>7,866</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.170">v1.0.170</a></td>
<td>2019-10-13T22:13:04Z</td>
<td>27,369</td>
<td>8,737</td>
<td>27,368</td>
<td>8,735</td>
<td>7,668</td>
<td>43,774</td>
<td>43,771</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.169">v1.0.169</a></td>
<td>2019-09-27T18:35:13Z</td>
<td>17,080</td>
<td>5,915</td>
<td>5,914</td>
<td>3,750</td>
<td>26,745</td>
<td>26,744</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.168">v1.0.168</a></td>
@@ -657,58 +969,58 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.167">v1.0.167</a></td>
<td>2019-09-10T08:48:37Z</td>
<td>16,778</td>
<td>16,777</td>
<td>5,698</td>
<td>3,700</td>
<td>26,176</td>
<td>26,175</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.166">v1.0.166</a></td>
<td>2019-09-09T17:35:54Z</td>
<td>1,950</td>
<td>557</td>
<td>235</td>
<td>2,742</td>
<td>556</td>
<td>234</td>
<td>2,740</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.165">v1.0.165</a></td>
<td>2019-08-14T21:46:29Z</td>
<td>18,875</td>
<td>6,968</td>
<td>6,967</td>
<td>5,459</td>
<td>31,302</td>
<td>31,301</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.161">v1.0.161</a></td>
<td>2019-07-13T18:30:00Z</td>
<td>19,272</td>
<td>19,271</td>
<td>6,348</td>
<td>4,132</td>
<td>29,752</td>
<td>4,131</td>
<td>29,750</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.160">v1.0.160</a></td>
<td>2019-06-15T00:21:40Z</td>
<td>30,455</td>
<td>7,741</td>
<td>30,450</td>
<td>7,738</td>
<td>8,098</td>
<td>46,294</td>
<td>46,286</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.159">v1.0.159</a></td>
<td>2019-06-08T00:00:19Z</td>
<td>5,188</td>
<td>2,174</td>
<td>1,105</td>
<td>8,467</td>
<td>1,104</td>
<td>8,466</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.158">v1.0.158</a></td>
<td>2019-05-27T19:01:18Z</td>
<td>9,809</td>
<td>3,534</td>
<td>9,808</td>
<td>3,533</td>
<td>1,934</td>
<td>15,277</td>
<td>15,275</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.157">v1.0.157</a></td>
@@ -719,12 +1031,20 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td>3,302</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.153">v1.0.153</a></td>
<td>2019-05-15T06:27:29Z</td>
<td>844</td>
<td>97</td>
<td>104</td>
<td>1,045</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.152">v1.0.152</a></td>
<td>2019-05-13T09:08:07Z</td>
<td>13,861</td>
<td>13,859</td>
<td>4,423</td>
<td>4,059</td>
<td>22,343</td>
<td>22,341</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.151">v1.0.151</a></td>
@@ -743,6 +1063,14 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td>611</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.148">v1.0.148</a></td>
<td>2019-05-08T19:12:24Z</td>
<td>127</td>
<td>52</td>
<td>94</td>
<td>273</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.145">v1.0.145</a></td>
<td>2019-05-03T09:16:53Z</td>
<td>6,998</td>
@@ -753,34 +1081,58 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.143">v1.0.143</a></td>
<td>2019-04-22T10:51:38Z</td>
<td>11,911</td>
<td>11,910</td>
<td>3,546</td>
<td>2,776</td>
<td>18,233</td>
<td>18,232</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.142">v1.0.142</a></td>
<td>2019-04-02T16:44:51Z</td>
<td>14,648</td>
<td>14,647</td>
<td>4,557</td>
<td>4,724</td>
<td>23,929</td>
<td>23,928</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.140">v1.0.140</a></td>
<td>2019-03-10T20:59:58Z</td>
<td>13,621</td>
<td>4,166</td>
<td>4,165</td>
<td>3,172</td>
<td>20,959</td>
<td>20,958</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.139">v1.0.139</a></td>
<td>2019-03-09T10:06:48Z</td>
<td>117</td>
<td>57</td>
<td>43</td>
<td>217</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.138">v1.0.138</a></td>
<td>2019-03-03T17:23:00Z</td>
<td>143</td>
<td>82</td>
<td>82</td>
<td>307</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.137">v1.0.137</a></td>
<td>2019-03-03T01:12:51Z</td>
<td>584</td>
<td>54</td>
<td>81</td>
<td>719</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.135">v1.0.135</a></td>
<td>2019-02-27T23:36:57Z</td>
<td>12,483</td>
<td>3,953</td>
<td>4,072</td>
<td>20,508</td>
<td>3,952</td>
<td>4,071</td>
<td>20,506</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.134">v1.0.134</a></td>
@@ -807,6 +1159,14 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td>15,825</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.126">v1.0.126</a></td>
<td>2019-02-09T19:46:16Z</td>
<td>926</td>
<td>67</td>
<td>115</td>
<td>1,108</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.125">v1.0.125</a></td>
<td>2019-01-26T18:14:33Z</td>
<td>10,245</td>
@@ -825,34 +1185,34 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.119">v1.0.119</a></td>
<td>2018-12-18T12:40:22Z</td>
<td>8,901</td>
<td>8,900</td>
<td>3,257</td>
<td>2,013</td>
<td>14,171</td>
<td>2,012</td>
<td>14,169</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.118">v1.0.118</a></td>
<td>2019-01-11T08:34:13Z</td>
<td>713</td>
<td>244</td>
<td>712</td>
<td>243</td>
<td>87</td>
<td>1,044</td>
<td>1,042</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.117">v1.0.117</a></td>
<td>2018-11-24T12:05:24Z</td>
<td>16,252</td>
<td>4,889</td>
<td>6,379</td>
<td>27,520</td>
<td>6,378</td>
<td>27,519</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.116">v1.0.116</a></td>
<td>2018-11-20T19:09:24Z</td>
<td>3,468</td>
<td>1,117</td>
<td>1,116</td>
<td>712</td>
<td>5,297</td>
<td>5,296</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.115">v1.0.115</a></td>
@@ -873,10 +1233,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.111">v1.0.111</a></td>
<td>2018-09-30T20:15:09Z</td>
<td>12,007</td>
<td>12,006</td>
<td>3,285</td>
<td>3,663</td>
<td>18,955</td>
<td>3,660</td>
<td>18,951</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.110">v1.0.110</a></td>
@@ -895,6 +1255,14 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td>3,122</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.108">v1.0.108</a></td>
<td>2018-09-29T18:49:29Z</td>
<td>25</td>
<td>17</td>
<td>11</td>
<td>53</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.107">v1.0.107</a></td>
<td>2018-09-16T19:51:07Z</td>
<td>7,145</td>
@@ -913,26 +1281,26 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.105">v1.0.105</a></td>
<td>2018-09-05T11:29:36Z</td>
<td>4,652</td>
<td>1,585</td>
<td>1,452</td>
<td>7,689</td>
<td>4,651</td>
<td>1,584</td>
<td>1,451</td>
<td>7,686</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.104">v1.0.104</a></td>
<td>2018-06-28T20:25:36Z</td>
<td>15,038</td>
<td>4,696</td>
<td>4,695</td>
<td>7,326</td>
<td>27,060</td>
<td>27,059</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.103">v1.0.103</a></td>
<td>2018-06-21T19:38:13Z</td>
<td>2,048</td>
<td>882</td>
<td>679</td>
<td>3,609</td>
<td>678</td>
<td>3,608</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.101">v1.0.101</a></td>
@@ -947,8 +1315,8 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td>2018-06-14T17:41:43Z</td>
<td>875</td>
<td>429</td>
<td>239</td>
<td>1,543</td>
<td>238</td>
<td>1,542</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.99">v1.0.99</a></td>
@@ -970,33 +1338,33 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.96">v1.0.96</a></td>
<td>2018-05-26T16:36:39Z</td>
<td>2,715</td>
<td>1,221</td>
<td>1,602</td>
<td>5,538</td>
<td>1,220</td>
<td>1,599</td>
<td>5,534</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.95">v1.0.95</a></td>
<td>2018-05-25T13:04:30Z</td>
<td>415</td>
<td>414</td>
<td>215</td>
<td>116</td>
<td>746</td>
<td>115</td>
<td>744</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.94">v1.0.94</a></td>
<td>2018-05-21T20:52:59Z</td>
<td>1,128</td>
<td>580</td>
<td>394</td>
<td>2,102</td>
<td>393</td>
<td>2,101</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.93">v1.0.93</a></td>
<td>2018-05-14T11:36:01Z</td>
<td>1,786</td>
<td>1,080</td>
<td>1,785</td>
<td>1,079</td>
<td>755</td>
<td>3,621</td>
<td>3,619</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.91">v1.0.91</a></td>
@@ -1025,10 +1393,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.83">v1.0.83</a></td>
<td>2018-04-04T19:43:58Z</td>
<td>4,808</td>
<td>4,806</td>
<td>2,529</td>
<td>2,656</td>
<td>9,993</td>
<td>9,991</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v1.0.82">v1.0.82</a></td>
@@ -1217,10 +1585,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v0.10.39">v0.10.39</a></td>
<td>2017-12-11T21:19:44Z</td>
<td>5,783</td>
<td>4,256</td>
<td>3,158</td>
<td>13,197</td>
<td>5,781</td>
<td>4,254</td>
<td>3,157</td>
<td>13,192</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v0.10.38">v0.10.38</a></td>
@@ -1307,8 +1675,8 @@ https://github.com/laurent22/joplin/blob/dev/readme/stats.md
<td>2017-11-24T14:27:49Z</td>
<td>148</td>
<td>694</td>
<td>6,377</td>
<td>7,219</td>
<td>6,373</td>
<td>7,215</td>
</tr>
<tr>
<td><a href="https://github.com/laurent22/joplin/releases/tag/v0.10.23">v0.10.23</a></td>

View File

@@ -8,11 +8,8 @@
"files.exclude": {
"lerna-debug.log": true,
"_mydocs/mdtest/": true,
"./packages/lib/plugin_types": true,
"_releases/": true,
"_vieux/": true,
".gitignore": true,
".eslintignore": true,
"./packages/app-cli/**/*.*~": true,
"./packages/app-cli/**/*.mo": true,
"./packages/app-cli/**/build/": true,

View File

@@ -10,10 +10,10 @@
"buildApiDoc": "npm start --prefix=packages/app-cli -- apidoc ../../readme/api/references/rest_api.md",
"buildDoc": "./packages/tools/build-all.sh",
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out docs/api/references/plugin_api packages/lib/services/plugins/api/",
"buildTranslations": "npm run tsc && node packages/tools/build-translation.js",
"buildTranslations": "node packages/tools/build-translation.js",
"buildWebsite": "npm run buildApiDoc && node ./packages/tools/build-website.js && npm run buildPluginDoc",
"clean": "lerna clean -y && lerna run clean",
"generateDatabaseTypes": "node packages/tools/generate-database-types",
"generatePluginTypes": "rm -rf ./plugin_types && node node_modules/typescript/bin/tsc --declaration --declarationDir ./plugin_types --project tsconfig.json",
"linkChecker": "linkchecker https://joplinapp.org",
"linter-ci": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"linter-precommit": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --fix --ext .js --ext .jsx --ext .ts --ext .tsx",
@@ -26,8 +26,6 @@
"releaseDesktop": "node packages/tools/release-electron.js",
"setupNewRelease": "node ./packages/tools/setupNewRelease",
"tsc": "lerna run tsc --stream --parallel",
"test": "lerna run test --stream",
"test-ci": "lerna run test-ci --stream",
"updateIgnored": "gulp updateIgnoredTypeScriptBuild",
"watch": "lerna run watch --stream --parallel"
},

View File

@@ -117,12 +117,7 @@ class Command extends BaseCommand {
lines.push('# Pagination');
lines.push('');
lines.push('All API calls that return multiple results will be paginated and will have the following structure:');
lines.push('');
lines.push('Key | Always present? | Description');
lines.push('--- | --- | ---');
lines.push('`items` | Yes | The array of items you have requested.');
lines.push('`has_more` | Yes | If `true`, there are more items after this page. If `false`, it means you have reached the end of the data set.');
lines.push('All API calls that return multiple results will be paginated. The actual results will be under the `items` key, and if there are more results, there will also be a `cursor` key, which allows you to fetch the next results. If the `cursor` key is not present, it means you have reached the end of the data set.');
lines.push('');
lines.push('You can specify how the results should be sorted using the `order_by` and `order_dir` query parameters, and you can specify the number of items to be returned using the `limit` parameter (the maximum being 100 items).');
lines.push('');
@@ -132,13 +127,15 @@ class Command extends BaseCommand {
lines.push('');
lines.push('This will return a result like this');
lines.push('');
lines.push('\t{ "items": [ /* 10 notes */ ], "has_more": true }');
lines.push('\t{ "items": [ /* 10 notes */ ], "cursor": "somecursor" }');
lines.push('');
lines.push('Then you will resume fetching the results using this query:');
lines.push('');
lines.push('\tcurl http://localhost:41184/notes?order_by=updated_time&order_dir=ASC&limit=10&page=2');
lines.push('\tcurl http://localhost:41184/notes?cursor=somecursor');
lines.push('');
lines.push('Eventually you will get some results that do not contain an "has_more" paramater, at which point you will have retrieved all the results');
lines.push('Note that you only need to pass the cursor to the next request, as it will continue the fetching process using the same parameters you initially provided.');
lines.push('');
lines.push('Eventually you will get some results that do not contain a "cursor" paramater, at which point you will have retrieved all the results');
lines.push('');
lines.push('As an example the pseudo-code below could be used to fetch all the notes:');
lines.push('');
@@ -149,11 +146,15 @@ async function fetchJson(url) {
}
async function fetchAllNotes() {
let pageNum = 1;
let query = '';
const url = 'http://localhost:41184/notes';
do {
const response = await fetchJson((http://localhost:41184/notes?page=' + pageNum++);
console.info('Printing notes:', response.items);
} while (response.has_more)
const response = await fetchJson(url + query);
console.info('Printing notes:');
console.info(response.items);
query = '?cursor' + response.cursor;
} while (response.cursor)
}`);
lines.push('```');
lines.push('');

View File

@@ -6,12 +6,12 @@ import executeSandboxCall from '@joplin/lib/services/plugins/utils/executeSandbo
import Global from '@joplin/lib/services/plugins/api/Global';
import mapEventHandlersToIds, { EventHandlers } from '@joplin/lib/services/plugins/utils/mapEventHandlersToIds';
function createConsoleWrapper(pluginId: string) {
const wrapper: any = {};
function createConsoleWrapper(pluginId:string) {
const wrapper:any = {};
for (const n in console) {
if (!console.hasOwnProperty(n)) continue;
wrapper[n] = (...args: any[]) => {
wrapper[n] = (...args:any[]) => {
const newArgs = args.slice();
newArgs.splice(0, 0, `Plugin "${pluginId}":`);
return (console as any)[n](...newArgs);
@@ -30,7 +30,7 @@ function createConsoleWrapper(pluginId: string) {
export default class PluginRunner extends BasePluginRunner {
private eventHandlers_: EventHandlers = {};
private eventHandlers_:EventHandlers = {};
constructor() {
super();
@@ -38,13 +38,13 @@ export default class PluginRunner extends BasePluginRunner {
this.eventHandler = this.eventHandler.bind(this);
}
private async eventHandler(eventHandlerId: string, args: any[]) {
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[]) => {
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);
};
@@ -54,8 +54,8 @@ export default class PluginRunner extends BasePluginRunner {
};
}
async run(plugin: Plugin, sandbox: Global): Promise<void> {
return new Promise((resolve: Function, reject: Function) => {
async run(plugin:Plugin, sandbox:Global):Promise<void> {
return new Promise((resolve:Function, reject:Function) => {
const onStarted = () => {
plugin.off('started', onStarted);
resolve();

View File

@@ -1,46 +0,0 @@
module.exports = {
testMatch: [
'**/tests/**/*.js',
// '**/tests/services_keychainService.js',
// '**/tests/urlUtils.js',
// '**/tests/models_BaseItem.js',
// '**/tests/markdownUtils.js',
// '**/tests/models_Resource.js',
],
testPathIgnorePatterns: [
'/node_modules/',
'tests/support/',
'test-utils.js',
'file_api_driver.js',
],
testEnvironment: 'node',
setupFilesAfterEnv: ['./jest.setup.js'],
};
// PASS tests/services_rest_Api.js (14.705 s)
// PASS tests/services_SearchFilter.js (21.203 s)
// PASS tests/services_InteropService.js (10.807 s)
// PASS tests/reducer.js (17.398 s)
// PASS tests/services_Revision.js (17.642 s)
// PASS tests/feature_NoteHistory.js (31.448 s)
// PASS tests/services_KeymapService.js
// PASS tests/services_EncryptionService.js (9.258 s)
// PASS tests/models_Note.js (10.59 s)
// PASS tests/services_ResourceService.js (13.177 s)
// PASS tests/models_Folder.js (11.468 s)
// PASS tests/MdToHtml.js (5.242 s)
// PASS tests/services_PluginService.js (5.456 s)
// PASS tests/models_Note_CustomSortOrder.js (5.52 s)
// PASS tests/feature_ShowAllNotes.js (13.567 s)
// PASS tests/models_Setting.js
// PASS tests/models_Tag.js (6.324 s)
// PASS tests/services_keychainService.js
// PASS tests/urlUtils.js
// PASS tests/models_BaseItem.js
// PASS tests/markdownUtils.js
// (node:15679) UnhandledPromiseRejectionWarning: Error: {"title":"folder1"}: Fields have not been loaded yet
// (node:15679) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To termi$ate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 31)
// (node:15679) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
// FAIL tests/models_Resource.js

View File

@@ -1,5 +0,0 @@
const { afterEachCleanUp } = require('./tests/test-utils.js');
global.afterEach(async () => {
await afterEachCleanUp();
});

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,8 @@
"author": "Laurent Cozic",
"private": true,
"scripts": {
"test": "jest --config=jest.config.js --runInBand --bail --forceExit",
"test-ci": "jest --config=jest.config.js --runInBand --forceExit",
"test": "node node_modules/jasmine/bin/jasmine.js --fail-fast=true --config=tests/support/jasmine.json",
"test-ci": "node node_modules/jasmine/bin/jasmine.js --config=tests/support/jasmine.json",
"build": "gulp build",
"start": "gulp build -L && node \"build/main.js\" --stack-trace-enabled --log-level debug --env dev",
"tsc": "node node_modules/typescript/bin/tsc --project tsconfig.json",
@@ -69,7 +69,6 @@
"@types/node": "^14.14.6",
"gulp": "^4.0.2",
"jasmine": "^3.5.0",
"jest": "^26.6.3",
"temp": "^0.9.1",
"typescript": "^4.0.5"
}

View File

@@ -5,7 +5,7 @@ const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('./tes
const shim = require('@joplin/lib/shim').default;
const { themeStyle } = require('@joplin/lib/theme');
function newTestMdToHtml(options: any = null) {
function newTestMdToHtml(options:any = null) {
options = {
ResourceModel: {
isResourceUrl: () => false,
@@ -19,7 +19,7 @@ function newTestMdToHtml(options: any = null) {
describe('MdToHtml', function() {
beforeEach(async (done: Function) => {
beforeEach(async (done:Function) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
@@ -39,7 +39,7 @@ describe('MdToHtml', function() {
// if (mdFilename !== 'sanitize_9.md') continue;
const mdToHtmlOptions: any = {
const mdToHtmlOptions:any = {
bodyOnly: true,
};
@@ -83,7 +83,7 @@ describe('MdToHtml', function() {
}));
it('should return enabled plugin assets', asyncTest(async () => {
const pluginOptions: any = {};
const pluginOptions:any = {};
const pluginNames = MdToHtml.pluginNames();
for (const n of pluginNames) pluginOptions[n] = { enabled: false };

View File

@@ -14,11 +14,6 @@ process.on('unhandledRejection', (reason, p) => {
const api = null;
// Adding empty test for Jest
it('will pass', () => {
expect(true).toBe(true);
});
// NOTE: These tests work with S3 and memory driver, but not
// with other targets like file system or Nextcloud.
// All this is tested in an indirect way in tests/synchronizer

View File

@@ -12,125 +12,125 @@ const makeTerm = (name, value, negated, quoted = false, wildcard = false) => {
describe('filterParser should be correct filter for keyword', () => {
it('title', () => {
const searchString = 'title: something';
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'something', false));
expect(filterParser(searchString)).toContain(makeTerm('title', 'something', false));
});
it('negated title', () => {
const searchString = '-title: something';
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'something', true));
expect(filterParser(searchString)).toContain(makeTerm('title', 'something', true));
});
it('body', () => {
const searchString = 'body:something';
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'something', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'something', false));
});
it('negated body', () => {
const searchString = '-body:something';
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'something', true));
expect(filterParser(searchString)).toContain(makeTerm('body', 'something', true));
});
it('title and body', () => {
const searchString = 'title:testTitle body:testBody';
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'testTitle', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'testBody', false));
expect(filterParser(searchString)).toContain(makeTerm('title', 'testTitle', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'testBody', false));
});
it('title with multiple words', () => {
const searchString = 'title:"word1 word2" body:testBody';
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'word1', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'word2', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'testBody', false));
expect(filterParser(searchString)).toContain(makeTerm('title', 'word1', false));
expect(filterParser(searchString)).toContain(makeTerm('title', 'word2', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'testBody', false));
});
it('body with multiple words', () => {
const searchString = 'title:testTitle body:"word1 word2"';
expect(filterParser(searchString)).toContainEqual(makeTerm('title', 'testTitle', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'word1', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'word2', false));
expect(filterParser(searchString)).toContain(makeTerm('title', 'testTitle', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'word1', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'word2', false));
});
it('single word text', () => {
const searchString = 'joplin';
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"joplin"', false));
expect(filterParser(searchString)).toContain(makeTerm('text', '"joplin"', false));
});
it('multi word text', () => {
const searchString = 'scott joplin';
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"scott"', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"joplin"', false));
expect(filterParser(searchString)).toContain(makeTerm('text', '"scott"', false));
expect(filterParser(searchString)).toContain(makeTerm('text', '"joplin"', false));
});
it('negated word text', () => {
const searchString = 'scott -joplin';
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"scott"', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"joplin"', true));
expect(filterParser(searchString)).toContain(makeTerm('text', '"scott"', false));
expect(filterParser(searchString)).toContain(makeTerm('text', '"joplin"', true));
});
it('phrase text search', () => {
const searchString = '"scott joplin"';
expect(filterParser(searchString)).toContainEqual(makeTerm('text', '"scott joplin"', false, true));
expect(filterParser(searchString)).toContain(makeTerm('text', '"scott joplin"', false, true));
});
it('multi word body', () => {
const searchString = 'body:"foo bar"';
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'foo', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('body', 'bar', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'foo', false));
expect(filterParser(searchString)).toContain(makeTerm('body', 'bar', false));
});
it('negated tag queries', () => {
const searchString = '-tag:mozart';
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', 'mozart', true));
expect(filterParser(searchString)).toContain(makeTerm('tag', 'mozart', true));
});
it('created after', () => {
const searchString = 'created:20151218'; // YYYYMMDD
expect(filterParser(searchString)).toContainEqual(makeTerm('created', '20151218', false));
expect(filterParser(searchString)).toContain(makeTerm('created', '20151218', false));
});
it('created before', () => {
const searchString = '-created:20151218'; // YYYYMMDD
expect(filterParser(searchString)).toContainEqual(makeTerm('created', '20151218', true));
expect(filterParser(searchString)).toContain(makeTerm('created', '20151218', true));
});
it('any', () => {
const searchString = 'any:1 tag:123';
expect(filterParser(searchString)).toContainEqual(makeTerm('any', '1', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', '123', false));
expect(filterParser(searchString)).toContain(makeTerm('any', '1', false));
expect(filterParser(searchString)).toContain(makeTerm('tag', '123', false));
});
it('wildcard tags', () => {
let searchString = 'tag:*';
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', '%', false));
expect(filterParser(searchString)).toContain(makeTerm('tag', '%', false));
searchString = '-tag:*';
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', '%', true));
expect(filterParser(searchString)).toContain(makeTerm('tag', '%', true));
searchString = 'tag:bl*sphemy';
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', 'bl%sphemy', false));
expect(filterParser(searchString)).toContain(makeTerm('tag', 'bl%sphemy', false));
searchString = 'tag:"space travel"';
expect(filterParser(searchString)).toContainEqual(makeTerm('tag', 'space travel', false));
expect(filterParser(searchString)).toContain(makeTerm('tag', 'space travel', false));
});
it('wildcard notebooks', () => {
const searchString = 'notebook:my*notebook';
expect(filterParser(searchString)).toContainEqual(makeTerm('notebook', 'my%notebook', false));
expect(filterParser(searchString)).toContain(makeTerm('notebook', 'my%notebook', false));
});
it('wildcard MIME types', () => {
const searchString = 'resource:image/*';
expect(filterParser(searchString)).toContainEqual(makeTerm('resource', 'image/%', false));
expect(filterParser(searchString)).toContain(makeTerm('resource', 'image/%', false));
});
it('sourceurl', () => {
let searchString = 'sourceurl:https://www.google.com';
expect(filterParser(searchString)).toContainEqual(makeTerm('sourceurl', 'https://www.google.com', false));
expect(filterParser(searchString)).toContain(makeTerm('sourceurl', 'https://www.google.com', false));
searchString = 'sourceurl:https://www.google.com -sourceurl:https://www.facebook.com';
expect(filterParser(searchString)).toContainEqual(makeTerm('sourceurl', 'https://www.google.com', false));
expect(filterParser(searchString)).toContainEqual(makeTerm('sourceurl', 'https://www.facebook.com', true));
expect(filterParser(searchString)).toContain(makeTerm('sourceurl', 'https://www.google.com', false));
expect(filterParser(searchString)).toContain(makeTerm('sourceurl', 'https://www.facebook.com', true));
});
it('handle invalid filters', () => {

View File

@@ -4,7 +4,7 @@ const { expectThrow } = require('./test-utils.js');
// On Windows, path.resolve is going to convert a path such as
// /tmp/file.txt to c:\tmp\file.txt
function platformPath(path: string) {
function platformPath(path:string) {
if (shim.isWindows()) {
return `c:${path.replace(/\//g, '\\')}`;
} else {

View File

@@ -5,7 +5,7 @@ const { asyncTest } = require('./test-utils.js');
const markdownUtils = require('@joplin/lib/markdownUtils').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at markdownUtils: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
describe('markdownUtils', function() {

View File

@@ -11,7 +11,7 @@ const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at models_BaseItem: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
async function allItems() {

View File

@@ -9,7 +9,7 @@ const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at models_Folder: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
async function allItems() {

View File

@@ -11,7 +11,7 @@ const ArrayUtils = require('@joplin/lib/ArrayUtils.js');
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at models_Note: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
async function allItems() {

View File

@@ -11,7 +11,7 @@ const ArrayUtils = require('@joplin/lib/ArrayUtils.js');
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at models_Note_CustomSortOrder: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
async function allItems() {

View File

@@ -3,7 +3,7 @@ import Setting from '@joplin/lib/models/Setting';
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('./test-utils.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at models_Setting: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
describe('models_Setting', function() {

View File

@@ -11,7 +11,7 @@ const BaseModel = require('@joplin/lib/BaseModel').default;
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at models_Tag: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
describe('models_Tag', function() {

View File

@@ -0,0 +1,16 @@
// import Setting from '@joplin/lib/models/Setting';
// const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('../test-utils.js');
// describe('plugin_api_JoplinSetting', function() {
// beforeEach(async (done) => {
// await setupDatabaseAndSynchronizer(1);
// await switchClient(1);
// done();
// });
// it('should get and set plugin-specific values', asyncTest(async () => {
// await
// }));
// });

View File

@@ -4,7 +4,7 @@ const { asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('../..
describe('services_plugins_sandboxProxy', function() {
beforeEach(async (done: Function) => {
beforeEach(async (done:Function) => {
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
@@ -12,13 +12,13 @@ describe('services_plugins_sandboxProxy', function() {
it('should create a new sandbox proxy', asyncTest(async () => {
interface Result {
path: string;
args: any[];
path: string,
args: any[],
}
const results: Result[] = [];
const results:Result[] = [];
const target: Target = (path: string, args: any[]) => {
const target:Target = (path:string, args:any[]) => {
results.push({ path, args });
};
@@ -35,13 +35,13 @@ describe('services_plugins_sandboxProxy', function() {
it('should allow importing a namespace', asyncTest(async () => {
interface Result {
path: string;
args: any[];
path: string,
args: any[],
}
const results: Result[] = [];
const results:Result[] = [];
const target: Target = (path: string, args: any[]) => {
const target:Target = (path:string, args:any[]) => {
results.push({ path, args });
};

View File

@@ -1,33 +1,31 @@
import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
import ToolbarButtonUtils from '@joplin/lib/services/commands/ToolbarButtonUtils';
import CommandService, { CommandDeclaration, CommandRuntime } from '@joplin/lib/services/CommandService';
import stateToWhenClauseContext from '@joplin/lib/services/commands/stateToWhenClauseContext';
import KeymapService from '@joplin/lib/services/KeymapService';
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, expectThrow, expectNotThrow } = require('./test-utils.js');
interface TestCommand {
declaration: CommandDeclaration;
runtime: CommandRuntime;
declaration: CommandDeclaration,
runtime: CommandRuntime,
}
function newService(): CommandService {
function newService():CommandService {
const service = new CommandService();
const mockStore = {
getState: () => {
return {};
},
};
service.initialize(mockStore, true, stateToWhenClauseContext);
service.initialize(mockStore, true);
return service;
}
function createCommand(name: string, options: any): TestCommand {
const declaration: CommandDeclaration = {
function createCommand(name:string, options:any):TestCommand {
const declaration:CommandDeclaration = {
name: name,
};
const runtime: CommandRuntime = {
const runtime:CommandRuntime = {
execute: options.execute,
};
@@ -36,7 +34,7 @@ function createCommand(name: string, options: any): TestCommand {
return { declaration, runtime };
}
function registerCommand(service: CommandService, cmd: TestCommand) {
function registerCommand(service:CommandService, cmd:TestCommand) {
service.registerDeclaration(cmd.declaration);
service.registerRuntime(cmd.declaration.name, cmd.runtime);
}
@@ -44,9 +42,6 @@ function registerCommand(service: CommandService, cmd: TestCommand) {
describe('services_CommandService', function() {
beforeEach(async (done) => {
KeymapService.destroyInstance();
KeymapService.instance().initialize();
await setupDatabaseAndSynchronizer(1);
await switchClient(1);
done();
@@ -56,7 +51,7 @@ describe('services_CommandService', function() {
const service = newService();
const toolbarButtonUtils = new ToolbarButtonUtils(service);
const executedCommands: string[] = [];
const executedCommands:string[] = [];
registerCommand(service, createCommand('test1', {
execute: () => {
@@ -173,9 +168,9 @@ describe('services_CommandService', function() {
execute: () => {},
}));
const clickedCommands: string[] = [];
const clickedCommands:string[] = [];
const onClick = (commandName: string) => {
const onClick = (commandName:string) => {
clickedCommands.push(commandName);
};
@@ -235,7 +230,7 @@ describe('services_CommandService', function() {
let propValue = null;
registerCommand(service, createCommand('test1', {
execute: (_context: any, greeting: string) => {
execute: (_context:any, greeting:string) => {
propValue = greeting;
},
}));

View File

@@ -15,7 +15,7 @@ const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry.js');
const EncryptionService = require('@joplin/lib/services/EncryptionService.js');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at services_EncryptionService: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
let service = null;

View File

@@ -12,14 +12,14 @@ const fs = require('fs-extra');
const ArrayUtils = require('@joplin/lib/ArrayUtils');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at services_InteropService: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
function exportDir() {
return `${__dirname}/export`;
}
function fieldsEqual(model1: any, model2: any, fieldNames: string[]) {
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}`);
@@ -86,7 +86,7 @@ describe('services_InteropService', function() {
await service.import({ path: filePath });
const allFolders = await Folder.all();
expect(allFolders.map((f: any) => 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 () => {
@@ -447,14 +447,14 @@ describe('services_InteropService', function() {
sourcePath: '',
};
const module: Module = {
const module:Module = {
type: ModuleType.Importer,
description: 'Test Import Module',
format: 'testing',
fileExtensions: ['test'],
isCustom: true,
onExec: async (context: CustomImportContext) => {
onExec: async (context:CustomImportContext) => {
result.hasBeenExecuted = true;
result.sourcePath = context.sourcePath;
},
@@ -479,7 +479,7 @@ describe('services_InteropService', function() {
const filePath = `${exportDir()}/example.test`;
const result: any = {
const result:any = {
destPath: '',
itemTypes: [],
items: [],
@@ -488,28 +488,28 @@ describe('services_InteropService', function() {
closeCalled: false,
};
const module: Module = {
const module:Module = {
type: ModuleType.Exporter,
description: 'Test Export Module',
format: 'testing',
fileExtensions: ['test'],
isCustom: true,
onInit: async (context: CustomExportContext) => {
onInit: async (context:CustomExportContext) => {
result.destPath = context.destPath;
},
onProcessItem: async (_context: CustomExportContext, itemType: number, item: any) => {
onProcessItem: async (_context:CustomExportContext, itemType:number, item:any) => {
result.itemTypes.push(itemType);
result.items.push(item);
},
onProcessResource: async (_context: CustomExportContext, resource: any, filePath: string) => {
onProcessResource: async (_context:CustomExportContext, resource:any, filePath:string) => {
result.resources.push(resource);
result.filePaths.push(filePath);
},
onClose: async (_context: CustomExportContext) => {
onClose: async (_context:CustomExportContext) => {
result.closeCalled = true;
},
};
@@ -524,7 +524,7 @@ describe('services_InteropService', function() {
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.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);

View File

@@ -9,7 +9,7 @@ const Note = require('@joplin/lib/models/Note');
const Folder = require('@joplin/lib/models/Folder');
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at services_PluginService: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
const testPluginDir = `${__dirname}/../tests/support/plugins`;
@@ -79,7 +79,7 @@ describe('services_PluginService', function() {
const allFolders = await Folder.all();
expect(allFolders.length).toBe(2);
expect(allFolders.map((f: any) => f.title).sort().join(', ')).toBe('multi - simple1, multi - simple2');
expect(allFolders.map((f:any) => f.title).sort().join(', ')).toBe('multi - simple1, multi - simple2');
}));
it('should load plugins from JS bundles', asyncTest(async () => {

View File

@@ -15,7 +15,7 @@ const RevisionService = require('@joplin/lib/services/RevisionService.js');
const shim = require('@joplin/lib/shim').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at services_Revision: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
describe('services_Revision', function() {

View File

@@ -157,88 +157,85 @@ describe('services_SearchEngine', function() {
expect(rows[1].id).toBe(n2.id);
}));
// TODO: Need to update and replace jasmine.mockDate() calls with Jest
// equivalent
it('should correctly weigh notes using BM25 and user_updated_time', asyncTest(async () => {
await mockDate(2020, 9, 30, 50);
const noteData = [
{
title: 'abc test2 test2',
updated_time: 1601425064756,
user_updated_time: 1601425064756,
created_time: 1601425064756,
user_created_time: 1601425064756,
},
{
title: 'foo foo',
updated_time: 1601425064758,
user_updated_time: 1601425064758,
created_time: 1601425064758,
user_created_time: 1601425064758,
},
{
title: 'dead beef',
updated_time: 1601425064760,
user_updated_time: 1601425064760,
created_time: 1601425064760,
user_created_time: 1601425064760,
},
{
title: 'test2 bar',
updated_time: 1601425064761,
user_updated_time: 1601425064761,
created_time: 1601425064761,
user_created_time: 1601425064761,
},
{
title: 'blah blah abc',
updated_time: 1601425064763,
user_updated_time: 1601425064763,
created_time: 1601425064763,
user_created_time: 1601425064763,
},
];
// it('should correctly weigh notes using BM25 and user_updated_time', asyncTest(async () => {
// await mockDate(2020, 9, 30, 50);
// const noteData = [
// {
// title: 'abc test2 test2',
// updated_time: 1601425064756,
// user_updated_time: 1601425064756,
// created_time: 1601425064756,
// user_created_time: 1601425064756,
// },
// {
// title: 'foo foo',
// updated_time: 1601425064758,
// user_updated_time: 1601425064758,
// created_time: 1601425064758,
// user_created_time: 1601425064758,
// },
// {
// title: 'dead beef',
// updated_time: 1601425064760,
// user_updated_time: 1601425064760,
// created_time: 1601425064760,
// user_created_time: 1601425064760,
// },
// {
// title: 'test2 bar',
// updated_time: 1601425064761,
// user_updated_time: 1601425064761,
// created_time: 1601425064761,
// user_created_time: 1601425064761,
// },
// {
// title: 'blah blah abc',
// updated_time: 1601425064763,
// user_updated_time: 1601425064763,
// created_time: 1601425064763,
// user_created_time: 1601425064763,
// },
// ];
const n0 = await Note.save(noteData[0], { autoTimestamp: false });
const n1 = await Note.save(noteData[1], { autoTimestamp: false });
const n2 = await Note.save(noteData[2], { autoTimestamp: false });
const n3 = await Note.save(noteData[3], { autoTimestamp: false });
const n4 = await Note.save(noteData[4], { autoTimestamp: false });
restoreDate();
await engine.syncTables();
await mockDate(2020, 9, 30, 50);
// const n0 = await Note.save(noteData[0], { autoTimestamp: false });
// const n1 = await Note.save(noteData[1], { autoTimestamp: false });
// const n2 = await Note.save(noteData[2], { autoTimestamp: false });
// const n3 = await Note.save(noteData[3], { autoTimestamp: false });
// const n4 = await Note.save(noteData[4], { autoTimestamp: false });
// restoreDate();
// await engine.syncTables();
// await mockDate(2020, 9, 30, 50);
let searchString = 'abc';
let scores = calculateScore(searchString, noteData);
let rows = await engine.search(searchString);
// let searchString = 'abc';
// let scores = calculateScore(searchString, noteData);
// let rows = await engine.search(searchString);
expect(rows[0].weight).toEqual(scores[0]);
expect(rows[1].weight).toEqual(scores[1]);
// expect(rows[0].weight).toEqual(scores[0]);
// expect(rows[1].weight).toEqual(scores[1]);
// console.log(rows);
// console.log(scores);
// // console.log(rows);
// // console.log(scores);
searchString = 'test2';
scores = calculateScore(searchString, noteData);
rows = await engine.search(searchString);
// searchString = 'test2';
// scores = calculateScore(searchString, noteData);
// rows = await engine.search(searchString);
// console.log(rows);
// console.log(scores);
// // console.log(rows);
// // console.log(scores);
expect(rows[0].weight).toEqual(scores[0]);
expect(rows[1].weight).toEqual(scores[1]);
// expect(rows[0].weight).toEqual(scores[0]);
// expect(rows[1].weight).toEqual(scores[1]);
searchString = 'foo';
scores = calculateScore(searchString, noteData);
rows = await engine.search(searchString);
// searchString = 'foo';
// scores = calculateScore(searchString, noteData);
// rows = await engine.search(searchString);
// console.log(rows);
// console.log(scores);
// // console.log(rows);
// // console.log(scores);
// expect(rows[0].weight).toEqual(scores[0]);
// await restoreDate();
// }));
expect(rows[0].weight).toEqual(scores[0]);
await restoreDate();
}));
it('should tell where the results are found', asyncTest(async () => {
const notes = [

View File

@@ -16,7 +16,7 @@ const ResourceService = require('@joplin/lib/services/ResourceService').default;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at services_SearchFilter: Promise', p, 'reason:', reason);
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
let engine = null;

View File

@@ -0,0 +1,161 @@
/* eslint-disable no-unused-vars */
/* eslint prefer-const: 0*/
//
// const time = require('@joplin/lib/time').default;
// const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest, db, synchronizer, fileApi, sleep, createNTestNotes, switchClient, createNTestFolders } = require('./test-utils.js');
// const SearchEngine = require('@joplin/lib/services/searchengine/SearchEngine');
// const Note = require('@joplin/lib/models/Note');
// const Folder = require('@joplin/lib/models/Folder');
// const Tag = require('@joplin/lib/models/Tag');
// const ItemChange = require('@joplin/lib/models/ItemChange');
// const Setting = require('@joplin/lib/models/Setting');
// const Resource = require('@joplin/lib/models/Resource.js');
// const shim = require('@joplin/lib/shim').default;
// const ResourceService = require('@joplin/lib/services/ResourceService').default;
// process.on('unhandledRejection', (reason, p) => {
// console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
// });
// let engine = null;
// const ids = (array) => array.map(a => a.id);
// describe('services_SearchFuzzy', function() {
// beforeEach(async (done) => {
// await setupDatabaseAndSynchronizer(1);
// await switchClient(1);
// engine = new SearchEngine();
// engine.setDb(db());
// Setting.setValue('db.fuzzySearchEnabled', 1);
// done();
// });
// it('should return note almost matching title', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'If It Ain\'t Baroque, Don\'t Fix It' });
// const n2 = await Note.save({ title: 'Important note' });
// await engine.syncTables();
// rows = await engine.search('Broke', { fuzzy: false });
// expect(rows.length).toBe(0);
// rows = await engine.search('Broke', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('title:Broke', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('title:"Broke"', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('Imprtant', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n2.id);
// }));
// it('should order results by min fuzziness', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'I demand you take me to him' });
// const n2 = await Note.save({ title: 'He demanded an answer' });
// const n3 = await Note.save({ title: 'Don\'t you make demands of me' });
// const n4 = await Note.save({ title: 'No drama for me' });
// const n5 = await Note.save({ title: 'Just minding my own business' });
// await engine.syncTables();
// rows = await engine.search('demand', { fuzzy: false });
// expect(rows.length).toBe(1);
// expect(rows[0].id).toBe(n1.id);
// rows = await engine.search('demand', { fuzzy: true });
// expect(rows.length).toBe(3);
// expect(rows[0].id).toBe(n1.id);
// expect(rows[1].id).toBe(n3.id);
// expect(rows[2].id).toBe(n2.id);
// }));
// it('should consider any:1', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'cat' });
// const n2 = await Note.save({ title: 'cats' });
// const n3 = await Note.save({ title: 'cot' });
// const n4 = await Note.save({ title: 'defenestrate' });
// const n5 = await Note.save({ title: 'defenstrate' });
// const n6 = await Note.save({ title: 'defenestrated' });
// const n7 = await Note.save({ title: 'he defenestrated the cat' });
// await engine.syncTables();
// rows = await engine.search('defenestrated cat', { fuzzy: true });
// expect(rows.length).toBe(1);
// rows = await engine.search('any:1 defenestrated cat', { fuzzy: true });
// expect(rows.length).toBe(7);
// }));
// it('should leave phrase searches alone', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'abc def' });
// const n2 = await Note.save({ title: 'def ghi' });
// const n3 = await Note.save({ title: 'ghi jkl' });
// const n4 = await Note.save({ title: 'def abc' });
// const n5 = await Note.save({ title: 'mno pqr ghi jkl' });
// await engine.syncTables();
// rows = await engine.search('abc def', { fuzzy: true });
// expect(rows.length).toBe(2);
// expect(rows.map(r=>r.id)).toContain(n1.id);
// expect(rows.map(r=>r.id)).toContain(n4.id);
// rows = await engine.search('"abc def"', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows.map(r=>r.id)).toContain(n1.id);
// rows = await engine.search('"ghi jkl"', { fuzzy: true });
// expect(rows.length).toBe(2);
// expect(rows.map(r=>r.id)).toContain(n3.id);
// expect(rows.map(r=>r.id)).toContain(n5.id);
// rows = await engine.search('"ghi jkl" mno', { fuzzy: true });
// expect(rows.length).toBe(1);
// expect(rows.map(r=>r.id)).toContain(n5.id);
// rows = await engine.search('any:1 "ghi jkl" mno', { fuzzy: true });
// expect(rows.length).toBe(2);
// expect(rows.map(r=>r.id)).toContain(n3.id);
// expect(rows.map(r=>r.id)).toContain(n5.id);
// }));
// it('should leave wild card searches alone', asyncTest(async () => {
// let rows;
// const n1 = await Note.save({ title: 'abc def' });
// const n2 = await Note.save({ title: 'abcc ghi' });
// const n3 = await Note.save({ title: 'abccc ghi' });
// const n4 = await Note.save({ title: 'abcccc ghi' });
// const n5 = await Note.save({ title: 'wxy zzz' });
// await engine.syncTables();
// rows = await engine.search('abc*', { fuzzy: true });
// expect(rows.length).toBe(4);
// expect(rows.map(r=>r.id)).toContain(n1.id);
// expect(rows.map(r=>r.id)).toContain(n2.id);
// expect(rows.map(r=>r.id)).toContain(n3.id);
// expect(rows.map(r=>r.id)).toContain(n4.id);
// }));
// });

View File

@@ -4,24 +4,22 @@ import Setting from '@joplin/lib/models/Setting';
const { db, asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('./test-utils.js');
function describeIfCompatible(name: string, fn: any, elseFn: any) {
function describeIfCompatible(name:string, fn:any) {
if (['win32', 'darwin'].includes(shim.platformName())) {
return describe(name, fn);
} else {
elseFn();
}
}
describeIfCompatible('services_KeychainService', function() {
beforeEach(async (done: Function) => {
beforeEach(async (done:Function) => {
await setupDatabaseAndSynchronizer(1, { keychainEnabled: true });
await switchClient(1, { keychainEnabled: true });
await Setting.deleteKeychainPasswords();
done();
});
afterEach(async (done: Function) => {
afterEach(async (done:Function) => {
await Setting.deleteKeychainPasswords();
done();
});
@@ -93,10 +91,4 @@ describeIfCompatible('services_KeychainService', function() {
}
}));
}, () => {
it('will pass', () => {
expect(true).toBe(true);
});
});

View File

@@ -2,16 +2,15 @@ import { PaginationOrderDir } from '@joplin/lib/models/utils/types';
import Api, { RequestMethod } from '@joplin/lib/services/rest/Api';
import shim from '@joplin/lib/shim';
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync, db } = require('./test-utils.js');
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder');
const Resource = require('@joplin/lib/models/Resource');
const Note = require('@joplin/lib/models/Note');
const Tag = require('@joplin/lib/models/Tag');
const NoteTag = require('@joplin/lib/models/NoteTag');
const ResourceService = require('@joplin/lib/services/ResourceService').default;
const SearchEngine = require('@joplin/lib/services/searchengine/SearchEngine');
async function msleep(ms: number) {
async function msleep(ms:number) {
return new Promise((resolve) => {
shim.setTimeout(() => {
resolve();
@@ -19,7 +18,7 @@ async function msleep(ms: number) {
});
}
const createFolderForPagination = async (num: number, time: number) => {
const createFolderForPagination = async (num:number, time:number) => {
await Folder.save({
title: `folder${num}`,
updated_time: time,
@@ -27,16 +26,7 @@ const createFolderForPagination = async (num: number, time: number) => {
}, { autoTimestamp: false });
};
const createNoteForPagination = async (num: number, time: number) => {
await Note.save({
title: `note${num}`,
body: `noteBody${num}`,
updated_time: time,
created_time: time,
}, { autoTimestamp: false });
};
let api: Api = null;
let api:Api = null;
describe('services_rest_Api', function() {
@@ -158,7 +148,7 @@ describe('services_rest_Api', function() {
}));
it('should allow setting note properties', asyncTest(async () => {
let response: any = null;
let response:any = null;
const f = await Folder.save({ title: 'mon carnet' });
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
@@ -363,6 +353,22 @@ describe('services_rest_Api', function() {
expect(response.body).toBe('**Bold text**');
}));
// it('should filter fields', asyncTest(async () => {
// let f = api.fields_({ query: { fields: 'one,two' } } as any, []);
// expect(f.length).toBe(2);
// expect(f[0]).toBe('one');
// expect(f[1]).toBe('two');
// f = api.fields_({ query: { fields: 'one ,, two ' } } as any, []);
// expect(f.length).toBe(2);
// expect(f[0]).toBe('one');
// expect(f[1]).toBe('two');
// f = api.fields_({ query: { fields: ' ' } } as any, ['def']);
// expect(f.length).toBe(1);
// expect(f[0]).toBe('def');
// }));
it('should handle tokens', asyncTest(async () => {
api = new Api('mytoken');
@@ -507,117 +513,82 @@ describe('services_rest_Api', function() {
await createFolderForPagination(4, 1004);
{
const baseQuery = {
const r1 = await api.route(RequestMethod.GET, 'folders', {
fields: ['id', 'title', 'updated_time'],
limit: 2,
order_dir: PaginationOrderDir.ASC,
order_by: 'updated_time',
};
});
const r1 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery });
expect(r1.has_more).toBe(true);
expect(r1.items.length).toBe(2);
expect(r1.items[0].title).toBe('folder1');
expect(r1.items[1].title).toBe('folder2');
const r2 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery, page: 2 });
const r2 = await api.route(RequestMethod.GET, 'folders', {
cursor: r1.cursor,
});
// The API currently doesn't check if there's effectively a
// page of data after the current one. If the number of
// returned items === limit, it sets `has_more` and the next
// result set will be empty
expect(r1.has_more).toBe(true);
expect(r2.items.length).toBe(2);
expect(r2.items[0].title).toBe('folder3');
expect(r2.items[1].title).toBe('folder4');
const r3 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery, page: 3 });
const r3 = await api.route(RequestMethod.GET, 'folders', {
cursor: r2.cursor,
});
expect(r3.items.length).toBe(0);
expect(r3.has_more).toBe(undefined);
expect(r3.cursor).toBe(undefined);
}
{
const baseQuery = {
const r1 = await api.route(RequestMethod.GET, 'folders', {
fields: ['id', 'title', 'updated_time'],
limit: 3,
order_dir: PaginationOrderDir.ASC,
order_by: 'updated_time',
};
const r1 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery });
});
expect(r1.items.length).toBe(3);
expect(r1.items[0].title).toBe('folder1');
expect(r1.items[1].title).toBe('folder2');
expect(r1.items[2].title).toBe('folder3');
const r2 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery, page: 2 });
const r2 = await api.route(RequestMethod.GET, 'folders', {
cursor: r1.cursor,
});
expect(r2.items.length).toBe(1);
expect(r2.items[0].title).toBe('folder4');
expect(r2.has_more).toBe(undefined);
expect(r2.cursor).toBe(undefined);
}
}));
it('should paginate results and handle duplicate field values', asyncTest(async () => {
// If, for example, ordering by updated_time, and two of the rows
// have the same updated_time, it should make sure that the sort
// order is stable and all results are correctly returned.
it('should paginate results and handle duplicate cursor field value', asyncTest(async () => {
await createFolderForPagination(1, 1001);
await createFolderForPagination(2, 1002);
await createFolderForPagination(3, 1002);
await createFolderForPagination(4, 1003);
const baseQuery = {
const r1 = await api.route(RequestMethod.GET, 'folders', {
fields: ['id', 'title', 'updated_time'],
limit: 2,
order_dir: PaginationOrderDir.ASC,
order_by: 'updated_time',
};
const r1 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery });
});
expect(r1.items.length).toBe(2);
expect(r1.items[0].title).toBe('folder1');
expect(['folder2', 'folder3'].includes(r1.items[1].title)).toBe(true);
const r2 = await api.route(RequestMethod.GET, 'folders', { ...baseQuery, page: 2 });
const r2 = await api.route(RequestMethod.GET, 'folders', {
cursor: r1.cursor,
});
expect(r2.items.length).toBe(2);
expect(r2.items[0].title).toBe(r1.items[1].title === 'folder2' ? 'folder3' : 'folder2');
expect(r2.items[1].title).toBe('folder4');
}));
it('should paginate results and return the requested fields only', asyncTest(async () => {
await createNoteForPagination(1, 1001);
await createNoteForPagination(2, 1002);
await createNoteForPagination(3, 1003);
const baseQuery = {
fields: ['id', 'title', 'body'],
limit: 2,
order_dir: PaginationOrderDir.ASC,
order_by: 'updated_time',
};
const r1 = await api.route(RequestMethod.GET, 'notes', { ...baseQuery });
expect(Object.keys(r1.items[0]).sort().join(',')).toBe('body,id,title');
expect(r1.items.length).toBe(2);
expect(r1.items[0].title).toBe('note1');
expect(r1.items[0].body).toBe('noteBody1');
expect(r1.items[1].title).toBe('note2');
expect(r1.items[1].body).toBe('noteBody2');
const r2 = await api.route(RequestMethod.GET, 'notes', { ...baseQuery, fields: ['id'], page: 2 });
expect(Object.keys(r2.items[0]).sort().join(',')).toBe('id');
expect(r2.items.length).toBe(1);
expect(!!r2.items[0].id).toBe(true);
}));
it('should paginate folder notes', asyncTest(async () => {
const folder = await Folder.save({});
const note1 = await Note.save({ parent_id: folder.id });
@@ -635,8 +606,7 @@ describe('services_rest_Api', function() {
expect(r1.items[1].id).toBe(note2.id);
const r2 = await api.route(RequestMethod.GET, `folders/${folder.id}/notes`, {
limit: 2,
page: 2,
cursor: r1.cursor,
});
expect(r2.items.length).toBe(1);
@@ -698,31 +668,4 @@ describe('services_rest_Api', function() {
expect(r.items.length).toBe(1);
expect(r.items[0]).toBe(note.id);
}));
it('should return search results', asyncTest(async () => {
SearchEngine.instance().setDb(db());
for (let i = 0; i < 10; i++) {
await Note.save({ title: 'a' });
}
await SearchEngine.instance().syncTables();
// Mostly testing pagination below
const r1 = await api.route(RequestMethod.GET, 'search', { query: 'a', limit: 4 });
expect(r1.items.length).toBe(4);
expect(r1.has_more).toBe(true);
const r2 = await api.route(RequestMethod.GET, 'search', { query: 'a', limit: 4, page: 2 });
expect(r2.items.length).toBe(4);
expect(r2.has_more).toBe(true);
const r3 = await api.route(RequestMethod.GET, 'search', { query: 'a', limit: 4, page: 3 });
expect(r3.items.length).toBe(2);
expect(!!r3.has_more).toBe(false);
await SearchEngine.instance().destroy();
}));
});

View File

@@ -0,0 +1,11 @@
{
"spec_dir": "tests",
"spec_files": [
"*.js",
"services/plugins/*.js",
"services/plugins/api/*.js",
"!test-utils.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}

View File

@@ -1,6 +1,6 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
/**
* @ignore
*/

View File

@@ -7,7 +7,7 @@ import JoplinCommands from './JoplinCommands';
import JoplinViews from './JoplinViews';
import JoplinInterop from './JoplinInterop';
import JoplinSettings from './JoplinSettings';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
/**
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
*/

View File

@@ -1,35 +1,25 @@
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}.
* 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/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/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:
* 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:
*
* * [Main screen commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/MainScreen/commands)
* * [Global commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/commands)
* * [Editor commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.ts)
* 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.
* 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 command can take any number of arguments, and the supported
* arguments will vary based on the command. For custom commands, this
* is the `args` passed to the `execute()` function. For built-in
* commands, you can find the supported arguments by checking the links
* above.
* <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:

View File

@@ -6,7 +6,7 @@ import { Path } from './types';
*
* This is also what you would use to search notes, via the `search` endpoint.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/simple)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/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:

View File

@@ -2,7 +2,7 @@ import { ExportModule, ImportModule } from './types';
/**
* Provides a way to create modules to import external data into Joplin or to export notes into any arbitrary format.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export)
*
* To implement an import or export module, you would simply define an object with various event handlers that are called
* by the application during the import/export process.

View File

@@ -1,5 +1,5 @@
import Plugin from '../Plugin';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
import { ContentScriptType, Script } from './types';
/**
* This class provides access to plugin-related features.
@@ -28,7 +28,7 @@ export default class JoplinPlugins {
* Note that registering a content script in itself will do nothing - it will only be loaded in specific cases by the relevant app modules
* (eg. the Markdown renderer or the code editor). So it is not a way to inject and run arbitrary code in the app, which for safety and performance reasons is not supported.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/content_script)
*
* @param type Defines how the script will be used. See the type definition for more information about each supported type.
* @param id A unique ID for the content script.

View File

@@ -7,7 +7,7 @@ import { SettingItem, SettingSection } from './types';
*
* Note: Currently this API does **not** provide access to Joplin's built-in settings. This is by design as plugins that modify user settings could give unexpected results
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/settings)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/settings)
*/
export default class JoplinSettings {
private plugin_;
@@ -37,7 +37,7 @@ export default class JoplinSettings {
*
* The list of available settings is not documented yet, but can be found by looking at the source code:
*
* https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/packages/app-mobile/lib/models/Setting.ts#L142
* https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/ReactNativeClient/lib/models/Setting.ts#L142
*/
globalValue(key: string): Promise<any>;
}

View File

@@ -5,7 +5,7 @@ import { ButtonSpec, ViewHandle, ButtonId } from './types';
* Dialogs are hidden by default and you need to call `open()` to open them. Once the user clicks on a button, the `open` call will return and provide the button ID that was
* clicked on. There is currently no "close" method since the dialog should be thought as a modal one and thus can only be closed by clicking on one of the buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/dialog)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/dialog)
*/
export default class JoplinViewsDialogs {
private store;
@@ -16,7 +16,7 @@ export default class JoplinViewsDialogs {
/**
* Creates a new dialog
*/
create(id: string): Promise<ViewHandle>;
create(): Promise<ViewHandle>;
/**
* Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel"
*/

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating and managing menu items.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command)
*/
export default class JoplinViewsMenuItems {
private store;
@@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems {
/**
* Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter.
*/
create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise<void>;
create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise<void>;
}

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating menus.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/menu)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/menu)
*/
export default class JoplinViewsMenus {
private store;
@@ -14,5 +14,5 @@ export default class JoplinViewsMenus {
* Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the
* menu as a sub-menu of the application build-in menus.
*/
create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise<void>;
create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise<void>;
}

View File

@@ -1,13 +1,10 @@
import Plugin from '../Plugin';
import { ViewHandle } from './types';
/**
* Allows creating and managing view panels. View panels currently are
* displayed at the right of the sidebar and allows displaying any HTML
* content (within a webview) and update it in real-time. For example it
* could be used to display a table of content for the active note, or
* display various metadata or graph.
* Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example
* it could be used to display a table of content for the active note, or display various metadata or graph.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/toc)
*/
export default class JoplinViewsPanels {
private store;
@@ -17,7 +14,7 @@ export default class JoplinViewsPanels {
/**
* Creates a new panel
*/
create(id: string): Promise<ViewHandle>;
create(): Promise<ViewHandle>;
/**
* Sets the panel webview HTML
*/

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating and managing toolbar buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command)
*/
export default class JoplinViewsToolbarButtons {
private store;
@@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons {
/**
* Creates a new toolbar button and associate it with the given command.
*/
create(id: string, commandName: string, location: ToolbarButtonLocation): Promise<void>;
create(commandName: string, location: ToolbarButtonLocation): Promise<void>;
}

View File

@@ -2,7 +2,7 @@
* The workspace service provides access to all the parts of Joplin that are being worked on - i.e. the currently selected notes or notebooks as well
* as various related events, such as when a new note is selected, or when the note content changes.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins)
*/
export default class JoplinWorkspace {
private store;

View File

@@ -6,7 +6,7 @@ export interface Command {
/**
* Name of command - must be globally unique
*/
name: string;
name: string
/**
* Label to be displayed on menu items or keyboard shortcut editor for example.
@@ -14,17 +14,17 @@ export interface Command {
* In that case the command will not appear in the shortcut editor or command panel, and logically
* should not be used as a menu item.
*/
label?: string;
label?: string
/**
* Icon to be used on toolbar buttons for example
*/
iconName?: string;
iconName?: string,
/**
* Code to be ran when the command is executed. It may return a result.
*/
execute(...args: any[]): Promise<any | void>;
execute(...args:any[]):Promise<any | void>
/**
* Defines whether the command should be enabled or disabled, which in turns affects
@@ -40,11 +40,13 @@ export interface Command {
* Or | \|\| | "noteIsTodo \|\| noteTodoCompleted"
* And | && | "oneNoteSelected && !inConflictFolder"
*
* Currently the supported context variables aren't documented, but you can [find the list here](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/services/commands/stateToWhenClauseContext.ts).
* Currently the supported context variables aren't documented, but you can find the list there:
*
* https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/services/commands/stateToWhenClauseContext.ts
*
* Note: Commands are enabled by default unless you use this property.
*/
enabledCondition?: string;
enabledCondition?: string
}
// =================================================================
@@ -62,7 +64,7 @@ export enum ImportModuleOutputFormat {
}
/**
* Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export) for an example.
* Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export) for an example.
*
* In general, all the event handlers you'll need to implement take a `context` object as a first argument. This object will contain the export or import path as well as various optional properties, such as which notes or notebooks need to be exported.
*
@@ -72,113 +74,113 @@ export interface ExportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string;
format: string,
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string;
description: string,
/**
* Whether the module will export a single file or multiple files in a directory. It affects the open dialog that will be presented to the user when using your exporter.
*/
target: FileSystemItem;
target: FileSystemItem,
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean;
isNoteArchive: boolean,
/**
* The extensions of the files exported by your module. For example, it is `["htm", "html"]` for the HTML module, and just `["jex"]` for the JEX module.
*/
fileExtensions?: string[];
fileExtensions?: string[],
/**
* Called when the export process starts.
*/
onInit(context: ExportContext): Promise<void>;
onInit(context:ExportContext): Promise<void>;
/**
* Called when an item needs to be processed. An "item" can be any Joplin object, such as a note, a folder, a notebook, etc.
*/
onProcessItem(context: ExportContext, itemType: number, item: any): Promise<void>;
onProcessItem(context:ExportContext, itemType:number, item:any):Promise<void>;
/**
* Called when a resource file needs to be exported.
*/
onProcessResource(context: ExportContext, resource: any, filePath: string): Promise<void>;
onProcessResource(context:ExportContext, resource:any, filePath:string):Promise<void>;
/**
* Called when the export process is done.
*/
onClose(context: ExportContext): Promise<void>;
onClose(context:ExportContext):Promise<void>;
}
export interface ImportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string;
format: string,
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string;
description: string,
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean;
isNoteArchive: boolean,
/**
* The type of sources that are supported by the module. Tells whether the module can import files or directories or both.
*/
sources: FileSystemItem[];
sources: FileSystemItem[],
/**
* Tells the file extensions of the exported files.
*/
fileExtensions?: string[];
fileExtensions?: string[],
/**
* Tells the type of notes that will be generated, either HTML or Markdown (default).
*/
outputFormat?: ImportModuleOutputFormat;
outputFormat?: ImportModuleOutputFormat,
/**
* Called when the import process starts. There is only one event handler within which you should import the complete data.
*/
onExec(context: ImportContext): Promise<void>;
onExec(context:ImportContext): Promise<void>;
}
export interface ExportOptions {
format?: string;
path?: string;
sourceFolderIds?: string[];
sourceNoteIds?: string[];
modulePath?: string;
target?: FileSystemItem;
format?: string,
path?:string,
sourceFolderIds?: string[],
sourceNoteIds?: string[],
modulePath?:string,
target?:FileSystemItem,
}
export interface ExportContext {
destPath: string;
options: ExportOptions;
destPath: string,
options: ExportOptions,
/**
* You can attach your own custom data using this propery - it will then be passed to each event handler, allowing you to keep state from one event to the next.
*/
userData?: any;
userData?: any,
}
export interface ImportContext {
sourcePath: string;
options: any;
warnings: string[];
sourcePath: string,
options: any,
warnings: string[],
}
// =================================================================
@@ -186,7 +188,7 @@ export interface ImportContext {
// =================================================================
export interface Script {
onStart?(event: any): Promise<void>;
onStart?(event:any):Promise<void>,
}
// =================================================================
@@ -194,7 +196,7 @@ export interface Script {
// =================================================================
export interface CreateMenuItemOptions {
accelerator: string;
accelerator: string,
}
export enum MenuItemLocation {
@@ -212,22 +214,22 @@ export interface MenuItem {
* Command that should be associated with the menu item. All menu item should
* have a command associated with them unless they are a sub-menu.
*/
commandName?: string;
commandName?: string,
/**
* Accelerator associated with the menu item
*/
accelerator?: string;
accelerator?: string,
/**
* Menu items that should appear below this menu item. Allows creating a menu tree.
*/
submenu?: MenuItem[];
submenu?: MenuItem[],
/**
* Menu item label. If not specified, the command label will be used instead.
*/
label?: string;
label?: string,
}
// =================================================================
@@ -235,9 +237,9 @@ export interface MenuItem {
// =================================================================
export interface ButtonSpec {
id: ButtonId;
title?: string;
onClick?(): void;
id: ButtonId,
title?: string,
onClick?():void,
}
export type ButtonId = string;
@@ -277,28 +279,28 @@ export enum SettingItemType {
// Redefine a simplified interface to mask internal details
// and to remove function calls as they would have to be async.
export interface SettingItem {
value: any;
type: SettingItemType;
public: boolean;
label: string;
value: any,
type: SettingItemType,
public: boolean,
label:string,
description?: string;
isEnum?: boolean;
section?: string;
options?: any;
appTypes?: string[];
secure?: boolean;
advanced?: boolean;
minimum?: number;
maximum?: number;
step?: number;
description?:string,
isEnum?: boolean,
section?: string,
options?:any,
appTypes?:string[],
secure?: boolean,
advanced?: boolean,
minimum?: number,
maximum?: number,
step?: number,
}
export interface SettingSection {
label: string;
iconName?: string;
description?: string;
name?: string;
label: string,
iconName?: string,
description?: string,
name?: string,
}
// =================================================================
@@ -320,30 +322,36 @@ export type Path = string[];
export enum ContentScriptType {
/**
* Registers a new Markdown-It plugin, which should follow the template below.
* Registers a new Markdown-It plugin, which should follow this template:
*
* ```javascript
* // The module should export an object as below:
*
* module.exports = {
*
* // The "context" variable is currently unused but could be used later on to provide
* // access to your own plugin so that the content script and plugin can communicate.
* default: function(context) {
* return {
*
* // This is the actual Markdown-It plugin - check the [official doc](https://github.com/markdown-it/markdown-it) for more information
* // The `options` parameter is of type [RuleOptions](https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/joplin-renderer/MdToHtml.ts), which
* // contains a number of options, mostly useful for Joplin's internal code.
* plugin: function(markdownIt, options) {
* // ...
* },
* assets: {
* // ...
* },
*
* // You may also specify additional assets such as JS or CSS that should be loaded in the rendered HTML document.
* // Check for example the Joplin [Mermaid plugin](https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.ts) to
* // see how the data should be structured.
* assets: {},
* }
* }
* }
* ```
*
* - The `context` argument is currently unused but could be used later on to provide access to your own plugin so that the content script and plugin can communicate.
*
* - The **required** `plugin` key is the actual Markdown-It plugin - check the [official doc](https://github.com/markdown-it/markdown-it) for more information. The `options` parameter is of type [RuleOptions](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml.ts), which contains a number of options, mostly useful for Joplin's internal code.
*
* - Using the **optional** `assets` key you may specify assets such as JS or CSS that should be loaded in the rendered HTML document. Check for example the Joplin [Mermaid plugin](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml/rules/mermaid.ts) to see how the data should be structured.
*
* To include a regular Markdown-It plugin, that doesn't make use of any Joplin-specific features, you would simply create a file such as this:
* To include a regular Markdown-It plugin, that doesn't make use of any Joplin-specific feature, you
* would simply create a file such as this:
*
* ```javascript
* module.exports = {

View File

@@ -1,6 +1,6 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
/**
* @ignore
*/

View File

@@ -7,7 +7,7 @@ import JoplinCommands from './JoplinCommands';
import JoplinViews from './JoplinViews';
import JoplinInterop from './JoplinInterop';
import JoplinSettings from './JoplinSettings';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
/**
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
*/

View File

@@ -1,35 +1,25 @@
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}.
* 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/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/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:
* 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:
*
* * [Main screen commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/MainScreen/commands)
* * [Global commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/commands)
* * [Editor commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.ts)
* 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.
* 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 command can take any number of arguments, and the supported
* arguments will vary based on the command. For custom commands, this
* is the `args` passed to the `execute()` function. For built-in
* commands, you can find the supported arguments by checking the links
* above.
* <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:

View File

@@ -6,7 +6,7 @@ import { Path } from './types';
*
* This is also what you would use to search notes, via the `search` endpoint.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/simple)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/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:

View File

@@ -2,7 +2,7 @@ import { ExportModule, ImportModule } from './types';
/**
* Provides a way to create modules to import external data into Joplin or to export notes into any arbitrary format.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export)
*
* To implement an import or export module, you would simply define an object with various event handlers that are called
* by the application during the import/export process.

View File

@@ -1,5 +1,5 @@
import Plugin from '../Plugin';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
import { ContentScriptType, Script } from './types';
/**
* This class provides access to plugin-related features.
@@ -28,7 +28,7 @@ export default class JoplinPlugins {
* Note that registering a content script in itself will do nothing - it will only be loaded in specific cases by the relevant app modules
* (eg. the Markdown renderer or the code editor). So it is not a way to inject and run arbitrary code in the app, which for safety and performance reasons is not supported.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/content_script)
*
* @param type Defines how the script will be used. See the type definition for more information about each supported type.
* @param id A unique ID for the content script.

View File

@@ -7,7 +7,7 @@ import { SettingItem, SettingSection } from './types';
*
* Note: Currently this API does **not** provide access to Joplin's built-in settings. This is by design as plugins that modify user settings could give unexpected results
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/settings)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/settings)
*/
export default class JoplinSettings {
private plugin_;
@@ -37,7 +37,7 @@ export default class JoplinSettings {
*
* The list of available settings is not documented yet, but can be found by looking at the source code:
*
* https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/packages/app-mobile/lib/models/Setting.ts#L142
* https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/ReactNativeClient/lib/models/Setting.ts#L142
*/
globalValue(key: string): Promise<any>;
}

View File

@@ -5,7 +5,7 @@ import { ButtonSpec, ViewHandle, ButtonId } from './types';
* Dialogs are hidden by default and you need to call `open()` to open them. Once the user clicks on a button, the `open` call will return and provide the button ID that was
* clicked on. There is currently no "close" method since the dialog should be thought as a modal one and thus can only be closed by clicking on one of the buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/dialog)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/dialog)
*/
export default class JoplinViewsDialogs {
private store;
@@ -16,7 +16,7 @@ export default class JoplinViewsDialogs {
/**
* Creates a new dialog
*/
create(id: string): Promise<ViewHandle>;
create(): Promise<ViewHandle>;
/**
* Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel"
*/

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating and managing menu items.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command)
*/
export default class JoplinViewsMenuItems {
private store;
@@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems {
/**
* Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter.
*/
create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise<void>;
create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise<void>;
}

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating menus.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/menu)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/menu)
*/
export default class JoplinViewsMenus {
private store;
@@ -14,5 +14,5 @@ export default class JoplinViewsMenus {
* Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the
* menu as a sub-menu of the application build-in menus.
*/
create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise<void>;
create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise<void>;
}

View File

@@ -1,13 +1,10 @@
import Plugin from '../Plugin';
import { ViewHandle } from './types';
/**
* Allows creating and managing view panels. View panels currently are
* displayed at the right of the sidebar and allows displaying any HTML
* content (within a webview) and update it in real-time. For example it
* could be used to display a table of content for the active note, or
* display various metadata or graph.
* Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example
* it could be used to display a table of content for the active note, or display various metadata or graph.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/toc)
*/
export default class JoplinViewsPanels {
private store;
@@ -17,7 +14,7 @@ export default class JoplinViewsPanels {
/**
* Creates a new panel
*/
create(id: string): Promise<ViewHandle>;
create(): Promise<ViewHandle>;
/**
* Sets the panel webview HTML
*/

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating and managing toolbar buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command)
*/
export default class JoplinViewsToolbarButtons {
private store;
@@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons {
/**
* Creates a new toolbar button and associate it with the given command.
*/
create(id: string, commandName: string, location: ToolbarButtonLocation): Promise<void>;
create(commandName: string, location: ToolbarButtonLocation): Promise<void>;
}

View File

@@ -2,7 +2,7 @@
* The workspace service provides access to all the parts of Joplin that are being worked on - i.e. the currently selected notes or notebooks as well
* as various related events, such as when a new note is selected, or when the note content changes.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins)
*/
export default class JoplinWorkspace {
private store;

View File

@@ -6,7 +6,7 @@ export interface Command {
/**
* Name of command - must be globally unique
*/
name: string;
name: string
/**
* Label to be displayed on menu items or keyboard shortcut editor for example.
@@ -14,17 +14,17 @@ export interface Command {
* In that case the command will not appear in the shortcut editor or command panel, and logically
* should not be used as a menu item.
*/
label?: string;
label?: string
/**
* Icon to be used on toolbar buttons for example
*/
iconName?: string;
iconName?: string,
/**
* Code to be ran when the command is executed. It may return a result.
*/
execute(...args: any[]): Promise<any | void>;
execute(...args:any[]):Promise<any | void>
/**
* Defines whether the command should be enabled or disabled, which in turns affects
@@ -40,11 +40,13 @@ export interface Command {
* Or | \|\| | "noteIsTodo \|\| noteTodoCompleted"
* And | && | "oneNoteSelected && !inConflictFolder"
*
* Currently the supported context variables aren't documented, but you can [find the list here](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/services/commands/stateToWhenClauseContext.ts).
* Currently the supported context variables aren't documented, but you can find the list there:
*
* https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/services/commands/stateToWhenClauseContext.ts
*
* Note: Commands are enabled by default unless you use this property.
*/
enabledCondition?: string;
enabledCondition?: string
}
// =================================================================
@@ -62,7 +64,7 @@ export enum ImportModuleOutputFormat {
}
/**
* Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export) for an example.
* Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export) for an example.
*
* In general, all the event handlers you'll need to implement take a `context` object as a first argument. This object will contain the export or import path as well as various optional properties, such as which notes or notebooks need to be exported.
*
@@ -72,113 +74,113 @@ export interface ExportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string;
format: string,
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string;
description: string,
/**
* Whether the module will export a single file or multiple files in a directory. It affects the open dialog that will be presented to the user when using your exporter.
*/
target: FileSystemItem;
target: FileSystemItem,
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean;
isNoteArchive: boolean,
/**
* The extensions of the files exported by your module. For example, it is `["htm", "html"]` for the HTML module, and just `["jex"]` for the JEX module.
*/
fileExtensions?: string[];
fileExtensions?: string[],
/**
* Called when the export process starts.
*/
onInit(context: ExportContext): Promise<void>;
onInit(context:ExportContext): Promise<void>;
/**
* Called when an item needs to be processed. An "item" can be any Joplin object, such as a note, a folder, a notebook, etc.
*/
onProcessItem(context: ExportContext, itemType: number, item: any): Promise<void>;
onProcessItem(context:ExportContext, itemType:number, item:any):Promise<void>;
/**
* Called when a resource file needs to be exported.
*/
onProcessResource(context: ExportContext, resource: any, filePath: string): Promise<void>;
onProcessResource(context:ExportContext, resource:any, filePath:string):Promise<void>;
/**
* Called when the export process is done.
*/
onClose(context: ExportContext): Promise<void>;
onClose(context:ExportContext):Promise<void>;
}
export interface ImportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string;
format: string,
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string;
description: string,
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean;
isNoteArchive: boolean,
/**
* The type of sources that are supported by the module. Tells whether the module can import files or directories or both.
*/
sources: FileSystemItem[];
sources: FileSystemItem[],
/**
* Tells the file extensions of the exported files.
*/
fileExtensions?: string[];
fileExtensions?: string[],
/**
* Tells the type of notes that will be generated, either HTML or Markdown (default).
*/
outputFormat?: ImportModuleOutputFormat;
outputFormat?: ImportModuleOutputFormat,
/**
* Called when the import process starts. There is only one event handler within which you should import the complete data.
*/
onExec(context: ImportContext): Promise<void>;
onExec(context:ImportContext): Promise<void>;
}
export interface ExportOptions {
format?: string;
path?: string;
sourceFolderIds?: string[];
sourceNoteIds?: string[];
modulePath?: string;
target?: FileSystemItem;
format?: string,
path?:string,
sourceFolderIds?: string[],
sourceNoteIds?: string[],
modulePath?:string,
target?:FileSystemItem,
}
export interface ExportContext {
destPath: string;
options: ExportOptions;
destPath: string,
options: ExportOptions,
/**
* You can attach your own custom data using this propery - it will then be passed to each event handler, allowing you to keep state from one event to the next.
*/
userData?: any;
userData?: any,
}
export interface ImportContext {
sourcePath: string;
options: any;
warnings: string[];
sourcePath: string,
options: any,
warnings: string[],
}
// =================================================================
@@ -186,7 +188,7 @@ export interface ImportContext {
// =================================================================
export interface Script {
onStart?(event: any): Promise<void>;
onStart?(event:any):Promise<void>,
}
// =================================================================
@@ -194,7 +196,7 @@ export interface Script {
// =================================================================
export interface CreateMenuItemOptions {
accelerator: string;
accelerator: string,
}
export enum MenuItemLocation {
@@ -212,22 +214,22 @@ export interface MenuItem {
* Command that should be associated with the menu item. All menu item should
* have a command associated with them unless they are a sub-menu.
*/
commandName?: string;
commandName?: string,
/**
* Accelerator associated with the menu item
*/
accelerator?: string;
accelerator?: string,
/**
* Menu items that should appear below this menu item. Allows creating a menu tree.
*/
submenu?: MenuItem[];
submenu?: MenuItem[],
/**
* Menu item label. If not specified, the command label will be used instead.
*/
label?: string;
label?: string,
}
// =================================================================
@@ -235,9 +237,9 @@ export interface MenuItem {
// =================================================================
export interface ButtonSpec {
id: ButtonId;
title?: string;
onClick?(): void;
id: ButtonId,
title?: string,
onClick?():void,
}
export type ButtonId = string;
@@ -277,28 +279,28 @@ export enum SettingItemType {
// Redefine a simplified interface to mask internal details
// and to remove function calls as they would have to be async.
export interface SettingItem {
value: any;
type: SettingItemType;
public: boolean;
label: string;
value: any,
type: SettingItemType,
public: boolean,
label:string,
description?: string;
isEnum?: boolean;
section?: string;
options?: any;
appTypes?: string[];
secure?: boolean;
advanced?: boolean;
minimum?: number;
maximum?: number;
step?: number;
description?:string,
isEnum?: boolean,
section?: string,
options?:any,
appTypes?:string[],
secure?: boolean,
advanced?: boolean,
minimum?: number,
maximum?: number,
step?: number,
}
export interface SettingSection {
label: string;
iconName?: string;
description?: string;
name?: string;
label: string,
iconName?: string,
description?: string,
name?: string,
}
// =================================================================
@@ -320,30 +322,36 @@ export type Path = string[];
export enum ContentScriptType {
/**
* Registers a new Markdown-It plugin, which should follow the template below.
* Registers a new Markdown-It plugin, which should follow this template:
*
* ```javascript
* // The module should export an object as below:
*
* module.exports = {
*
* // The "context" variable is currently unused but could be used later on to provide
* // access to your own plugin so that the content script and plugin can communicate.
* default: function(context) {
* return {
*
* // This is the actual Markdown-It plugin - check the [official doc](https://github.com/markdown-it/markdown-it) for more information
* // The `options` parameter is of type [RuleOptions](https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/joplin-renderer/MdToHtml.ts), which
* // contains a number of options, mostly useful for Joplin's internal code.
* plugin: function(markdownIt, options) {
* // ...
* },
* assets: {
* // ...
* },
*
* // You may also specify additional assets such as JS or CSS that should be loaded in the rendered HTML document.
* // Check for example the Joplin [Mermaid plugin](https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.ts) to
* // see how the data should be structured.
* assets: {},
* }
* }
* }
* ```
*
* - The `context` argument is currently unused but could be used later on to provide access to your own plugin so that the content script and plugin can communicate.
*
* - The **required** `plugin` key is the actual Markdown-It plugin - check the [official doc](https://github.com/markdown-it/markdown-it) for more information. The `options` parameter is of type [RuleOptions](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml.ts), which contains a number of options, mostly useful for Joplin's internal code.
*
* - Using the **optional** `assets` key you may specify assets such as JS or CSS that should be loaded in the rendered HTML document. Check for example the Joplin [Mermaid plugin](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml/rules/mermaid.ts) to see how the data should be structured.
*
* To include a regular Markdown-It plugin, that doesn't make use of any Joplin-specific features, you would simply create a file such as this:
* To include a regular Markdown-It plugin, that doesn't make use of any Joplin-specific feature, you
* would simply create a file such as this:
*
* ```javascript
* module.exports = {

View File

@@ -4,12 +4,12 @@ joplin.plugins.register({
onStart: async function() {
const dialogs = joplin.views.dialogs;
const handle = await dialogs.create('myDialog1');
const handle = await dialogs.create();
await dialogs.setHtml(handle, '<p>Testing dialog with default buttons</p><p>Second line</p><p>Third line</p>');
const result = await dialogs.open(handle);
alert('This button was clicked: ' + result);
const handle2 = await dialogs.create('myDialog2');
const handle2 = await dialogs.create();
await dialogs.setHtml(handle2, '<p>Testing dialog with custom buttons</p><p>Second line</p><p>Third line</p>');
await dialogs.setButtons(handle2, [
{

View File

@@ -1,6 +1,6 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
/**
* @ignore
*/

View File

@@ -7,7 +7,7 @@ import JoplinCommands from './JoplinCommands';
import JoplinViews from './JoplinViews';
import JoplinInterop from './JoplinInterop';
import JoplinSettings from './JoplinSettings';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
/**
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
*/

View File

@@ -1,35 +1,25 @@
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}.
* 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/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/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:
* 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:
*
* * [Main screen commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/MainScreen/commands)
* * [Global commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/commands)
* * [Editor commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.ts)
* 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.
* 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 command can take any number of arguments, and the supported
* arguments will vary based on the command. For custom commands, this
* is the `args` passed to the `execute()` function. For built-in
* commands, you can find the supported arguments by checking the links
* above.
* <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:

View File

@@ -6,7 +6,7 @@ import { Path } from './types';
*
* This is also what you would use to search notes, via the `search` endpoint.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/simple)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/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:

View File

@@ -2,7 +2,7 @@ import { ExportModule, ImportModule } from './types';
/**
* Provides a way to create modules to import external data into Joplin or to export notes into any arbitrary format.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export)
*
* To implement an import or export module, you would simply define an object with various event handlers that are called
* by the application during the import/export process.

View File

@@ -1,5 +1,5 @@
import Plugin from '../Plugin';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
import { ContentScriptType, Script } from './types';
/**
* This class provides access to plugin-related features.
@@ -28,7 +28,7 @@ export default class JoplinPlugins {
* Note that registering a content script in itself will do nothing - it will only be loaded in specific cases by the relevant app modules
* (eg. the Markdown renderer or the code editor). So it is not a way to inject and run arbitrary code in the app, which for safety and performance reasons is not supported.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/content_script)
*
* @param type Defines how the script will be used. See the type definition for more information about each supported type.
* @param id A unique ID for the content script.

View File

@@ -7,7 +7,7 @@ import { SettingItem, SettingSection } from './types';
*
* Note: Currently this API does **not** provide access to Joplin's built-in settings. This is by design as plugins that modify user settings could give unexpected results
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/settings)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/settings)
*/
export default class JoplinSettings {
private plugin_;
@@ -37,7 +37,7 @@ export default class JoplinSettings {
*
* The list of available settings is not documented yet, but can be found by looking at the source code:
*
* https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/packages/app-mobile/lib/models/Setting.ts#L142
* https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/ReactNativeClient/lib/models/Setting.ts#L142
*/
globalValue(key: string): Promise<any>;
}

View File

@@ -5,7 +5,7 @@ import { ButtonSpec, ViewHandle, ButtonId } from './types';
* Dialogs are hidden by default and you need to call `open()` to open them. Once the user clicks on a button, the `open` call will return and provide the button ID that was
* clicked on. There is currently no "close" method since the dialog should be thought as a modal one and thus can only be closed by clicking on one of the buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/dialog)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/dialog)
*/
export default class JoplinViewsDialogs {
private store;
@@ -16,7 +16,7 @@ export default class JoplinViewsDialogs {
/**
* Creates a new dialog
*/
create(id: string): Promise<ViewHandle>;
create(): Promise<ViewHandle>;
/**
* Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel"
*/

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating and managing menu items.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command)
*/
export default class JoplinViewsMenuItems {
private store;
@@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems {
/**
* Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter.
*/
create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise<void>;
create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise<void>;
}

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating menus.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/menu)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/menu)
*/
export default class JoplinViewsMenus {
private store;
@@ -14,5 +14,5 @@ export default class JoplinViewsMenus {
* Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the
* menu as a sub-menu of the application build-in menus.
*/
create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise<void>;
create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise<void>;
}

View File

@@ -1,13 +1,10 @@
import Plugin from '../Plugin';
import { ViewHandle } from './types';
/**
* Allows creating and managing view panels. View panels currently are
* displayed at the right of the sidebar and allows displaying any HTML
* content (within a webview) and update it in real-time. For example it
* could be used to display a table of content for the active note, or
* display various metadata or graph.
* Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example
* it could be used to display a table of content for the active note, or display various metadata or graph.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/toc)
*/
export default class JoplinViewsPanels {
private store;
@@ -17,7 +14,7 @@ export default class JoplinViewsPanels {
/**
* Creates a new panel
*/
create(id: string): Promise<ViewHandle>;
create(): Promise<ViewHandle>;
/**
* Sets the panel webview HTML
*/

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating and managing toolbar buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command)
*/
export default class JoplinViewsToolbarButtons {
private store;
@@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons {
/**
* Creates a new toolbar button and associate it with the given command.
*/
create(id: string, commandName: string, location: ToolbarButtonLocation): Promise<void>;
create(commandName: string, location: ToolbarButtonLocation): Promise<void>;
}

View File

@@ -2,7 +2,7 @@
* The workspace service provides access to all the parts of Joplin that are being worked on - i.e. the currently selected notes or notebooks as well
* as various related events, such as when a new note is selected, or when the note content changes.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins)
*/
export default class JoplinWorkspace {
private store;

View File

@@ -6,7 +6,7 @@ export interface Command {
/**
* Name of command - must be globally unique
*/
name: string;
name: string
/**
* Label to be displayed on menu items or keyboard shortcut editor for example.
@@ -14,17 +14,17 @@ export interface Command {
* In that case the command will not appear in the shortcut editor or command panel, and logically
* should not be used as a menu item.
*/
label?: string;
label?: string
/**
* Icon to be used on toolbar buttons for example
*/
iconName?: string;
iconName?: string,
/**
* Code to be ran when the command is executed. It may return a result.
*/
execute(...args: any[]): Promise<any | void>;
execute(...args:any[]):Promise<any | void>
/**
* Defines whether the command should be enabled or disabled, which in turns affects
@@ -40,11 +40,13 @@ export interface Command {
* Or | \|\| | "noteIsTodo \|\| noteTodoCompleted"
* And | && | "oneNoteSelected && !inConflictFolder"
*
* Currently the supported context variables aren't documented, but you can [find the list here](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/services/commands/stateToWhenClauseContext.ts).
* Currently the supported context variables aren't documented, but you can find the list there:
*
* https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/services/commands/stateToWhenClauseContext.ts
*
* Note: Commands are enabled by default unless you use this property.
*/
enabledCondition?: string;
enabledCondition?: string
}
// =================================================================
@@ -62,7 +64,7 @@ export enum ImportModuleOutputFormat {
}
/**
* Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export) for an example.
* Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export) for an example.
*
* In general, all the event handlers you'll need to implement take a `context` object as a first argument. This object will contain the export or import path as well as various optional properties, such as which notes or notebooks need to be exported.
*
@@ -72,113 +74,113 @@ export interface ExportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string;
format: string,
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string;
description: string,
/**
* Whether the module will export a single file or multiple files in a directory. It affects the open dialog that will be presented to the user when using your exporter.
*/
target: FileSystemItem;
target: FileSystemItem,
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean;
isNoteArchive: boolean,
/**
* The extensions of the files exported by your module. For example, it is `["htm", "html"]` for the HTML module, and just `["jex"]` for the JEX module.
*/
fileExtensions?: string[];
fileExtensions?: string[],
/**
* Called when the export process starts.
*/
onInit(context: ExportContext): Promise<void>;
onInit(context:ExportContext): Promise<void>;
/**
* Called when an item needs to be processed. An "item" can be any Joplin object, such as a note, a folder, a notebook, etc.
*/
onProcessItem(context: ExportContext, itemType: number, item: any): Promise<void>;
onProcessItem(context:ExportContext, itemType:number, item:any):Promise<void>;
/**
* Called when a resource file needs to be exported.
*/
onProcessResource(context: ExportContext, resource: any, filePath: string): Promise<void>;
onProcessResource(context:ExportContext, resource:any, filePath:string):Promise<void>;
/**
* Called when the export process is done.
*/
onClose(context: ExportContext): Promise<void>;
onClose(context:ExportContext):Promise<void>;
}
export interface ImportModule {
/**
* The format to be exported, eg "enex", "jex", "json", etc.
*/
format: string;
format: string,
/**
* The description that will appear in the UI, for example in the menu item.
*/
description: string;
description: string,
/**
* Only applies to single file exporters or importers
* It tells whether the format can package multiple notes into one file.
* For example JEX or ENEX can, but HTML cannot.
*/
isNoteArchive: boolean;
isNoteArchive: boolean,
/**
* The type of sources that are supported by the module. Tells whether the module can import files or directories or both.
*/
sources: FileSystemItem[];
sources: FileSystemItem[],
/**
* Tells the file extensions of the exported files.
*/
fileExtensions?: string[];
fileExtensions?: string[],
/**
* Tells the type of notes that will be generated, either HTML or Markdown (default).
*/
outputFormat?: ImportModuleOutputFormat;
outputFormat?: ImportModuleOutputFormat,
/**
* Called when the import process starts. There is only one event handler within which you should import the complete data.
*/
onExec(context: ImportContext): Promise<void>;
onExec(context:ImportContext): Promise<void>;
}
export interface ExportOptions {
format?: string;
path?: string;
sourceFolderIds?: string[];
sourceNoteIds?: string[];
modulePath?: string;
target?: FileSystemItem;
format?: string,
path?:string,
sourceFolderIds?: string[],
sourceNoteIds?: string[],
modulePath?:string,
target?:FileSystemItem,
}
export interface ExportContext {
destPath: string;
options: ExportOptions;
destPath: string,
options: ExportOptions,
/**
* You can attach your own custom data using this propery - it will then be passed to each event handler, allowing you to keep state from one event to the next.
*/
userData?: any;
userData?: any,
}
export interface ImportContext {
sourcePath: string;
options: any;
warnings: string[];
sourcePath: string,
options: any,
warnings: string[],
}
// =================================================================
@@ -186,7 +188,7 @@ export interface ImportContext {
// =================================================================
export interface Script {
onStart?(event: any): Promise<void>;
onStart?(event:any):Promise<void>,
}
// =================================================================
@@ -194,7 +196,7 @@ export interface Script {
// =================================================================
export interface CreateMenuItemOptions {
accelerator: string;
accelerator: string,
}
export enum MenuItemLocation {
@@ -212,22 +214,22 @@ export interface MenuItem {
* Command that should be associated with the menu item. All menu item should
* have a command associated with them unless they are a sub-menu.
*/
commandName?: string;
commandName?: string,
/**
* Accelerator associated with the menu item
*/
accelerator?: string;
accelerator?: string,
/**
* Menu items that should appear below this menu item. Allows creating a menu tree.
*/
submenu?: MenuItem[];
submenu?: MenuItem[],
/**
* Menu item label. If not specified, the command label will be used instead.
*/
label?: string;
label?: string,
}
// =================================================================
@@ -235,9 +237,9 @@ export interface MenuItem {
// =================================================================
export interface ButtonSpec {
id: ButtonId;
title?: string;
onClick?(): void;
id: ButtonId,
title?: string,
onClick?():void,
}
export type ButtonId = string;
@@ -277,28 +279,28 @@ export enum SettingItemType {
// Redefine a simplified interface to mask internal details
// and to remove function calls as they would have to be async.
export interface SettingItem {
value: any;
type: SettingItemType;
public: boolean;
label: string;
value: any,
type: SettingItemType,
public: boolean,
label:string,
description?: string;
isEnum?: boolean;
section?: string;
options?: any;
appTypes?: string[];
secure?: boolean;
advanced?: boolean;
minimum?: number;
maximum?: number;
step?: number;
description?:string,
isEnum?: boolean,
section?: string,
options?:any,
appTypes?:string[],
secure?: boolean,
advanced?: boolean,
minimum?: number,
maximum?: number,
step?: number,
}
export interface SettingSection {
label: string;
iconName?: string;
description?: string;
name?: string;
label: string,
iconName?: string,
description?: string,
name?: string,
}
// =================================================================
@@ -320,30 +322,36 @@ export type Path = string[];
export enum ContentScriptType {
/**
* Registers a new Markdown-It plugin, which should follow the template below.
* Registers a new Markdown-It plugin, which should follow this template:
*
* ```javascript
* // The module should export an object as below:
*
* module.exports = {
*
* // The "context" variable is currently unused but could be used later on to provide
* // access to your own plugin so that the content script and plugin can communicate.
* default: function(context) {
* return {
*
* // This is the actual Markdown-It plugin - check the [official doc](https://github.com/markdown-it/markdown-it) for more information
* // The `options` parameter is of type [RuleOptions](https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/joplin-renderer/MdToHtml.ts), which
* // contains a number of options, mostly useful for Joplin's internal code.
* plugin: function(markdownIt, options) {
* // ...
* },
* assets: {
* // ...
* },
*
* // You may also specify additional assets such as JS or CSS that should be loaded in the rendered HTML document.
* // Check for example the Joplin [Mermaid plugin](https://github.com/laurent22/joplin/blob/dev/ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.ts) to
* // see how the data should be structured.
* assets: {},
* }
* }
* }
* ```
*
* - The `context` argument is currently unused but could be used later on to provide access to your own plugin so that the content script and plugin can communicate.
*
* - The **required** `plugin` key is the actual Markdown-It plugin - check the [official doc](https://github.com/markdown-it/markdown-it) for more information. The `options` parameter is of type [RuleOptions](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml.ts), which contains a number of options, mostly useful for Joplin's internal code.
*
* - Using the **optional** `assets` key you may specify assets such as JS or CSS that should be loaded in the rendered HTML document. Check for example the Joplin [Mermaid plugin](https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml/rules/mermaid.ts) to see how the data should be structured.
*
* To include a regular Markdown-It plugin, that doesn't make use of any Joplin-specific features, you would simply create a file such as this:
* To include a regular Markdown-It plugin, that doesn't make use of any Joplin-specific feature, you
* would simply create a file such as this:
*
* ```javascript
* module.exports = {

View File

@@ -1,6 +1,6 @@
import Plugin from '../Plugin';
import Joplin from './Joplin';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
/**
* @ignore
*/

View File

@@ -7,7 +7,7 @@ import JoplinCommands from './JoplinCommands';
import JoplinViews from './JoplinViews';
import JoplinInterop from './JoplinInterop';
import JoplinSettings from './JoplinSettings';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
/**
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
*/

View File

@@ -1,35 +1,25 @@
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}.
* 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/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/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:
* 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:
*
* * [Main screen commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/MainScreen/commands)
* * [Global commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/commands)
* * [Editor commands](https://github.com/laurent22/joplin/tree/dev/packages/app-desktop/gui/NoteEditor/commands/editorCommandDeclarations.ts)
* 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.
* 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 command can take any number of arguments, and the supported
* arguments will vary based on the command. For custom commands, this
* is the `args` passed to the `execute()` function. For built-in
* commands, you can find the supported arguments by checking the links
* above.
* <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:

View File

@@ -6,7 +6,7 @@ import { Path } from './types';
*
* This is also what you would use to search notes, via the `search` endpoint.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/simple)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/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:

View File

@@ -2,7 +2,7 @@ import { ExportModule, ImportModule } from './types';
/**
* Provides a way to create modules to import external data into Joplin or to export notes into any arbitrary format.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/json_export)
*
* To implement an import or export module, you would simply define an object with various event handlers that are called
* by the application during the import/export process.

View File

@@ -1,5 +1,5 @@
import Plugin from '../Plugin';
import Logger from '../../../Logger';
import Logger from 'lib/Logger';
import { ContentScriptType, Script } from './types';
/**
* This class provides access to plugin-related features.
@@ -28,7 +28,7 @@ export default class JoplinPlugins {
* Note that registering a content script in itself will do nothing - it will only be loaded in specific cases by the relevant app modules
* (eg. the Markdown renderer or the code editor). So it is not a way to inject and run arbitrary code in the app, which for safety and performance reasons is not supported.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/content_script)
*
* @param type Defines how the script will be used. See the type definition for more information about each supported type.
* @param id A unique ID for the content script.

View File

@@ -7,7 +7,7 @@ import { SettingItem, SettingSection } from './types';
*
* Note: Currently this API does **not** provide access to Joplin's built-in settings. This is by design as plugins that modify user settings could give unexpected results
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/settings)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/settings)
*/
export default class JoplinSettings {
private plugin_;
@@ -37,7 +37,7 @@ export default class JoplinSettings {
*
* The list of available settings is not documented yet, but can be found by looking at the source code:
*
* https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/packages/app-mobile/lib/models/Setting.ts#L142
* https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/ReactNativeClient/lib/models/Setting.ts#L142
*/
globalValue(key: string): Promise<any>;
}

View File

@@ -5,7 +5,7 @@ import { ButtonSpec, ViewHandle, ButtonId } from './types';
* Dialogs are hidden by default and you need to call `open()` to open them. Once the user clicks on a button, the `open` call will return and provide the button ID that was
* clicked on. There is currently no "close" method since the dialog should be thought as a modal one and thus can only be closed by clicking on one of the buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/dialog)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/dialog)
*/
export default class JoplinViewsDialogs {
private store;
@@ -16,7 +16,7 @@ export default class JoplinViewsDialogs {
/**
* Creates a new dialog
*/
create(id: string): Promise<ViewHandle>;
create(): Promise<ViewHandle>;
/**
* Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel"
*/

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating and managing menu items.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command)
*/
export default class JoplinViewsMenuItems {
private store;
@@ -12,5 +12,5 @@ export default class JoplinViewsMenuItems {
/**
* Creates a new menu item and associate it with the given command. You can specify under which menu the item should appear using the `location` parameter.
*/
create(id: string, commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise<void>;
create(commandName: string, location?: MenuItemLocation, options?: CreateMenuItemOptions): Promise<void>;
}

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating menus.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/menu)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/menu)
*/
export default class JoplinViewsMenus {
private store;
@@ -14,5 +14,5 @@ export default class JoplinViewsMenus {
* Creates a new menu from the provided menu items and place it at the given location. As of now, it is only possible to place the
* menu as a sub-menu of the application build-in menus.
*/
create(id: string, label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise<void>;
create(label: string, menuItems: MenuItem[], location?: MenuItemLocation): Promise<void>;
}

View File

@@ -1,13 +1,10 @@
import Plugin from '../Plugin';
import { ViewHandle } from './types';
/**
* Allows creating and managing view panels. View panels currently are
* displayed at the right of the sidebar and allows displaying any HTML
* content (within a webview) and update it in real-time. For example it
* could be used to display a table of content for the active note, or
* display various metadata or graph.
* Allows creating and managing view panels. View panels currently are displayed at the right of the sidebar and allows displaying any HTML content (within a webview) and update it in real-time. For example
* it could be used to display a table of content for the active note, or display various metadata or graph.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/toc)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/toc)
*/
export default class JoplinViewsPanels {
private store;
@@ -17,7 +14,7 @@ export default class JoplinViewsPanels {
/**
* Creates a new panel
*/
create(id: string): Promise<ViewHandle>;
create(): Promise<ViewHandle>;
/**
* Sets the panel webview HTML
*/

View File

@@ -3,7 +3,7 @@ import Plugin from '../Plugin';
/**
* Allows creating and managing toolbar buttons.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/register_command)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins/register_command)
*/
export default class JoplinViewsToolbarButtons {
private store;
@@ -12,5 +12,5 @@ export default class JoplinViewsToolbarButtons {
/**
* Creates a new toolbar button and associate it with the given command.
*/
create(id: string, commandName: string, location: ToolbarButtonLocation): Promise<void>;
create(commandName: string, location: ToolbarButtonLocation): Promise<void>;
}

View File

@@ -2,7 +2,7 @@
* The workspace service provides access to all the parts of Joplin that are being worked on - i.e. the currently selected notes or notebooks as well
* as various related events, such as when a new note is selected, or when the note content changes.
*
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins)
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/CliClient/tests/support/plugins)
*/
export default class JoplinWorkspace {
private store;

Some files were not shown because too many files have changed in this diff Show More