From 643a5e5b169ffeae2e5f4ef53b394777557e48c1 Mon Sep 17 00:00:00 2001 From: zadam Date: Tue, 19 Apr 2022 23:06:46 +0200 Subject: [PATCH] moving `deleteNote` and `deleteBranch` into entities to make them accessible to scripts, #2792 --- docs/backend_api/AbstractEntity.html | 4 +- docs/backend_api/Attribute.html | 4 +- docs/backend_api/Branch.html | 228 +++++++++++- docs/backend_api/EtapiToken.html | 4 +- docs/backend_api/Note.html | 346 ++++++++++++++---- docs/backend_api/NoteRevision.html | 4 +- docs/backend_api/Option.html | 4 +- docs/backend_api/RecentNote.html | 4 +- .../becca_entities_abstract_entity.js.html | 2 + .../backend_api/becca_entities_branch.js.html | 61 +++ docs/backend_api/becca_entities_note.js.html | 21 ++ package-lock.json | 78 ++-- package.json | 6 +- src/becca/entities/abstract_entity.js | 2 + src/becca/entities/branch.js | 61 +++ src/becca/entities/note.js | 21 ++ src/etapi/branches.js | 2 +- src/etapi/notes.js | 2 +- src/routes/api/branches.js | 2 +- src/routes/api/notes.js | 2 +- src/services/cloning.js | 3 +- src/services/notes.js | 66 +--- 22 files changed, 734 insertions(+), 193 deletions(-) diff --git a/docs/backend_api/AbstractEntity.html b/docs/backend_api/AbstractEntity.html index e017cf1c9..ab08501ce 100644 --- a/docs/backend_api/AbstractEntity.html +++ b/docs/backend_api/AbstractEntity.html @@ -158,6 +158,8 @@
Mark the entity as (soft) deleted. It will be completely erased later. + +This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead.
@@ -265,7 +267,7 @@
Source:
diff --git a/docs/backend_api/Attribute.html b/docs/backend_api/Attribute.html index 59b518036..8633da77b 100644 --- a/docs/backend_api/Attribute.html +++ b/docs/backend_api/Attribute.html @@ -1030,6 +1030,8 @@ and relation (representing named relationship between source and target note) Mark the entity as (soft) deleted. It will be completely erased later. + +This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead. @@ -1142,7 +1144,7 @@ and relation (representing named relationship between source and target note)Source:
diff --git a/docs/backend_api/Branch.html b/docs/backend_api/Branch.html index 4be4b47f5..f89ff45ad 100644 --- a/docs/backend_api/Branch.html +++ b/docs/backend_api/Branch.html @@ -94,7 +94,7 @@ parents.
Source:
@@ -205,7 +205,7 @@ parents.
Source:
@@ -263,7 +263,7 @@ parents.
Source:
@@ -331,7 +331,7 @@ parents.
Source:
@@ -399,7 +399,7 @@ parents.
Source:
@@ -467,7 +467,7 @@ parents.
Source:
@@ -525,7 +525,7 @@ parents.
Source:
@@ -593,7 +593,7 @@ parents.
Source:
@@ -661,7 +661,7 @@ parents.
Source:
@@ -729,7 +729,7 @@ parents.
Source:
@@ -757,6 +757,210 @@ parents. +

deleteBranch(deleteIdopt, taskContextopt) → {boolean}

+ + + + + + +
+ Delete a branch. If this is a last note's branch, delete the note as well. +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
deleteId + + +string + + + + + + <optional>
+ + + + + +
optional delete identified
taskContext + + +TaskContext + + + + + + <optional>
+ + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ - true if note has been deleted, false otherwise +
+ + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + + +

markAsDeleted(deleteIdopt)

