mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-10-31 04:51:31 +08:00 
			
		
		
		
	Merge remote-tracking branch 'origin/next58' into next58
# Conflicts: # src/public/app/components/note_context.js
This commit is contained in:
		
						commit
						1f468f81cc
					
				| @ -30,6 +30,7 @@ rm -r $BUILD_DIR/swiftshader | |||||||
| cp bin/tpl/anonymize-database.sql $BUILD_DIR/ | cp bin/tpl/anonymize-database.sql $BUILD_DIR/ | ||||||
| 
 | 
 | ||||||
| cp -r dump-db $BUILD_DIR/ | cp -r dump-db $BUILD_DIR/ | ||||||
|  | rm -rf $BUILD_DIR/dump-db/node_modules | ||||||
| 
 | 
 | ||||||
| cp bin/tpl/trilium-portable.sh $BUILD_DIR/ | cp bin/tpl/trilium-portable.sh $BUILD_DIR/ | ||||||
| chmod 755 $BUILD_DIR/trilium-portable.sh | chmod 755 $BUILD_DIR/trilium-portable.sh | ||||||
|  | |||||||
| @ -26,6 +26,7 @@ mv "./dist/Trilium Notes-darwin-x64" $BUILD_DIR | |||||||
| cp bin/tpl/anonymize-database.sql $BUILD_DIR/ | cp bin/tpl/anonymize-database.sql $BUILD_DIR/ | ||||||
| 
 | 
 | ||||||
| cp -r dump-db $BUILD_DIR/ | cp -r dump-db $BUILD_DIR/ | ||||||
|  | rm -rf $BUILD_DIR/dump-db/node_modules | ||||||
| 
 | 
 | ||||||
| echo "Zipping mac x64 electron distribution..." | echo "Zipping mac x64 electron distribution..." | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -31,6 +31,7 @@ chmod 755 $PKG_DIR/trilium.sh | |||||||
| cp bin/tpl/anonymize-database.sql $PKG_DIR/ | cp bin/tpl/anonymize-database.sql $PKG_DIR/ | ||||||
| 
 | 
 | ||||||
| cp -r dump-db $PKG_DIR/ | cp -r dump-db $PKG_DIR/ | ||||||
|  | rm -rf $PKG_DIR/dump-db/node_modules | ||||||
| 
 | 
 | ||||||
| VERSION=`jq -r ".version" package.json` | VERSION=`jq -r ".version" package.json` | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -28,6 +28,7 @@ rm -r $BUILD_DIR/swiftshader | |||||||
| cp bin/tpl/anonymize-database.sql $BUILD_DIR/ | cp bin/tpl/anonymize-database.sql $BUILD_DIR/ | ||||||
| 
 | 
 | ||||||
| cp -r dump-db $BUILD_DIR/ | cp -r dump-db $BUILD_DIR/ | ||||||
|  | rm -rf $BUILD_DIR/dump-db/node_modules | ||||||
| 
 | 
 | ||||||
| cp bin/tpl/trilium-{portable,no-cert-check,safe-mode}.{bat,ps1} $BUILD_DIR/ | cp bin/tpl/trilium-{portable,no-cert-check,safe-mode}.{bat,ps1} $BUILD_DIR/ | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -14,9 +14,6 @@ mkdir $DIR | |||||||
| 
 | 
 | ||||||
| echo "Copying Trilium to build directory $DIR" | echo "Copying Trilium to build directory $DIR" | ||||||
| 
 | 
 | ||||||
| cp -r dump-db $DIR/ |  | ||||||
| rm -rf $DIR/dump-db/node_modules |  | ||||||
| 
 |  | ||||||
