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