@@ -766,6 +970,8 @@ parents.
Mark the entity as (soft) deleted. It will be completely erased later. + +This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead.
@@ -878,7 +1084,7 @@ parents.
Source:
diff --git a/docs/backend_api/EtapiToken.html b/docs/backend_api/EtapiToken.html index bffcb3658..b675d8cdc 100644 --- a/docs/backend_api/EtapiToken.html +++ b/docs/backend_api/EtapiToken.html @@ -587,6 +587,8 @@ from tokenHash and token.
Mark the entity as (soft) deleted. It will be completely erased later. + +This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead.
@@ -699,7 +701,7 @@ from tokenHash and token.
Source:
diff --git a/docs/backend_api/Note.html b/docs/backend_api/Note.html index 5a24d464a..c5eea4f2f 100644 --- a/docs/backend_api/Note.html +++ b/docs/backend_api/Note.html @@ -93,7 +93,7 @@
Source:
@@ -204,7 +204,7 @@
Source:
@@ -279,7 +279,7 @@
Source:
@@ -347,7 +347,7 @@
Source:
@@ -415,7 +415,7 @@
Source:
@@ -486,7 +486,7 @@
Source:
@@ -554,7 +554,7 @@
Source:
@@ -622,7 +622,7 @@
Source:
@@ -690,7 +690,7 @@
Source:
@@ -758,7 +758,7 @@
Source:
@@ -833,7 +833,7 @@
Source:
@@ -901,7 +901,7 @@
Source:
@@ -969,7 +969,7 @@
Source:
@@ -1037,7 +1037,7 @@
Source:
@@ -1112,7 +1112,7 @@
Source:
@@ -1180,7 +1180,7 @@
Source:
@@ -1248,7 +1248,7 @@
Source:
@@ -1316,7 +1316,7 @@
Source:
@@ -1384,7 +1384,7 @@
Source:
@@ -1452,7 +1452,7 @@
Source:
@@ -1528,7 +1528,7 @@
Source:
@@ -1630,7 +1630,7 @@
Source:
@@ -1678,6 +1678,188 @@ + + + + + + +

deleteNote(deleteIdopt, taskContextopt)