| cp -r images $DIR/ | cp -r images $DIR/ | ||||||
| cp -r libraries $DIR/ | cp -r libraries $DIR/ | ||||||
| cp -r src $DIR/ | cp -r src $DIR/ | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| module.exports = () => { | module.exports = () => { | ||||||
|     const hiddenSubtreeService = require('../../src/services/hidden_subtree.js'); |     const hiddenSubtreeService = require('../../src/services/hidden_subtree'); | ||||||
|     const cls = require("../../src/services/cls"); |     const cls = require("../../src/services/cls"); | ||||||
|     const beccaLoader = require("../../src/becca/becca_loader"); |     const beccaLoader = require("../../src/becca/becca_loader"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| const yargs = require('yargs/yargs') | const yargs = require('yargs/yargs') | ||||||
| const { hideBin } = require('yargs/helpers') | const { hideBin } = require('yargs/helpers') | ||||||
| const dumpService = require("./inc/dump.js"); | const dumpService = require("./inc/dump"); | ||||||
| 
 | 
 | ||||||
| yargs(hideBin(process.argv)) | yargs(hideBin(process.argv)) | ||||||
|     .command('$0 <path_to_document> <target_directory>', 'dump the contents of document.db into the target directory', (yargs) => { |     .command('$0 <path_to_document> <target_directory>', 'dump the contents of document.db into the target directory', (yargs) => { | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| const crypto = require("crypto"); | const crypto = require("crypto"); | ||||||
| const sql = require("./sql.js"); | const sql = require("./sql"); | ||||||
| const decryptService = require("./decrypt.js"); | const decryptService = require("./decrypt"); | ||||||
| 
 | 
 | ||||||
| function getDataKey(password) { | function getDataKey(password) { | ||||||
|     if (!password) { |     if (!password) { | ||||||
|  | |||||||
| @ -1,9 +1,9 @@ | |||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
| const sanitize = require("sanitize-filename"); | const sanitize = require("sanitize-filename"); | ||||||
| const sql = require("./sql.js"); | const sql = require("./sql"); | ||||||
| const decryptService = require("./decrypt.js"); | const decryptService = require("./decrypt"); | ||||||
| const dataKeyService = require("./data_key.js"); | const dataKeyService = require("./data_key"); | ||||||
| const extensionService = require("./extension.js"); | const extensionService = require("./extension"); | ||||||
| 
 | 
 | ||||||
| function dumpDocument(documentPath, targetPath, options) { | function dumpDocument(documentPath, targetPath, options) { | ||||||
|     const stats = { |     const stats = { | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								libraries/boxicons/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								libraries/boxicons/LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | The MIT License (MIT) | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2015-2021 Aniket Suvarna | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
										
											Binary file not shown.
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -5,7 +5,6 @@ | |||||||
|   "requires": true, |   "requires": true, | ||||||
|   "packages": { |   "packages": { | ||||||
|     "": { |     "": { | ||||||
|       "name": "trilium", |  | ||||||
|       "version": "0.57.3", |       "version": "0.57.3", | ||||||
|       "hasInstallScript": true, |       "hasInstallScript": true, | ||||||
|       "license": "AGPL-3.0-only", |       "license": "AGPL-3.0-only", | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ const Branch = require('../../src/becca/entities/branch'); | |||||||
| const SearchContext = require('../../src/services/search/search_context'); | const SearchContext = require('../../src/services/search/search_context'); | ||||||
| const dateUtils = require('../../src/services/date_utils'); | const dateUtils = require('../../src/services/date_utils'); | ||||||
| const becca = require('../../src/becca/becca'); | const becca = require('../../src/becca/becca'); | ||||||
| const {NoteBuilder, findNoteByTitle, note} = require('./becca_mocking.js'); | const {NoteBuilder, findNoteByTitle, note} = require('./becca_mocking'); | ||||||
| 
 | 
 | ||||||
| describe("Search", () => { | describe("Search", () => { | ||||||
|     let rootNote; |     let rootNote; | ||||||
|  | |||||||
| @ -1,4 +1,4 @@ | |||||||
| const {note} = require('./becca_mocking.js'); | const {note} = require('./becca_mocking'); | ||||||
| const ValueExtractor = require('../../src/services/search/value_extractor'); | const ValueExtractor = require('../../src/services/search/value_extractor'); | ||||||
| const becca = require('../../src/becca/becca'); | const becca = require('../../src/becca/becca'); | ||||||
| const SearchContext = require("../../src/services/search/search_context"); | const SearchContext = require("../../src/services/search/search_context"); | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const becca = require('./becca.js'); | const becca = require('./becca'); | ||||||
| const cls = require('../services/cls'); | const cls = require('../services/cls'); | ||||||
| const protectedSessionService = require('../services/protected_session'); | const protectedSessionService = require('../services/protected_session'); | ||||||
| const log = require('../services/log'); | const log = require('../services/log'); | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ const Note = require('./note'); | |||||||
| const AbstractEntity = require("./abstract_entity"); | const AbstractEntity = require("./abstract_entity"); | ||||||
| const sql = require("../../services/sql"); | const sql = require("../../services/sql"); | ||||||
| const dateUtils = require("../../services/date_utils"); | const dateUtils = require("../../services/date_utils"); | ||||||
| const utils = require("../../services/utils.js"); | const utils = require("../../services/utils"); | ||||||
| const TaskContext = require("../../services/task_context"); | const TaskContext = require("../../services/task_context"); | ||||||
| const cls = require("../../services/cls"); | const cls = require("../../services/cls"); | ||||||
| const log = require("../../services/log"); | const log = require("../../services/log"); | ||||||
|  | |||||||
| @ -1329,7 +1329,7 @@ class Note extends AbstractEntity { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     isLaunchBarConfig() { |     isLaunchBarConfig() { | ||||||
|         return this.type === 'launcher' || ['lbRoot', 'lbAvailableShortcuts', 'lbVisibleShortcuts']; |         return this.type === 'launcher' || ['lbRoot', 'lbAvailableLaunchers', 'lbVisibleLaunchers'].includes(this.noteId); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     isOptions() { |     isOptions() { | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| const becca = require('./becca'); | const becca = require('./becca'); | ||||||
| const log = require('../services/log'); | const log = require('../services/log'); | ||||||
| const beccaService = require('./becca_service.js'); | const beccaService = require('./becca_service'); | ||||||
| const dateUtils = require('../services/date_utils'); | const dateUtils = require('../services/date_utils'); | ||||||
| const { JSDOM } = require("jsdom"); | const { JSDOM } = require("jsdom"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								src/errors/not_found_error.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/errors/not_found_error.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | class NotFoundError { | ||||||
|  |     constructor(message) { | ||||||
|  |         this.message = message; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = NotFoundError; | ||||||
							
								
								
									
										7
									
								
								src/errors/validation_error.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/errors/validation_error.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | class ValidationError { | ||||||
|  |     constructor(message) { | ||||||
|  |         this.message = message; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | module.exports = ValidationError; | ||||||
| @ -1,5 +1,5 @@ | |||||||
| const appInfo = require('../services/app_info'); | const appInfo = require('../services/app_info'); | ||||||
| const eu = require("./etapi_utils.js"); | const eu = require("./etapi_utils"); | ||||||
| 
 | 
 | ||||||
| function register(router) { | function register(router) { | ||||||
|     eu.route(router, 'get', '/etapi/app-info', (req, res, next) => { |     eu.route(router, 'get', '/etapi/app-info', (req, res, next) => { | ||||||
|  | |||||||
| @ -63,7 +63,10 @@ class NoteContext extends Component { | |||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (this.hoistedNoteId === 'root' && this.notePath.startsWith("root/hidden")) { |         if (this.hoistedNoteId === 'root' | ||||||
|  |             && this.notePath.startsWith("root/hidden") | ||||||
|  |             && !this.note.hasLabel("keepCurrentHoisting") | ||||||
|  |         ) { | ||||||
|             // hidden subtree displays only when hoisted so it doesn't make sense to keep root as hoisted note
 |             // hidden subtree displays only when hoisted so it doesn't make sense to keep root as hoisted note
 | ||||||
| 
 | 
 | ||||||
|             let hoistedNoteId = 'hidden'; |             let hoistedNoteId = 'hidden'; | ||||||
|  | |||||||
| @ -828,7 +828,7 @@ class NoteShort { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     isLaunchBarConfig() { |     isLaunchBarConfig() { | ||||||
|         return this.type === 'launcher' || ['lbRoot', 'lbAvailableShortcuts', 'lbVisibleShortcuts'].includes(this.noteId); |         return this.type === 'launcher' || ['lbRoot', 'lbAvailableLaunchers', 'lbVisibleLaunchers'].includes(this.noteId); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     isOptions() { |     isOptions() { | ||||||
|  | |||||||
| @ -90,27 +90,27 @@ function getNotePathFromLink($link) { | |||||||
|     return url ? getNotePathFromUrl(url) : null; |     return url ? getNotePathFromUrl(url) : null; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function goToLink(e) { | function goToLink(evt) { | ||||||
|     const $link = $(e.target).closest("a,.block-link"); |     const $link = $(evt.target).closest("a,.block-link"); | ||||||
|     const address = $link.attr('href'); |     const address = $link.attr('href'); | ||||||
| 
 | 
 | ||||||
|     if (address?.startsWith("data:")) { |     if (address?.startsWith("data:")) { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     e.preventDefault(); |     evt.preventDefault(); | ||||||
|     e.stopPropagation(); |     evt.stopPropagation(); | ||||||
| 
 | 
 | ||||||
|     const notePath = getNotePathFromLink($link); |     const notePath = getNotePathFromLink($link); | ||||||
| 
 | 
 | ||||||
|     const ctrlKey = (!utils.isMac() && e.ctrlKey) || (utils.isMac() && e.metaKey); |     const ctrlKey = utils.isCtrlKey(evt); | ||||||
| 
 | 
 | ||||||
|     if (notePath) { |     if (notePath) { | ||||||
|         if ((e.which === 1 && ctrlKey) || e.which === 2) { |         if ((evt.which === 1 && ctrlKey) || evt.which === 2) { | ||||||
|             appContext.tabManager.openTabWithNoteWithHoisting(notePath); |             appContext.tabManager.openTabWithNoteWithHoisting(notePath); | ||||||
|         } |         } | ||||||
|         else if (e.which === 1) { |         else if (evt.which === 1) { | ||||||
|             const ntxId = $(e.target).closest("[data-ntx-id]").attr("data-ntx-id"); |             const ntxId = $(evt.target).closest("[data-ntx-id]").attr("data-ntx-id"); | ||||||
| 
 | 
 | ||||||
|             const noteContext = ntxId |             const noteContext = ntxId | ||||||
|                 ? appContext.tabManager.getNoteContextById(ntxId) |                 ? appContext.tabManager.getNoteContextById(ntxId) | ||||||
| @ -124,7 +124,7 @@ function goToLink(e) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         if ((e.which === 1 && ctrlKey) || e.which === 2 |         if ((evt.which === 1 && ctrlKey) || evt.which === 2 | ||||||
|             || $link.hasClass("ck-link-actions__preview") // within edit link dialog single click suffices
 |             || $link.hasClass("ck-link-actions__preview") // within edit link dialog single click suffices
 | ||||||
|             || $link.closest("[contenteditable]").length === 0 // outside of CKEditor single click suffices
 |             || $link.closest("[contenteditable]").length === 0 // outside of CKEditor single click suffices
 | ||||||
|         ) { |         ) { | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import utils from './utils.js'; | import utils from './utils.js'; | ||||||
|  | import ValidationError from "./validation_error.js"; | ||||||
| 
 | 
 | ||||||
| const REQUEST_LOGGING_ENABLED = false; | const REQUEST_LOGGING_ENABLED = false; | ||||||
| 
 | 
 | ||||||
| @ -102,10 +103,15 @@ async function call(method, url, data, headers = {}) { | |||||||
|     return resp.body; |     return resp.body; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function reportError(method, url, status, error) { | async function reportError(method, url, status, response) { | ||||||
|     const message = "Error when calling " + method + " " + url + ": " + status + " - " + error; |  | ||||||
| 
 |  | ||||||
|     const toastService = (await import("./toast.js")).default; |     const toastService = (await import("./toast.js")).default; | ||||||
|  | 
 | ||||||
|  |     if ([400, 404].includes(status) && response && typeof response === 'object') { | ||||||
|  |         toastService.showError(response.message); | ||||||
|  |         throw new ValidationError(response); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const message = "Error when calling " + method + " " + url + ": " + status + " - " + responseText; | ||||||
|     toastService.showError(message); |     toastService.showError(message); | ||||||
|     toastService.throwError(message); |     toastService.throwError(message); | ||||||
| } | } | ||||||
|  | |||||||
| @ -60,6 +60,11 @@ function isMac() { | |||||||
|     return navigator.platform.indexOf('Mac') > -1; |     return navigator.platform.indexOf('Mac') > -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | function isCtrlKey(evt) { | ||||||
|  |     return (!isMac() && evt.ctrlKey) | ||||||
|  |         || (isMac() && evt.metaKey); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| function assertArguments() { | function assertArguments() { | ||||||
|     for (const i in arguments) { |     for (const i in arguments) { | ||||||
|         if (!arguments[i]) { |         if (!arguments[i]) { | ||||||
| @ -362,6 +367,7 @@ export default { | |||||||
|     now, |     now, | ||||||
|     isElectron, |     isElectron, | ||||||
|     isMac, |     isMac, | ||||||
|  |     isCtrlKey, | ||||||
|     assertArguments, |     assertArguments, | ||||||
|     escapeHtml, |     escapeHtml, | ||||||
|     stopWatch, |     stopWatch, | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								src/public/app/services/validation_error.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/public/app/services/validation_error.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | export default class ValidationError { | ||||||
|  |     constructor(resp) { | ||||||
|  |         for (const key in resp) { | ||||||
|  |             this[key] = resp[key]; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -357,7 +357,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget { | |||||||
|         // disable spellcheck for attribute editor
 |         // disable spellcheck for attribute editor
 | ||||||
|         this.textEditor.editing.view.change(writer => writer.setAttribute('spellcheck', 'false', this.textEditor.editing.view.document.getRoot())); |         this.textEditor.editing.view.change(writer => writer.setAttribute('spellcheck', 'false', this.textEditor.editing.view.document.getRoot())); | ||||||
| 
 | 
 | ||||||
|         //await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector.js');
 |         //await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector');
 | ||||||
|         //CKEditorInspector.attach(this.textEditor);
 |         //CKEditorInspector.attach(this.textEditor);
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,23 +1,55 @@ | |||||||
| import AbstractLauncher from "./abstract_launcher.js"; | import AbstractLauncher from "./abstract_launcher.js"; | ||||||
| import dialogService from "../../../services/dialog.js"; | import dialogService from "../../../services/dialog.js"; | ||||||
| import appContext from "../../../components/app_context.js"; | import appContext from "../../../components/app_context.js"; | ||||||
|  | import utils from "../../../services/utils.js"; | ||||||
|  | import linkContextMenuService from "../../../menus/link_context_menu.js"; | ||||||
| 
 | 
 | ||||||
|  | // we're intentionally displaying the launcher title and icon instead of the target
 | ||||||
|  | // e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok),
 | ||||||
|  | // but on the launchpad you want them distinguishable.
 | ||||||
|  | // for titles, the note titles may follow a different scheme than maybe desirable on the launchpad
 | ||||||
|  | // another reason is the discrepancy between what user sees on the launchpad and in the config (esp. icons).
 | ||||||
|  | // The only downside is more work in setting up the typical case
 | ||||||
|  | // where you actually want to have both title and icon in sync, but for those cases there are bookmarks
 | ||||||
| export default class NoteLauncher extends AbstractLauncher { | export default class NoteLauncher extends AbstractLauncher { | ||||||
|     constructor(launcherNote) { |     constructor(launcherNote) { | ||||||
|         super(launcherNote); |         super(launcherNote); | ||||||
| 
 | 
 | ||||||
|         this.title(this.launcherNote.title) |         this.title(this.launcherNote.title) | ||||||
|             .icon(this.launcherNote.getIcon()) |             .icon(this.launcherNote.getIcon()) | ||||||
|             .onClick(() => this.launch()); |             .onClick((widget, evt) => this.launch(evt)) | ||||||
|  |             .onAuxClick((widget, evt) => this.launch(evt)) | ||||||
|  |             .onContextMenu(evt => { | ||||||
|  |                 const targetNoteId = this.getTargetNoteId(); | ||||||
|  |                 if (!targetNoteId) { | ||||||
|  |                     return; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|     launch() { |                 linkContextMenuService.openContextMenu(targetNoteId, evt); | ||||||
|         // we're intentionally displaying the launcher title and icon instead of the target
 |             }); | ||||||
|         // e.g. you want to make launchers to 2 mermaid diagrams which both have mermaid icon (ok),
 |     } | ||||||
|         // but on the launchpad you want them distinguishable.
 | 
 | ||||||
|         // for titles, the note titles may follow a different scheme than maybe desirable on the launchpad
 |     launch(evt) { | ||||||
|         // another reason is the discrepancy between what user sees on the launchpad and in the config (esp. icons).
 |         const targetNoteId = this.getTargetNoteId(); | ||||||
|         // The only (but major) downside is more work in setting up the typical case where you actually want to have both title and icon in sync.
 |         if (!targetNoteId) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!evt) { | ||||||
|  |             // keyboard shortcut
 | ||||||
|  |             appContext.tabManager.getActiveContext().setNote(targetNoteId) | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const ctrlKey = utils.isCtrlKey(evt); | ||||||
|  |         if ((evt.which === 1 && ctrlKey) || evt.which === 2) { | ||||||
|  |             appContext.tabManager.openTabWithNoteWithHoisting(targetNoteId); | ||||||
|  |         } else { | ||||||
|  |             appContext.tabManager.getActiveContext().setNote(targetNoteId); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getTargetNoteId() { | ||||||
|         const targetNoteId = this.launcherNote.getRelationValue('targetNote'); |         const targetNoteId = this.launcherNote.getRelationValue('targetNote'); | ||||||
| 
 | 
 | ||||||
|         if (!targetNoteId) { |         if (!targetNoteId) { | ||||||
| @ -25,7 +57,7 @@ export default class NoteLauncher extends AbstractLauncher { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         appContext.tabManager.openTabWithNoteWithHoisting(targetNoteId, true); |         return targetNoteId; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     getTitle() { |     getTitle() { | ||||||
|  | |||||||
| @ -13,10 +13,23 @@ export default class OnClickButtonWidget extends AbstractButtonWidget { | |||||||
|         } else { |         } else { | ||||||
|             console.warn(`Button widget '${this.componentId}' has no defined click handler`, this.settings); |             console.warn(`Button widget '${this.componentId}' has no defined click handler`, this.settings); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         if (this.settings.onAuxClick) { | ||||||
|  |             this.$widget.on("auxclick", e => { | ||||||
|  |                 this.$widget.tooltip("hide"); | ||||||
|  | 
 | ||||||
|  |                 this.settings.onAuxClick(this, e); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     onClick(handler) { |     onClick(handler) { | ||||||
|         this.settings.onClick = handler; |         this.settings.onClick = handler; | ||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     onAuxClick(handler) { | ||||||
|  |         this.settings.onAuxClick = handler; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -452,7 +452,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                     if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { |                     if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { | ||||||
|                         const files = [...dataTransfer.files]; // chrome has issue that dataTransfer.files empties after async operation
 |                         const files = [...dataTransfer.files]; // chrome has issue that dataTransfer.files empties after async operation
 | ||||||
| 
 | 
 | ||||||
|                         const importService = await import('../services/import.js'); |                         const importService = await import('../services/import'); | ||||||
| 
 | 
 | ||||||
|                         importService.uploadFiles(node.data.noteId, files, { |                         importService.uploadFiles(node.data.noteId, files, { | ||||||
|                             safeImport: true, |                             safeImport: true, | ||||||
| @ -568,7 +568,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { | |||||||
|                     $span.append($refreshSearchButton); |                     $span.append($refreshSearchButton); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 if (!['search', 'launcher'].includes(note.type) && !note.isOptions()) { |                 if (!['search', 'launcher'].includes(note.type) && !note.isOptions() && !note.isLaunchBarConfig()) { | ||||||
|                     const $createChildNoteButton = $('<span class="tree-item-button add-note-button bx bx-plus" title="Create child note"></span>'); |                     const $createChildNoteButton = $('<span class="tree-item-button add-note-button bx bx-plus" title="Create child note"></span>'); | ||||||
| 
 | 
 | ||||||
|                     $span.append($createChildNoteButton); |                     $span.append($createChildNoteButton); | ||||||
|  | |||||||
| @ -132,7 +132,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { | |||||||
|         this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate()); |         this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate()); | ||||||
| 
 | 
 | ||||||
|         if (glob.isDev && ENABLE_INSPECTOR) { |         if (glob.isDev && ENABLE_INSPECTOR) { | ||||||
|             await import(/* webpackIgnore: true */'../../../libraries/ckeditor/inspector.js'); |             await import(/* webpackIgnore: true */'../../../libraries/ckeditor/inspector'); | ||||||
|             CKEditorInspector.attach(this.textEditor); |             CKEditorInspector.attach(this.textEditor); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -5,6 +5,8 @@ const log = require('../../services/log'); | |||||||
| const attributeService = require('../../services/attributes'); | const attributeService = require('../../services/attributes'); | ||||||
| const Attribute = require('../../becca/entities/attribute'); | const Attribute = require('../../becca/entities/attribute'); | ||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
|  | const ValidationError = require("../../errors/validation_error"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| function getEffectiveNoteAttributes(req) { | function getEffectiveNoteAttributes(req) { | ||||||
|     const note = becca.getNote(req.params.noteId); |     const note = becca.getNote(req.params.noteId); | ||||||
| @ -21,11 +23,11 @@ function updateNoteAttribute(req) { | |||||||
|         attribute = becca.getAttribute(body.attributeId); |         attribute = becca.getAttribute(body.attributeId); | ||||||
| 
 | 
 | ||||||
|         if (!attribute) { |         if (!attribute) { | ||||||
|             return [404, `Attribute '${body.attributeId}' does not exist.`]; |             throw new NotFoundError(`Attribute '${body.attributeId}' does not exist.`); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (attribute.noteId !== noteId) { |         if (attribute.noteId !== noteId) { | ||||||
|             return [400, `Attribute '${body.attributeId}' is not owned by ${noteId}`]; |             throw new ValidationError(`Attribute '${body.attributeId}' is not owned by ${noteId}`); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (body.type !== attribute.type |         if (body.type !== attribute.type | ||||||
| @ -106,7 +108,7 @@ function deleteNoteAttribute(req) { | |||||||
| 
 | 
 | ||||||
|     if (attribute) { |     if (attribute) { | ||||||
|         if (attribute.noteId !== noteId) { |         if (attribute.noteId !== noteId) { | ||||||
|             return [400, `Attribute ${attributeId} is not owned by ${noteId}`]; |             throw new ValidationError(`Attribute ${attributeId} is not owned by ${noteId}`); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         attribute.markAsDeleted(); |         attribute.markAsDeleted(); | ||||||
|  | |||||||
| @ -8,7 +8,9 @@ const noteService = require('../../services/notes'); | |||||||
| const becca = require('../../becca/becca'); | const becca = require('../../becca/becca'); | ||||||
| const TaskContext = require('../../services/task_context'); | const TaskContext = require('../../services/task_context'); | ||||||
| const branchService = require("../../services/branches"); | const branchService = require("../../services/branches"); | ||||||
| const log = require("../../services/log.js"); | const log = require("../../services/log"); | ||||||
|  | const ValidationError = require("../../errors/validation_error"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Code in this file deals with moving and cloning branches. Relationship between note and parent note is unique |  * Code in this file deals with moving and cloning branches. Relationship between note and parent note is unique | ||||||
| @ -22,7 +24,7 @@ function moveBranchToParent(req) { | |||||||
|     const branchToMove = becca.getBranch(branchId); |     const branchToMove = becca.getBranch(branchId); | ||||||
| 
 | 
 | ||||||
|     if (!parentBranch || !branchToMove) { |     if (!parentBranch || !branchToMove) { | ||||||
|         return [400, `One or both branches ${branchId}, ${parentBranchId} have not been found`]; |         throw new ValidationError(`One or both branches ${branchId}, ${parentBranchId} have not been found`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return branchService.moveBranchToBranch(branchToMove, parentBranch, branchId); |     return branchService.moveBranchToBranch(branchToMove, parentBranch, branchId); | ||||||
| @ -35,11 +37,11 @@ function moveBranchBeforeNote(req) { | |||||||
|     const beforeBranch = becca.getBranch(beforeBranchId); |     const beforeBranch = becca.getBranch(beforeBranchId); | ||||||
| 
 | 
 | ||||||
|     if (!branchToMove) { |     if (!branchToMove) { | ||||||
|         return [404, `Can't find branch ${branchId}`]; |         throw new NotFoundError(`Can't find branch '${branchId}'`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!beforeBranch) { |     if (!beforeBranch) { | ||||||
|         return [404, `Can't find branch ${beforeBranchId}`]; |         throw new NotFoundError(`Can't find branch '${beforeBranchId}'`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const validationResult = treeService.validateParentChild(beforeBranch.parentNoteId, branchToMove.noteId, branchId); |     const validationResult = treeService.validateParentChild(beforeBranch.parentNoteId, branchToMove.noteId, branchId); | ||||||
| @ -193,7 +195,7 @@ function deleteBranch(req) { | |||||||
|     const branch = becca.getBranch(req.params.branchId); |     const branch = becca.getBranch(req.params.branchId); | ||||||
| 
 | 
 | ||||||
|     if (!branch) { |     if (!branch) { | ||||||
|         return [404, `Branch ${req.params.branchId} not found`]; |         throw new NotFoundError(`Branch '${req.params.branchId}' not found`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const taskContext = TaskContext.getInstance(req.query.taskId, 'delete-notes'); |     const taskContext = TaskContext.getInstance(req.query.taskId, 'delete-notes'); | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ const opmlExportService = require('../../services/export/opml'); | |||||||
| const becca = require('../../becca/becca'); | const becca = require('../../becca/becca'); | ||||||
| const TaskContext = require("../../services/task_context"); | const TaskContext = require("../../services/task_context"); | ||||||
| const log = require("../../services/log"); | const log = require("../../services/log"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| function exportBranch(req, res) { | function exportBranch(req, res) { | ||||||
|     const {branchId, type, format, version, taskId} = req.params; |     const {branchId, type, format, version, taskId} = req.params; | ||||||
| @ -34,11 +35,11 @@ function exportBranch(req, res) { | |||||||
|             opmlExportService.exportToOpml(taskContext, branch, version, res); |             opmlExportService.exportToOpml(taskContext, branch, version, res); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             return [404, "Unrecognized export format " + format]; |             throw new NotFoundError(`Unrecognized export format '${format}'`); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     catch (e) { |     catch (e) { | ||||||
|         const message = "Export failed with following error: '" + e.message + "'. More details might be in the logs."; |         const message = `Export failed with following error: '${e.message}'. More details might be in the logs.`; | ||||||
|         taskContext.reportError(message); |         taskContext.reportError(message); | ||||||
| 
 | 
 | ||||||
|         log.error(message + e.stack); |         log.error(message + e.stack); | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ const { Readable } = require('stream'); | |||||||
| const chokidar = require('chokidar'); | const chokidar = require('chokidar'); | ||||||
| const ws = require('../../services/ws'); | const ws = require('../../services/ws'); | ||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| function updateFile(req) { | function updateFile(req) { | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
| @ -18,7 +19,7 @@ function updateFile(req) { | |||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note ${noteId} doesn't exist.`]; |         throw new NotFoundError(`Note '${noteId}' doesn't exist.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     note.saveNoteRevision(); |     note.saveNoteRevision(); | ||||||
| @ -116,7 +117,7 @@ function saveToTmpDir(req) { | |||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404,`Note ${noteId} doesn't exist.`]; |         throw new NotFoundError(`Note '${noteId}' doesn't exist.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const tmpObj = tmp.fileSync({postfix: getFilename(note)}); |     const tmpObj = tmp.fileSync({postfix: getFilename(note)}); | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ const imageService = require('../../services/image'); | |||||||
| const becca = require('../../becca/becca'); | const becca = require('../../becca/becca'); | ||||||
| const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR; | const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR; | ||||||
| const fs = require('fs'); | const fs = require('fs'); | ||||||
|  | const ValidationError = require("../../errors/validation_error"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| function returnImage(req, res) { | function returnImage(req, res) { | ||||||
|     const image = becca.getNote(req.params.noteId); |     const image = becca.getNote(req.params.noteId); | ||||||
| @ -51,11 +53,11 @@ function uploadImage(req) { | |||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note ${noteId} doesn't exist.`]; |         throw new NotFoundError(`Note '${noteId}' doesn't exist.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!["image/png", "image/jpg", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { |     if (!["image/png", "image/jpg", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { | ||||||
|         return [400, "Unknown image type: " + file.mimetype]; |         throw new ValidationError(`Unknown image type: ${file.mimetype}`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const {url} = imageService.saveImage(noteId, file.buffer, file.originalname, true, true); |     const {url} = imageService.saveImage(noteId, file.buffer, file.originalname, true, true); | ||||||
| @ -73,7 +75,7 @@ function updateImage(req) { | |||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note ${noteId} doesn't exist.`]; |         throw new NotFoundError(`Note '${noteId}' doesn't exist.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { |     if (!["image/png", "image/jpeg", "image/gif", "image/webp", "image/svg+xml"].includes(file.mimetype)) { | ||||||
|  | |||||||
| @ -10,6 +10,8 @@ const becca = require('../../becca/becca'); | |||||||
| const beccaLoader = require('../../becca/becca_loader'); | const beccaLoader = require('../../becca/becca_loader'); | ||||||
| const log = require('../../services/log'); | const log = require('../../services/log'); | ||||||
| const TaskContext = require('../../services/task_context'); | const TaskContext = require('../../services/task_context'); | ||||||
|  | const ValidationError = require("../../errors/validation_error"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| async function importToBranch(req) { | async function importToBranch(req) { | ||||||
|     const {parentNoteId} = req.params; |     const {parentNoteId} = req.params; | ||||||
| @ -27,13 +29,13 @@ async function importToBranch(req) { | |||||||
|     const file = req.file; |     const file = req.file; | ||||||
| 
 | 
 | ||||||
|     if (!file) { |     if (!file) { | ||||||
|         return [400, "No file has been uploaded"]; |         throw new ValidationError("No file has been uploaded"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const parentNote = becca.getNote(parentNoteId); |     const parentNote = becca.getNote(parentNoteId); | ||||||
| 
 | 
 | ||||||
|     if (!parentNote) { |     if (!parentNote) { | ||||||
|         return [404, `Note ${parentNoteId} doesn't exist.`]; |         throw new NotFoundError(`Note '${parentNoteId}' doesn't exist.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const extension = path.extname(file.originalname).toLowerCase(); |     const extension = path.extname(file.originalname).toLowerCase(); | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
| const { JSDOM } = require("jsdom"); | const { JSDOM } = require("jsdom"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| function buildDescendantCountMap() { | function buildDescendantCountMap() { | ||||||
|     const noteIdToCountMap = {}; |     const noteIdToCountMap = {}; | ||||||
| @ -326,7 +327,7 @@ function getBacklinkCount(req) { | |||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, "Not found"]; |         throw new NotFoundError(`Note '${noteId}' not found`); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         return { |         return { | ||||||
| @ -340,7 +341,7 @@ function getBacklinks(req) { | |||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note ${noteId} was not found`]; |         throw new NotFoundError(`Note '${noteId}' was not found`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let backlinksWithExcerptCount = 0; |     let backlinksWithExcerptCount = 0; | ||||||
|  | |||||||
| @ -6,16 +6,17 @@ const sql = require('../../services/sql'); | |||||||
| const utils = require('../../services/utils'); | const utils = require('../../services/utils'); | ||||||
| const log = require('../../services/log'); | const log = require('../../services/log'); | ||||||
| const TaskContext = require('../../services/task_context'); | const TaskContext = require('../../services/task_context'); | ||||||
| const protectedSessionService = require('../../services/protected_session'); |  | ||||||
| const fs = require('fs'); | const fs = require('fs'); | ||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
|  | const ValidationError = require("../../errors/validation_error"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| function getNote(req) { | function getNote(req) { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, "Note " + noteId + " has not been found."]; |         throw new NotFoundError(`Note '${noteId}' has not been found.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const pojo = note.getPojo(); |     const pojo = note.getPojo(); | ||||||
| @ -197,11 +198,11 @@ function changeTitle(req) { | |||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note '${noteId}' has not been found`]; |         throw new NotFoundError(`Note '${noteId}' has not been found`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!note.isContentAvailable()) { |     if (!note.isContentAvailable()) { | ||||||
|         return [400, `Note '${noteId}' is not available for change`]; |         throw new ValidationError(`Note '${noteId}' is not available for change`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const noteTitleChanged = note.title !== title; |     const noteTitleChanged = note.title !== title; | ||||||
| @ -290,7 +291,7 @@ function uploadModifiedFile(req) { | |||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note '${noteId}' has not been found`]; |         throw new NotFoundError(`Note '${noteId}' has not been found`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     log.info(`Updating note '${noteId}' with content from ${filePath}`); |     log.info(`Updating note '${noteId}' with content from ${filePath}`); | ||||||
| @ -300,7 +301,7 @@ function uploadModifiedFile(req) { | |||||||
|     const fileContent = fs.readFileSync(filePath); |     const fileContent = fs.readFileSync(filePath); | ||||||
| 
 | 
 | ||||||
|     if (!fileContent) { |     if (!fileContent) { | ||||||
|         return [400, `File ${fileContent} is empty`]; |         throw new ValidationError(`File '${fileContent}' is empty`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     note.setContent(fileContent); |     note.setContent(fileContent); | ||||||
| @ -311,11 +312,11 @@ function forceSaveNoteRevision(req) { | |||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note ${noteId} not found.`]; |         throw new NotFoundError(`Note '${noteId}' not found.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!note.isContentAvailable()) { |     if (!note.isContentAvailable()) { | ||||||
|         return [400, `Note revision of a protected note cannot be created outside of a protected session.`]; |         throw new ValidationError(`Note revision of a protected note cannot be created outside of a protected session.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     note.saveNoteRevision(); |     note.saveNoteRevision(); | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| const optionService = require('../../services/options'); | const optionService = require('../../services/options'); | ||||||
| const log = require('../../services/log'); | const log = require('../../services/log'); | ||||||
| const searchService = require('../../services/search/services/search'); | const searchService = require('../../services/search/services/search'); | ||||||
|  | const ValidationError = require("../../errors/validation_error"); | ||||||
| 
 | 
 | ||||||
| // options allowed to be updated directly in options dialog
 | // options allowed to be updated directly in options dialog
 | ||||||
| const ALLOWED_OPTIONS = new Set([ | const ALLOWED_OPTIONS = new Set([ | ||||||
| @ -82,7 +83,7 @@ function updateOption(req) { | |||||||
|     const {name, value} = req.params; |     const {name, value} = req.params; | ||||||
| 
 | 
 | ||||||
|     if (!update(name, value)) { |     if (!update(name, value)) { | ||||||
|         return [400, "not allowed option to change"]; |         throw new ValidationError("not allowed option to change"); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| const passwordService = require('../../services/password'); | const passwordService = require('../../services/password'); | ||||||
|  | const ValidationError = require("../../errors/validation_error"); | ||||||
| 
 | 
 | ||||||
| function changePassword(req) { | function changePassword(req) { | ||||||
|     if (passwordService.isPasswordSet()) { |     if (passwordService.isPasswordSet()) { | ||||||
| @ -14,7 +15,7 @@ function changePassword(req) { | |||||||
| function resetPassword(req) { | function resetPassword(req) { | ||||||
|     // protection against accidental call (not a security measure)
 |     // protection against accidental call (not a security measure)
 | ||||||
|     if (req.query.really !== "yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes") { |     if (req.query.really !== "yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes") { | ||||||
|         return [400, "Incorrect password reset confirmation"]; |         throw new ValidationError("Incorrect password reset confirmation"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return passwordService.resetPassword(); |     return passwordService.resetPassword(); | ||||||
|  | |||||||
| @ -6,12 +6,14 @@ const searchService = require('../../services/search/services/search'); | |||||||
| const bulkActionService = require("../../services/bulk_actions"); | const bulkActionService = require("../../services/bulk_actions"); | ||||||
| const cls = require("../../services/cls"); | const cls = require("../../services/cls"); | ||||||
| const {formatAttrForSearch} = require("../../services/attribute_formatter"); | const {formatAttrForSearch} = require("../../services/attribute_formatter"); | ||||||
|  | const ValidationError = require("../../errors/validation_error"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| function searchFromNote(req) { | function searchFromNote(req) { | ||||||
|     const note = becca.getNote(req.params.noteId); |     const note = becca.getNote(req.params.noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note ${req.params.noteId} has not been found.`]; |         throw new NotFoundError(`Note '${req.params.noteId}' has not been found.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (note.isDeleted) { |     if (note.isDeleted) { | ||||||
| @ -20,7 +22,7 @@ function searchFromNote(req) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (note.type !== 'search') { |     if (note.type !== 'search') { | ||||||
|         return [400, `Note ${req.params.noteId} is not a search note.`] |         throw new ValidationError(`Note '${req.params.noteId}' is not a search note.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return searchService.searchFromNote(note); |     return searchService.searchFromNote(note); | ||||||
| @ -30,16 +32,16 @@ function searchAndExecute(req) { | |||||||
|     const note = becca.getNote(req.params.noteId); |     const note = becca.getNote(req.params.noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note ${req.params.noteId} has not been found.`]; |         throw new NotFoundError(`Note '${req.params.noteId}' has not been found.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (note.isDeleted) { |     if (note.isDeleted) { | ||||||
|         // this can be triggered from recent changes and it's harmless to return empty list rather than fail
 |         // this can be triggered from recent changes, and it's harmless to return empty list rather than fail
 | ||||||
|         return []; |         return []; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (note.type !== 'search') { |     if (note.type !== 'search') { | ||||||
|         return [400, `Note ${req.params.noteId} is not a search note.`] |         throw new ValidationError(`Note '${req.params.noteId}' is not a search note.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const {searchResultNoteIds} = searchService.searchFromNote(note); |     const {searchResultNoteIds} = searchService.searchFromNote(note); | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| const similarityService = require('../../becca/similarity'); | const similarityService = require('../../becca/similarity'); | ||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| async function getSimilarNotes(req) { | async function getSimilarNotes(req) { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
| @ -9,7 +10,7 @@ async function getSimilarNotes(req) { | |||||||
|     const note = becca.getNote(noteId); |     const note = becca.getNote(noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note ${noteId} not found.`]; |         throw new NotFoundError(`Note '${noteId}' not found.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return await similarityService.findSimilarNotes(noteId); |     return await similarityService.findSimilarNotes(noteId); | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| const sql = require('../../services/sql'); | const sql = require('../../services/sql'); | ||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| function getSchema() { | function getSchema() { | ||||||
|     const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); |     const tableNames = sql.getColumn(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`); | ||||||
| @ -21,7 +22,7 @@ function execute(req) { | |||||||
|     const note = becca.getNote(req.params.noteId); |     const note = becca.getNote(req.params.noteId); | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note ${req.params.noteId} was not found.`]; |         throw new NotFoundError(`Note '${req.params.noteId}' was not found.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const queries = note.getContent().split("\n---"); |     const queries = note.getContent().split("\n---"); | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| const sql = require('../../services/sql'); | const sql = require('../../services/sql'); | ||||||
| const becca = require('../../becca/becca'); | const becca = require('../../becca/becca'); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| function getNoteSize(req) { | function getNoteSize(req) { | ||||||
|     const {noteId} = req.params; |     const {noteId} = req.params; | ||||||
| @ -26,7 +27,7 @@ function getSubtreeSize(req) { | |||||||
|     const note = becca.notes[noteId]; |     const note = becca.notes[noteId]; | ||||||
| 
 | 
 | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         return [404, `Note ${noteId} was not found.`]; |         throw new NotFoundError(`Note '${noteId}' was not found.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const subTreeNoteIds = note.getSubtreeNoteIds(); |     const subTreeNoteIds = note.getSubtreeNoteIds(); | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| const becca = require('../../becca/becca'); | const becca = require('../../becca/becca'); | ||||||
| const log = require('../../services/log'); | const log = require('../../services/log'); | ||||||
|  | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| function getNotesAndBranchesAndAttributes(noteIds) { | function getNotesAndBranchesAndAttributes(noteIds) { | ||||||
|     noteIds = new Set(noteIds); |     noteIds = new Set(noteIds); | ||||||
| @ -141,7 +142,7 @@ function getTree(req) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!(subTreeNoteId in becca.notes)) { |     if (!(subTreeNoteId in becca.notes)) { | ||||||
|         return [404, `Note ${subTreeNoteId} not found in the cache`]; |         throw new NotFoundError(`Note '${subTreeNoteId}' not found in the cache`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     collect(becca.notes[subTreeNoteId]); |     collect(becca.notes[subTreeNoteId]); | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ const myScryptService = require('../services/my_scrypt'); | |||||||
| const log = require('../services/log'); | const log = require('../services/log'); | ||||||
| const passwordService = require("../services/password"); | const passwordService = require("../services/password"); | ||||||
| const assetPath = require("../services/asset_path"); | const assetPath = require("../services/asset_path"); | ||||||
|  | const ValidationError = require("../errors/validation_error"); | ||||||
| 
 | 
 | ||||||
| function loginPage(req, res) { | function loginPage(req, res) { | ||||||
|     res.render('login', { |     res.render('login', { | ||||||
| @ -23,7 +24,7 @@ function setPasswordPage(req, res) { | |||||||
| 
 | 
 | ||||||
| function setPassword(req, res) { | function setPassword(req, res) { | ||||||
|     if (passwordService.isPasswordSet()) { |     if (passwordService.isPasswordSet()) { | ||||||
|         return [400, "Password has been already set"]; |         throw new ValidationError("Password has been already set"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let {password1, password2} = req.body; |     let {password1, password2} = req.body; | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ const loginRoute = require('./login'); | |||||||
| const indexRoute = require('./index'); | const indexRoute = require('./index'); | ||||||
| const utils = require('../services/utils'); | const utils = require('../services/utils'); | ||||||
| const multer = require('multer'); | const multer = require('multer'); | ||||||
|  | const ValidationError = require("../errors/validation_error"); | ||||||
| 
 | 
 | ||||||
| // API routes
 | // API routes
 | ||||||
| const treeApiRoute = require('./api/tree'); | const treeApiRoute = require('./api/tree'); | ||||||
| @ -61,6 +62,7 @@ const csurf = require('csurf'); | |||||||
| const {createPartialContentHandler} = require("express-partial-content"); | const {createPartialContentHandler} = require("express-partial-content"); | ||||||
| const rateLimit = require("express-rate-limit"); | const rateLimit = require("express-rate-limit"); | ||||||
| const AbstractEntity = require("../becca/entities/abstract_entity"); | const AbstractEntity = require("../becca/entities/abstract_entity"); | ||||||
|  | const NotFoundError = require("../errors/not_found_error"); | ||||||
| 
 | 
 | ||||||
| const csrfMiddleware = csurf({ | const csrfMiddleware = csurf({ | ||||||
|     cookie: true, |     cookie: true, | ||||||
| @ -169,13 +171,7 @@ function route(method, path, middleware, routeHandler, resultHandler, transactio | |||||||
| 
 | 
 | ||||||
|                             log.request(req, res, Date.now() - start, responseLength); |                             log.request(req, res, Date.now() - start, responseLength); | ||||||
|                         }) |                         }) | ||||||
|                         .catch(e => { |                         .catch(e => handleException(method, path, e, res)); | ||||||
|                             log.error(`${method} ${path} threw exception: ` + e.stack); |  | ||||||
| 
 |  | ||||||
|                             res.setHeader("Content-Type", "text/plain") |  | ||||||
|                                 .status(500) |  | ||||||
|                                 .send(e.message); |  | ||||||
|                         }); |  | ||||||
|                 } |                 } | ||||||
|                 else { |                 else { | ||||||
|                     const responseLength = resultHandler(req, res, result); |                     const responseLength = resultHandler(req, res, result); | ||||||
| @ -185,13 +181,31 @@ function route(method, path, middleware, routeHandler, resultHandler, transactio | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         catch (e) { |         catch (e) { | ||||||
|  |             handleException(method, path, e, res); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function handleException(method, path, e, res) { | ||||||
|     log.error(`${method} ${path} threw exception: ` + e.stack); |     log.error(`${method} ${path} threw exception: ` + e.stack); | ||||||
| 
 | 
 | ||||||
|  |     if (e instanceof ValidationError) { | ||||||
|  |         res.setHeader("Content-Type", "application/json") | ||||||
|  |             .status(400) | ||||||
|  |             .send({ | ||||||
|  |                 message: e.message | ||||||
|  |             }); | ||||||
|  |     } if (e instanceof NotFoundError) { | ||||||
|  |         res.setHeader("Content-Type", "application/json") | ||||||
|  |             .status(404) | ||||||
|  |             .send({ | ||||||
|  |                 message: e.message | ||||||
|  |             }); | ||||||
|  |     } else { | ||||||
|         res.setHeader("Content-Type", "text/plain") |         res.setHeader("Content-Type", "text/plain") | ||||||
|             .status(500) |             .status(500) | ||||||
|             .send(e.message); |             .send(e.message); | ||||||
|     } |     } | ||||||
|     }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const MAX_ALLOWED_FILE_SIZE_MB = 250; | const MAX_ALLOWED_FILE_SIZE_MB = 250; | ||||||
|  | |||||||
| @ -60,6 +60,7 @@ module.exports = [ | |||||||
|     { type: 'label', name: 'template' }, |     { type: 'label', name: 'template' }, | ||||||
|     { type: 'label', name: 'toc' }, |     { type: 'label', name: 'toc' }, | ||||||
|     { type: 'label', name: 'color' }, |     { type: 'label', name: 'color' }, | ||||||
|  |     { type: 'label', name: 'keepCurrentHoisting'}, | ||||||
| 
 | 
 | ||||||
|     // relation names
 |     // relation names
 | ||||||
|     { type: 'relation', name: 'internalLink' }, |     { type: 'relation', name: 'internalLink' }, | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| const becca = require("../becca/becca"); | const becca = require("../becca/becca"); | ||||||
| const noteService = require("./notes"); | const noteService = require("./notes"); | ||||||
| const log = require("./log.js"); | const log = require("./log"); | ||||||
| 
 | 
 | ||||||
| const LBTPL_ROOT = "lbTplRoot"; | const LBTPL_ROOT = "lbTplRoot"; | ||||||
| const LBTPL_BASE = "lbTplBase"; | const LBTPL_BASE = "lbTplBase"; | ||||||
| @ -36,7 +36,8 @@ const HIDDEN_SUBTREE_DEFINITION = { | |||||||
|             title: 'Note Map', |             title: 'Note Map', | ||||||
|             type: 'noteMap', |             type: 'noteMap', | ||||||
|             attributes: [ |             attributes: [ | ||||||
|                 { type: 'label', name: 'mapRootId', value: 'hoisted' } |                 { type: 'label', name: 'mapRootNoteId', value: 'hoisted' }, | ||||||
|  |                 { type: 'label', name: 'keepCurrentHoisting' } | ||||||
|             ] |             ] | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @ -56,6 +57,12 @@ const HIDDEN_SUBTREE_DEFINITION = { | |||||||
|             title: 'Bulk action', |             title: 'Bulk action', | ||||||
|             type: 'doc', |             type: 'doc', | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             // place for user scripts hidden stuff (scripts should not create notes directly under hidden root)
 | ||||||
|  |             id: 'userHidden', | ||||||
|  |             title: 'User Hidden', | ||||||
|  |             type: 'text', | ||||||
|  |         }, | ||||||
|         { |         { | ||||||
|             id: LBTPL_ROOT, |             id: LBTPL_ROOT, | ||||||
|             title: 'Launch Bar Templates', |             title: 'Launch Bar Templates', | ||||||
|  | |||||||
| @ -18,7 +18,8 @@ const Branch = require('../becca/entities/branch'); | |||||||
| const Note = require('../becca/entities/note'); | const Note = require('../becca/entities/note'); | ||||||
| const Attribute = require('../becca/entities/attribute'); | const Attribute = require('../becca/entities/attribute'); | ||||||
| const dayjs = require("dayjs"); | const dayjs = require("dayjs"); | ||||||
| const htmlSanitizer = require("./html_sanitizer.js"); | const htmlSanitizer = require("./html_sanitizer"); | ||||||
|  | const ValidationError = require("../errors/validation_error"); | ||||||
| 
 | 
 | ||||||
| function getNewNotePosition(parentNoteId) { | function getNewNotePosition(parentNoteId) { | ||||||
|     const note = becca.notes[parentNoteId]; |     const note = becca.notes[parentNoteId]; | ||||||
| @ -107,15 +108,15 @@ function getAndValidateParent(params) { | |||||||
|     const parentNote = becca.notes[params.parentNoteId]; |     const parentNote = becca.notes[params.parentNoteId]; | ||||||
| 
 | 
 | ||||||
|     if (!parentNote) { |     if (!parentNote) { | ||||||
|         throw new Error(`Parent note "${params.parentNoteId}" not found.`); |         throw new ValidationError(`Parent note "${params.parentNoteId}" not found.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (parentNote.type === 'launcher') { |     if (parentNote.type === 'launcher' && parentNote.noteId !== 'lbBookmarks') { | ||||||
|         throw new Error(`Launchers should not have child notes.`); |         throw new ValidationError(`Creating child notes into launcher notes is not allowed.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!params.ignoreForbiddenParents && (parentNote.isLaunchBarConfig() || parentNote.isOptions())) { |     if (!params.ignoreForbiddenParents && (['lbRoot', 'hidden'].includes(parentNote.noteId) || parentNote.isOptions())) { | ||||||
|         throw new Error(`Creating child notes into '${parentNote.noteId}' is not allowed.`); |         throw new ValidationError(`Creating child notes into '${parentNote.noteId}' is not allowed.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return parentNote; |     return parentNote; | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ const PropertyComparisonExp = require('../expressions/property_comparison'); | |||||||
| const AttributeExistsExp = require('../expressions/attribute_exists'); | const AttributeExistsExp = require('../expressions/attribute_exists'); | ||||||
| const LabelComparisonExp = require('../expressions/label_comparison'); | const LabelComparisonExp = require('../expressions/label_comparison'); | ||||||
| const NoteFlatTextExp = require('../expressions/note_flat_text'); | const NoteFlatTextExp = require('../expressions/note_flat_text'); | ||||||
| const NoteContentFulltextExp = require('../expressions/note_content_fulltext.js'); | const NoteContentFulltextExp = require('../expressions/note_content_fulltext'); | ||||||
| const OrderByAndLimitExp = require('../expressions/order_by_and_limit'); | const OrderByAndLimitExp = require('../expressions/order_by_and_limit'); | ||||||
| const AncestorExp = require("../expressions/ancestor"); | const AncestorExp = require("../expressions/ancestor"); | ||||||
| const buildComparator = require('./build_comparator'); | const buildComparator = require('./build_comparator'); | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ const becca = require('../../../becca/becca'); | |||||||
| const beccaService = require('../../../becca/becca_service'); | const beccaService = require('../../../becca/becca_service'); | ||||||
| const utils = require('../../utils'); | const utils = require('../../utils'); | ||||||
| const log = require('../../log'); | const log = require('../../log'); | ||||||
| const scriptService = require("../../script.js"); | const scriptService = require("../../script"); | ||||||
| 
 | 
 | ||||||
| function searchFromNote(note) { | function searchFromNote(note) { | ||||||
|     let searchResultNoteIds, highlightedTokens; |     let searchResultNoteIds, highlightedTokens; | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ const becca = require("../becca/becca"); | |||||||
| const noteService = require("./notes"); | const noteService = require("./notes"); | ||||||
| const cls = require("./cls"); | const cls = require("./cls"); | ||||||
| const dateUtils = require("./date_utils"); | const dateUtils = require("./date_utils"); | ||||||
| const log = require("./log.js"); | const log = require("./log"); | ||||||
| const hiddenSubtreeService = require("./hidden_subtree"); | const hiddenSubtreeService = require("./hidden_subtree"); | ||||||
| 
 | 
 | ||||||
| function getInboxNote(date) { | function getInboxNote(date) { | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ const ws = require('./ws'); | |||||||
| const taskContexts = {}; | const taskContexts = {}; | ||||||
| 
 | 
 | ||||||
| class TaskContext { | class TaskContext { | ||||||
|     constructor(taskId, taskType, data) { |     constructor(taskId, taskType, data = null) { | ||||||
|         this.taskId = taskId; |         this.taskId = taskId; | ||||||
|         this.taskType = taskType; |         this.taskType = taskType; | ||||||
|         this.data = data; |         this.data = data; | ||||||
| @ -24,7 +24,7 @@ class TaskContext { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** @returns {TaskContext} */ |     /** @returns {TaskContext} */ | ||||||
|     static getInstance(taskId, taskType, data) { |     static getInstance(taskId, taskType, data = null) { | ||||||
|         if (!taskContexts[taskId]) { |         if (!taskContexts[taskId]) { | ||||||
|             taskContexts[taskId] = new TaskContext(taskId, taskType, data); |             taskContexts[taskId] = new TaskContext(taskId, taskType, data); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -53,6 +53,11 @@ function init(httpServer, sessionParser) { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|  | 
 | ||||||
|  |     webSocketServer.on('error', error => { | ||||||
|  |         // https://github.com/zadam/trilium/issues/3374#issuecomment-1341053765
 | ||||||
|  |         console.log(error); | ||||||
|  |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function sendMessage(client, message) { | function sendMessage(client, message) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam