mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	more relation events, events are now not triggered on sync changes
This commit is contained in:
		| @@ -6,6 +6,9 @@ const protectedSessionService = require('../services/protected_session'); | ||||
| const repository = require('../services/repository'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
|  | ||||
| const LABEL = 'label'; | ||||
| const RELATION = 'relation'; | ||||
|  | ||||
| class Note extends Entity { | ||||
|     static get tableName() { return "notes"; } | ||||
|     static get primaryKeyName() { return "noteId"; } | ||||
| @@ -78,6 +81,14 @@ class Note extends Entity { | ||||
|         return this.__attributeCache; | ||||
|     } | ||||
|  | ||||
|     async getLabels() { | ||||
|         return (await this.getAttributes()).filter(attr => attr.type === LABEL); | ||||
|     } | ||||
|  | ||||
|     async getRelations() { | ||||
|         return (await this.getAttributes()).filter(attr => attr.type === RELATION); | ||||
|     } | ||||
|  | ||||
|     invalidateAttributeCache() { | ||||
|         this.__attributeCache = null; | ||||
|     } | ||||
| @@ -145,55 +156,55 @@ class Note extends Entity { | ||||
|         this.__attributeCache = filteredAttributes; | ||||
|     } | ||||
|  | ||||
|     async hasLabel(name) { | ||||
|         return !!await this.getLabel(name); | ||||
|     async hasAttribute(type, name) { | ||||
|         return !!await this.getAttribute(type, name); | ||||
|     } | ||||
|  | ||||
|     // WARNING: this doesn't take into account the possibility to have multi-valued labels! | ||||
|     async getLabel(name) { | ||||
|     async getAttribute(type, name) { | ||||
|         const attributes = await this.getAttributes(); | ||||
|  | ||||
|         return attributes.find(attr => attr.type === 'label' && attr.name === name); | ||||
|         return attributes.find(attr => attr.type === type && attr.name === name); | ||||
|     } | ||||
|  | ||||
|     async getLabelValue(name) { | ||||
|         const label = await this.getLabel(name); | ||||
|     async getAttributeValue(type, name) { | ||||
|         const attr = await this.getAttribute(type, name); | ||||
|  | ||||
|         return label ? label.value : null; | ||||
|         return attr ? attr.value : null; | ||||
|     } | ||||
|  | ||||
|     async toggleLabel(enabled, name, value = "") { | ||||
|     async toggleAttribute(type, enabled, name, value = "") { | ||||
|         if (enabled) { | ||||
|             await this.setLabel(name, value); | ||||
|             await this.setAttribute(type, name, value); | ||||
|         } | ||||
|         else { | ||||
|             await this.removeLabel(name, value); | ||||
|             await this.removeAttribute(type, name, value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async setLabel(name, value = "") { | ||||
|     async setAttribute(type, name, value = "") { | ||||
|         const attributes = await this.getOwnedAttributes(); | ||||
|         let label = attributes.find(attr => attr.type === 'label' && attr.value === value); | ||||
|         let attr = attributes.find(attr => attr.type === type && attr.value === value); | ||||
|  | ||||
|         if (!label) { | ||||
|             label = new Attribute({ | ||||
|         if (!attr) { | ||||
|             attr = new Attribute({ | ||||
|                 noteId: this.noteId, | ||||
|                 type: 'label', | ||||
|                 type: type, | ||||
|                 name: name, | ||||
|                 value: value | ||||
|             }); | ||||
|  | ||||
|             await label.save(); | ||||
|             await attr.save(); | ||||
|  | ||||
|             this.invalidateAttributeCache(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async removeLabel(name, value = "") { | ||||
|     async removeAttribute(type, name, value = "") { | ||||
|         const attributes = await this.getOwnedAttributes(); | ||||
|  | ||||
|         for (const attribute of attributes) { | ||||
|             if (attribute.type === 'label' && (!value || value === attribute.value)) { | ||||
|             if (attribute.type === type && (!value || value === attribute.value)) { | ||||
|                 attribute.isDeleted = true; | ||||
|                 await attribute.save(); | ||||
|  | ||||
| @@ -202,6 +213,24 @@ class Note extends Entity { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async hasLabel(name) { return await this.hasAttribute(LABEL, name); } | ||||
|     async hasRelation(name) { return await this.hasAttribute(RELATION, name); } | ||||
|  | ||||
|     async getLabel(name) { return await this.getAttribute(LABEL, name); } | ||||
|     async getRelation(name) { return await this.getAttribute(RELATION, name); } | ||||
|  | ||||
|     async getLabelValue(name) { return await this.getAttributeValue(LABEL, name); } | ||||
|     async getRelationValue(name) { return await this.getAttributeValue(RELATION, name); } | ||||
|  | ||||
|     async toggleLabel(enabled, name, value = "") { return await this.toggleAttribute(LABEL, enabled, name, value); } | ||||
|     async toggleRelation(enabled, name, value = "") { return await this.toggleAttribute(RELATION, enabled, name, value); } | ||||
|  | ||||
|     async setLabel(name, value = "") { return await this.setAttribute(LABEL, name, value); } | ||||
|     async setRelation(name, value = "") { return await this.setAttribute(RELATION, name, value); } | ||||
|  | ||||
|     async removeLabel(name, value = "") { return await this.removeAttribute(LABEL, name, value); } | ||||
|     async removeRelation(name, value = "") { return await this.removeAttribute(RELATION, name, value); } | ||||
|  | ||||
|     async getRevisions() { | ||||
|         return await repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]); | ||||
|     } | ||||
|   | ||||
| @@ -19,7 +19,11 @@ const BUILTIN_ATTRIBUTES = [ | ||||
|  | ||||
|     // relation names | ||||
|     { type: 'relation', name: 'runOnNoteView' }, | ||||
|     { type: 'relation', name: 'runOnNoteCreation' }, | ||||
|     { type: 'relation', name: 'runOnNoteTitleChange' }, | ||||
|     { type: 'relation', name: 'runOnNoteChange' }, | ||||
|     { type: 'relation', name: 'runOnChildNoteCreation' }, | ||||
|     { type: 'relation', name: 'runOnAttributeCreation' }, | ||||
|     { type: 'relation', name: 'runOnAttributeChange' }, | ||||
|     { type: 'relation', name: 'inheritAttributes' } | ||||
| ]; | ||||
| @@ -59,20 +63,19 @@ async function createAttribute(attribute) { | ||||
| } | ||||
|  | ||||
| async function getAttributeNames(type, nameLike) { | ||||
|     let names; | ||||
|     nameLike = nameLike.toLowerCase(); | ||||
|  | ||||
|     if (!nameLike) { | ||||
|         names = BUILTIN_ATTRIBUTES | ||||
|             .filter(attribute => attribute.type === type) | ||||
|             .map(attribute => attribute.name); | ||||
|     } | ||||
|     else { | ||||
|         names = await sql.getColumn( | ||||
|     const names = await sql.getColumn( | ||||
|         `SELECT DISTINCT name  | ||||
|              FROM attributes  | ||||
|              WHERE isDeleted = 0 | ||||
|                AND type = ? | ||||
|                AND name LIKE '%${utils.sanitizeSql(nameLike)}%'`, [type]); | ||||
|  | ||||
|     for (const attr of BUILTIN_ATTRIBUTES) { | ||||
|         if (attr.type === type && attr.name.toLowerCase().includes(nameLike) && !names.includes(attr.name)) { | ||||
|             names.push(attr.name); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     names.sort(); | ||||
|   | ||||
| @@ -2,7 +2,9 @@ const log = require('./log'); | ||||
|  | ||||
| const NOTE_TITLE_CHANGED = "NOTE_TITLE_CHANGED"; | ||||
| const ENTER_PROTECTED_SESSION = "ENTER_PROTECTED_SESSION"; | ||||
| const ENTITY_CREATED = "ENTITY_CREATED"; | ||||
| const ENTITY_CHANGED = "ENTITY_CHANGED"; | ||||
| const CHILD_NOTE_CREATED = "CHILD_NOTE_CREATED"; | ||||
|  | ||||
| const eventListeners = {}; | ||||
|  | ||||
| @@ -33,5 +35,7 @@ module.exports = { | ||||
|     // event types: | ||||
|     NOTE_TITLE_CHANGED, | ||||
|     ENTER_PROTECTED_SESSION, | ||||
|     ENTITY_CHANGED | ||||
|     ENTITY_CREATED, | ||||
|     ENTITY_CHANGED, | ||||
|     CHILD_NOTE_CREATED | ||||
| }; | ||||
| @@ -2,12 +2,10 @@ const eventService = require('./events'); | ||||
| const scriptService = require('./script'); | ||||
| const treeService = require('./tree'); | ||||
| const messagingService = require('./messaging'); | ||||
| const repository = require('./repository'); | ||||
| const log = require('./log'); | ||||
|  | ||||
| async function runAttachedRelations(note, relationName, originEntity) { | ||||
|     const attributes = await note.getAttributes(); | ||||
|     const runRelations = attributes.filter(relation => relation.type === 'relation' && relation.name === relationName); | ||||
|     const runRelations = (await note.getRelations()).filter(relation => relation.name === relationName); | ||||
|  | ||||
|     for (const relation of runRelations) { | ||||
|         const scriptNote = await relation.getTargetNote(); | ||||
| @@ -37,10 +35,24 @@ eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => { | ||||
|     } | ||||
| }); | ||||
|  | ||||
| eventService.subscribe(eventService.ENTITY_CHANGED, async ({ entityId, entityName }) => { | ||||
| eventService.subscribe(eventService.ENTITY_CHANGED, async ({ entityName, entity }) => { | ||||
|     if (entityName === 'attributes') { | ||||
|         const attribute = await repository.getEntityFromName(entityName, entityId); | ||||
|  | ||||
|         await runAttachedRelations(await attribute.getNote(), 'runOnAttributeChange', attribute); | ||||
|         await runAttachedRelations(await entity.getNote(), 'runOnAttributeChange', entity); | ||||
|     } | ||||
|     else if (entityName === 'notes') { | ||||
|         await runAttachedRelations(entity, 'runOnNoteChange', entity); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| eventService.subscribe(eventService.ENTITY_CREATED, async ({ entityName, entity }) => { | ||||
|     if (entityName === 'attributes') { | ||||
|         await runAttachedRelations(await entity.getNote(), 'runOnAttributeCreation', entity); | ||||
|     } | ||||
|     else if (entityName === 'notes') { | ||||
|         await runAttachedRelations(entity, 'runOnNoteCreation', entity); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| eventService.subscribe(eventService.CHILD_NOTE_CREATED, async ({ parentNote, childNote }) => { | ||||
|     await runAttachedRelations(parentNote, 'runOnChildNoteCreation', childNote); | ||||
| }); | ||||
| @@ -228,13 +228,13 @@ function getNotePath(noteId) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId}) => { | ||||
| eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entity}) => { | ||||
|     if (!loaded) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (entityName === 'notes') { | ||||
|         const note = await repository.getNote(entityId); | ||||
|         const note = entity; | ||||
|  | ||||
|         if (note.isDeleted) { | ||||
|             delete noteTitles[note.noteId]; | ||||
| @@ -245,7 +245,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId | ||||
|         } | ||||
|     } | ||||
|     else if (entityName === 'branches') { | ||||
|         const branch = await repository.getBranch(entityId); | ||||
|         const branch = entity; | ||||
|  | ||||
|         if (childToParent[branch.noteId]) { | ||||
|             childToParent[branch.noteId] = childToParent[branch.noteId].filter(noteId => noteId !== branch.parentNoteId) | ||||
| @@ -266,7 +266,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId | ||||
|         } | ||||
|     } | ||||
|     else if (entityName === 'attributes') { | ||||
|         const attribute = await repository.getAttribute(entityId); | ||||
|         const attribute = entity; | ||||
|  | ||||
|         if (attribute.type === 'label' && attribute.name === 'archived') { | ||||
|             // we're not using label object directly, since there might be other non-deleted archived label | ||||
|   | ||||
| @@ -35,6 +35,10 @@ async function getNewNotePosition(parentNoteId, noteData) { | ||||
|     return newNotePos; | ||||
| } | ||||
|  | ||||
| async function triggerChildNoteCreated(childNote, parentNote) { | ||||
|     await eventService.emit(eventService.CHILD_NOTE_CREATED, { childNote, parentNote }); | ||||
| } | ||||
|  | ||||
| async function triggerNoteTitleChanged(note) { | ||||
|     await eventService.emit(eventService.NOTE_TITLE_CHANGED, note); | ||||
| } | ||||
| @@ -42,12 +46,10 @@ async function triggerNoteTitleChanged(note) { | ||||
| async function createNewNote(parentNoteId, noteData) { | ||||
|     const newNotePos = await getNewNotePosition(parentNoteId, noteData); | ||||
|  | ||||
|     if (parentNoteId !== 'root') { | ||||
|         const parent = await repository.getNote(parentNoteId); | ||||
|     const parentNote = await repository.getNote(parentNoteId); | ||||
|  | ||||
|         noteData.type = noteData.type || parent.type; | ||||
|         noteData.mime = noteData.mime || parent.mime; | ||||
|     } | ||||
|     noteData.type = noteData.type || parentNote.type; | ||||
|     noteData.mime = noteData.mime || parentNote.mime; | ||||
|  | ||||
|     const note = await new Note({ | ||||
|         title: noteData.title, | ||||
| @@ -66,6 +68,7 @@ async function createNewNote(parentNoteId, noteData) { | ||||
|     }).save(); | ||||
|  | ||||
|     await triggerNoteTitleChanged(note); | ||||
|     await triggerChildNoteCreated(note, parentNote); | ||||
|  | ||||
|     return { | ||||
|         note, | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| const sql = require('./sql'); | ||||
| const syncTableService = require('../services/sync_table'); | ||||
| const eventService = require('./events'); | ||||
|  | ||||
| let entityConstructor; | ||||
|  | ||||
| @@ -56,6 +57,11 @@ async function getOption(name) { | ||||
| } | ||||
|  | ||||
| async function updateEntity(entity) { | ||||
|     const entityName = entity.constructor.tableName; | ||||
|     const primaryKeyName = entity.constructor.primaryKeyName; | ||||
|  | ||||
|     const isNewEntity = !entity[primaryKeyName]; | ||||
|  | ||||
|     if (entity.beforeSaving) { | ||||
|         await entity.beforeSaving(); | ||||
|     } | ||||
| @@ -75,12 +81,24 @@ async function updateEntity(entity) { | ||||
|     } | ||||
|  | ||||
|     await sql.transactional(async () => { | ||||
|         await sql.replace(entity.constructor.tableName, clone); | ||||
|         await sql.replace(entityName, clone); | ||||
|  | ||||
|         const primaryKey = entity[entity.constructor.primaryKeyName]; | ||||
|         const primaryKey = entity[primaryKeyName]; | ||||
|  | ||||
|         if (entity.isChanged && (entity.constructor.tableName !== 'options' || entity.isSynced)) { | ||||
|             await syncTableService.addEntitySync(entity.constructor.tableName, primaryKey); | ||||
|         if (entity.isChanged && (entityName !== 'options' || entity.isSynced)) { | ||||
|             await syncTableService.addEntitySync(entityName, primaryKey); | ||||
|  | ||||
|             if (isNewEntity) { | ||||
|                 await eventService.emit(eventService.ENTITY_CREATED, { | ||||
|                     entityName, | ||||
|                     entity | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             await eventService.emit(eventService.ENTITY_CHANGED, { | ||||
|                 entityName, | ||||
|                 entity | ||||
|             }); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|   | ||||
| @@ -3,7 +3,6 @@ const sourceIdService = require('./source_id'); | ||||
| const dateUtils = require('./date_utils'); | ||||
| const log = require('./log'); | ||||
| const cls = require('./cls'); | ||||
| const eventService = require('./events'); | ||||
|  | ||||
| async function addNoteSync(noteId, sourceId) { | ||||
|     await addEntitySync("notes", noteId, sourceId) | ||||
| @@ -52,11 +51,6 @@ async function addEntitySync(entityName, entityId, sourceId) { | ||||
|         syncDate: dateUtils.nowDate(), | ||||
|         sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId() | ||||
|     }); | ||||
|  | ||||
|     await eventService.emit(eventService.ENTITY_CHANGED, { | ||||
|         entityName, | ||||
|         entityId | ||||
|     }); | ||||
| } | ||||
|  | ||||
| async function cleanupSyncRowsForMissingEntities(entityName, entityKey) { | ||||
| @@ -116,6 +110,5 @@ module.exports = { | ||||
|     addAttributeSync, | ||||
|     addApiTokenSync, | ||||
|     addEntitySync, | ||||
|     cleanupSyncRowsForMissingEntities, | ||||
|     fillAllSyncRows | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user