+ + + + + + +
+ (Soft) delete a note and all its descendants. +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
deleteId + + +string + + + + + + <optional>
+ + + + + +
optional delete identified
taskContext + + +TaskContext + + + + + + <optional>
+ + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + @@ -1732,7 +1914,7 @@
Source:
@@ -1838,7 +2020,7 @@
Source:
@@ -2012,7 +2194,7 @@
Source:
@@ -2212,7 +2394,7 @@
Source:
@@ -2390,7 +2572,7 @@
Source:
@@ -2501,7 +2683,7 @@
Source:
@@ -2603,7 +2785,7 @@
Source:
@@ -2705,7 +2887,7 @@
Source:
@@ -2807,7 +2989,7 @@
Source:
@@ -2909,7 +3091,7 @@
Source:
@@ -3017,7 +3199,7 @@
Source:
@@ -3123,7 +3305,7 @@
Source:
@@ -3274,7 +3456,7 @@
Source:
@@ -3444,7 +3626,7 @@
Source:
@@ -3599,7 +3781,7 @@
Source:
@@ -3769,7 +3951,7 @@
Source:
@@ -3875,7 +4057,7 @@
Source:
@@ -4077,7 +4259,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -4255,7 +4437,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -4413,7 +4595,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -4583,7 +4765,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -4738,7 +4920,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -4908,7 +5090,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5063,7 +5245,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5233,7 +5415,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5388,7 +5570,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5497,7 +5679,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5599,7 +5781,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5750,7 +5932,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5920,7 +6102,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6075,7 +6257,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6184,7 +6366,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6293,7 +6475,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6395,7 +6577,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6497,7 +6679,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6599,7 +6781,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6706,7 +6888,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6808,7 +6990,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6959,7 +7141,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7137,7 +7319,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7292,7 +7474,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7447,7 +7629,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7602,7 +7784,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7752,7 +7934,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7858,7 +8040,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7964,7 +8146,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8070,7 +8252,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8176,7 +8358,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8282,7 +8464,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8349,6 +8531,8 @@ This method can be significantly faster than the getAttribute()
Mark the entity as (soft) deleted. It will be completely erased later. + +This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead.
@@ -8461,7 +8645,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8672,7 +8856,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8852,7 +9036,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9032,7 +9216,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9354,7 +9538,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9534,7 +9718,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9694,7 +9878,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9936,7 +10120,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -10147,7 +10331,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -10358,7 +10542,7 @@ This method can be significantly faster than the getAttribute()
Source:
diff --git a/docs/backend_api/NoteRevision.html b/docs/backend_api/NoteRevision.html index f9575d7df..ad8b80285 100644 --- a/docs/backend_api/NoteRevision.html +++ b/docs/backend_api/NoteRevision.html @@ -1300,6 +1300,8 @@ It's used for seamless note versioning.
Mark the entity as (soft) deleted. It will be completely erased later. + +This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead.
@@ -1412,7 +1414,7 @@ It's used for seamless note versioning.
Source:
diff --git a/docs/backend_api/Option.html b/docs/backend_api/Option.html index 2c43460c7..c812d8ea6 100644 --- a/docs/backend_api/Option.html +++ b/docs/backend_api/Option.html @@ -445,6 +445,8 @@
Mark the entity as (soft) deleted. It will be completely erased later. + +This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead.
@@ -557,7 +559,7 @@
Source:
diff --git a/docs/backend_api/RecentNote.html b/docs/backend_api/RecentNote.html index 264580c72..621598ed2 100644 --- a/docs/backend_api/RecentNote.html +++ b/docs/backend_api/RecentNote.html @@ -377,6 +377,8 @@
Mark the entity as (soft) deleted. It will be completely erased later. + +This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead.
@@ -489,7 +491,7 @@
Source:
diff --git a/docs/backend_api/becca_entities_abstract_entity.js.html b/docs/backend_api/becca_entities_abstract_entity.js.html index 43b0899a8..a66b5654a 100644 --- a/docs/backend_api/becca_entities_abstract_entity.js.html +++ b/docs/backend_api/becca_entities_abstract_entity.js.html @@ -139,6 +139,8 @@ class AbstractEntity { /** * Mark the entity as (soft) deleted. It will be completely erased later. * + * This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead. + * * @param [deleteId=null] */ markAsDeleted(deleteId = null) { diff --git a/docs/backend_api/becca_entities_branch.js.html b/docs/backend_api/becca_entities_branch.js.html index 94ffed9dc..73fd62a3b 100644 --- a/docs/backend_api/becca_entities_branch.js.html +++ b/docs/backend_api/becca_entities_branch.js.html @@ -32,6 +32,10 @@ const Note = require('./note'); const AbstractEntity = require("./abstract_entity"); const sql = require("../../services/sql"); const dateUtils = require("../../services/date_utils"); +const utils = require("../../services/utils.js"); +const TaskContext = require("../../services/task_context.js"); +const cls = require("../../services/cls.js"); +const log = require("../../services/log.js"); /** * Branch represents a relationship between a child note and its parent note. Trilium allows a note to have multiple @@ -142,6 +146,63 @@ class Branch extends AbstractEntity { return !(this.branchId in this.becca.branches); } + /** + * Delete a branch. If this is a last note's branch, delete the note as well. + * + * @param {string} [deleteId] - optional delete identified + * @param {TaskContext} [taskContext] + * + * @return {boolean} - true if note has been deleted, false otherwise + */ + deleteBranch(deleteId, taskContext) { + if (!deleteId) { + deleteId = utils.randomString(10); + } + + if (!taskContext) { + taskContext = new TaskContext('no-progress-reporting'); + } + + taskContext.increaseProgressCount(); + + if (this.branchId === 'root' + || this.noteId === 'root' + || this.noteId === cls.getHoistedNoteId()) { + + throw new Error("Can't delete root or hoisted branch/note"); + } + + this.markAsDeleted(deleteId); + + const note = this.getNote(); + const notDeletedBranches = note.getParentBranches(); + + if (notDeletedBranches.length === 0) { + for (const childBranch of note.getChildBranches()) { + childBranch.deleteBranch(deleteId, taskContext); + } + + // first delete children and then parent - this will show up better in recent changes + + log.info("Deleting note " + note.noteId); + + for (const attribute of note.getOwnedAttributes()) { + attribute.markAsDeleted(deleteId); + } + + for (const relation of note.getTargetRelations()) { + relation.markAsDeleted(deleteId); + } + + note.markAsDeleted(deleteId); + + return true; + } + else { + return false; + } + } + beforeSaving() { if (this.notePosition === undefined || this.notePosition === null) { // TODO finding new position can be refactored into becca diff --git a/docs/backend_api/becca_entities_note.js.html b/docs/backend_api/becca_entities_note.js.html index 2aae3ae51..ad2571123 100644 --- a/docs/backend_api/becca_entities_note.js.html +++ b/docs/backend_api/becca_entities_note.js.html @@ -36,6 +36,7 @@ const dateUtils = require('../../services/date_utils'); const entityChangesService = require('../../services/entity_changes'); const AbstractEntity = require("./abstract_entity"); const NoteRevision = require("./note_revision"); +const TaskContext = require("../../services/task_context.js"); const LABEL = 'label'; const RELATION = 'relation'; @@ -1153,6 +1154,26 @@ class Note extends AbstractEntity { return cloningService.cloneNoteToBranch(this.noteId, branch.branchId); } + /** + * (Soft) delete a note and all its descendants. + * + * @param {string} [deleteId] - optional delete identified + * @param {TaskContext} [taskContext] + */ + deleteNote(deleteId, taskContext) { + if (!deleteId) { + deleteId = utils.randomString(10); + } + + if (!taskContext) { + taskContext = new TaskContext('no-progress-reporting'); + } + + for (const branch of this.getParentBranches()) { + branch.deleteBranch(deleteId, taskContext); + } + } + decrypt() { if (this.isProtected && !this.isDecrypted && protectedSessionService.isProtectedSessionAvailable()) { try { diff --git a/package-lock.json b/package-lock.json index 93ccebfa5..f82666a52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "trilium", "version": "0.51.0-beta", "license": "AGPL-3.0-only", "dependencies": { @@ -28,7 +29,7 @@ "express-partial-content": "1.0.2", "express-rate-limit": "6.3.0", "express-session": "1.17.2", - "fs-extra": "10.0.1", + "fs-extra": "10.1.0", "helmet": "5.0.2", "html": "1.0.0", "html2plaintext": "2.1.4", @@ -43,7 +44,7 @@ "jsdom": "19.0.0", "mime-types": "2.1.35", "multer": "1.4.4", - "node-abi": "3.8.0", + "node-abi": "3.15.0", "normalize-strings": "1.1.1", "open": "8.4.0", "portscanner": "2.2.0", @@ -71,7 +72,7 @@ "cross-env": "7.0.3", "electron": "16.2.1", "electron-builder": "23.0.3", - "electron-packager": "15.4.0", + "electron-packager": "15.5.0", "electron-rebuild": "3.2.7", "esm": "3.2.25", "jasmine": "4.1.0", @@ -4130,12 +4131,13 @@ } }, "node_modules/electron-packager": { - "version": "15.4.0", - "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-15.4.0.tgz", - "integrity": "sha512-JrrLcBP15KGrPj0cZ/ALKGmaQ4gJkn3mocf0E3bRKdR3kxKWYcDRpCvdhksYDXw/r3I6tMEcZ7XzyApWFXdVpw==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-15.5.0.tgz", + "integrity": "sha512-8mITLQgTm9xdrO8XL/PsK0EZGU7zK/ay7TI8M1C9pc1UZ++HlaWQJBRJHlOXf4TL/7FsiF4OciEhiqhMn+LKQQ==", "dev": true, "dependencies": { "@electron/get": "^1.6.0", + "@electron/universal": "^1.2.1", "asar": "^3.1.0", "cross-spawn-windows-exe": "^1.2.0", "debug": "^4.0.1", @@ -4164,6 +4166,24 @@ "url": "https://github.com/electron/electron-packager?sponsor=1" } }, + "node_modules/electron-packager/node_modules/@electron/universal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.2.1.tgz", + "integrity": "sha512-7323HyMh7KBAl/nPDppdLsC87G6RwRU02dy5FPeGB1eS7rUePh55+WNWiDPLhFQqqVPHzh77M69uhmoT8XnwMQ==", + "dev": true, + "dependencies": { + "@malept/cross-spawn-promise": "^1.1.0", + "asar": "^3.1.0", + "debug": "^4.3.1", + "dir-compare": "^2.4.0", + "fs-extra": "^9.0.1", + "minimatch": "^3.0.4", + "plist": "^3.0.4" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/electron-packager/node_modules/cross-spawn-windows-exe": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz", @@ -5576,9 +5596,9 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "node_modules/fs-extra": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", - "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -7661,9 +7681,9 @@ "dev": true }, "node_modules/node-abi": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.8.0.tgz", - "integrity": "sha512-tzua9qWWi7iW4I42vUPKM+SfaF0vQSLAm4yO5J83mSwB7GeoWrDKC/K+8YCnYNwqP5duwazbw2X9l4m8SC2cUw==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.15.0.tgz", + "integrity": "sha512-Ic6z/j6I9RLm4ov7npo1I48UQr2BEyFCqh6p7S1dhEx9jPO0GPGq/e2Rb7x7DroQrmiVMz/Bw1vJm9sPAl2nxA==", "dependencies": { "semver": "^7.3.5" }, @@ -14556,12 +14576,13 @@ } }, "electron-packager": { - "version": "15.4.0", - "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-15.4.0.tgz", - "integrity": "sha512-JrrLcBP15KGrPj0cZ/ALKGmaQ4gJkn3mocf0E3bRKdR3kxKWYcDRpCvdhksYDXw/r3I6tMEcZ7XzyApWFXdVpw==", + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-15.5.0.tgz", + "integrity": "sha512-8mITLQgTm9xdrO8XL/PsK0EZGU7zK/ay7TI8M1C9pc1UZ++HlaWQJBRJHlOXf4TL/7FsiF4OciEhiqhMn+LKQQ==", "dev": true, "requires": { "@electron/get": "^1.6.0", + "@electron/universal": "^1.2.1", "asar": "^3.1.0", "cross-spawn-windows-exe": "^1.2.0", "debug": "^4.0.1", @@ -14581,6 +14602,21 @@ "yargs-parser": "^20.0.0" }, "dependencies": { + "@electron/universal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.2.1.tgz", + "integrity": "sha512-7323HyMh7KBAl/nPDppdLsC87G6RwRU02dy5FPeGB1eS7rUePh55+WNWiDPLhFQqqVPHzh77M69uhmoT8XnwMQ==", + "dev": true, + "requires": { + "@malept/cross-spawn-promise": "^1.1.0", + "asar": "^3.1.0", + "debug": "^4.3.1", + "dir-compare": "^2.4.0", + "fs-extra": "^9.0.1", + "minimatch": "^3.0.4", + "plist": "^3.0.4" + } + }, "cross-spawn-windows-exe": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz", @@ -15507,9 +15543,9 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "fs-extra": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", - "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -17133,9 +17169,9 @@ "dev": true }, "node-abi": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.8.0.tgz", - "integrity": "sha512-tzua9qWWi7iW4I42vUPKM+SfaF0vQSLAm4yO5J83mSwB7GeoWrDKC/K+8YCnYNwqP5duwazbw2X9l4m8SC2cUw==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.15.0.tgz", + "integrity": "sha512-Ic6z/j6I9RLm4ov7npo1I48UQr2BEyFCqh6p7S1dhEx9jPO0GPGq/e2Rb7x7DroQrmiVMz/Bw1vJm9sPAl2nxA==", "requires": { "semver": "^7.3.5" } diff --git a/package.json b/package.json index 1ad93025a..3a0356b2d 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "express-partial-content": "1.0.2", "express-rate-limit": "6.3.0", "express-session": "1.17.2", - "fs-extra": "10.0.1", + "fs-extra": "10.1.0", "helmet": "5.0.2", "html": "1.0.0", "html2plaintext": "2.1.4", @@ -59,7 +59,7 @@ "jsdom": "19.0.0", "mime-types": "2.1.35", "multer": "1.4.4", - "node-abi": "3.8.0", + "node-abi": "3.15.0", "normalize-strings": "1.1.1", "open": "8.4.0", "portscanner": "2.2.0", @@ -84,7 +84,7 @@ "cross-env": "7.0.3", "electron": "16.2.1", "electron-builder": "23.0.3", - "electron-packager": "15.4.0", + "electron-packager": "15.5.0", "electron-rebuild": "3.2.7", "esm": "3.2.25", "jasmine": "4.1.0", diff --git a/src/becca/entities/abstract_entity.js b/src/becca/entities/abstract_entity.js index 9981373c3..574c5bae5 100644 --- a/src/becca/entities/abstract_entity.js +++ b/src/becca/entities/abstract_entity.js @@ -111,6 +111,8 @@ class AbstractEntity { /** * Mark the entity as (soft) deleted. It will be completely erased later. * + * This is a low level method, for notes and branches use `note.deleteNote()` and 'branch.deleteBranch()` instead. + * * @param [deleteId=null] */ markAsDeleted(deleteId = null) { diff --git a/src/becca/entities/branch.js b/src/becca/entities/branch.js index 10ce46a54..8df3febb8 100644 --- a/src/becca/entities/branch.js +++ b/src/becca/entities/branch.js @@ -4,6 +4,10 @@ const Note = require('./note'); const AbstractEntity = require("./abstract_entity"); const sql = require("../../services/sql"); const dateUtils = require("../../services/date_utils"); +const utils = require("../../services/utils.js"); +const TaskContext = require("../../services/task_context.js"); +const cls = require("../../services/cls.js"); +const log = require("../../services/log.js"); /** * Branch represents a relationship between a child note and its parent note. Trilium allows a note to have multiple @@ -114,6 +118,63 @@ class Branch extends AbstractEntity { return !(this.branchId in this.becca.branches); } + /** + * Delete a branch. If this is a last note's branch, delete the note as well. + * + * @param {string} [deleteId] - optional delete identified + * @param {TaskContext} [taskContext] + * + * @return {boolean} - true if note has been deleted, false otherwise + */ + deleteBranch(deleteId, taskContext) { + if (!deleteId) { + deleteId = utils.randomString(10); + } + + if (!taskContext) { + taskContext = new TaskContext('no-progress-reporting'); + } + + taskContext.increaseProgressCount(); + + if (this.branchId === 'root' + || this.noteId === 'root' + || this.noteId === cls.getHoistedNoteId()) { + + throw new Error("Can't delete root or hoisted branch/note"); + } + + this.markAsDeleted(deleteId); + + const note = this.getNote(); + const notDeletedBranches = note.getParentBranches(); + + if (notDeletedBranches.length === 0) { + for (const childBranch of note.getChildBranches()) { + childBranch.deleteBranch(deleteId, taskContext); + } + + // first delete children and then parent - this will show up better in recent changes + + log.info("Deleting note " + note.noteId); + + for (const attribute of note.getOwnedAttributes()) { + attribute.markAsDeleted(deleteId); + } + + for (const relation of note.getTargetRelations()) { + relation.markAsDeleted(deleteId); + } + + note.markAsDeleted(deleteId); + + return true; + } + else { + return false; + } + } + beforeSaving() { if (this.notePosition === undefined || this.notePosition === null) { // TODO finding new position can be refactored into becca diff --git a/src/becca/entities/note.js b/src/becca/entities/note.js index 5f23e2d15..0192cff60 100644 --- a/src/becca/entities/note.js +++ b/src/becca/entities/note.js @@ -8,6 +8,7 @@ const dateUtils = require('../../services/date_utils'); const entityChangesService = require('../../services/entity_changes'); const AbstractEntity = require("./abstract_entity"); const NoteRevision = require("./note_revision"); +const TaskContext = require("../../services/task_context.js"); const LABEL = 'label'; const RELATION = 'relation'; @@ -1125,6 +1126,26 @@ class Note extends AbstractEntity { return cloningService.cloneNoteToBranch(this.noteId, branch.branchId); } + /** + * (Soft) delete a note and all its descendants. + * + * @param {string} [deleteId] - optional delete identified + * @param {TaskContext} [taskContext] + */ + deleteNote(deleteId, taskContext) { + if (!deleteId) { + deleteId = utils.randomString(10); + } + + if (!taskContext) { + taskContext = new TaskContext('no-progress-reporting'); + } + + for (const branch of this.getParentBranches()) { + branch.deleteBranch(deleteId, taskContext); + } + } + decrypt() { if (this.isProtected && !this.isDecrypted && protectedSessionService.isProtectedSessionAvailable()) { try { diff --git a/src/etapi/branches.js b/src/etapi/branches.js index 71117e339..be6d9f516 100644 --- a/src/etapi/branches.js +++ b/src/etapi/branches.js @@ -71,7 +71,7 @@ function register(router) { return res.sendStatus(204); } - noteService.deleteBranch(branch, null, new TaskContext('no-progress-reporting')); + branch.deleteBranch(); res.sendStatus(204); }); diff --git a/src/etapi/notes.js b/src/etapi/notes.js index b35c296ff..788b0f4ff 100644 --- a/src/etapi/notes.js +++ b/src/etapi/notes.js @@ -98,7 +98,7 @@ function register(router) { return res.sendStatus(204); } - noteService.deleteNote(note, null, new TaskContext('no-progress-reporting')); + note.deleteNote(null, new TaskContext('no-progress-reporting')); res.sendStatus(204); }); diff --git a/src/routes/api/branches.js b/src/routes/api/branches.js index f05a6e0c3..3af5fe8ed 100644 --- a/src/routes/api/branches.js +++ b/src/routes/api/branches.js @@ -194,7 +194,7 @@ function deleteBranch(req) { const taskContext = TaskContext.getInstance(req.query.taskId, 'delete-notes'); const deleteId = utils.randomString(10); - const noteDeleted = noteService.deleteBranch(branch, deleteId, taskContext); + const noteDeleted = branch.deleteBranch(deleteId, taskContext); if (eraseNotes) { noteService.eraseNotesWithDeleteId(deleteId); diff --git a/src/routes/api/notes.js b/src/routes/api/notes.js index f602189e2..82dbb813a 100644 --- a/src/routes/api/notes.js +++ b/src/routes/api/notes.js @@ -73,7 +73,7 @@ function deleteNote(req) { const taskContext = TaskContext.getInstance(taskId, 'delete-notes'); - noteService.deleteNote(note, deleteId, taskContext); + note.deleteNote(deleteId, taskContext); if (eraseNotes) { noteService.eraseNotesWithDeleteId(deleteId); diff --git a/src/services/cloning.js b/src/services/cloning.js index 6fbd3adf1..d929a1cbf 100644 --- a/src/services/cloning.js +++ b/src/services/cloning.js @@ -89,8 +89,7 @@ function ensureNoteIsAbsentFromParent(noteId, parentNoteId) { throw new Error(`Cannot remove branch ${branch.branchId} between child ${noteId} and parent ${parentNoteId} because this would delete the note as well.`); } - const deleteId = utils.randomString(10); - noteService.deleteBranch(branch, deleteId, new TaskContext()); + branch.deleteBranch(); log.info(`Ensured note ${noteId} is NOT in parent note ${parentNoteId}`); } diff --git a/src/services/notes.js b/src/services/notes.js index 5609e0582..e69e603d4 100644 --- a/src/services/notes.js +++ b/src/services/notes.js @@ -17,6 +17,7 @@ const becca = require('../becca/becca'); const Branch = require('../becca/entities/branch'); const Note = require('../becca/entities/note'); const Attribute = require('../becca/entities/attribute'); +const TaskContext = require("./task_context.js"); function getNewNotePosition(parentNoteId) { const note = becca.notes[parentNoteId]; @@ -524,69 +525,6 @@ function updateNote(noteId, noteUpdates) { }; } -/** - * @param {Branch} branch - * @param {string|null} deleteId - * @param {TaskContext} taskContext - * - * @return {boolean} - true if note has been deleted, false otherwise - */ -function deleteBranch(branch, deleteId, taskContext) { - taskContext.increaseProgressCount(); - - if (!branch) { - return false; - } - - if (branch.branchId === 'root' - || branch.noteId === 'root' - || branch.noteId === cls.getHoistedNoteId()) { - - throw new Error("Can't delete root or hoisted branch/note"); - } - - branch.markAsDeleted(deleteId); - - const note = branch.getNote(); - const notDeletedBranches = note.getParentBranches(); - - if (notDeletedBranches.length === 0) { - for (const childBranch of note.getChildBranches()) { - deleteBranch(childBranch, deleteId, taskContext); - } - - // first delete children and then parent - this will show up better in recent changes - - log.info("Deleting note " + note.noteId); - - for (const attribute of note.getOwnedAttributes()) { - attribute.markAsDeleted(deleteId); - } - - for (const relation of note.getTargetRelations()) { - relation.markAsDeleted(deleteId); - } - - note.markAsDeleted(deleteId); - - return true; - } - else { - return false; - } -} - -/** - * @param {Note} note - * @param {string|null} deleteId - * @param {TaskContext} taskContext - */ -function deleteNote(note, deleteId, taskContext) { - for (const branch of note.getParentBranches()) { - deleteBranch(branch, deleteId, taskContext); - } -} - /** * @param {string} noteId * @param {TaskContext} taskContext @@ -938,8 +876,6 @@ module.exports = { createNewNote, createNewNoteWithTarget, updateNote, - deleteBranch, - deleteNote, undeleteNote, protectNoteRecursively, scanForLinks,