Compare commits
35 Commits
v1.5.9
...
server_fil
Author | SHA1 | Date | |
---|---|---|---|
|
8801e82cab | ||
|
c4757d6c60 | ||
|
af6c79b844 | ||
|
0e0de1207f | ||
|
2fda067034 | ||
|
29177330b0 | ||
|
66a5490b54 | ||
|
469cd19ec1 | ||
|
41684a64ef | ||
|
2cd7839552 | ||
|
c3d4617612 | ||
|
158fafc4a0 | ||
|
0f59731c06 | ||
|
d0f1a67d96 | ||
|
2a1434f987 | ||
|
1ee177880d | ||
|
f6d899eb29 | ||
|
a97f25fd61 | ||
|
325a5ab08f | ||
|
c158878b66 | ||
|
0f0f9c1161 | ||
|
79612163b2 | ||
|
fab5ed165c | ||
|
4897c763bd | ||
|
17b9867bf2 | ||
|
b8f14d50f5 | ||
|
9e2f60523f | ||
|
321ff4fced | ||
|
2a31f914bb | ||
|
0b71c33d09 | ||
|
502c812d9c | ||
|
5dc3baa50c | ||
|
a9af58146b | ||
|
17edebb6b1 | ||
|
bb2855bd80 |
9
.dockerignore
Normal 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
@@ -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
|
220
.eslintignore
@@ -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
|
||||
|
@@ -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
@@ -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
|
||||
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 113 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 134 KiB |
BIN
Assets/macOsIcon_2.psd
Normal file
3
Dockerfile.db
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM postgres:13.1
|
||||
|
||||
EXPOSE 5432
|
71
Dockerfile.server
Normal 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" ]
|
@@ -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
|
||||
|
||||
|
28
docker-compose.server-dev.yml
Normal 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
@@ -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
|
@@ -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">
|
||||
|
@@ -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">
|
||||
|
@@ -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's to enable clojure highlighting in code blocks. <code>codeMirrorResources</code> would be set to <code>['mode/clojure/clojure']</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's to enable clojure highlighting in code blocks.
|
||||
<code>codeMirrorResources</code> would be set to <code>['mode/clojure/clojure']</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>{'lineNumbers': 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>{'lineNumbers': 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'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'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'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'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> {
|
||||
|
@@ -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"> = "folderContextMenu"</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"> = "noteListContextMenu"</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"> = "tagContextMenu"</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>
|
||||
|
@@ -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. "notes", "folders", "tags", etc.)
|
||||
[1]: (Optional) Resource ID.
|
||||
[2]: (Optional) Resource link.</p>
|
||||
<ul>
|
||||
<li><strong>[0]</strong>: Resource name (eg. "notes", "folders", "tags", 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>
|
||||
|
@@ -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>
|
||||
|
@@ -142,7 +142,7 @@
|
||||
<td>"oneNoteSelected && !inConflictFolder"</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
<p>Currently the supported context variables aren'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'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>
|
||||
|
@@ -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 "_"</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 -> 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>
|
||||
|
@@ -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>
|
||||
|
@@ -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 -> Text editor command.</p>
|
||||
<p>Some example configurations are: (comments after #)</p>
|
||||
|
@@ -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 "JoplinProfile" 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 "JoplinProfile" 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>
|
||||
|
@@ -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>
|
||||
|
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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": {
|
||||
|
@@ -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`);
|
||||
|
2
packages/app-cli/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "joplin",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -31,7 +31,7 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.0",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
|
@@ -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 });
|
||||
|
21
packages/app-cli/tests/enex_to_md/invisible_text2.html
Normal 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&t=nmp&midToken=AQFSKz2SrPsC9Q&tracking=eml-comm_nu_digest-see_all_updates&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>
|
11
packages/app-cli/tests/enex_to_md/invisible_text2.md
Normal 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>
|
11
packages/app-cli/tests/enex_to_md/tableWithCaption.html
Normal 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>
|
5
packages/app-cli/tests/enex_to_md/tableWithCaption.md
Normal file
@@ -0,0 +1,5 @@
|
||||
| Video Segments | Download |
|
||||
| --- | --- |
|
||||
| one | two |
|
||||
|
||||
MP4
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -1,2 +1,3 @@
|
||||
dist/*
|
||||
node_modules/
|
||||
*.jpl
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -20,4 +20,4 @@
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.11"
|
||||
}
|
||||
}
|
||||
}
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
@@ -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 };
|
||||
|
@@ -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'",
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
17
packages/app-desktop/gui/utils/convertToScreenCoordinates.ts
Normal 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}`);
|
||||
}
|
2
packages/app-desktop/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "1.5.9",
|
||||
"version": "1.6.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -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,
|
||||
|
@@ -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"
|
||||
}
|
||||
|
@@ -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",
|
||||
|
2
packages/fork-htmlparser2/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/fork-htmlparser2",
|
||||
"version": "4.1.13",
|
||||
"version": "4.1.14",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -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"
|
||||
|
2
packages/fork-sax/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/fork-sax",
|
||||
"version": "1.2.17",
|
||||
"version": "1.2.18",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -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"
|
||||
|
@@ -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 %>",
|
||||
|
2
packages/generator-joplin/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "generator-joplin",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.3",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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);
|
||||
|
174
packages/lib/JoplinServerApi2.ts
Normal 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;
|
||||
}
|
||||
}
|
@@ -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');
|
||||
|
91
packages/lib/SyncTargetJoplinServer.ts
Normal 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'));
|
||||
}
|
||||
}
|
@@ -336,6 +336,7 @@ export default class Synchronizer {
|
||||
let syncLock = null;
|
||||
|
||||
try {
|
||||
await this.api().initialize();
|
||||
this.api().setTempDirName(Dirnames.Temp);
|
||||
|
||||
try {
|
||||
|
196
packages/lib/file-api-driver-joplinServer.ts
Normal 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);
|
||||
}
|
||||
}
|
@@ -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());
|
||||
}
|
||||
}
|
||||
|
@@ -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++) {
|
||||
|
@@ -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)',
|
||||
},
|
||||
|
2
packages/lib/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/lib",
|
||||
"version": "1.0.14",
|
||||
"version": "1.0.15",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -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",
|
||||
|
@@ -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',
|
||||
|
@@ -181,7 +181,7 @@ const shim = {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
uploadBlob: () => {
|
||||
uploadBlob: (_url: string, _options: any) => {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
|
@@ -109,7 +109,7 @@ class Time {
|
||||
}
|
||||
|
||||
msleep(ms: number) {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise((resolve: Function) => {
|
||||
shim.setTimeout(() => {
|
||||
resolve();
|
||||
}, ms);
|
||||
|
@@ -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 {
|
||||
|
@@ -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="$$ " data-joplin-source-close=" $$ ">${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="$$ " data-joplin-source-close=" $$ ">${markdownIt.utils.escapeHtml(latex)}</pre>${outputHtml}</div>`;
|
||||
};
|
||||
|
||||
const blockRenderer = function(tokens: any[], idx: number) {
|
||||
|
2
packages/renderer/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/renderer",
|
||||
"version": "1.0.22",
|
||||
"version": "1.0.23",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -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
@@ -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
@@ -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`
|
BIN
packages/server/assets/tests/photo.jpg
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
packages/server/assets/tests/poster.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
14
packages/server/jest.config.js
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
testMatch: [
|
||||
'**/*.test.js',
|
||||
],
|
||||
|
||||
testPathIgnorePatterns: [
|
||||
'<rootDir>/node_modules/',
|
||||
'<rootDir>/assets/',
|
||||
],
|
||||
|
||||
testEnvironment: 'node',
|
||||
|
||||
slowTestThreshold: 20,
|
||||
};
|