1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-24 20:19:10 +02:00

Compare commits

...

35 Commits

Author SHA1 Message Date
Laurent Cozic
8801e82cab pagination 2020-12-29 14:35:51 +00:00
Laurent Cozic
c4757d6c60 pagination 2020-12-29 00:13:04 +00:00
Laurent Cozic
af6c79b844 file manager 2020-12-28 17:24:06 +00:00
Laurent Cozic
0e0de1207f Server: Adding basic file manager 2020-12-28 16:37:12 +00:00
Laurent Cozic
2fda067034 Server: Improved logging and error handling 2020-12-28 15:15:30 +00:00
Laurent Cozic
29177330b0 Merge branch 'release-1.5' into dev 2020-12-28 14:31:37 +00:00
Laurent Cozic
66a5490b54 Desktop release v1.5.12 2020-12-28 14:29:37 +00:00
Laurent Cozic
469cd19ec1 Desktop: Fix issue when importing ENEX file that contains invalid list elements 2020-12-28 14:29:02 +00:00
Laurent
41684a64ef Server: Add Joplin Server package (#1872) 2020-12-28 11:48:47 +00:00
Laurent Cozic
2cd7839552 Increase version num to 1.6 2020-12-27 22:46:27 +00:00
Laurent Cozic
c3d4617612 Update website 2020-12-27 22:41:47 +00:00
Laurent Cozic
158fafc4a0 Merge branch 'release-1.5' into dev 2020-12-27 22:40:26 +00:00
Laurent Cozic
0f59731c06 Plugin Generator release v1.5.3 2020-12-27 22:28:16 +00:00
Laurent Cozic
d0f1a67d96 Plugins: Updated types 2020-12-27 22:26:17 +00:00
Laurent Cozic
2a1434f987 Generator: Fixed publish logic 2020-12-27 22:25:17 +00:00
Laurent Cozic
1ee177880d Plugin Generator release v1.5.2 2020-12-27 22:23:18 +00:00
Laurent Cozic
f6d899eb29 Generator: Fixed publish script 2020-12-27 22:22:29 +00:00
Laurent Cozic
a97f25fd61 Update website 2020-12-27 19:55:34 +00:00
Laurent Cozic
325a5ab08f ios-v10.5.1 2020-12-27 19:47:44 +00:00
Laurent Cozic
c158878b66 Desktop release v1.5.11 2020-12-27 19:19:13 +00:00
Laurent Cozic
0f0f9c1161 macOS: Update app icon 2020-12-26 13:49:00 +00:00
Laurent Cozic
79612163b2 Android release v1.5.1 2020-12-26 00:57:31 +00:00
Laurent Cozic
fab5ed165c CLI v1.5.1 2020-12-26 00:48:02 +00:00
Laurent Cozic
4897c763bd Releasing sub-packages 2020-12-26 00:45:11 +00:00
Laurent Cozic
17b9867bf2 Desktop release v1.5.10 2020-12-26 00:17:15 +00:00
Laurent Cozic
b8f14d50f5 Desktop, Cli: Add table captions when importing ENEX files 2020-12-25 17:44:51 +00:00
Laurent Cozic
9e2f60523f Desktop, Cli: Fixed issues when importing hidden tables within hidden sections in Enex files 2020-12-25 16:37:05 +00:00
Laurent Cozic
321ff4fced Merge branch 'dev' of https://github.com/laurent22/joplin into dev 2020-12-24 12:25:19 +00:00
Laurent Cozic
2a31f914bb Desktop: Update macOS icon for macOS Big Sur 2020-12-24 12:24:40 +00:00
Manuel Tassi
0b71c33d09 All: Translation: Update it_IT.po (#4247) 2020-12-23 21:39:26 -05:00
Laurent Cozic
502c812d9c Chore: remove debug code 2020-12-23 23:46:36 +00:00
Laurent Cozic
5dc3baa50c Desktop: Removed warning for Markdown editor spell checking 2020-12-23 23:19:35 +00:00
Laurent Cozic
a9af58146b Desktop: Fixes #4201: Fixed context menu when the UI is zoomed in or out 2020-12-23 23:17:12 +00:00
Laurent Cozic
17edebb6b1 Desktop: Fixes #4243: Prevent double paste when using Shift+Ctrl+V 2020-12-23 20:03:38 +00:00
Laurent Cozic
bb2855bd80 Desktop, Mobile: Display Katex parsing errors 2020-12-23 19:46:21 +00:00
211 changed files with 16422 additions and 770 deletions

9
.dockerignore Normal file
View File

@@ -0,0 +1,9 @@
**/node_modules
Assets/
.git/
_releases/
packages/app-desktop
packages/app-cli
packages/app-mobile
packages/app-clipper
packages/generator-joplin

9
.env-sample Normal file
View File

@@ -0,0 +1,9 @@
# Example of local config, for development:
#
# JOPLIN_BASE_URL=http://localhost:22300
# JOPLIN_PORT=22300
# Example of config for production:
#
# JOPLIN_BASE_URL=https://example.com/joplin
# JOPLIN_PORT=22300

View File

@@ -6,6 +6,7 @@ _releases/
**/node_modules/
Assets/
docs/
packages/server/dist/
highlight.pack.js
Modules/TinyMCE/IconPack/postinstall.js
Modules/TinyMCE/JoplinLists/
@@ -718,6 +719,9 @@ packages/app-desktop/gui/style/StyledTextInput.js.map
packages/app-desktop/gui/utils/NoteListUtils.d.ts
packages/app-desktop/gui/utils/NoteListUtils.js
packages/app-desktop/gui/utils/NoteListUtils.js.map
packages/app-desktop/gui/utils/convertToScreenCoordinates.d.ts
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
packages/app-desktop/gui/utils/convertToScreenCoordinates.js.map
packages/app-desktop/plugins/GotoAnything.d.ts
packages/app-desktop/plugins/GotoAnything.js
packages/app-desktop/plugins/GotoAnything.js.map
@@ -886,12 +890,18 @@ packages/lib/InMemoryCache.js.map
packages/lib/JoplinServerApi.d.ts
packages/lib/JoplinServerApi.js
packages/lib/JoplinServerApi.js.map
packages/lib/JoplinServerApi2.d.ts
packages/lib/JoplinServerApi2.js
packages/lib/JoplinServerApi2.js.map
packages/lib/Logger.d.ts
packages/lib/Logger.js
packages/lib/Logger.js.map
packages/lib/PoorManIntervals.d.ts
packages/lib/PoorManIntervals.js
packages/lib/PoorManIntervals.js.map
packages/lib/SyncTargetJoplinServer.d.ts
packages/lib/SyncTargetJoplinServer.js
packages/lib/SyncTargetJoplinServer.js.map
packages/lib/Synchronizer.d.ts
packages/lib/Synchronizer.js
packages/lib/Synchronizer.js.map
@@ -913,6 +923,9 @@ packages/lib/errorUtils.js.map
packages/lib/eventManager.d.ts
packages/lib/eventManager.js
packages/lib/eventManager.js.map
packages/lib/file-api-driver-joplinServer.d.ts
packages/lib/file-api-driver-joplinServer.js
packages/lib/file-api-driver-joplinServer.js.map
packages/lib/fs-driver-base.d.ts
packages/lib/fs-driver-base.js
packages/lib/fs-driver-base.js.map
@@ -1384,4 +1397,211 @@ packages/renderer/pathUtils.js.map
packages/renderer/utils.d.ts
packages/renderer/utils.js
packages/renderer/utils.js.map
packages/server/src/app.d.ts
packages/server/src/app.js
packages/server/src/app.js.map
packages/server/src/config-base.d.ts
packages/server/src/config-base.js
packages/server/src/config-base.js.map
packages/server/src/config-buildTypes.d.ts
packages/server/src/config-buildTypes.js
packages/server/src/config-buildTypes.js.map
packages/server/src/config-dev.d.ts
packages/server/src/config-dev.js
packages/server/src/config-dev.js.map
packages/server/src/config-prod.d.ts
packages/server/src/config-prod.js
packages/server/src/config-prod.js.map
packages/server/src/config-tests.d.ts
packages/server/src/config-tests.js
packages/server/src/config-tests.js.map
packages/server/src/config.d.ts
packages/server/src/config.js
packages/server/src/config.js.map
packages/server/src/controllers/BaseController.d.ts
packages/server/src/controllers/BaseController.js
packages/server/src/controllers/BaseController.js.map
packages/server/src/controllers/api/FileController.d.ts
packages/server/src/controllers/api/FileController.js
packages/server/src/controllers/api/FileController.js.map
packages/server/src/controllers/api/FileController.test.d.ts
packages/server/src/controllers/api/FileController.test.js
packages/server/src/controllers/api/FileController.test.js.map
packages/server/src/controllers/api/OAuthController.d.ts
packages/server/src/controllers/api/OAuthController.js
packages/server/src/controllers/api/OAuthController.js.map
packages/server/src/controllers/api/SessionController.d.ts
packages/server/src/controllers/api/SessionController.js
packages/server/src/controllers/api/SessionController.js.map
packages/server/src/controllers/api/SessionController.test.d.ts
packages/server/src/controllers/api/SessionController.test.js
packages/server/src/controllers/api/SessionController.test.js.map
packages/server/src/controllers/api/UserController.d.ts
packages/server/src/controllers/api/UserController.js
packages/server/src/controllers/api/UserController.js.map
packages/server/src/controllers/api/UserController.test.d.ts
packages/server/src/controllers/api/UserController.test.js
packages/server/src/controllers/api/UserController.test.js.map
packages/server/src/controllers/factory.d.ts
packages/server/src/controllers/factory.js
packages/server/src/controllers/factory.js.map
packages/server/src/controllers/index/FileController.d.ts
packages/server/src/controllers/index/FileController.js
packages/server/src/controllers/index/FileController.js.map
packages/server/src/controllers/index/HomeController.d.ts
packages/server/src/controllers/index/HomeController.js
packages/server/src/controllers/index/HomeController.js.map
packages/server/src/controllers/index/LoginController.d.ts
packages/server/src/controllers/index/LoginController.js
packages/server/src/controllers/index/LoginController.js.map
packages/server/src/controllers/index/ProfileController.d.ts
packages/server/src/controllers/index/ProfileController.js
packages/server/src/controllers/index/ProfileController.js.map
packages/server/src/controllers/index/UserController.d.ts
packages/server/src/controllers/index/UserController.js
packages/server/src/controllers/index/UserController.js.map
packages/server/src/db.d.ts
packages/server/src/db.js
packages/server/src/db.js.map
packages/server/src/migrations/20190913171451_create.d.ts
packages/server/src/migrations/20190913171451_create.js
packages/server/src/migrations/20190913171451_create.js.map
packages/server/src/models/ApiClientModel.d.ts
packages/server/src/models/ApiClientModel.js
packages/server/src/models/ApiClientModel.js.map
packages/server/src/models/BaseModel.d.ts
packages/server/src/models/BaseModel.js
packages/server/src/models/BaseModel.js.map
packages/server/src/models/ChangeModel.d.ts
packages/server/src/models/ChangeModel.js
packages/server/src/models/ChangeModel.js.map
packages/server/src/models/ChangeModel.test.d.ts
packages/server/src/models/ChangeModel.test.js
packages/server/src/models/ChangeModel.test.js.map
packages/server/src/models/FileModel.d.ts
packages/server/src/models/FileModel.js
packages/server/src/models/FileModel.js.map
packages/server/src/models/FileModel.test.d.ts
packages/server/src/models/FileModel.test.js
packages/server/src/models/FileModel.test.js.map
packages/server/src/models/PermissionModel.d.ts
packages/server/src/models/PermissionModel.js
packages/server/src/models/PermissionModel.js.map
packages/server/src/models/SessionModel.d.ts
packages/server/src/models/SessionModel.js
packages/server/src/models/SessionModel.js.map
packages/server/src/models/UserModel.d.ts
packages/server/src/models/UserModel.js
packages/server/src/models/UserModel.js.map
packages/server/src/models/factory.d.ts
packages/server/src/models/factory.js
packages/server/src/models/factory.js.map
packages/server/src/models/utils/pagination.d.ts
packages/server/src/models/utils/pagination.js
packages/server/src/models/utils/pagination.js.map
packages/server/src/models/utils/pagination.test.d.ts
packages/server/src/models/utils/pagination.test.js
packages/server/src/models/utils/pagination.test.js.map
packages/server/src/routes/api/files.d.ts
packages/server/src/routes/api/files.js
packages/server/src/routes/api/files.js.map
packages/server/src/routes/api/index.d.ts
packages/server/src/routes/api/index.js
packages/server/src/routes/api/index.js.map
packages/server/src/routes/api/ping.d.ts
packages/server/src/routes/api/ping.js
packages/server/src/routes/api/ping.js.map
packages/server/src/routes/api/sessions.d.ts
packages/server/src/routes/api/sessions.js
packages/server/src/routes/api/sessions.js.map
packages/server/src/routes/default.d.ts
packages/server/src/routes/default.js
packages/server/src/routes/default.js.map
packages/server/src/routes/index/files.d.ts
packages/server/src/routes/index/files.js
packages/server/src/routes/index/files.js.map
packages/server/src/routes/index/home.d.ts
packages/server/src/routes/index/home.js
packages/server/src/routes/index/home.js.map
packages/server/src/routes/index/login.d.ts
packages/server/src/routes/index/login.js
packages/server/src/routes/index/login.js.map
packages/server/src/routes/index/logout.d.ts
packages/server/src/routes/index/logout.js
packages/server/src/routes/index/logout.js.map
packages/server/src/routes/index/profile.d.ts
packages/server/src/routes/index/profile.js
packages/server/src/routes/index/profile.js.map
packages/server/src/routes/index/user.d.ts
packages/server/src/routes/index/user.js
packages/server/src/routes/index/user.js.map
packages/server/src/routes/index/users.d.ts
packages/server/src/routes/index/users.js
packages/server/src/routes/index/users.js.map
packages/server/src/routes/oauth2/authorize.d.ts
packages/server/src/routes/oauth2/authorize.js
packages/server/src/routes/oauth2/authorize.js.map
packages/server/src/routes/routes.d.ts
packages/server/src/routes/routes.js
packages/server/src/routes/routes.js.map
packages/server/src/services/MustacheService.d.ts
packages/server/src/services/MustacheService.js
packages/server/src/services/MustacheService.js.map
packages/server/src/tools/db-migrate.d.ts
packages/server/src/tools/db-migrate.js
packages/server/src/tools/db-migrate.js.map
packages/server/src/tools/dbTools.d.ts
packages/server/src/tools/dbTools.js
packages/server/src/tools/dbTools.js.map
packages/server/src/tools/generate-types.d.ts
packages/server/src/tools/generate-types.js
packages/server/src/tools/generate-types.js.map
packages/server/src/utils/TransactionHandler.d.ts
packages/server/src/utils/TransactionHandler.js
packages/server/src/utils/TransactionHandler.js.map
packages/server/src/utils/auth.d.ts
packages/server/src/utils/auth.js
packages/server/src/utils/auth.js.map
packages/server/src/utils/base64.d.ts
packages/server/src/utils/base64.js
packages/server/src/utils/base64.js.map
packages/server/src/utils/cache.d.ts
packages/server/src/utils/cache.js
packages/server/src/utils/cache.js.map
packages/server/src/utils/defaultView.d.ts
packages/server/src/utils/defaultView.js
packages/server/src/utils/defaultView.js.map
packages/server/src/utils/errors.d.ts
packages/server/src/utils/errors.js
packages/server/src/utils/errors.js.map
packages/server/src/utils/htmlUtils.d.ts
packages/server/src/utils/htmlUtils.js
packages/server/src/utils/htmlUtils.js.map
packages/server/src/utils/koaIf.d.ts
packages/server/src/utils/koaIf.js
packages/server/src/utils/koaIf.js.map
packages/server/src/utils/requestUtils.d.ts
packages/server/src/utils/requestUtils.js
packages/server/src/utils/requestUtils.js.map
packages/server/src/utils/routeUtils.d.ts
packages/server/src/utils/routeUtils.js
packages/server/src/utils/routeUtils.js.map
packages/server/src/utils/routeUtils.test.d.ts
packages/server/src/utils/routeUtils.test.js
packages/server/src/utils/routeUtils.test.js.map
packages/server/src/utils/testUtils.d.ts
packages/server/src/utils/testUtils.js
packages/server/src/utils/testUtils.js.map
packages/server/src/utils/testing/testRouters.d.ts
packages/server/src/utils/testing/testRouters.js
packages/server/src/utils/testing/testRouters.js.map
packages/server/src/utils/time.d.ts
packages/server/src/utils/time.js
packages/server/src/utils/time.js.map
packages/server/src/utils/types.d.ts
packages/server/src/utils/types.js
packages/server/src/utils/types.js.map
packages/server/src/utils/uuidgen.d.ts
packages/server/src/utils/uuidgen.js
packages/server/src/utils/uuidgen.js.map
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD

View File

@@ -1,4 +1,5 @@
module.exports = {
'root': true,
'env': {
'browser': true,
'es6': true,
@@ -34,6 +35,9 @@ module.exports = {
'chrome': 'readonly',
'browser': 'readonly',
// Server admin UI global variables
'onDocumentReady': 'readonly',
'tinymce': 'readonly',
},
'parserOptions': {

220
.gitignore vendored
View File

@@ -48,6 +48,7 @@ TODO.md
packages/tools/commit_hook.txt
packages/tools/github_oauth_token.txt
lerna-debug.log
.env
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
packages/app-cli/app/LinkSelector.d.ts
@@ -707,6 +708,9 @@ packages/app-desktop/gui/style/StyledTextInput.js.map
packages/app-desktop/gui/utils/NoteListUtils.d.ts
packages/app-desktop/gui/utils/NoteListUtils.js
packages/app-desktop/gui/utils/NoteListUtils.js.map
packages/app-desktop/gui/utils/convertToScreenCoordinates.d.ts
packages/app-desktop/gui/utils/convertToScreenCoordinates.js
packages/app-desktop/gui/utils/convertToScreenCoordinates.js.map
packages/app-desktop/plugins/GotoAnything.d.ts
packages/app-desktop/plugins/GotoAnything.js
packages/app-desktop/plugins/GotoAnything.js.map
@@ -875,12 +879,18 @@ packages/lib/InMemoryCache.js.map
packages/lib/JoplinServerApi.d.ts
packages/lib/JoplinServerApi.js
packages/lib/JoplinServerApi.js.map
packages/lib/JoplinServerApi2.d.ts
packages/lib/JoplinServerApi2.js
packages/lib/JoplinServerApi2.js.map
packages/lib/Logger.d.ts
packages/lib/Logger.js
packages/lib/Logger.js.map
packages/lib/PoorManIntervals.d.ts
packages/lib/PoorManIntervals.js
packages/lib/PoorManIntervals.js.map
packages/lib/SyncTargetJoplinServer.d.ts
packages/lib/SyncTargetJoplinServer.js
packages/lib/SyncTargetJoplinServer.js.map
packages/lib/Synchronizer.d.ts
packages/lib/Synchronizer.js
packages/lib/Synchronizer.js.map
@@ -902,6 +912,9 @@ packages/lib/errorUtils.js.map
packages/lib/eventManager.d.ts
packages/lib/eventManager.js
packages/lib/eventManager.js.map
packages/lib/file-api-driver-joplinServer.d.ts
packages/lib/file-api-driver-joplinServer.js
packages/lib/file-api-driver-joplinServer.js.map
packages/lib/fs-driver-base.d.ts
packages/lib/fs-driver-base.js
packages/lib/fs-driver-base.js.map
@@ -1373,4 +1386,211 @@ packages/renderer/pathUtils.js.map
packages/renderer/utils.d.ts
packages/renderer/utils.js
packages/renderer/utils.js.map
packages/server/src/app.d.ts
packages/server/src/app.js
packages/server/src/app.js.map
packages/server/src/config-base.d.ts
packages/server/src/config-base.js
packages/server/src/config-base.js.map
packages/server/src/config-buildTypes.d.ts
packages/server/src/config-buildTypes.js
packages/server/src/config-buildTypes.js.map
packages/server/src/config-dev.d.ts
packages/server/src/config-dev.js
packages/server/src/config-dev.js.map
packages/server/src/config-prod.d.ts
packages/server/src/config-prod.js
packages/server/src/config-prod.js.map
packages/server/src/config-tests.d.ts
packages/server/src/config-tests.js
packages/server/src/config-tests.js.map
packages/server/src/config.d.ts
packages/server/src/config.js
packages/server/src/config.js.map
packages/server/src/controllers/BaseController.d.ts
packages/server/src/controllers/BaseController.js
packages/server/src/controllers/BaseController.js.map
packages/server/src/controllers/api/FileController.d.ts
packages/server/src/controllers/api/FileController.js
packages/server/src/controllers/api/FileController.js.map
packages/server/src/controllers/api/FileController.test.d.ts
packages/server/src/controllers/api/FileController.test.js
packages/server/src/controllers/api/FileController.test.js.map
packages/server/src/controllers/api/OAuthController.d.ts
packages/server/src/controllers/api/OAuthController.js
packages/server/src/controllers/api/OAuthController.js.map
packages/server/src/controllers/api/SessionController.d.ts
packages/server/src/controllers/api/SessionController.js
packages/server/src/controllers/api/SessionController.js.map
packages/server/src/controllers/api/SessionController.test.d.ts
packages/server/src/controllers/api/SessionController.test.js
packages/server/src/controllers/api/SessionController.test.js.map
packages/server/src/controllers/api/UserController.d.ts
packages/server/src/controllers/api/UserController.js
packages/server/src/controllers/api/UserController.js.map
packages/server/src/controllers/api/UserController.test.d.ts
packages/server/src/controllers/api/UserController.test.js
packages/server/src/controllers/api/UserController.test.js.map
packages/server/src/controllers/factory.d.ts
packages/server/src/controllers/factory.js
packages/server/src/controllers/factory.js.map
packages/server/src/controllers/index/FileController.d.ts
packages/server/src/controllers/index/FileController.js
packages/server/src/controllers/index/FileController.js.map
packages/server/src/controllers/index/HomeController.d.ts
packages/server/src/controllers/index/HomeController.js
packages/server/src/controllers/index/HomeController.js.map
packages/server/src/controllers/index/LoginController.d.ts
packages/server/src/controllers/index/LoginController.js
packages/server/src/controllers/index/LoginController.js.map
packages/server/src/controllers/index/ProfileController.d.ts
packages/server/src/controllers/index/ProfileController.js
packages/server/src/controllers/index/ProfileController.js.map
packages/server/src/controllers/index/UserController.d.ts
packages/server/src/controllers/index/UserController.js
packages/server/src/controllers/index/UserController.js.map
packages/server/src/db.d.ts
packages/server/src/db.js
packages/server/src/db.js.map
packages/server/src/migrations/20190913171451_create.d.ts
packages/server/src/migrations/20190913171451_create.js
packages/server/src/migrations/20190913171451_create.js.map
packages/server/src/models/ApiClientModel.d.ts
packages/server/src/models/ApiClientModel.js
packages/server/src/models/ApiClientModel.js.map
packages/server/src/models/BaseModel.d.ts
packages/server/src/models/BaseModel.js
packages/server/src/models/BaseModel.js.map
packages/server/src/models/ChangeModel.d.ts
packages/server/src/models/ChangeModel.js
packages/server/src/models/ChangeModel.js.map
packages/server/src/models/ChangeModel.test.d.ts
packages/server/src/models/ChangeModel.test.js
packages/server/src/models/ChangeModel.test.js.map
packages/server/src/models/FileModel.d.ts
packages/server/src/models/FileModel.js
packages/server/src/models/FileModel.js.map
packages/server/src/models/FileModel.test.d.ts
packages/server/src/models/FileModel.test.js
packages/server/src/models/FileModel.test.js.map
packages/server/src/models/PermissionModel.d.ts
packages/server/src/models/PermissionModel.js
packages/server/src/models/PermissionModel.js.map
packages/server/src/models/SessionModel.d.ts
packages/server/src/models/SessionModel.js
packages/server/src/models/SessionModel.js.map
packages/server/src/models/UserModel.d.ts
packages/server/src/models/UserModel.js
packages/server/src/models/UserModel.js.map
packages/server/src/models/factory.d.ts
packages/server/src/models/factory.js
packages/server/src/models/factory.js.map
packages/server/src/models/utils/pagination.d.ts
packages/server/src/models/utils/pagination.js
packages/server/src/models/utils/pagination.js.map
packages/server/src/models/utils/pagination.test.d.ts
packages/server/src/models/utils/pagination.test.js
packages/server/src/models/utils/pagination.test.js.map
packages/server/src/routes/api/files.d.ts
packages/server/src/routes/api/files.js
packages/server/src/routes/api/files.js.map
packages/server/src/routes/api/index.d.ts
packages/server/src/routes/api/index.js
packages/server/src/routes/api/index.js.map
packages/server/src/routes/api/ping.d.ts
packages/server/src/routes/api/ping.js
packages/server/src/routes/api/ping.js.map
packages/server/src/routes/api/sessions.d.ts
packages/server/src/routes/api/sessions.js
packages/server/src/routes/api/sessions.js.map
packages/server/src/routes/default.d.ts
packages/server/src/routes/default.js
packages/server/src/routes/default.js.map
packages/server/src/routes/index/files.d.ts
packages/server/src/routes/index/files.js
packages/server/src/routes/index/files.js.map
packages/server/src/routes/index/home.d.ts
packages/server/src/routes/index/home.js
packages/server/src/routes/index/home.js.map
packages/server/src/routes/index/login.d.ts
packages/server/src/routes/index/login.js
packages/server/src/routes/index/login.js.map
packages/server/src/routes/index/logout.d.ts
packages/server/src/routes/index/logout.js
packages/server/src/routes/index/logout.js.map
packages/server/src/routes/index/profile.d.ts
packages/server/src/routes/index/profile.js
packages/server/src/routes/index/profile.js.map
packages/server/src/routes/index/user.d.ts
packages/server/src/routes/index/user.js
packages/server/src/routes/index/user.js.map
packages/server/src/routes/index/users.d.ts
packages/server/src/routes/index/users.js
packages/server/src/routes/index/users.js.map
packages/server/src/routes/oauth2/authorize.d.ts
packages/server/src/routes/oauth2/authorize.js
packages/server/src/routes/oauth2/authorize.js.map
packages/server/src/routes/routes.d.ts
packages/server/src/routes/routes.js
packages/server/src/routes/routes.js.map
packages/server/src/services/MustacheService.d.ts
packages/server/src/services/MustacheService.js
packages/server/src/services/MustacheService.js.map
packages/server/src/tools/db-migrate.d.ts
packages/server/src/tools/db-migrate.js
packages/server/src/tools/db-migrate.js.map
packages/server/src/tools/dbTools.d.ts
packages/server/src/tools/dbTools.js
packages/server/src/tools/dbTools.js.map
packages/server/src/tools/generate-types.d.ts
packages/server/src/tools/generate-types.js
packages/server/src/tools/generate-types.js.map
packages/server/src/utils/TransactionHandler.d.ts
packages/server/src/utils/TransactionHandler.js
packages/server/src/utils/TransactionHandler.js.map
packages/server/src/utils/auth.d.ts
packages/server/src/utils/auth.js
packages/server/src/utils/auth.js.map
packages/server/src/utils/base64.d.ts
packages/server/src/utils/base64.js
packages/server/src/utils/base64.js.map
packages/server/src/utils/cache.d.ts
packages/server/src/utils/cache.js
packages/server/src/utils/cache.js.map
packages/server/src/utils/defaultView.d.ts
packages/server/src/utils/defaultView.js
packages/server/src/utils/defaultView.js.map
packages/server/src/utils/errors.d.ts
packages/server/src/utils/errors.js
packages/server/src/utils/errors.js.map
packages/server/src/utils/htmlUtils.d.ts
packages/server/src/utils/htmlUtils.js
packages/server/src/utils/htmlUtils.js.map
packages/server/src/utils/koaIf.d.ts
packages/server/src/utils/koaIf.js
packages/server/src/utils/koaIf.js.map
packages/server/src/utils/requestUtils.d.ts
packages/server/src/utils/requestUtils.js
packages/server/src/utils/requestUtils.js.map
packages/server/src/utils/routeUtils.d.ts
packages/server/src/utils/routeUtils.js
packages/server/src/utils/routeUtils.js.map
packages/server/src/utils/routeUtils.test.d.ts
packages/server/src/utils/routeUtils.test.js
packages/server/src/utils/routeUtils.test.js.map
packages/server/src/utils/testUtils.d.ts
packages/server/src/utils/testUtils.js
packages/server/src/utils/testUtils.js.map
packages/server/src/utils/testing/testRouters.d.ts
packages/server/src/utils/testing/testRouters.js
packages/server/src/utils/testing/testRouters.js.map
packages/server/src/utils/time.d.ts
packages/server/src/utils/time.js
packages/server/src/utils/time.js.map
packages/server/src/utils/types.d.ts
packages/server/src/utils/types.js
packages/server/src/utils/types.js.map
packages/server/src/utils/uuidgen.d.ts
packages/server/src/utils/uuidgen.js
packages/server/src/utils/uuidgen.js.map
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 134 KiB

BIN
Assets/macOsIcon_2.psd Normal file

Binary file not shown.

3
Dockerfile.db Normal file
View File

@@ -0,0 +1,3 @@
FROM postgres:13.1
EXPOSE 5432

71
Dockerfile.server Normal file
View File

@@ -0,0 +1,71 @@
# https://versatile.nl/blog/deploying-lerna-web-apps-with-docker
FROM node:12
RUN apt-get update
RUN apt-get --yes install vim
ARG user=joplin
RUN useradd --create-home --shell /bin/bash $user
USER $user
ENV NODE_ENV development
WORKDIR /home/$user
RUN mkdir /home/$user/logs
# To take advantage of the Docker cache, we first copy all the package.json
# and package-lock.json files, as they rarely change? and then bootstrap
# all the packages.
#
# Note that bootstrapping the packages will run all the postinstall
# scripts, which means that for packages that have such scripts, we need to
# copy all the files.
#
# We can't run boostrap with "--ignore-scripts" because that would
# prevent certain sub-packages, such as sqlite3, from being built
COPY --chown=$user:$user package*.json ./
# Install the root scripts but don't run postinstall (which would bootstrap
# and build TypeScript files, but we don't have the TypeScript files at
# this point)
RUN npm install --ignore-scripts
COPY --chown=$user:$user packages/fork-sax/package*.json ./packages/fork-sax/
COPY --chown=$user:$user packages/lib/package*.json ./packages/lib/
COPY --chown=$user:$user packages/renderer/package*.json ./packages/renderer/
COPY --chown=$user:$user packages/tools/package*.json ./packages/tools/
COPY --chown=$user:$user packages/server/package*.json ./packages/server/
COPY --chown=$user:$user lerna.json .
COPY --chown=$user:$user tsconfig.json .
# The following have postinstall scripts so we need to copy all the files.
# Since they should rarely change this is not an issue
COPY --chown=$user:$user packages/turndown ./packages/turndown
COPY --chown=$user:$user packages/turndown-plugin-gfm ./packages/turndown-plugin-gfm
COPY --chown=$user:$user packages/fork-htmlparser2 ./packages/fork-htmlparser2
RUN ls -la /home/$user
# Then bootstrap only, without compiling the TypeScript files
RUN npm run bootstrap
COPY --chown=$user:$user packages/fork-sax ./packages/fork-sax
COPY --chown=$user:$user packages/lib ./packages/lib
COPY --chown=$user:$user packages/renderer ./packages/renderer
COPY --chown=$user:$user packages/tools ./packages/tools
COPY --chown=$user:$user packages/server ./packages/server
# Finally build everything, in particular the TypeScript files.
RUN npm run build
EXPOSE ${JOPLIN_PORT}
CMD [ "npm", "--prefix", "packages/server", "start" ]

View File

@@ -20,9 +20,9 @@ Three types of applications are available: for the **desktop** (Windows, macOS a
Operating System | Download | Alternative
---|---|---
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.4.19/Joplin-Setup-1.4.19.exe'><img alt='Get it on Windows' width="134px" src='https://joplinapp.org/images/BadgeWindows.png'/></a> | Or get the <a href='https://github.com/laurent22/joplin/releases/download/v1.4.19/JoplinPortable.exe'>Portable version</a><br><br>The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.4.19/Joplin-1.4.19.dmg'><img alt='Get it on macOS' width="134px" src='https://joplinapp.org/images/BadgeMacOS.png'/></a> | -
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.4.19/Joplin-1.4.19.AppImage'><img alt='Get it on Linux' width="134px" src='https://joplinapp.org/images/BadgeLinux.png'/></a> | The recommended way is to use the following installation script as it will handle the desktop icon too:<br><br> `wget -O - https://raw.githubusercontent.com/laurent22/joplin/dev/Joplin_install_and_update.sh \| bash`
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.5.11/Joplin-Setup-1.5.11.exe'><img alt='Get it on Windows' width="134px" src='https://joplinapp.org/images/BadgeWindows.png'/></a> | Or get the <a href='https://github.com/laurent22/joplin/releases/download/v1.5.11/JoplinPortable.exe'>Portable version</a><br><br>The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.5.11/Joplin-1.5.11.dmg'><img alt='Get it on macOS' width="134px" src='https://joplinapp.org/images/BadgeMacOS.png'/></a> | -
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.5.11/Joplin-1.5.11.AppImage'><img alt='Get it on Linux' width="134px" src='https://joplinapp.org/images/BadgeLinux.png'/></a> | The recommended way is to use the following installation script as it will handle the desktop icon too:<br><br> `wget -O - https://raw.githubusercontent.com/laurent22/joplin/dev/Joplin_install_and_update.sh \| bash`
## Mobile applications

View File

@@ -0,0 +1,28 @@
# For development, the easiest might be to only start the Postgres container and
# run the app directly with `npm start`. Or use sqlite3.
version: '3'
services:
# app:
# build:
# context: .
# dockerfile: Dockerfile.server-dev
# ports:
# - "22300:22300"
# # volumes:
# # - ./packages/server/:/var/www/joplin/packages/server/
# # - /var/www/joplin/packages/server/node_modules/
db:
build:
context: .
dockerfile: Dockerfile.db
ports:
- "5432:5432"
environment:
# TODO: Considering the database is only exposed to the
# application, and not to the outside world, is there a need to
# pick a secure password?
- POSTGRES_PASSWORD=joplin
- POSTGRES_USER=joplin
- POSTGRES_DB=joplin

40
docker-compose.server.yml Normal file
View File

@@ -0,0 +1,40 @@
version: '3'
services:
app:
environment:
- JOPLIN_BASE_URL=${JOPLIN_BASE_URL}
- JOPLIN_PORT=${JOPLIN_PORT}
restart: unless-stopped
build:
context: .
dockerfile: Dockerfile.server
ports:
- "${JOPLIN_PORT}:${JOPLIN_PORT}"
# volumes:
# # Mount the server directory so that it's possible to edit file
# # while the container is running. However don't mount the
# # node_modules directory which will be specific to the Docker
# # image (eg native modules will be built for Ubuntu, while the
# # container might be running in Windows)
# # https://stackoverflow.com/a/37898591/561309
# - ./packages/server:/home/joplin/packages/server
# - /home/joplin/packages/server/node_modules/
db:
restart: unless-stopped
# By default, the Postgres image saves the data to a Docker volume,
# so it persists whenever the server is restarted using
# `docker-compose up`. Note that it would however be deleted when
# running `docker-compose down`.
build:
context: .
dockerfile: Dockerfile.db
ports:
- "5432:5432"
environment:
# TODO: Considering the database is only exposed to the
# application, and not to the outside world, is there a need to
# pick a secure password?
- POSTGRES_PASSWORD=joplin
- POSTGRES_USER=joplin
- POSTGRES_DB=joplin

File diff suppressed because one or more lines are too long

View File

@@ -150,13 +150,21 @@
</aside>
<div class="tsd-comment tsd-typography">
<div class="lead">
<p>Registers a new content script. Unlike regular plugin code, which runs in a separate process, content scripts run within the main process code
and thus allow improved performances and more customisations in specific cases. It can be used for example to load a Markdown or editor plugin.</p>
<p>Registers a new content script. Unlike regular plugin code, which
runs in a separate process, content scripts run within the main
process code and thus allow improved performances and more
customisations in specific cases. It can be used for example to load
a Markdown or editor plugin.</p>
</div>
<p>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.</p>
<p><a href="https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script">View the renderer demo plugin</a>
<a href="https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script">View the editor demo plugin</a></p>
<p>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.</p>
<ul>
<li><a href="https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script">View the renderer demo plugin</a></li>
<li><a href="https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script">View the editor demo plugin</a></li>
</ul>
</div>
<h4 class="tsd-parameters-title">Parameters</h4>
<ul class="tsd-parameters">

View File

@@ -124,7 +124,7 @@
<p>Gets a global setting value, including app-specific settings and those set by other plugins.</p>
</div>
<p>The list of available settings is not documented yet, but can be found by looking at the source code:</p>
<p><a href="https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/packages/app-mobile/lib/models/Setting.ts#L142">https://github.com/laurent22/joplin/blob/3539a452a359162c461d2849829d2d42973eab50/packages/app-mobile/lib/models/Setting.ts#L142</a></p>
<p><a href="https://github.com/laurent22/joplin/blob/dev/packages/lib/models/Setting.ts#L142">https://github.com/laurent22/joplin/blob/dev/packages/lib/models/Setting.ts#L142</a></p>
</div>
<h4 class="tsd-parameters-title">Parameters</h4>
<ul class="tsd-parameters">

View File

@@ -88,7 +88,8 @@
</aside>
<div class="tsd-comment tsd-typography">
<div class="lead">
<p>Registers a new CodeMirror plugin, which should follow the template below.</p>
<p>Registers a new CodeMirror plugin, which should follow the template
below.</p>
</div>
<pre><code class="language-javascript"><span class="hljs-built_in">module</span>.exports = {
<span class="hljs-attr">default</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context</span>) </span>{
@@ -98,8 +99,8 @@
},
<span class="hljs-attr">codeMirrorResources</span>: [],
<span class="hljs-attr">codeMirrorOptions</span>: {
<span class="hljs-comment">// ...</span>
},
<span class="hljs-comment">// ...</span>
},
<span class="hljs-attr">assets</span>: {
<span class="hljs-comment">// ...</span>
},
@@ -107,19 +108,42 @@
}
}</code></pre>
<ul>
<li><p>The <code>context</code> 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.</p>
<li><p>The <code>context</code> 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.</p>
</li>
<li><p>The <code>plugin</code> key is your CodeMirror plugin. This is where you can register new commands with CodeMirror or interact with the CodeMirror instance as needed.</p>
<li><p>The <code>plugin</code> key is your CodeMirror plugin. This is where you can
register new commands with CodeMirror or interact with the
CodeMirror instance as needed.</p>
</li>
<li><p>The <code>codeMirrorResources</code> key is an array of CodeMirror resources that will be loaded and attached to the CodeMirror module. These are made up of addons, keymaps, and modes. For example, for a plugin that want&#39;s to enable clojure highlighting in code blocks. <code>codeMirrorResources</code> would be set to <code>[&#39;mode/clojure/clojure&#39;]</code>.</p>
<li><p>The <code>codeMirrorResources</code> key is an array of CodeMirror resources
that will be loaded and attached to the CodeMirror module. These
are made up of addons, keymaps, and modes. For example, for a
plugin that want&#39;s to enable clojure highlighting in code blocks.
<code>codeMirrorResources</code> would be set to <code>[&#39;mode/clojure/clojure&#39;]</code>.</p>
</li>
<li><p>The <code>codeMirrorOptions</code> key contains all the <a href="https://codemirror.net/doc/manual.html#config">CodeMirror</a> options that will be set or changed by this plugin. New options can alse be declared via <a href="https://codemirror.net/doc/manual.html#defineOption"><code>CodeMirror.defineOption</code></a>, and then have their value set here. For example, a plugin that enables line numbers would set <code>codeMirrorOptions</code> to <code>{&#39;lineNumbers&#39;: true}</code>.</p>
<li><p>The <code>codeMirrorOptions</code> key contains all the
<a href="https://codemirror.net/doc/manual.html#config">CodeMirror</a>
options that will be set or changed by this plugin. New options
can alse be declared via
<a href="https://codemirror.net/doc/manual.html#defineOption"><code>CodeMirror.defineOption</code></a>,
and then have their value set here. For example, a plugin that
enables line numbers would set <code>codeMirrorOptions</code> to
<code>{&#39;lineNumbers&#39;: true}</code>.</p>
</li>
<li><p>Using the <strong>optional</strong> <code>assets</code> key you may specify <strong>only</strong> CSS assets that should be loaded in the rendered HTML document. Check for example the Joplin <a href="https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml/rules/mermaid.ts">Mermaid plugin</a> to see how the data should be structured.</p>
<li><p>Using the <strong>optional</strong> <code>assets</code> key you may specify <strong>only</strong> CSS
assets that should be loaded in the rendered HTML document. Check
for example the Joplin [Mermaid
plugin](<a href="https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts">https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts</a>)
to see how the data should be structured.</p>
</li>
</ul>
<p>One of the <code>plugin</code>, <code>codeMirrorResources</code>, or <code>codeMirrorOptions</code> keys must be provided for the plugin to be valid. Having multiple or all provided is also okay.</p>
<p>See the <a href="https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script">demo plugin</a> for an example of all these keys being used in one plugin.</p>
<p>One of the <code>plugin</code>, <code>codeMirrorResources</code>, or <code>codeMirrorOptions</code>
keys must be provided for the plugin to be valid. Having multiple or
all provided is also okay.</p>
<p>See the <a href="https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script">demo
plugin</a>
for an example of all these keys being used in one plugin.</p>
</div>
</section>
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
@@ -130,7 +154,8 @@
</aside>
<div class="tsd-comment tsd-typography">
<div class="lead">
<p>Registers a new Markdown-It plugin, which should follow the template below.</p>
<p>Registers a new Markdown-It plugin, which should follow the template
below.</p>
</div>
<pre><code class="language-javascript"><span class="hljs-built_in">module</span>.exports = {
<span class="hljs-attr">default</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context</span>) </span>{
@@ -144,15 +169,50 @@
}
}
}</code></pre>
<p>See <a href="https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script">the
demo</a>
for a simple Markdown-it plugin example.</p>
<a href="#exported-members" id="exported-members" style="color: inherit; text-decoration: none;">
<h2>Exported members</h2>
</a>
<ul>
<li><p>The <code>context</code> 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.</p>
<li><p>The <code>context</code> 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.</p>
</li>
<li><p>The <strong>required</strong> <code>plugin</code> key is the actual Markdown-It plugin - check the <a href="https://github.com/markdown-it/markdown-it">official doc</a> for more information. The <code>options</code> parameter is of type <a href="https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml.ts">RuleOptions</a>, which contains a number of options, mostly useful for Joplin&#39;s internal code.</p>
<li><p>The <strong>required</strong> <code>plugin</code> key is the actual Markdown-It plugin -
check the [official
doc](<a href="https://github.com/markdown-it/markdown-it">https://github.com/markdown-it/markdown-it</a>) for more
information. The <code>options</code> parameter is of type
<a href="https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml.ts">RuleOptions</a>,
which contains a number of options, mostly useful for Joplin&#39;s
internal code.</p>
</li>
<li><p>Using the <strong>optional</strong> <code>assets</code> key you may specify assets such as JS or CSS that should be loaded in the rendered HTML document. Check for example the Joplin <a href="https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/joplin-renderer/MdToHtml/rules/mermaid.ts">Mermaid plugin</a> to see how the data should be structured.</p>
<li><p>Using the <strong>optional</strong> <code>assets</code> 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](<a href="https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts">https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts</a>)
to see how the data should be structured.</p>
</li>
</ul>
<p>To include a regular Markdown-It plugin, that doesn&#39;t make use of any Joplin-specific features, you would simply create a file such as this:</p>
<a href="#passing-messages-from-the-content-script-to-your-plugin" id="passing-messages-from-the-content-script-to-your-plugin" style="color: inherit; text-decoration: none;">
<h2>Passing messages from the content script to your plugin</h2>
</a>
<p>The application provides the following function to allow executing
commands from the rendered HTML code:</p>
<p><code>webviewApi.executeCommand(commandName, ...args)</code></p>
<p>So you can use this mechanism to pass messages from the note viewer
to your own plugin. To do so you would define a command, using
<code>joplin.commands.register</code>, then you would call this command using
the <code>webviewApi</code> object. See again <a href="https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script">the
demo</a>
to see how this can be done.</p>
<a href="#registering-an-existing-markdown-it-plugin" id="registering-an-existing-markdown-it-plugin" style="color: inherit; text-decoration: none;">
<h2>Registering an existing Markdown-it plugin</h2>
</a>
<p>To include a regular Markdown-It plugin, that doesn&#39;t make use of
any Joplin-specific features, you would simply create a file such as
this:</p>
<pre><code class="language-javascript"><span class="hljs-built_in">module</span>.exports = {
<span class="hljs-attr">default</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">context</span>) </span>{
<span class="hljs-keyword">return</span> {

View File

@@ -75,9 +75,11 @@
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="menuitemlocation.html#edit" class="tsd-kind-icon">Edit</a></li>
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="menuitemlocation.html#editorcontextmenu" class="tsd-kind-icon">Editor<wbr>Context<wbr>Menu</a></li>
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="menuitemlocation.html#file" class="tsd-kind-icon">File</a></li>
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="menuitemlocation.html#foldercontextmenu" class="tsd-kind-icon">Folder<wbr>Context<wbr>Menu</a></li>
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="menuitemlocation.html#help" class="tsd-kind-icon">Help</a></li>
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="menuitemlocation.html#note" class="tsd-kind-icon">Note</a></li>
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="menuitemlocation.html#notelistcontextmenu" class="tsd-kind-icon">Note<wbr>List<wbr>Context<wbr>Menu</a></li>
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="menuitemlocation.html#tagcontextmenu" class="tsd-kind-icon">Tag<wbr>Context<wbr>Menu</a></li>
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="menuitemlocation.html#tools" class="tsd-kind-icon">Tools</a></li>
<li class="tsd-kind-enum-member tsd-parent-kind-enum"><a href="menuitemlocation.html#view" class="tsd-kind-icon">View</a></li>
</ul>
@@ -122,6 +124,22 @@
<aside class="tsd-sources">
</aside>
</section>
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
<a name="foldercontextmenu" class="tsd-anchor"></a>
<h3>Folder<wbr>Context<wbr>Menu</h3>
<div class="tsd-signature tsd-kind-icon">Folder<wbr>Context<wbr>Menu<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> = &quot;folderContextMenu&quot;</span></div>
<aside class="tsd-sources">
</aside>
<div class="tsd-comment tsd-typography">
<div class="lead">
<p>When a command is called from a folder context menu, the
command will receive the following arguments:</p>
</div>
<ul>
<li><code>folderId:string</code>: ID of the folder that was right-clicked on</li>
</ul>
</div>
</section>
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
<a name="help" class="tsd-anchor"></a>
<h3>Help</h3>
@@ -142,6 +160,31 @@
<div class="tsd-signature tsd-kind-icon">Note<wbr>List<wbr>Context<wbr>Menu<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> = &quot;noteListContextMenu&quot;</span></div>
<aside class="tsd-sources">
</aside>
<div class="tsd-comment tsd-typography">
<div class="lead">
<p>When a command is called from the note list context menu, the
command will receive the following arguments:</p>
</div>
<ul>
<li><code>noteIds:string[]</code>: IDs of the notes that were right-clicked on.</li>
</ul>
</div>
</section>
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
<a name="tagcontextmenu" class="tsd-anchor"></a>
<h3>Tag<wbr>Context<wbr>Menu</h3>
<div class="tsd-signature tsd-kind-icon">Tag<wbr>Context<wbr>Menu<span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol"> = &quot;tagContextMenu&quot;</span></div>
<aside class="tsd-sources">
</aside>
<div class="tsd-comment tsd-typography">
<div class="lead">
<p>When a command is called from a tag context menu, the
command will receive the following arguments:</p>
</div>
<ul>
<li><code>tagId:string</code>: ID of the tag that was right-clicked on</li>
</ul>
</div>
</section>
<section class="tsd-panel tsd-member tsd-kind-enum-member tsd-parent-kind-enum">
<a name="tools" class="tsd-anchor"></a>
@@ -188,6 +231,9 @@
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
<a href="menuitemlocation.html#file" class="tsd-kind-icon">File</a>
</li>
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
<a href="menuitemlocation.html#foldercontextmenu" class="tsd-kind-icon">Folder<wbr>Context<wbr>Menu</a>
</li>
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
<a href="menuitemlocation.html#help" class="tsd-kind-icon">Help</a>
</li>
@@ -197,6 +243,9 @@
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
<a href="menuitemlocation.html#notelistcontextmenu" class="tsd-kind-icon">Note<wbr>List<wbr>Context<wbr>Menu</a>
</li>
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
<a href="menuitemlocation.html#tagcontextmenu" class="tsd-kind-icon">Tag<wbr>Context<wbr>Menu</a>
</li>
<li class=" tsd-kind-enum-member tsd-parent-kind-enum">
<a href="menuitemlocation.html#tools" class="tsd-kind-icon">Tools</a>
</li>

View File

@@ -133,6 +133,12 @@
<li class="tsd-kind-variable"><a href="globals.html#logger" class="tsd-kind-icon">logger</a></li>
</ul>
</section>
<section class="tsd-index-section ">
<h3>Functions</h3>
<ul class="tsd-index-list">
<li class="tsd-kind-function"><a href="globals.html#iscontextmenuitemlocation" class="tsd-kind-icon">is<wbr>Context<wbr>Menu<wbr>Item<wbr>Location</a></li>
</ul>
</section>
</div>
</section>
</section>
@@ -187,9 +193,11 @@
<div class="lead">
<p>An array of at least one element and at most three elements.</p>
</div>
<p>[0]: Resource name (eg. &quot;notes&quot;, &quot;folders&quot;, &quot;tags&quot;, etc.)
[1]: (Optional) Resource ID.
[2]: (Optional) Resource link.</p>
<ul>
<li><strong>[0]</strong>: Resource name (eg. &quot;notes&quot;, &quot;folders&quot;, &quot;tags&quot;, etc.)</li>
<li><strong>[1]</strong>: (Optional) Resource ID.</li>
<li><strong>[2]</strong>: (Optional) Resource link.</li>
</ul>
</div>
</section>
<section class="tsd-panel tsd-member tsd-kind-type-alias">
@@ -242,6 +250,33 @@
</aside>
</section>
</section>
<section class="tsd-panel-group tsd-member-group ">
<h2>Functions</h2>
<section class="tsd-panel tsd-member tsd-kind-function">
<a name="iscontextmenuitemlocation" class="tsd-anchor"></a>
<h3>is<wbr>Context<wbr>Menu<wbr>Item<wbr>Location</h3>
<ul class="tsd-signatures tsd-kind-function">
<li class="tsd-signature tsd-kind-icon">is<wbr>Context<wbr>Menu<wbr>Item<wbr>Location<span class="tsd-signature-symbol">(</span>location<span class="tsd-signature-symbol">: </span><a href="enums/menuitemlocation.html" class="tsd-signature-type">MenuItemLocation</a><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><span class="tsd-signature-type">boolean</span></li>
</ul>
<ul class="tsd-descriptions">
<li class="tsd-description">
<aside class="tsd-sources">
</aside>
<h4 class="tsd-parameters-title">Parameters</h4>
<ul class="tsd-parameters">
<li>
<h5>location: <a href="enums/menuitemlocation.html" class="tsd-signature-type">MenuItemLocation</a></h5>
</li>
</ul>
<!-- JOPLINCHANGE
<h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">boolean</span></h4>
-->
</li>
</ul>
</section>
</section>
</div>
<div class="col-4 col-menu menu-sticky-wrap menu-highlight">
<!--
@@ -381,6 +416,9 @@
<li class=" tsd-kind-type-alias">
<a href="globals.html#viewhandle" class="tsd-kind-icon">ViewHandle</a>
</li>
<li class=" tsd-kind-function">
<a href="globals.html#iscontextmenuitemlocation" class="tsd-kind-icon">isContextMenuItemLocation</a>
</li>
</ul>
</nav>
</div>

View File

@@ -206,6 +206,9 @@
<li class=" tsd-kind-type-alias">
<a href="globals.html#viewhandle" class="tsd-kind-icon">ViewHandle</a>
</li>
<li class=" tsd-kind-function">
<a href="globals.html#iscontextmenuitemlocation" class="tsd-kind-icon">isContextMenuItemLocation</a>
</li>
</ul>
</nav>
</div>

View File

@@ -142,7 +142,7 @@
<td>&quot;oneNoteSelected &amp;&amp; !inConflictFolder&quot;</td>
</tr>
</tbody></table>
<p>Currently the supported context variables aren&#39;t documented, but you can <a href="https://github.com/laurent22/joplin/blob/dev/packages/app-mobile/lib/services/commands/stateToWhenClauseContext.ts">find the list here</a>.</p>
<p>Currently the supported context variables aren&#39;t documented, but you can <a href="https://github.com/laurent22/joplin/blob/dev/packages/lib/services/commands/stateToWhenClauseContext.ts">find the list here</a>.</p>
<p>Note: Commands are enabled by default unless you use this property.</p>
</div>
</section>

View File

@@ -400,6 +400,46 @@ https://github.com/laurent22/joplin/blob/dev/readme/changelog.md
<div class="main">
<h1>Joplin changelog<a name="joplin-changelog" href="#joplin-changelog" class="heading-anchor">🔗</a></h1>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/v1.5.11">v1.5.11</a> - 2020-12-27T19:54:07Z<a name="v1-5-11-https-github-com-laurent22-joplin-releases-tag-v1-5-11-2020-12-27t19-54-07z" href="#v1-5-11-https-github-com-laurent22-joplin-releases-tag-v1-5-11-2020-12-27t19-54-07z" class="heading-anchor">🔗</a></h2>
<ul>
<li>New: Add support for media players (video, audio and PDF)</li>
<li>New: Add table captions when importing ENEX files</li>
<li>New: Added doc about Rich Text editor and added way to dismiss warning banner</li>
<li>New: MacOS: Notarize application</li>
<li>New: Plugins: Add support for content script asset files, for Markdown-it plugins</li>
<li>New: Plugins: Add support for context menu items on notebooks and tags</li>
<li>New: Plugins: Add support for workspace.onSyncStart event</li>
<li>New: Plugins: Added a way to execute commands from Markdown-it content scripts</li>
<li>Fixed: Fix End key behavior with Codemirror spellcheck (<a href="https://github.com/laurent22/joplin/issues/4215">#4215</a> by Caleb John)</li>
<li>Fixed: Fixed basic search when executing a query in Chinese (<a href="https://github.com/laurent22/joplin/issues/4034">#4034</a> by Naveen M V)</li>
<li>Fixed: Fixed context menu when the UI is zoomed in or out (<a href="https://github.com/laurent22/joplin/issues/4201">#4201</a>)</li>
<li>Fixed: Fixed importing certain code blocks from ENEX</li>
<li>Fixed: Fixed importing ENEX files that contain empty resources</li>
<li>Fixed: Fixed importing ENEX files that contain resources with invalid mime type</li>
<li>Fixed: Fixed issue when searching for text that contains diacritic (<a href="https://github.com/laurent22/joplin/issues/4152">#4152</a>) (<a href="https://github.com/laurent22/joplin/issues/4025">#4025</a> by Roman Musin)</li>
<li>Fixed: Fixed issue with attachment paths being invalid when user has spaces in home directory path.</li>
<li>Fixed: Fixed issue with note not being saved when a column is added or remove from Rich Text editor</li>
<li>Fixed: Fixed issues when importing hidden tables within hidden sections in Enex files</li>
<li>Fixed: Fixed numbered list bug in markdown editor (<a href="https://github.com/laurent22/joplin/issues/4116">#4116</a>) (<a href="https://github.com/laurent22/joplin/issues/3917">#3917</a> by <a href="https://github.com/MichBoi">@MichBoi</a>)</li>
<li>Fixed: Fixed potential crash when watching note files or resources</li>
<li>Fixed: Fixed title input field width on small windows</li>
<li>Fixed: Focus editor after pressing toolbar buttons (<a href="https://github.com/laurent22/joplin/issues/4037">#4037</a>) (<a href="https://github.com/laurent22/joplin/issues/4036">#4036</a> by <a href="https://github.com/CalebJohn">@CalebJohn</a>)</li>
<li>Fixed: Plugins: Fixed disabling plugin files that start with &quot;_&quot;</li>
<li>Fixed: Prevent double paste when using Shift+Ctrl+V (<a href="https://github.com/laurent22/joplin/issues/4243">#4243</a>)</li>
<li>Fixed: Prevents crash when invalid spell checker language is selected, and provide fallback for invalid language codes (<a href="https://github.com/laurent22/joplin/issues/4146">#4146</a>)</li>
<li>Fixed: Register Markdown editor commands with the Keyboard Shortcut editor (<a href="https://github.com/laurent22/joplin/issues/4136">#4136</a>) (<a href="https://github.com/laurent22/joplin/issues/4130">#4130</a> by Caleb John)</li>
<li>Improved: Display Katex parsing errors</li>
<li>Improved: Improved warning banner colors</li>
<li>Improved: Plugins: Commands would not show up in keymap editor when no shortcut was associated with them</li>
<li>Improved: Plugins: Improved note change event handling.</li>
<li>Improved: Removed warning for Markdown editor spell checking</li>
<li>Improved: Restrict auto-detection of links, and added option to toggle linkify (<a href="https://github.com/laurent22/joplin/issues/4205">#4205</a>)</li>
<li>Improved: Rich Text: Do not converts to markdown links URLs that would be linkified</li>
<li>Improved: Translation: Update zh_CN (<a href="https://github.com/laurent22/joplin/issues/4195">#4195</a> by Zhang YANG)</li>
<li>Improved: Update macOS icon for macOS Big Sur</li>
<li>Improved: Update Mermaid: 8.8.1 -&gt; 8.8.4 (<a href="https://github.com/laurent22/joplin/issues/4193">#4193</a> by Helmut K. C. Tessarek)</li>
<li>Improved: Use plugins whenever printing or exporting notes</li>
</ul>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/v1.4.19">v1.4.19</a> - 2020-12-01T11:11:16Z<a name="v1-4-19-https-github-com-laurent22-joplin-releases-tag-v1-4-19-2020-12-01t11-11-16z" href="#v1-4-19-https-github-com-laurent22-joplin-releases-tag-v1-4-19-2020-12-01t11-11-16z" class="heading-anchor">🔗</a></h2>
<ul>
<li>Improved: Disable soft-break by default in Markdown rendering</li>

View File

@@ -400,6 +400,22 @@ https://github.com/laurent22/joplin/blob/dev/readme/changelog_cli.md
<div class="main">
<h1>Joplin terminal app changelog<a name="joplin-terminal-app-changelog" href="#joplin-terminal-app-changelog" class="heading-anchor">🔗</a></h1>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/cli-v1.5.1">cli-v1.5.1</a> - 2020-12-26T00:46:31Z<a name="cli-v1-5-1-https-github-com-laurent22-joplin-releases-tag-cli-v1-5-1-2020-12-26t00-46-31z" href="#cli-v1-5-1-https-github-com-laurent22-joplin-releases-tag-cli-v1-5-1-2020-12-26t00-46-31z" class="heading-anchor">🔗</a></h2>
<ul>
<li>New: Add table captions when importing ENEX files</li>
<li>Improved: Allow exporting conflict notes (#4095)</li>
<li>Improved: Allow lowercase filters when doing search</li>
<li>Improved: Improved error handling when importing ENEX files</li>
<li>Improved: Partially reverts #3975 (link rendering)</li>
<li>Fixed: Fix sorting by title in a case insensitive way</li>
<li>Fixed: Fixed basic search when executing a query in Chinese (#4034 by Naveen M V)</li>
<li>Fixed: Fixed importing ENEX files that contain empty resources</li>
<li>Fixed: Fixed importing ENEX files that contain resources with invalid mime type</li>
<li>Fixed: Fixed importing certain ENEX files that contain invalid dates</li>
<li>Fixed: Fixed importing certain code blocks from ENEX</li>
<li>Fixed: Fixed issue when searching for text that contains diacritic (#4152) (#4025 by Roman Musin)</li>
<li>Fixed: Fixed issues when importing hidden tables within hidden sections in Enex files</li>
</ul>
<h2><a href="https://github.com/laurent22/joplin/releases/tag/cli-v1.4.9">cli-v1.4.9</a> - 2020-11-26T15:00:37Z<a name="cli-v1-4-9-https-github-com-laurent22-joplin-releases-tag-cli-v1-4-9-2020-11-26t15-00-37z" href="#cli-v1-4-9-https-github-com-laurent22-joplin-releases-tag-cli-v1-4-9-2020-11-26t15-00-37z" class="heading-anchor">🔗</a></h2>
<ul>
<li>Improved: Allow exporting conflict notes (#4095)</li>

View File

@@ -411,6 +411,9 @@ https://github.com/laurent22/joplin/blob/dev/readme/faq.md
</ul>
<p>Now try to install again and it should work.</p>
<p>More info there: <a href="https://github.com/electron-userland/electron-builder/issues/4057">https://github.com/electron-userland/electron-builder/issues/4057</a></p>
<h2>How can I pass arguments to the Linux installation script?<a name="how-can-i-pass-arguments-to-the-linux-installation-script" href="#how-can-i-pass-arguments-to-the-linux-installation-script" class="heading-anchor">🔗</a></h2>
<p>You can pass <a href="https://github.com/laurent22/joplin/blob/dev/Joplin_install_and_update.sh#L37">arguments</a> to the installation script by using this command.</p>
<p><code>wget -O - https://raw.githubusercontent.com/laurent22/joplin/dev/Joplin_install_and_update.sh \| bash -s -- --argument1 --argument2</code></p>
<h2>How can I edit my note in an external text editor?<a name="how-can-i-edit-my-note-in-an-external-text-editor" href="#how-can-i-edit-my-note-in-an-external-text-editor" class="heading-anchor">🔗</a></h2>
<p>The editor command (may include arguments) defines which editor will be used to open a note. If none is provided it will try to auto-detect the default editor. If this does nothing or you want to change it for Joplin, you need to configure it in the Preferences -&gt; Text editor command.</p>
<p>Some example configurations are: (comments after #)</p>

View File

@@ -420,17 +420,17 @@ https://github.com/laurent22/joplin/blob/dev/README.md
<tbody>
<tr>
<td>Windows (32 and 64-bit)</td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v1.4.19/Joplin-Setup-1.4.19.exe'><img alt='Get it on Windows' width="134px" src='https://joplinapp.org/images/BadgeWindows.png'/></a></td>
<td>Or get the <a href='https://github.com/laurent22/joplin/releases/download/v1.4.19/JoplinPortable.exe'>Portable version</a><br><br>The <a href="https://en.wikipedia.org/wiki/Portable_application">portable application</a> allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called &quot;JoplinProfile&quot; next to the executable file.</td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v1.5.11/Joplin-Setup-1.5.11.exe'><img alt='Get it on Windows' width="134px" src='https://joplinapp.org/images/BadgeWindows.png'/></a></td>
<td>Or get the <a href='https://github.com/laurent22/joplin/releases/download/v1.5.11/JoplinPortable.exe'>Portable version</a><br><br>The <a href="https://en.wikipedia.org/wiki/Portable_application">portable application</a> allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called &quot;JoplinProfile&quot; next to the executable file.</td>
</tr>
<tr>
<td>macOS</td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v1.4.19/Joplin-1.4.19.dmg'><img alt='Get it on macOS' width="134px" src='https://joplinapp.org/images/BadgeMacOS.png'/></a></td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v1.5.11/Joplin-1.5.11.dmg'><img alt='Get it on macOS' width="134px" src='https://joplinapp.org/images/BadgeMacOS.png'/></a></td>
<td>-</td>
</tr>
<tr>
<td>Linux</td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v1.4.19/Joplin-1.4.19.AppImage'><img alt='Get it on Linux' width="134px" src='https://joplinapp.org/images/BadgeLinux.png'/></a></td>
<td><a href='https://github.com/laurent22/joplin/releases/download/v1.5.11/Joplin-1.5.11.AppImage'><img alt='Get it on Linux' width="134px" src='https://joplinapp.org/images/BadgeLinux.png'/></a></td>
<td>The recommended way is to use the following installation script as it will handle the desktop icon too:<br><br> <code>wget -O - https://raw.githubusercontent.com/laurent22/joplin/dev/Joplin_install_and_update.sh | bash</code></td>
</tr>
</tbody>

View File

@@ -400,6 +400,10 @@ https://github.com/laurent22/joplin/blob/dev/readme/nextcloud_app.md
<div class="main">
<h1>Joplin Web API for Nextcloud<a name="joplin-web-api-for-nextcloud" href="#joplin-web-api-for-nextcloud" class="heading-anchor">🔗</a></h1>
<hr>
<p><strong>IMPORTANT: THIS APPLICATION IS DEPRECATED AND WILL NO LONGER BE SUPPORTED FROM JOPLIN 1.6.x</strong></p>
<p>It is deprecated in favour of <a href="https://discourse.joplinapp.org/t/joplin-web-api-for-nextcloud/4491/72?u=laurent">Joplin Server</a>, so if you are relying on it please do not upgrade to Joplin 1.6.x till you are ready to migrate to Joplin Server or some other alternative.</p>
<hr>
<p><strong>This is a beta feature, not yet completed. More info coming soon!</strong></p>
<p>The app can be downloaded from there: <a href="https://apps.nextcloud.com/apps/joplin">https://apps.nextcloud.com/apps/joplin</a></p>
<p>The Joplin Web API for Nextcloud is a helper application that enables certain features that are not possible otherwise. In particular:</p>

File diff suppressed because it is too large Load Diff

View File

@@ -116,6 +116,7 @@
"**/_vieux/": true,
"**/.DS_Store": true,
"**/*.base64": true,
"**/*~": true,
"**/*.bundle.js": true,
"**/*.eot": true,
"**/*.icns": true,
@@ -309,6 +310,10 @@
"packages/renderer/MdToHtml/rules/sanitize_html.js": true,
"packages/app-mobile/lib/rnInjectedJs/": true,
"packages/app-mobile/lib/sql-extensions/spellfix.so": true,
"packages/server/dist/": true,
"packages/server/db-*.sqlite": true,
"packages/server/test.pid": true,
"packages/server/temp": true,
"packages/generator-joplin/generators/app/templates/api/": true,
"packages/app-mobile/node_modules/": true,
"phpunit.xml": true,

146
package-lock.json generated
View File

@@ -289,6 +289,75 @@
"dedent": "^0.7.0",
"npmlog": "^4.1.2",
"yargs": "^14.2.2"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yargs": {
"version": "14.2.3",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
"integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
"dev": true,
"requires": {
"cliui": "^5.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^15.0.1"
}
},
"yargs-parser": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
"integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
"@lerna/collect-uncommitted": {
@@ -2183,6 +2252,14 @@
"ssri": "^6.0.1",
"unique-filename": "^1.1.1",
"y18n": "^4.0.0"
},
"dependencies": {
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
}
}
},
"cache-base": {
@@ -10578,80 +10655,11 @@
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},
"yargs": {
"version": "14.2.3",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
"integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
"dev": true,
"requires": {
"cliui": "^5.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^15.0.1"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"yargs-parser": {
"version": "15.0.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
"integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
}

View File

@@ -20,12 +20,16 @@
"buildTranslationsNoTsc": "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",
"circularDependencyCheck": "npx madge --warning --circular --extensions js ./",
"generateDatabaseTypes": "node packages/tools/generate-database-types",
"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",
"linter": "./node_modules/.bin/eslint --resolve-plugins-relative-to . --fix --quiet --ext .js --ext .jsx --ext .ts --ext .tsx",
"postinstall": "lerna bootstrap --no-ci && npm run tsc",
"bootstrap": "lerna bootstrap --no-ci",
"bootstrapIgnoreScripts": "lerna bootstrap --ignore-scripts --no-ci",
"postinstall": "npm run bootstrap --no-ci && npm run build",
"build": "lerna run build && npm run tsc",
"publishAll": "git pull && lerna version --yes --no-private --no-git-tag-version && gulp completePublishAll",
"releaseAndroid": "node packages/tools/release-android.js",
"releaseCli": "node packages/tools/release-cli.js",
@@ -39,7 +43,8 @@
"updateIgnored": "gulp updateIgnoredTypeScriptBuild",
"updatePluginTypes": "./packages/generator-joplin/updateTypes.sh",
"watch": "lerna run watch --stream --parallel",
"i": "lerna add --no-bootstrap --scope"
"i": "lerna add --no-bootstrap --scope",
"server-start-dev": "docker-compose --file docker-compose.server-dev.yml up"
},
"husky": {
"hooks": {

View File

@@ -1,6 +1,7 @@
const gulp = require('gulp');
const fs = require('fs-extra');
const utils = require('@joplin/tools/gulp/utils');
const { setPackagePrivateField } = require('@joplin/tools/tool-utils');
const tasks = {
// compileExtensions: {
// fn: require('../Tools/gulp/tasks/compileExtensions.js'),
@@ -10,12 +11,12 @@ const tasks = {
// updateIgnoredTypeScriptBuild: require('../Tools/gulp/tasks/updateIgnoredTypeScriptBuild'),
};
async function makePackagePublic(filePath) {
const text = await fs.readFile(filePath, 'utf8');
const obj = JSON.parse(text);
delete obj.private;
await fs.writeFile(filePath, JSON.stringify(obj), 'utf8');
}
// async function makePackagePublic(filePath) {
// const text = await fs.readFile(filePath, 'utf8');
// const obj = JSON.parse(text);
// delete obj.private;
// await fs.writeFile(filePath, JSON.stringify(obj), 'utf8');
// }
tasks.prepareBuild = {
fn: async () => {
@@ -25,7 +26,8 @@ tasks.prepareBuild = {
});
await utils.copyFile(`${__dirname}/package.json`, `${buildDir}/package.json`);
await makePackagePublic(`${buildDir}/package.json`);
// await makePackagePublic(`${buildDir}/package.json`);
await setPackagePrivateField(`${buildDir}/package.json`, false);
await utils.copyFile(`${__dirname}/package-lock.json`, `${buildDir}/package-lock.json`);
await utils.copyFile(`${__dirname}/gulpfile.js`, `${buildDir}/gulpfile.js`);

View File

@@ -1,6 +1,6 @@
{
"name": "joplin",
"version": "1.5.0",
"version": "1.5.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -31,7 +31,7 @@
],
"owner": "Laurent Cozic"
},
"version": "1.5.0",
"version": "1.6.0",
"bin": {
"joplin": "./main.js"
},

View File

@@ -1,7 +1,7 @@
import Setting from '@joplin/lib/models/Setting';
import { allNotesFolders, remoteNotesAndFolders, localNotesFoldersSameAsRemote } from './test-utils-synchronizer';
const { syncTargetName, synchronizerStart, setupDatabaseAndSynchronizer, synchronizer, sleep, switchClient, syncTargetId, fileApi } = require('./test-utils.js');
const { syncTargetName, afterAllCleanUp, synchronizerStart, setupDatabaseAndSynchronizer, synchronizer, sleep, switchClient, syncTargetId, fileApi } = require('./test-utils.js');
const Folder = require('@joplin/lib/models/Folder.js');
const Note = require('@joplin/lib/models/Note.js');
const BaseItem = require('@joplin/lib/models/BaseItem.js');
@@ -16,6 +16,10 @@ describe('Synchronizer.basics', function() {
done();
});
afterAll(async () => {
await afterAllCleanUp();
});
it('should create remote items', (async () => {
const folder = await Folder.save({ title: 'folder1' });
await Note.save({ title: 'un', parent_id: folder.id });
@@ -123,10 +127,6 @@ describe('Synchronizer.basics', function() {
}));
it('should delete local notes', (async () => {
// For these tests we pass the context around for each user. This is to make sure that the "deletedItemsProcessed"
// property of the basicDelta() function is cleared properly at the end of a sync operation. If it is not cleared
// it means items will no longer be deleted locally via sync.
const folder1 = await Folder.save({ title: 'folder1' });
const note1 = await Note.save({ title: 'un', parent_id: folder1.id });
const note2 = await Note.save({ title: 'deux', parent_id: folder1.id });

View File

@@ -0,0 +1,21 @@
<p>Check that a hidden table within a hidden block results in a hidden block with a table inside.</p>
<div style="background-color:none;width:0;max-height:0;visibility:collapse;overflow:hidden;float:left;display:none;font-size:0;line-height:0;">
<table border="0" cellspacing="0" cellpadding="0" style="font-family:Helvetica,Arial,sans-serif; background-color: none; width: 0; max-height: 0; visibility: collapse; overflow: hidden; float: left; display: none; font-size:0; line-height:0; mso-hide:all" width="100%">
<tr>
<td colspan="1" rowspan="1">
<table width="1" border="0" cellspacing="0" cellpadding="1">
<tr>
<td colspan="1" rowspan="1">
<div style="height:5px;font-size:5px;line-height:5px"> </div>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td colspan="1" rowspan="1">
<a shape="rect" href="http://www.linkedin.com/e/v2?e=1506x2-huob5maa-42&amp;t=nmp&amp;midToken=AQFSKz2SrPsC9Q&amp;tracking=eml-comm_nu_digest-see_all_updates&amp;ek=nu_digest" style="border:none;outline:none;text-decoration:none;color:#0077B5;">See all updates <en-media width="4" height="8" style="border: none; outline: none; text-decoration: none; height: auto;" type="image/png" hash="de3a477878915a74f7bf042345acec5c"/></a>
</td>
</tr>
</table>
</div>

View File

@@ -0,0 +1,11 @@
Check that a hidden table within a hidden block results in a hidden block with a table inside.
<div style="display: none;">
| |
| --- |
| |
[See all updates](http://www.linkedin.com/e/v2?e=1506x2-huob5maa-42&t=nmp&midToken=AQFSKz2SrPsC9Q&tracking=eml-comm_nu_digest-see_all_updates&ek=nu_digest)
</div>

View File

@@ -0,0 +1,11 @@
<table>
<caption><en-media alt="Video recordings download options" width="20" height="20" style="border:0px;margin:0px;padding:0px;-webkit-text-size-adjust:none;vertical-align:text-bottom;max-width:100%;height:auto;margin-right:5px;" hash="50b609a6abfeddd7005382bdcc6120f4" type="image/png"></en-media>MP4</caption>
<tr>
<th>Video Segments</th>
<th>Download</th>
</tr>
<tr>
<td>one</td>
<td>two</td>
</tr>
</table>

View File

@@ -0,0 +1,5 @@
| Video Segments | Download |
| --- | --- |
| one | two |
MP4

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -1,2 +1,3 @@
dist/*
node_modules/
*.jpl

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -20,4 +20,4 @@
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
}
}
}

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -208,7 +208,7 @@ export enum MenuItemLocation {
Note = 'note',
Tools = 'tools',
Help = 'help',
/**
* @deprecated Do not use - same as NoteListContextMenu
*/
@@ -223,7 +223,7 @@ export enum MenuItemLocation {
* - `noteIds:string[]`: IDs of the notes that were right-clicked on.
*/
NoteListContextMenu = 'noteListContextMenu',
EditorContextMenu = 'editorContextMenu',
/**
@@ -243,7 +243,7 @@ export enum MenuItemLocation {
TagContextMenu = 'tagContextMenu',
}
export function isContextMenuItemLocation(location:MenuItemLocation):boolean {
export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
return [
MenuItemLocation.Context,
MenuItemLocation.NoteListContextMenu,

View File

@@ -15,6 +15,7 @@ import KeychainServiceDriver from '@joplin/lib/services/keychain/KeychainService
import KeychainServiceDriverDummy from '@joplin/lib/services/keychain/KeychainServiceDriver.dummy';
import PluginRunner from '../app/services/plugins/PluginRunner';
import PluginService from '@joplin/lib/services/plugins/PluginService';
import FileApiDriverJoplinServer from '@joplin/lib/file-api-driver-joplinServer';
const fs = require('fs-extra');
const { JoplinDatabase } = require('@joplin/lib/joplin-database.js');
@@ -43,12 +44,14 @@ const SyncTargetOneDrive = require('@joplin/lib/SyncTargetOneDrive.js');
const SyncTargetNextcloud = require('@joplin/lib/SyncTargetNextcloud.js');
const SyncTargetDropbox = require('@joplin/lib/SyncTargetDropbox.js');
const SyncTargetAmazonS3 = require('@joplin/lib/SyncTargetAmazonS3.js');
const SyncTargetJoplinServer = require('@joplin/lib/SyncTargetJoplinServer').default;
const EncryptionService = require('@joplin/lib/services/EncryptionService.js');
const DecryptionWorker = require('@joplin/lib/services/DecryptionWorker.js');
const RevisionService = require('@joplin/lib/services/RevisionService.js');
const ResourceFetcher = require('@joplin/lib/services/ResourceFetcher.js');
const WebDavApi = require('@joplin/lib/WebDavApi');
const DropboxApi = require('@joplin/lib/DropboxApi');
const JoplinServerApi = require('@joplin/lib/JoplinServerApi2').default;
const { OneDriveApi } = require('@joplin/lib/onedrive-api');
const { loadKeychainServiceAndSettings } = require('@joplin/lib/services/SettingUtils');
const md5 = require('md5');
@@ -116,6 +119,7 @@ SyncTargetRegistry.addClass(SyncTargetOneDrive);
SyncTargetRegistry.addClass(SyncTargetNextcloud);
SyncTargetRegistry.addClass(SyncTargetDropbox);
SyncTargetRegistry.addClass(SyncTargetAmazonS3);
SyncTargetRegistry.addClass(SyncTargetJoplinServer);
let syncTargetName_ = '';
let syncTargetId_: number = null;
@@ -132,7 +136,7 @@ function setSyncTargetName(name: string) {
syncTargetName_ = name;
syncTargetId_ = SyncTargetRegistry.nameToId(syncTargetName_);
sleepTime = syncTargetId_ == SyncTargetRegistry.nameToId('filesystem') ? 1001 : 100;// 400;
isNetworkSyncTarget_ = ['nextcloud', 'dropbox', 'onedrive', 'amazon_s3'].includes(syncTargetName_);
isNetworkSyncTarget_ = ['nextcloud', 'dropbox', 'onedrive', 'amazon_s3', 'joplinServer'].includes(syncTargetName_);
synchronizers_ = [];
return previousName;
}
@@ -142,6 +146,7 @@ setSyncTargetName('memory');
// setSyncTargetName('dropbox');
// setSyncTargetName('onedrive');
// setSyncTargetName('amazon_s3');
// setSyncTargetName('joplinServer');
// console.info(`Testing with sync target: ${syncTargetName_}`);
@@ -214,6 +219,16 @@ async function afterEachCleanUp() {
KeymapService.destroyInstance();
}
async function afterAllCleanUp() {
if (fileApi()) {
try {
await fileApi().clearRoot();
} catch (error) {
console.warn('Could not clear sync target root:', error);
}
}
}
async function switchClient(id: number, options: any = null) {
options = Object.assign({}, { keychainEnabled: false }, options);
@@ -346,7 +361,7 @@ async function setupDatabaseAndSynchronizer(id: number, options: any = null) {
if (!synchronizers_[id]) {
const SyncTargetClass = SyncTargetRegistry.classById(syncTargetId_);
const syncTarget = new SyncTargetClass(db(id));
await initFileApi();
await initFileApi(suiteName_);
syncTarget.setFileApi(fileApi());
syncTarget.setLogger(logger);
synchronizers_[id] = await syncTarget.synchronizer();
@@ -361,6 +376,7 @@ async function setupDatabaseAndSynchronizer(id: number, options: any = null) {
resourceFetchers_[id] = new ResourceFetcher(() => { return synchronizers_[id].api(); });
kvStores_[id] = new KvStore();
await fileApi().initialize();
await fileApi().clearRoot();
}
@@ -440,7 +456,7 @@ async function loadEncryptionMasterKey(id: number = null, useExisting = false) {
return masterKey;
}
async function initFileApi() {
async function initFileApi(suiteName: string) {
if (fileApis_[syncTargetId_]) return;
let fileApi = null;
@@ -482,7 +498,6 @@ async function initFileApi() {
if (!process.argv.includes('--runInBand')) {
throw new Error('OneDrive tests must be run sequentially, with the --runInBand arg. eg `npm test -- --runInBand`');
}
const { parameters, setEnvOverride } = require('@joplin/lib/parameters.js');
@@ -506,6 +521,16 @@ async function initFileApi() {
if (!amazonS3Creds || !amazonS3Creds.accessKeyId) throw new Error(`AWS auth JSON missing in ${amazonS3CredsPath} format should be: { "accessKeyId": "", "secretAccessKey": "", "bucket": "mybucket"}`);
const api = new S3({ accessKeyId: amazonS3Creds.accessKeyId, secretAccessKey: amazonS3Creds.secretAccessKey, s3UseArnRegion: true });
fileApi = new FileApi('', new FileApiDriverAmazonS3(api, amazonS3Creds.bucket));
} else if (syncTargetId_ == SyncTargetRegistry.nameToId('joplinServer')) {
// Note that to test the API in parallel mode, you need to use Postgres
// as database, as the SQLite database is not reliable when being
// read/write from multiple processes at the same time.
const api = new JoplinServerApi({
baseUrl: () => 'http://localhost:22300',
username: () => 'admin@localhost',
password: () => 'admin',
});
fileApi = new FileApi(`root:/Apps/Joplin-${suiteName}`, new FileApiDriverJoplinServer(api));
}
fileApi.setLogger(logger);
@@ -743,18 +768,18 @@ class TestApp extends BaseApplication {
private middlewareCalls_: any[];
private logger_: LoggerWrapper;
constructor(hasGui = true) {
public constructor(hasGui = true) {
super();
this.hasGui_ = hasGui;
this.middlewareCalls_ = [];
this.logger_ = super.logger();
}
hasGui() {
public hasGui() {
return this.hasGui_;
}
async start(argv: any[]) {
public async start(argv: any[]) {
this.logger_.info('Test app starting...');
if (!argv.includes('--profile')) {
@@ -775,7 +800,7 @@ class TestApp extends BaseApplication {
this.logger_.info('Test app started...');
}
async generalMiddleware(store: any, next: any, action: any) {
public async generalMiddleware(store: any, next: any, action: any) {
this.middlewareCalls_.push(true);
try {
await super.generalMiddleware(store, next, action);
@@ -784,7 +809,7 @@ class TestApp extends BaseApplication {
}
}
async wait() {
public async wait() {
return new Promise((resolve) => {
const iid = shim.setInterval(() => {
if (!this.middlewareCalls_.length) {
@@ -795,11 +820,11 @@ class TestApp extends BaseApplication {
});
}
async profileDir() {
public async profileDir() {
return Setting.value('profileDir');
}
async destroy() {
public async destroy() {
this.logger_.info('Test app stopping...');
await this.wait();
await ItemChange.waitForAllSaved();
@@ -809,4 +834,4 @@ class TestApp extends BaseApplication {
}
}
module.exports = { exportDir, newPluginService, newPluginScript, synchronizerStart, afterEachCleanUp, syncTargetName, setSyncTargetName, syncDir, createTempDir, isNetworkSyncTarget, kvStore, expectThrow, logger, expectNotThrow, resourceService, resourceFetcher, tempFilePath, allSyncTargetItemsEncrypted, msleep, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, checkThrow, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };
module.exports = { afterAllCleanUp, exportDir, newPluginService, newPluginScript, synchronizerStart, afterEachCleanUp, syncTargetName, setSyncTargetName, syncDir, createTempDir, isNetworkSyncTarget, kvStore, expectThrow, logger, expectNotThrow, resourceService, resourceFetcher, tempFilePath, allSyncTargetItemsEncrypted, msleep, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, checkThrow, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 2,
"name": "Joplin Web Clipper [DEV]",
"version": "1.5.0",
"version": "1.6.0",
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
"homepage_url": "https://joplinapp.org",
"content_security_policy": "script-src 'self'; object-src 'self'",

View File

@@ -24,6 +24,7 @@ import { themeStyle } from '@joplin/lib/theme';
import { ThemeAppearance } from '@joplin/lib/themes/type';
import SpellCheckerService from '@joplin/lib/services/spellChecker/SpellCheckerService';
import dialogs from '../../../dialogs';
import convertToScreenCoordinates from '../../../utils/convertToScreenCoordinates';
const Note = require('@joplin/lib/models/Note.js');
const { clipboard } = require('electron');
@@ -613,7 +614,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
function pointerInsideEditor(x: number, y: number) {
const elements = document.getElementsByClassName('codeMirrorEditor');
if (!elements.length) return null;
const rect = elements[0].getBoundingClientRect();
const rect = convertToScreenCoordinates(elements[0].getBoundingClientRect());
return rect.x < x && rect.y < y && rect.right > x && rect.bottom > y;
}

View File

@@ -19,7 +19,7 @@ const taboverride = require('taboverride');
const { reg } = require('@joplin/lib/registry.js');
const BaseItem = require('@joplin/lib/models/BaseItem');
const { themeStyle } = require('@joplin/lib/theme');
const { clipboard } = require('electron');
// const { clipboard } = require('electron');
const supportedLocales = require('./supportedLocales');
function markupRenderOptions(override: any = null) {
@@ -1015,14 +1015,19 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
}
}
function onKeyDown(event: any) {
function onKeyDown(_event: any) {
// It seems "paste as text" is now handled automatically by
// either Chrome, Electron and/or TinyMCE so the code below
// should not longer be necessary. Also fixes
// https://github.com/laurent22/joplin/issues/4243
// Handle "paste as text". Note that when pressing CtrlOrCmd+Shift+V it's going
// to trigger the "keydown" event but not the "paste" event, so it's ok to process
// it here and we don't need to do anything special in onPaste
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.code === 'KeyV') {
const pastedText = clipboard.readText();
if (pastedText) editor.insertContent(pastedText);
}
// if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.code === 'KeyV') {
// const pastedText = clipboard.readText();
// if (pastedText) editor.insertContent(pastedText);
// }
}
editor.on('keyup', onKeyUp);

View File

@@ -6,6 +6,7 @@ import bridge from '../../../../../services/bridge';
import { menuItems, ContextMenuOptions, ContextMenuItemType } from '../../../utils/contextMenu';
import MenuUtils from '@joplin/lib/services/commands/MenuUtils';
import CommandService from '@joplin/lib/services/CommandService';
import convertToScreenCoordinates from '../../../../utils/convertToScreenCoordinates';
const Resource = require('@joplin/lib/models/Resource');
@@ -20,7 +21,7 @@ function contextMenuElement(editor: any, x: number, y: number) {
const iframes = document.getElementsByClassName('tox-edit-area__iframe');
if (!iframes.length) return null;
const iframeRect = iframes[0].getBoundingClientRect();
const iframeRect = convertToScreenCoordinates(iframes[0].getBoundingClientRect());
if (iframeRect.x < x && iframeRect.y < y && iframeRect.right > x && iframeRect.bottom > y) {
const relativeX = x - iframeRect.x;

View File

@@ -0,0 +1,17 @@
// Converts world coordinate to screen coordinates by applying the current
// zoom.
export default function convertToScreenCoordinates(o: any): any {
const pixelRatio = window.devicePixelRatio;
if (typeof o === 'number') return o * pixelRatio;
if (typeof o === 'object' && o !== null) {
o = JSON.parse(JSON.stringify(o));
for (const k of Object.keys(o)) {
o[k] = convertToScreenCoordinates(o[k]);
}
return o;
}
throw new Error(`Cannot convert to screen coordinates: ${typeof o}`);
}

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "1.5.9",
"version": "1.6.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/app-desktop",
"version": "1.5.9",
"version": "1.6.0",
"description": "Joplin for Desktop",
"main": "main.js",
"private": true,

View File

@@ -138,8 +138,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2097613
versionName "1.5.0"
versionCode 2097614
versionName "1.6.0"
ndk {
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}

View File

@@ -338,13 +338,13 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 57;
CURRENT_PROJECT_VERSION = 58;
DEVELOPMENT_TEAM = A9BXAFS6CT;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 10.5.0;
MARKETING_VERSION = 10.6.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -365,12 +365,12 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 57;
CURRENT_PROJECT_VERSION = 58;
DEVELOPMENT_TEAM = A9BXAFS6CT;
INFOPLIST_FILE = Joplin/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 10.5.0;
MARKETING_VERSION = 10.6.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/fork-htmlparser2",
"version": "4.1.13",
"version": "4.1.14",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,7 +1,7 @@
{
"name": "@joplin/fork-htmlparser2",
"description": "Fast & forgiving HTML/XML/RSS parser",
"version": "4.1.13",
"version": "4.1.14",
"author": "Felix Boehm <me@feedic.com>",
"publishConfig": {
"access": "public"

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/fork-sax",
"version": "1.2.17",
"version": "1.2.18",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -2,7 +2,7 @@
"name": "@joplin/fork-sax",
"description": "An evented streaming XML parser in JavaScript",
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
"version": "1.2.17",
"version": "1.2.18",
"main": "lib/sax.js",
"publishConfig": {
"access": "public"

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"id": "<%= pluginId %>",
"app_min_version": "1.5",
"app_min_version": "1.6",
"version": "1.0.0",
"name": "<%= pluginName %>",
"description": "<%= pluginDescription %>",

View File

@@ -1,6 +1,6 @@
{
"name": "generator-joplin",
"version": "1.5.0",
"version": "1.5.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,9 +1,8 @@
{
"name": "generator-joplin",
"version": "1.5.0",
"version": "1.6.0",
"description": "Scaffolds out a new Joplin plugin",
"homepage": "https://joplinapp.org",
"private": true,
"author": {
"name": "Laurent Cozic",
"url": "https://joplinapp.org"
@@ -25,5 +24,6 @@
"yosay": "^2.0.1"
},
"repository": "https://github.com/laurent22/generator-joplin",
"license": "MIT"
"license": "MIT",
"private": true
}

View File

@@ -6,6 +6,7 @@ import reducer from './reducer';
import KeychainServiceDriver from './services/keychain/KeychainServiceDriver.node';
import { _, setLocale } from './locale';
import KvStore from './services/KvStore';
import SyncTargetJoplinServer from './SyncTargetJoplinServer';
const { createStore, applyMiddleware } = require('redux');
const { defaultState, stateUtils } = require('./reducer');
@@ -681,6 +682,7 @@ export default class BaseApplication {
SyncTargetRegistry.addClass(SyncTargetWebDAV);
SyncTargetRegistry.addClass(SyncTargetDropbox);
SyncTargetRegistry.addClass(SyncTargetAmazonS3);
SyncTargetRegistry.addClass(SyncTargetJoplinServer);
try {
await shim.fsDriver().remove(tempDir);

View File

@@ -0,0 +1,174 @@
import shim from './shim';
const { rtrimSlashes } = require('./path-utils.js');
const JoplinError = require('./JoplinError');
const { stringify } = require('query-string');
interface Options {
baseUrl(): string;
username(): string;
password(): string;
}
enum ExecOptionsResponseFormat {
Json = 'json',
Text = 'text',
}
enum ExecOptionsTarget {
String = 'string',
File = 'file',
}
interface ExecOptions {
responseFormat?: ExecOptionsResponseFormat;
target?: ExecOptionsTarget;
path?: string;
source?: string;
}
export default class JoplinServerApi {
private options_: Options;
private session_: any;
public constructor(options: Options) {
this.options_ = options;
}
private baseUrl() {
return rtrimSlashes(this.options_.baseUrl());
}
private async session() {
// TODO: handle invalid session
if (this.session_) return this.session_;
this.session_ = await this.exec('POST', 'api/sessions', null, {
email: this.options_.username(),
password: this.options_.password(),
});
return this.session_;
}
private async sessionId() {
const session = await this.session();
return session ? session.id : '';
}
// private requestToCurl_(url: string, options: any) {
// const output = [];
// output.push('curl');
// output.push('-v');
// if (options.method) output.push(`-X ${options.method}`);
// if (options.headers) {
// for (const n in options.headers) {
// if (!options.headers.hasOwnProperty(n)) continue;
// output.push(`${'-H ' + '"'}${n}: ${options.headers[n]}"`);
// }
// }
// if (options.body) output.push(`${'--data ' + '\''}${JSON.stringify(options.body)}'`);
// output.push(url);
// return output.join(' ');
// }
public async exec(method: string, path: string = '', query: Record<string, any> = null, body: any = null, headers: any = null, options: ExecOptions = null) {
if (headers === null) headers = {};
if (options === null) options = {};
if (!options.responseFormat) options.responseFormat = ExecOptionsResponseFormat.Json;
if (!options.target) options.target = ExecOptionsTarget.String;
let sessionId = '';
if (path !== 'api/sessions' && !sessionId) {
sessionId = await this.sessionId();
}
if (sessionId) headers['X-API-AUTH'] = sessionId;
const fetchOptions: any = {};
fetchOptions.headers = headers;
fetchOptions.method = method;
if (options.path) fetchOptions.path = options.path;
if (body) {
if (typeof body === 'object') {
fetchOptions.body = JSON.stringify(body);
fetchOptions.headers['Content-Type'] = 'application/json';
} else {
fetchOptions.body = body;
}
fetchOptions.headers['Content-Length'] = `${shim.stringByteLength(fetchOptions.body)}`;
}
let url = `${this.baseUrl()}/${path}`;
if (query) {
url += url.indexOf('?') < 0 ? '?' : '&';
url += stringify(query);
}
let response: any = null;
// console.info('Joplin API Call', `${method} ${url}`, headers, options);
// console.info(this.requestToCurl_(url, fetchOptions));
if (options.source == 'file' && (method == 'POST' || method == 'PUT')) {
if (fetchOptions.path) {
const fileStat = await shim.fsDriver().stat(fetchOptions.path);
if (fileStat) fetchOptions.headers['Content-Length'] = `${fileStat.size}`;
}
response = await shim.uploadBlob(url, fetchOptions);
} else if (options.target == 'string') {
if (typeof body === 'string') fetchOptions.headers['Content-Length'] = `${shim.stringByteLength(body)}`;
response = await shim.fetch(url, fetchOptions);
} else {
// file
response = await shim.fetchBlob(url, fetchOptions);
}
const responseText = await response.text();
// console.info('Joplin API Response', responseText);
// Creates an error object with as much data as possible as it will appear in the log, which will make debugging easier
const newError = (message: string, code: number = 0) => {
// Gives a shorter response for error messages. Useful for cases where a full HTML page is accidentally loaded instead of
// JSON. That way the error message will still show there's a problem but without filling up the log or screen.
const shortResponseText = (`${responseText}`).substr(0, 1024);
return new JoplinError(`${method} ${path}: ${message} (${code}): ${shortResponseText}`, code);
};
let responseJson_: any = null;
const loadResponseJson = async () => {
if (!responseText) return null;
if (responseJson_) return responseJson_;
responseJson_ = JSON.parse(responseText);
if (!responseJson_) throw newError('Cannot parse JSON response', response.status);
return responseJson_;
};
if (!response.ok) {
if (options.target === 'file') throw newError('fetchBlob error', response.status);
let json = null;
try {
json = await loadResponseJson();
} catch (error) {
// Just send back the plain text in newErro()
}
if (json && json.message) {
throw newError(`${json.message}`, response.status);
}
throw newError('Unknown error', response.status);
}
if (options.responseFormat === 'text') return responseText;
const output = await loadResponseJson();
return output;
}
}

View File

@@ -1,6 +1,7 @@
const moment = require('moment');
const time = require('./time').default;
const { FsDriverDummy } = require('./fs-driver-dummy.js');
const { sprintf } = require('sprintf-js');
export enum TargetType {
Database = 'database',
@@ -24,6 +25,12 @@ interface Target {
prefix?: string;
path?: string;
source?: string;
// Default message format
format?: string;
// If specified, will use this as format if it's an info message
formatInfo?: string;
}
export interface LoggerWrapper {
@@ -173,9 +180,25 @@ class Logger {
if (level == LogLevel.Warn) fn = 'warn';
if (level == LogLevel.Info) fn = 'info';
const consoleObj = target.console ? target.console : console;
const prefixItems = [moment().format('HH:mm:ss')];
if (targetPrefix) prefixItems.push(targetPrefix);
const items = [`${prefixItems.join(': ')}:`].concat(...object);
let items:any[] = [];
if (target.format) {
const format = level === LogLevel.Info && target.formatInfo ? target.formatInfo : target.format;
const s = sprintf(format, {
date_time: moment().format('YYYY-MM-DD HH:mm:ss'),
level: Logger.levelIdToString(level),
prefix: targetPrefix || '',
message: '',
});
items = [s.trim()].concat(...object);
} else {
const prefixItems = [moment().format('HH:mm:ss')];
if (targetPrefix) prefixItems.push(targetPrefix);
items = [`${prefixItems.join(': ')}:`].concat(...object);
}
consoleObj[fn](...items);
} else if (target.type == 'file') {
const timestamp = moment().format('YYYY-MM-DD HH:mm:ss');

View File

@@ -0,0 +1,91 @@
import FileApiDriverJoplinServer from './file-api-driver-joplinServer';
import Setting from './models/Setting';
import Synchronizer from './Synchronizer';
import { _ } from './locale.js';
import JoplinServerApi from './JoplinServerApi2';
const BaseSyncTarget = require('./BaseSyncTarget.js');
const { FileApi } = require('./file-api.js');
interface FileApiOptions {
path(): string;
username(): string;
password(): string;
directory(): string;
}
export default class SyncTargetJoplinServer extends BaseSyncTarget {
static id() {
return 9;
}
static supportsConfigCheck() {
return true;
}
static targetName() {
return 'joplinServer';
}
static label() {
return _('Joplin Server');
}
async isAuthenticated() {
return true;
}
static async newFileApi_(options: FileApiOptions) {
const apiOptions = {
baseUrl: () => options.path(),
username: () => options.username(),
password: () => options.password(),
};
const api = new JoplinServerApi(apiOptions);
const driver = new FileApiDriverJoplinServer(api);
const fileApi = new FileApi(() => `root:/${options.directory()}`, driver);
fileApi.setSyncTargetId(this.id());
await fileApi.initialize();
return fileApi;
}
static async checkConfig(options: FileApiOptions) {
const fileApi = await SyncTargetJoplinServer.newFileApi_(options);
fileApi.requestRepeatCount_ = 0;
const output = {
ok: false,
errorMessage: '',
};
try {
const result = await fileApi.stat('');
if (!result) throw new Error(`Sync directory not found: "${options.directory()}" on server "${options.path()}"`);
output.ok = true;
} catch (error) {
output.errorMessage = error.message;
if (error.code) output.errorMessage += ` (Code ${error.code})`;
}
return output;
}
async initFileApi() {
const fileApi = await SyncTargetJoplinServer.newFileApi_({
path: () => Setting.value('sync.9.path'),
username: () => Setting.value('sync.9.username'),
password: () => Setting.value('sync.9.password'),
directory: () => Setting.value('sync.9.directory'),
});
fileApi.setLogger(this.logger());
return fileApi;
}
async initSynchronizer() {
return new Synchronizer(this.db(), await this.fileApi(), Setting.value('appType'));
}
}

View File

@@ -336,6 +336,7 @@ export default class Synchronizer {
let syncLock = null;
try {
await this.api().initialize();
this.api().setTempDirName(Dirnames.Temp);
try {

View File

@@ -0,0 +1,196 @@
import JoplinServerApi from './JoplinServerApi2';
const { dirname, basename } = require('./path-utils');
function removeTrailingColon(path: string) {
if (!path || !path.length) return '';
if (path[path.length - 1] === ':') return path.substr(0, path.length - 1);
return path;
}
// All input paths should be in the format: "SPECIAL_DIR:/path/to/file"
// The trailing colon must not be included as it's automatically added
// when doing the API call.
// Only supported special dir at the moment is "root"
export default class FileApiDriverJoplinServer {
private api_: JoplinServerApi;
public constructor(api: JoplinServerApi) {
this.api_ = api;
}
public async initialize(basePath: string) {
const pieces = removeTrailingColon(basePath).split('/');
if (!pieces.length) return;
let parent = pieces.splice(0, 1)[0];
for (const p of pieces) {
// Syncing with the root, which is ok, and in that
// case there's no sub-dir to create.
if (!p && pieces.length === 1) return;
const subPath = `${parent}/${p}`;
await this.mkdir(subPath);
parent = subPath;
}
}
public api() {
return this.api_;
}
public requestRepeatCount() {
return 3;
}
private metadataToStat_(md: any, path: string, isDeleted: boolean = false) {
const output = {
path: path,
updated_time: md.updated_time,
isDir: !!md.is_directory,
isDeleted: isDeleted,
};
// TODO - HANDLE DELETED
// if (md['.tag'] === 'deleted') output.isDeleted = true;
return output;
}
private metadataToStats_(mds: any[]) {
const output = [];
for (let i = 0; i < mds.length; i++) {
output.push(this.metadataToStat_(mds[i], mds[i].name));
}
return output;
}
private apiFilePath_(p: string) {
if (p !== 'root') p += ':';
return `api/files/${p}`;
}
public async stat(path: string) {
try {
const response = await this.api().exec('GET', this.apiFilePath_(path));
return this.metadataToStat_(response, path);
} catch (error) {
if (error.code === 404) return null;
throw error;
}
}
public async delta(path: string, options: any) {
const context = options ? options.context : null;
let cursor = context ? context.cursor : null;
while (true) {
try {
const query = cursor ? { cursor } : {};
const response = await this.api().exec('GET', `${this.apiFilePath_(path)}/delta`, query);
const stats = response.items.map((item: any) => {
return this.metadataToStat_(item.item, item.item.name, item.type === 3);
});
const output = {
items: stats,
hasMore: response.has_more,
context: { cursor: response.cursor },
};
return output;
} catch (error) {
// If there's an error related to an invalid cursor, clear the cursor and retry.
if (cursor && error.code === 'resyncRequired') {
cursor = null;
continue;
}
throw error;
}
}
}
public async list(path: string, options: any = null) {
options = {
context: null,
...options,
};
const query = options.context?.cursor ? { cursor: options.context.cursor } : null;
const results = await this.api().exec('GET', `${this.apiFilePath_(path)}/children`, query);
const newContext: any = {};
if (results.cursor) newContext.cursor = results.cursor;
return {
items: this.metadataToStats_(results.items),
hasMore: results.has_more,
context: newContext,
} as any;
}
public async get(path: string, options: any) {
if (!options) options = {};
if (!options.responseFormat) options.responseFormat = 'text';
try {
const response = await this.api().exec('GET', `${this.apiFilePath_(path)}/content`, null, null, null, options);
return response;
} catch (error) {
if (error.code !== 404) throw error;
return null;
}
}
private parentPath_(path: string) {
let output = dirname(path);
// This is the root or a special folder
if (output.split('/').length === 1) {
output = output.substr(0, output.length - 1);
}
return output;
}
private basename_(path: string) {
return basename(path);
}
public async mkdir(path: string) {
const parentPath = this.parentPath_(path);
const filename = this.basename_(path);
try {
const response = await this.api().exec('POST', `${this.apiFilePath_(parentPath)}/children`, null, {
name: filename,
is_directory: 1,
});
return response;
} catch (error) {
// 409 is OK - directory already exists
if (error.code !== 409) throw error;
}
}
public async put(path: string, content: any, options: any = null) {
return this.api().exec('PUT', `${this.apiFilePath_(path)}/content`, null, content, {
'Content-Type': 'application/octet-stream',
}, options);
}
public async delete(path: string) {
return this.api().exec('DELETE', this.apiFilePath_(path));
}
public format() {
throw new Error('Not supported');
}
public async clearRoot(path: string) {
await this.delete(path);
await this.mkdir(path);
}
}

View File

@@ -8,6 +8,8 @@ const time = require('./time').default;
const { sprintf } = require('sprintf-js');
const Mutex = require('async-mutex').Mutex;
const logger = Logger.create('FileApi');
function requestCanBeRepeated(error) {
const errorCode = typeof error === 'object' && error.code ? error.code : null;
@@ -61,8 +63,14 @@ class FileApi {
this.remoteDateOffset_ = 0;
this.remoteDateNextCheckTime_ = 0;
this.remoteDateMutex_ = new Mutex();
this.initialized_ = false;
}
async initialize() {
if (this.initialized_) return;
this.initialized_ = true;
if (this.driver_.initialize) return this.driver_.initialize(this.fullPath_(''));
}
async fetchRemoteDateOffset_() {
const tempFile = `${this.tempDirName()}/timeCheck${Math.round(Math.random() * 1000000)}.txt`;
@@ -108,7 +116,7 @@ class FileApi {
this.remoteDateNextCheckTime_ = Date.now() + 10 * 60 * 1000;
}
} catch (error) {
this.logger().warn('Could not retrieve remote date - defaulting to device date:', error);
logger.warn('Could not retrieve remote date - defaulting to device date:', error);
this.remoteDateOffset_ = 0;
this.remoteDateNextCheckTime_ = Date.now() + 60 * 1000;
} finally {
@@ -137,7 +145,7 @@ class FileApi {
}
baseDir() {
return this.baseDir_;
return typeof this.baseDir_ === 'function' ? this.baseDir_() : this.baseDir_;
}
tempDirName() {
@@ -191,7 +199,7 @@ class FileApi {
if (!('includeDirs' in options)) options.includeDirs = true;
if (!('syncItemsOnly' in options)) options.syncItemsOnly = false;
this.logger().debug(`list ${this.baseDir()}`);
logger.debug(`list ${this.baseDir()}`);
const result = await tryAndRepeat(() => this.driver_.list(this.fullPath_(path), options), this.requestRepeatCount());
@@ -216,18 +224,18 @@ class FileApi {
// Deprectated
setTimestamp(path, timestampMs) {
this.logger().debug(`setTimestamp ${this.fullPath_(path)}`);
logger.debug(`setTimestamp ${this.fullPath_(path)}`);
return tryAndRepeat(() => this.driver_.setTimestamp(this.fullPath_(path), timestampMs), this.requestRepeatCount());
// return this.driver_.setTimestamp(this.fullPath_(path), timestampMs);
}
mkdir(path) {
this.logger().debug(`mkdir ${this.fullPath_(path)}`);
logger.debug(`mkdir ${this.fullPath_(path)}`);
return tryAndRepeat(() => this.driver_.mkdir(this.fullPath_(path)), this.requestRepeatCount());
}
async stat(path) {
this.logger().debug(`stat ${this.fullPath_(path)}`);
logger.debug(`stat ${this.fullPath_(path)}`);
const output = await tryAndRepeat(() => this.driver_.stat(this.fullPath_(path)), this.requestRepeatCount());
@@ -246,12 +254,12 @@ class FileApi {
get(path, options = null) {
if (!options) options = {};
if (!options.encoding) options.encoding = 'utf8';
this.logger().debug(`get ${this.fullPath_(path)}`);
logger.debug(`get ${this.fullPath_(path)}`);
return tryAndRepeat(() => this.driver_.get(this.fullPath_(path), options), this.requestRepeatCount());
}
async put(path, content, options = null) {
this.logger().debug(`put ${this.fullPath_(path)}`, options);
logger.debug(`put ${this.fullPath_(path)}`, options);
if (options && options.source === 'file') {
if (!(await this.fsDriver().exists(options.path))) throw new JoplinError(`File not found: ${options.path}`, 'fileNotFound');
@@ -261,13 +269,13 @@ class FileApi {
}
delete(path) {
this.logger().debug(`delete ${this.fullPath_(path)}`);
logger.debug(`delete ${this.fullPath_(path)}`);
return tryAndRepeat(() => this.driver_.delete(this.fullPath_(path)), this.requestRepeatCount());
}
// Deprectated
move(oldPath, newPath) {
this.logger().debug(`move ${this.fullPath_(oldPath)} => ${this.fullPath_(newPath)}`);
logger.debug(`move ${this.fullPath_(oldPath)} => ${this.fullPath_(newPath)}`);
return tryAndRepeat(() => this.driver_.move(this.fullPath_(oldPath), this.fullPath_(newPath)), this.requestRepeatCount());
}
@@ -281,7 +289,7 @@ class FileApi {
}
delta(path, options = null) {
this.logger().debug(`delta ${this.fullPath_(path)}`);
logger.debug(`delta ${this.fullPath_(path)}`);
return tryAndRepeat(() => this.driver_.delta(this.fullPath_(path), options), this.requestRepeatCount());
}
}

View File

@@ -412,6 +412,37 @@ function isSpanStyleItalic(attributes) {
return (style.toLowerCase().includes('font-style:italic;'));
}
function displaySaxWarning(context, message) {
const line = [];
const parser = context ? context._parser : null;
if (parser) {
line.push(`Line ${parser.line}:${parser.column}`);
}
line.push(message);
console.warn(line.join(': '));
}
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
function removeSectionParent(section) {
if (typeof section === 'string') return section;
section = { ...section };
delete section.parent;
section.lines = section.lines.slice();
for (let i = 0; i < section.lines.length; i++) {
section.lines[i] = removeSectionParent(section.lines[i]);
}
return section;
}
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
function printSection(section) {
console.info(JSON.stringify(removeSectionParent(section), null, 4));
}
function enexXmlToMdArray(stream, resources) {
const remainingResources = resources.slice();
@@ -501,18 +532,15 @@ function enexXmlToMdArray(stream, resources) {
currentList.startedText = true;
}
// Note that the order of if/else blocks is important. In
// particular table-related blocks should always be on top and
// take priority over, in particular, hidden blocks. This is so
// that a block that is both table-related and hidden is simply
// handled as table-related. This is to ensure that the table
// structure is valid.
if (n == 'en-note') {
// Start of note
} else if (isInvisibleBlock(nodeAttributes)) {
const newSection = {
type: 'hidden',
lines: [],
parent: section,
};
section.lines.push(newSection);
section = newSection;
} else if (isBlockTag(n)) {
section.lines.push(BLOCK_OPEN);
} else if (n == 'table') {
const newSection = {
type: 'table',
@@ -524,9 +552,20 @@ function enexXmlToMdArray(stream, resources) {
} else if (n == 'tbody' || n == 'thead') {
// Ignore it
} else if (n == 'tr') {
// Note: Even if we encounter tags in the wrong place, we
// create the sections anyway so that the data is imported.
// Invalid HTML like would most likely be from clipped
// pages which would look like a mess in Evernote. So it
// will look like a mess in Joplin too but at least the
// data will be there.
//
// Also if we simply skip the section, it will cause an
// error in drawTable() later on.
//
// https://discourse.joplinapp.org/t/not-all-notes-imported-from-evernote/13056/12?u=laurent
if (section.type != 'table') {
console.warn('Found a <tr> tag outside of a table');
return;
displaySaxWarning(this, 'Found a <tr> tag outside of a table');
// return;
}
const newSection = {
@@ -540,8 +579,8 @@ function enexXmlToMdArray(stream, resources) {
section = newSection;
} else if (n == 'td' || n == 'th') {
if (section.type != 'tr') {
console.warn('Found a <td> tag outside of a <tr>');
return;
displaySaxWarning(this, 'Found a <td> tag outside of a <tr>');
// return;
}
if (n == 'th') section.isHeader = true;
@@ -554,13 +593,37 @@ function enexXmlToMdArray(stream, resources) {
section.lines.push(newSection);
section = newSection;
} else if (n == 'caption') {
if (section.type != 'table') {
displaySaxWarning(this, 'Found a <caption> tag outside of a <table>');
// return;
}
const newSection = {
type: 'caption',
lines: [],
parent: section,
};
section.lines.push(newSection);
section = newSection;
} else if (isInvisibleBlock(nodeAttributes)) {
const newSection = {
type: 'hidden',
lines: [],
parent: section,
};
section.lines.push(newSection);
section = newSection;
} else if (isBlockTag(n)) {
section.lines.push(BLOCK_OPEN);
} else if (isListTag(n)) {
section.lines.push(BLOCK_OPEN);
state.lists.push({ tag: n, counter: 1, startedText: false });
} else if (n == 'li') {
section.lines.push(BLOCK_OPEN);
if (!state.lists.length) {
console.warn('Found <li> tag without being inside a list');
displaySaxWarning(this, 'Found <li> tag without being inside a list');
return;
}
@@ -752,7 +815,7 @@ function enexXmlToMdArray(stream, resources) {
section.lines.push(BLOCK_CLOSE);
} else if (n == 'td' || n == 'th') {
if (section && section.parent) section = section.parent;
} else if (n == 'tr') {
} else if (n == 'tr' || n == 'caption') {
if (section && section.parent) section = section.parent;
} else if (n == 'table') {
if (section && section.parent) section = section.parent;
@@ -950,6 +1013,10 @@ function tableHasSubTables(table) {
for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) {
const td = tr.lines[tdIndex];
// We are inside a CAPTION, not a TD
if (typeof td === 'string') continue;
for (let i = 0; i < td.lines.length; i++) {
if (typeof td.lines[i] === 'object') return true;
}
@@ -975,8 +1042,15 @@ function drawTable(table) {
let lines = [];
lines.push(BLOCK_OPEN);
let headerDone = false;
let caption = null;
for (let trIndex = 0; trIndex < table.lines.length; trIndex++) {
const tr = table.lines[trIndex];
if (tr.type === 'caption') {
caption = tr;
continue;
}
const isHeader = tr.isHeader;
const line = [];
const headerLine = [];
@@ -999,13 +1073,14 @@ function drawTable(table) {
// In here, recursively render the tables
for (let i = 0; i < td.lines.length; i++) {
const c = td.lines[i];
if (typeof c === 'object' && ['table', 'td', 'tr', 'th'].indexOf(c.type) >= 0) {
if (typeof c === 'object' && ['table', 'td', 'tr', 'th', 'caption'].indexOf(c.type) >= 0) {
// This is a table
renderCurrentCells();
currentCells = currentCells.concat(drawTable(c));
} else {
// This is plain text
currentCells.push(c);
// currentCells.push(c);
currentCells = currentCells.concat(renderLine(c));
}
}
@@ -1066,6 +1141,11 @@ function drawTable(table) {
lines.push(BLOCK_CLOSE);
if (caption) {
const captionLines = renderLines(caption.lines);
lines = lines.concat(captionLines);
}
return flatRender ? lines : lines.join(`<<<<:D>>>>${NEWLINE}<<<<:D>>>>`).split('<<<<:D>>>>');
}
@@ -1121,37 +1201,46 @@ function postProcessMarkdown(lines) {
return lines;
}
// A "line" can be some Markdown text, or it can be a section, like a table,
// etc. so this function returns an array of strings.
function renderLine(line) {
if (typeof line === 'object' && line.type === 'table') {
// A table
const table = line;
return drawTable(table);
} else if (typeof line === 'object' && line.type === 'code') {
return line.lines;
} else if (typeof line === 'object' && line.type === 'hidden') {
// ENEX notes sometimes have hidden tags. We could strip off these
// sections but in the spirit of preserving all data we wrap them in
// a hidden tag too.
let hiddenLines = ['<div style="display: none;">'];
hiddenLines = hiddenLines.concat(renderLines(line.lines));
hiddenLines.push('</div>');
return hiddenLines;
} else if (typeof line === 'object') {
console.warn('Unhandled object type:', line);
return line.lines;
} else {
// an actual line
return [line];
}
}
function renderLines(lines) {
let mdLines = [];
for (let i = 0; i < lines.length; i++) {
const renderedLines = renderLine(lines[i]);
mdLines = mdLines.concat(renderedLines);
}
return mdLines;
}
async function enexXmlToMd(xmlString, resources, options = {}) {
const stream = stringToStream(xmlString);
const result = await enexXmlToMdArray(stream, resources, options);
let mdLines = [];
for (let i = 0; i < result.content.lines.length; i++) {
const line = result.content.lines[i];
if (typeof line === 'object' && line.type === 'table') {
// A table
const table = line;
const tableLines = drawTable(table);
mdLines = mdLines.concat(tableLines);
} else if (typeof line === 'object' && line.type === 'code') {
mdLines = mdLines.concat(line.lines);
} else if (typeof line === 'object' && line.type === 'hidden') {
// ENEX notes sometimes have hidden tags. We could strip off these
// sections but in the spirit of preserving all data we wrap them in
// a hidden tag too.
let hiddenLines = ['<div style="display: none;">'];
hiddenLines = hiddenLines.concat(line.lines);
hiddenLines.push('</div>');
mdLines = mdLines.concat(hiddenLines);
} else if (typeof line === 'object') {
console.warn('Unhandled object type:', line);
mdLines = mdLines.concat(line.lines);
} else {
// an actual line
mdLines.push(line);
}
}
let mdLines = renderLines(result.content.lines);
let firstAttachment = true;
for (let i = 0; i < result.resources.length; i++) {

View File

@@ -1,5 +1,6 @@
import shim from '../shim';
import { _, supportedLocalesToLanguages, defaultLocale } from '../locale';
import { ltrimSlashes } from '../path-utils';
const BaseModel = require('../BaseModel').default;
const { Database } = require('../database.js');
const SyncTargetRegistry = require('../SyncTargetRegistry.js');
@@ -309,6 +310,52 @@ class Setting extends BaseModel {
secure: true,
},
'sync.9.path': {
value: '',
type: SettingItemType.String,
section: 'sync',
show: (settings: any) => {
return settings['sync.target'] == SyncTargetRegistry.nameToId('joplinServer');
},
public: true,
label: () => _('Joplin Server URL'),
description: () => emptyDirWarning,
},
'sync.9.directory': {
value: 'Apps/Joplin',
type: SettingItemType.String,
section: 'sync',
show: (settings: any) => {
return settings['sync.target'] == SyncTargetRegistry.nameToId('joplinServer');
},
filter: value => {
return value ? ltrimSlashes(rtrimSlashes(value)) : '';
},
public: true,
label: () => _('Joplin Server Directory'),
},
'sync.9.username': {
value: '',
type: SettingItemType.String,
section: 'sync',
show: (settings: any) => {
return settings['sync.target'] == SyncTargetRegistry.nameToId('joplinServer');
},
public: true,
label: () => _('Joplin Server username'),
},
'sync.9.password': {
value: '',
type: SettingItemType.String,
section: 'sync',
show: (settings: any) => {
return settings['sync.target'] == SyncTargetRegistry.nameToId('joplinServer');
},
public: true,
label: () => _('Joplin Server password'),
secure: true,
},
'sync.5.syncTargets': { value: {}, type: SettingItemType.Object, public: false },
'sync.resourceDownloadMode': {
@@ -333,6 +380,7 @@ class Setting extends BaseModel {
'sync.3.auth': { value: '', type: SettingItemType.String, public: false },
'sync.4.auth': { value: '', type: SettingItemType.String, public: false },
'sync.7.auth': { value: '', type: SettingItemType.String, public: false },
'sync.9.auth': { value: '', type: SettingItemType.String, public: false },
'sync.1.context': { value: '', type: SettingItemType.String, public: false },
'sync.2.context': { value: '', type: SettingItemType.String, public: false },
'sync.3.context': { value: '', type: SettingItemType.String, public: false },
@@ -341,6 +389,7 @@ class Setting extends BaseModel {
'sync.6.context': { value: '', type: SettingItemType.String, public: false },
'sync.7.context': { value: '', type: SettingItemType.String, public: false },
'sync.8.context': { value: '', type: SettingItemType.String, public: false },
'sync.9.context': { value: '', type: SettingItemType.String, public: false },
'sync.maxConcurrentConnections': { value: 5, type: SettingItemType.Int, public: true, advanced: true, section: 'sync', label: () => _('Max concurrent connections'), minimum: 1, maximum: 20, step: 1 },
@@ -793,7 +842,6 @@ class Setting extends BaseModel {
type: SettingItemType.Bool,
public: true,
appTypes: ['desktop'],
advanced: true,
label: () => 'Enable spell checking in Markdown editor? (WARNING BETA feature)',
description: () => 'Spell checker in the Markdown editor was previously unstable (cursor location was not stable, sometimes edits would not be saved or reflected in the viewer, etc.) however it appears to be more reliable now. If you notice any issue, please report it on GitHub or the Joplin Forum (Help -> Joplin Forum)',
},

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/lib",
"version": "1.0.14",
"version": "1.0.15",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/lib",
"version": "1.0.14",
"version": "1.0.15",
"description": "Joplin Core library",
"author": "Laurent Cozic",
"homepage": "",
@@ -22,11 +22,11 @@
"typescript": "^4.0.5"
},
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.13",
"@joplin/fork-sax": "^1.2.17",
"@joplin/renderer": "^1.0.22",
"@joplin/turndown": "^4.0.35",
"@joplin/turndown-plugin-gfm": "^1.0.17",
"@joplin/fork-htmlparser2": "^4.1.14",
"@joplin/fork-sax": "^1.2.18",
"@joplin/renderer": "^1.0.23",
"@joplin/turndown": "^4.0.36",
"@joplin/turndown-plugin-gfm": "^1.0.18",
"async-mutex": "^0.1.3",
"aws-sdk": "^2.588.0",
"base-64": "^0.1.0",

View File

@@ -176,10 +176,10 @@ export default class SpellCheckerService {
},
// Can be removed once it does work
{
label: '⚠ Spell checker doesn\'t work in Markdown editor ⚠',
enabled: false,
},
// {
// label: '⚠ Spell checker doesn\'t work in Markdown editor ⚠',
// enabled: false,
// },
{
type: 'separator',

View File

@@ -181,7 +181,7 @@ const shim = {
throw new Error('Not implemented');
},
uploadBlob: () => {
uploadBlob: (_url: string, _options: any) => {
throw new Error('Not implemented');
},

View File

@@ -109,7 +109,7 @@ class Time {
}
msleep(ms: number) {
return new Promise((resolve) => {
return new Promise((resolve: Function) => {
shim.setTimeout(() => {
resolve();
}, ms);

View File

@@ -1,6 +1,11 @@
const createUuidV4 = require('uuid/v4');
const { customAlphabet } = require('nanoid/non-secure');
// https://zelark.github.io/nano-id-cc/
// https://security.stackexchange.com/a/41749/1873
// > On the other hand, 128 bits (between 21 and 22 characters
// > alphanumeric) is beyond the reach of brute-force attacks pretty much
// > indefinitely
const nanoid = customAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 22);
export default {

View File

@@ -272,6 +272,11 @@ function renderToStringWithCache(latex: string, katexOptions: any) {
}
}
function renderKatexError(latex: string, error: any): string {
console.error('Katex error for:', latex, error);
return `<div class="inline-code">${error.message}</div>`;
}
export default {
plugin: function(markdownIt: any, options: RuleOptions) {
// Keep macros that persist across Katex blocks to allow defining a macro
@@ -286,12 +291,13 @@ export default {
// set KaTeX as the renderer for markdown-it-simplemath
const katexInline = function(latex: string) {
katexOptions.displayMode = false;
let outputHtml: string = '';
try {
return `<span class="joplin-editable"><span class="joplin-source" data-joplin-language="katex" data-joplin-source-open="$" data-joplin-source-close="$">${markdownIt.utils.escapeHtml(latex)}</span>${renderToStringWithCache(latex, katexOptions)}</span>`;
outputHtml = renderToStringWithCache(latex, katexOptions);
} catch (error) {
console.error('Katex error for:', latex, error);
return latex;
outputHtml = renderKatexError(latex, error);
}
return `<span class="joplin-editable"><span class="joplin-source" data-joplin-language="katex" data-joplin-source-open="$" data-joplin-source-close="$">${markdownIt.utils.escapeHtml(latex)}</span>${outputHtml}</span>`;
};
const inlineRenderer = function(tokens: any[], idx: number) {
@@ -300,12 +306,13 @@ export default {
const katexBlock = function(latex: string) {
katexOptions.displayMode = true;
let outputHtml: string = '';
try {
return `<div class="joplin-editable"><pre class="joplin-source" data-joplin-language="katex" data-joplin-source-open="$$&#10;" data-joplin-source-close="&#10;$$&#10;">${markdownIt.utils.escapeHtml(latex)}</pre>${renderToStringWithCache(latex, katexOptions)}</div>`;
outputHtml = renderToStringWithCache(latex, katexOptions);
} catch (error) {
console.error('Katex error for:', latex, error);
return latex;
outputHtml = renderKatexError(latex, error);
}
return `<div class="joplin-editable"><pre class="joplin-source" data-joplin-language="katex" data-joplin-source-open="$$&#10;" data-joplin-source-close="&#10;$$&#10;">${markdownIt.utils.escapeHtml(latex)}</pre>${outputHtml}</div>`;
};
const blockRenderer = function(tokens: any[], idx: number) {

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/renderer",
"version": "1.0.22",
"version": "1.0.23",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@joplin/renderer",
"version": "1.0.22",
"version": "1.0.23",
"description": "The Joplin note renderer, used the mobile and desktop application",
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/renderer",
"main": "index.js",
@@ -24,7 +24,7 @@
"typescript": "^4.0.5"
},
"dependencies": {
"@joplin/fork-htmlparser2": "^4.1.13",
"@joplin/fork-htmlparser2": "^4.1.14",
"font-awesome-filetypes": "^2.1.0",
"fs-extra": "^8.1.0",
"highlight.js": "^10.2.1",

9
packages/server/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
node_modules/
dist
db.sqlite
db-*.sqlite
*.sqlite-journal
*.pid
logs/
tests/temp/
temp/

66
packages/server/README.md Normal file
View File

@@ -0,0 +1,66 @@
# Installing
## Configuration
First copy `.env-sample` to `.env` and edit the values in there:
- `JOPLIN_BASE_URL`: This is the base public URL where the service will be running. For example, if you want it to run from `https://example.com/joplin`, this is what you should set the URL to. The base URL can include the port.
- `JOPLIN_PORT`: The local port on which the Docker container will listen. You would typically map this port to 443 (TLS) with a reverse proxy.
## Install application
```shell
git clone https://github.com/laurent22/joplin
cd joplin
npm install
docker-compose --file docker-compose.server.yml up --detach
```
This will start the server, which will listen on port **22300** on **localhost**.
Due to the restart policy defined in the docker-compose file, the server will be restarted automatically whenever the host reboots.
## Setup reverse proxy
You will then need to expose this server to the internet by setting up a reverse proxy, and that will depend on how your server is currently configured, and whether you already have Nginx or Apache running:
- [Apache Reverse Proxy](https://httpd.apache.org/docs/current/mod/mod_proxy.html)
- [Nginx Reverse Proxy](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/)
## Setup admin user
For the following instructions, we'll assume that the Joplin server is running on `https://example.com/joplin`.
By default, the instance will be setup with an admin user with email **admin@localhost** and password **admin** and you should change this by opening the admin UI. To do so, open `https://example.com/joplin/login`. From there, go to Profile and change the admin password.
## Setup a user for sync
While the admin user can be used for synchronisation, it is recommended to create a separate non-admin user for it. To do, open the admin UI and navigate to the Users page - from there you can create a new user.
Once this is done, you can use the email and password you specified to sync this user account with your Joplin clients.
## Checking the logs
Checking the log can be done the standard Docker way:
```shell
docker-compose --file docker-compose.server.yml logs
```
# Set up for development
## Setting up the database
### SQLite
The server supports SQLite for development and test units. To use it, open `src/config-dev.ts` and uncomment the sqlite3 config.
### PostgreSQL
It's best to use PostgreSQL as this is what is used in production, however it requires Docker.
To use it, from the monorepo root, run `docker-compose --file docker-compose.server-dev.yml up`, which will start the PostgreSQL database.
## Starting the server
From `packages/server`, run `npm run start-dev`

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,14 @@
module.exports = {
testMatch: [
'**/*.test.js',
],
testPathIgnorePatterns: [
'<rootDir>/node_modules/',
'<rootDir>/assets/',
],
testEnvironment: 'node',
slowTestThreshold: 20,
};

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