mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	Merge branch 'master' into trilium-docs-noformat
This commit is contained in:
		| @@ -36,7 +36,10 @@ 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 TaskContext = require("../../services/task_context"); | ||||
| const dayjs = require("dayjs"); | ||||
| const utc = require('dayjs/plugin/utc') | ||||
| dayjs.extend(utc) | ||||
|  | ||||
| const LABEL = 'label'; | ||||
| const RELATION = 'relation'; | ||||
| @@ -112,13 +115,17 @@ class Note extends AbstractEntity { | ||||
|     } | ||||
|  | ||||
|     init() { | ||||
|         /** @type {Branch[]} */ | ||||
|         /** @type {Branch[]} | ||||
|          * @private */ | ||||
|         this.parentBranches = []; | ||||
|         /** @type {Note[]} */ | ||||
|         /** @type {Note[]} | ||||
|          * @private */ | ||||
|         this.parents = []; | ||||
|         /** @type {Note[]} */ | ||||
|         /** @type {Note[]} | ||||
|          * @private*/ | ||||
|         this.children = []; | ||||
|         /** @type {Attribute[]} */ | ||||
|         /** @type {Attribute[]} | ||||
|          * @private */ | ||||
|         this.ownedAttributes = []; | ||||
|  | ||||
|         /** @type {Attribute[]|null} | ||||
| @@ -128,7 +135,8 @@ class Note extends AbstractEntity { | ||||
|          * @private*/ | ||||
|         this.inheritableAttributeCache = null; | ||||
|  | ||||
|         /** @type {Attribute[]} */ | ||||
|         /** @type {Attribute[]} | ||||
|          * @private*/ | ||||
|         this.targetRelations = []; | ||||
|  | ||||
|         this.becca.addNote(this.noteId, this); | ||||
| @@ -142,16 +150,19 @@ class Note extends AbstractEntity { | ||||
|         /** | ||||
|          * size of the content in bytes | ||||
|          * @type {int|null} | ||||
|          * @private | ||||
|          */ | ||||
|         this.contentSize = null; | ||||
|         /** | ||||
|          * size of the content and note revision contents in bytes | ||||
|          * @type {int|null} | ||||
|          * @private | ||||
|          */ | ||||
|         this.noteSize = null; | ||||
|         /** | ||||
|          * number of note revisions for this note | ||||
|          * @type {int|null} | ||||
|          * @private | ||||
|          */ | ||||
|         this.revisionCount = null; | ||||
|     } | ||||
| @@ -253,6 +264,22 @@ class Note extends AbstractEntity { | ||||
|             WHERE noteId = ?`, [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     get dateCreatedObj() { | ||||
|         return this.dateCreated === null ? null : dayjs(this.dateCreated); | ||||
|     } | ||||
|  | ||||
|     get utcDateCreatedObj() { | ||||
|         return this.utcDateCreated === null ? null : dayjs.utc(this.utcDateCreated); | ||||
|     } | ||||
|  | ||||
|     get dateModifiedObj() { | ||||
|         return this.dateModified === null ? null : dayjs(this.dateModified); | ||||
|     } | ||||
|  | ||||
|     get utcDateModifiedObj() { | ||||
|         return this.utcDateModified === null ? null : dayjs.utc(this.utcDateModified); | ||||
|     } | ||||
|  | ||||
|     /** @returns {*} */ | ||||
|     getJsonContent() { | ||||
|         const content = this.getContent(); | ||||
| @@ -378,6 +405,7 @@ class Note extends AbstractEntity { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** @private */ | ||||
|     __getAttributes(path) { | ||||
|         if (path.includes(this.noteId)) { | ||||
|             return []; | ||||
| @@ -400,7 +428,11 @@ class Note extends AbstractEntity { | ||||
|                     const templateNote = this.becca.notes[ownedAttr.value]; | ||||
|  | ||||
|                     if (templateNote) { | ||||
|                         templateAttributes.push(...templateNote.__getAttributes(newPath)); | ||||
|                         templateAttributes.push( | ||||
|                             ...templateNote.__getAttributes(newPath) | ||||
|                                 // template attr is used as a marker for templates, but it's not meant to be inherited | ||||
|                                 .filter(attr => !(attr.type === 'label' && attr.name === 'template')) | ||||
|                         ); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @@ -429,7 +461,10 @@ class Note extends AbstractEntity { | ||||
|         return this.__attributeCache; | ||||
|     } | ||||
|  | ||||
|     /** @returns {Attribute[]} */ | ||||
|     /** | ||||
|      * @private | ||||
|      * @returns {Attribute[]} | ||||
|      */ | ||||
|     __getInheritableAttributes(path) { | ||||
|         if (path.includes(this.noteId)) { | ||||
|             return []; | ||||
| @@ -442,8 +477,18 @@ class Note extends AbstractEntity { | ||||
|         return this.inheritableAttributeCache; | ||||
|     } | ||||
|  | ||||
|     hasAttribute(type, name) { | ||||
|         return !!this.getAttributes().find(attr => attr.type === type && attr.name === name); | ||||
|     /** | ||||
|      * @param type | ||||
|      * @param name | ||||
|      * @param [value] | ||||
|      * @returns {boolean} | ||||
|      */ | ||||
|     hasAttribute(type, name, value) { | ||||
|         return !!this.getAttributes().find(attr => | ||||
|             attr.type === type | ||||
|             && attr.name === name | ||||
|             && (value === undefined || value === null || attr.value === value) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     getAttributeCaseInsensitive(type, name, value) { | ||||
| @@ -464,27 +509,31 @@ class Note extends AbstractEntity { | ||||
|  | ||||
|     /** | ||||
|      * @param {string} name - label name | ||||
|      * @param {string} [value] - label value | ||||
|      * @returns {boolean} true if label exists (including inherited) | ||||
|      */ | ||||
|     hasLabel(name) { return this.hasAttribute(LABEL, name); } | ||||
|     hasLabel(name, value) { return this.hasAttribute(LABEL, name, value); } | ||||
|  | ||||
|     /** | ||||
|      * @param {string} name - label name | ||||
|      * @param {string} [value] - label value | ||||
|      * @returns {boolean} true if label exists (excluding inherited) | ||||
|      */ | ||||
|     hasOwnedLabel(name) { return this.hasOwnedAttribute(LABEL, name); } | ||||
|     hasOwnedLabel(name, value) { return this.hasOwnedAttribute(LABEL, name, value); } | ||||
|  | ||||
|     /** | ||||
|      * @param {string} name - relation name | ||||
|      * @param {string} [value] - relation value | ||||
|      * @returns {boolean} true if relation exists (including inherited) | ||||
|      */ | ||||
|     hasRelation(name) { return this.hasAttribute(RELATION, name); } | ||||
|     hasRelation(name, value) { return this.hasAttribute(RELATION, name, value); } | ||||
|  | ||||
|     /** | ||||
|      * @param {string} name - relation name | ||||
|      * @param {string} [value] - relation value | ||||
|      * @returns {boolean} true if relation exists (excluding inherited) | ||||
|      */ | ||||
|     hasOwnedRelation(name) { return this.hasOwnedAttribute(RELATION, name); } | ||||
|     hasOwnedRelation(name, value) { return this.hasOwnedAttribute(RELATION, name, value); } | ||||
|  | ||||
|     /** | ||||
|      * @param {string} name - label name | ||||
| @@ -537,10 +586,11 @@ class Note extends AbstractEntity { | ||||
|     /** | ||||
|      * @param {string} type - attribute type (label, relation, etc.) | ||||
|      * @param {string} name - attribute name | ||||
|      * @param {string} [value] - attribute value | ||||
|      * @returns {boolean} true if note has an attribute with given type and name (excluding inherited) | ||||
|      */ | ||||
|     hasOwnedAttribute(type, name) { | ||||
|         return !!this.getOwnedAttribute(type, name); | ||||
|     hasOwnedAttribute(type, name, value) { | ||||
|         return !!this.getOwnedAttribute(type, name, value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -627,15 +677,19 @@ class Note extends AbstractEntity { | ||||
|     /** | ||||
|      * @param {string} [type] - (optional) attribute type to filter | ||||
|      * @param {string} [name] - (optional) attribute name to filter | ||||
|      * @param {string} [value] - (optional) attribute value to filter | ||||
|      * @returns {Attribute[]} note's "owned" attributes - excluding inherited ones | ||||
|      */ | ||||
|     getOwnedAttributes(type, name) { | ||||
|     getOwnedAttributes(type, name, value) { | ||||
|         // it's a common mistake to include # or ~ into attribute name | ||||
|         if (name && ["#", "~"].includes(name[0])) { | ||||
|             name = name.substr(1); | ||||
|         } | ||||
|  | ||||
|         if (type && name) { | ||||
|         if (type && name && value !== undefined && value !== null) { | ||||
|             return this.ownedAttributes.filter(attr => attr.type === type && attr.name === name && attr.value === value); | ||||
|         } | ||||
|         else if (type && name) { | ||||
|             return this.ownedAttributes.filter(attr => attr.type === type && attr.name === name); | ||||
|         } | ||||
|         else if (type) { | ||||
| @@ -654,8 +708,8 @@ class Note extends AbstractEntity { | ||||
|      * | ||||
|      * This method can be significantly faster than the getAttribute() | ||||
|      */ | ||||
|     getOwnedAttribute(type, name) { | ||||
|         const attrs = this.getOwnedAttributes(type, name); | ||||
|     getOwnedAttribute(type, name, value) { | ||||
|         const attrs = this.getOwnedAttributes(type, name, value); | ||||
|  | ||||
|         return attrs.length > 0 ? attrs[0] : null; | ||||
|     } | ||||
| @@ -673,9 +727,11 @@ class Note extends AbstractEntity { | ||||
|     sortParents() { | ||||
|         this.parentBranches.sort((a, b) => | ||||
|             a.branchId.startsWith('virt-') | ||||
|             || a.parentNote.hasInheritableOwnedArchivedLabel() ? 1 : -1); | ||||
|             || a.parentNote?.hasInheritableOwnedArchivedLabel() ? 1 : -1); | ||||
|  | ||||
|         this.parents = this.parentBranches.map(branch => branch.parentNote); | ||||
|         this.parents = this.parentBranches | ||||
|             .map(branch => branch.parentNote) | ||||
|             .filter(note => !!note); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -1170,6 +1226,10 @@ class Note extends AbstractEntity { | ||||
|         return this.searchNotesInSubtree(searchString)[0]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param parentNoteId | ||||
|      * @returns {{success: boolean, message: string}} | ||||
|      */ | ||||
|     cloneTo(parentNoteId) { | ||||
|         const cloningService = require("../../services/cloning"); | ||||
|  | ||||
| @@ -1185,6 +1245,10 @@ class Note extends AbstractEntity { | ||||
|      * @param {TaskContext} [taskContext] | ||||
|      */ | ||||
|     deleteNote(deleteId, taskContext) { | ||||
|         if (this.isDeleted) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!deleteId) { | ||||
|             deleteId = utils.randomString(10); | ||||
|         } | ||||
| @@ -1193,6 +1257,11 @@ class Note extends AbstractEntity { | ||||
|             taskContext = new TaskContext('no-progress-reporting'); | ||||
|         } | ||||
|  | ||||
|         // needs to be run before branches and attributes are deleted and thus attached relations disappear | ||||
|         const handlers = require("../../services/handlers"); | ||||
|         handlers.runAttachedRelations(this, 'runOnNoteDeletion', this); | ||||
|         taskContext.noteDeletionHandlerTriggered = true; | ||||
|  | ||||
|         for (const branch of this.getParentBranches()) { | ||||
|             branch.deleteBranch(deleteId, taskContext); | ||||
|         } | ||||
| @@ -1216,6 +1285,41 @@ class Note extends AbstractEntity { | ||||
|         return !(this.noteId in this.becca.notes); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return {NoteRevision|null} | ||||
|      */ | ||||
|     saveNoteRevision() { | ||||
|         const content = this.getContent(); | ||||
|  | ||||
|         if (!content || (Buffer.isBuffer(content) && content.byteLength === 0)) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         const contentMetadata = this.getContentMetadata(); | ||||
|  | ||||
|         const noteRevision = new NoteRevision({ | ||||
|             noteId: this.noteId, | ||||
|             // title and text should be decrypted now | ||||
|             title: this.title, | ||||
|             type: this.type, | ||||
|             mime: this.mime, | ||||
|             isProtected: this.isProtected, | ||||
|             utcDateLastEdited: this.utcDateModified > contentMetadata.utcDateModified | ||||
|                 ? this.utcDateModified | ||||
|                 : contentMetadata.utcDateModified, | ||||
|             utcDateCreated: dateUtils.utcNowDateTime(), | ||||
|             utcDateModified: dateUtils.utcNowDateTime(), | ||||
|             dateLastEdited: this.dateModified > contentMetadata.dateModified | ||||
|                 ? this.dateModified | ||||
|                 : contentMetadata.dateModified, | ||||
|             dateCreated: dateUtils.localNowDateTime() | ||||
|         }, true).save(); | ||||
|  | ||||
|         noteRevision.setContent(content); | ||||
|  | ||||
|         return noteRevision; | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user