mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	added runOnAttributeChange event
This commit is contained in:
		| @@ -28,6 +28,18 @@ class Attribute extends Entity { | |||||||
|         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); |         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     async getTargetNote() { | ||||||
|  |         if (this.type !== 'relation') { | ||||||
|  |             throw new Error(`Attribute ${this.attributeId} is not relation`); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.value) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.value]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     isDefinition() { |     isDefinition() { | ||||||
|         return this.type === 'label-definition' || this.type === 'relation-definition'; |         return this.type === 'label-definition' || this.type === 'relation-definition'; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -9,6 +9,22 @@ const ApiToken = require('../entities/api_token'); | |||||||
| const Option = require('../entities/option'); | const Option = require('../entities/option'); | ||||||
| const repository = require('../services/repository'); | const repository = require('../services/repository'); | ||||||
|  |  | ||||||
|  | const TABLE_NAME_TO_ENTITY = { | ||||||
|  |     "attributes": Attribute.constructor, | ||||||
|  |     "images": Image.constructor, | ||||||
|  |     "note_images": NoteImage.constructor, | ||||||
|  |     "branches": Branch.constructor, | ||||||
|  |     "notes": Note.constructor, | ||||||
|  |     "note_revisions": NoteRevision.constructor, | ||||||
|  |     "recent_notes": RecentNote.constructor, | ||||||
|  |     "options": Option.constructor, | ||||||
|  |     "api_tokens": ApiToken.constructor, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | function getEntityFromTableName(tableName) { | ||||||
|  |     return TABLE_NAME_TO_ENTITY[tableName]; | ||||||
|  | } | ||||||
|  |  | ||||||
| function createEntityFromRow(row) { | function createEntityFromRow(row) { | ||||||
|     let entity; |     let entity; | ||||||
|  |  | ||||||
| @@ -46,8 +62,9 @@ function createEntityFromRow(row) { | |||||||
|     return entity; |     return entity; | ||||||
| } | } | ||||||
|  |  | ||||||
| repository.setEntityConstructor(createEntityFromRow); |  | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|     createEntityFromRow |     createEntityFromRow, | ||||||
|  |     getEntityFromTableName | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | repository.setEntityConstructor(module.exports); | ||||||
|   | |||||||
| @@ -1,14 +1,14 @@ | |||||||
| import ScriptContext from "./script_context.js"; | import ScriptContext from "./script_context.js"; | ||||||
| import server from "./server.js"; | import server from "./server.js"; | ||||||
|  |  | ||||||
| async function getAndExecuteBundle(noteId, workNote = null) { | async function getAndExecuteBundle(noteId, workEntity = null) { | ||||||
|     const bundle = await server.get('script/bundle/' + noteId); |     const bundle = await server.get('script/bundle/' + noteId); | ||||||
|  |  | ||||||
|     await executeBundle(bundle, workNote); |     await executeBundle(bundle, workEntity); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function executeBundle(bundle, workNote) { | async function executeBundle(bundle, workEntity) { | ||||||
|     const apiContext = ScriptContext(bundle.note, bundle.allNotes, workNote); |     const apiContext = ScriptContext(bundle.note, bundle.allNotes, workEntity); | ||||||
|  |  | ||||||
|     return await (function () { |     return await (function () { | ||||||
|         return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`); |         return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`); | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ import utils from './utils.js'; | |||||||
| import infoService from './info.js'; | import infoService from './info.js'; | ||||||
| import linkService from './link.js'; | import linkService from './link.js'; | ||||||
|  |  | ||||||
| function ScriptApi(startNote, currentNote, workNote = null) { | function ScriptApi(startNote, currentNote, workEntity = null) { | ||||||
|     const $pluginButtons = $("#plugin-buttons"); |     const $pluginButtons = $("#plugin-buttons"); | ||||||
|  |  | ||||||
|     async function activateNote(notePath) { |     async function activateNote(notePath) { | ||||||
| @@ -44,7 +44,8 @@ function ScriptApi(startNote, currentNote, workNote = null) { | |||||||
|             params: prepareParams(params), |             params: prepareParams(params), | ||||||
|             startNoteId: startNote.noteId, |             startNoteId: startNote.noteId, | ||||||
|             currentNoteId: currentNote.noteId, |             currentNoteId: currentNote.noteId, | ||||||
|             workNoteId: workNote ? workNote.noteId : null |             workEntityName: workEntity ? workEntity.constructor.tableName() : null | ||||||
|  |             workEntityId: workEntity ? workEntity.noteId : null | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         return ret.executionResult; |         return ret.executionResult; | ||||||
| @@ -53,7 +54,7 @@ function ScriptApi(startNote, currentNote, workNote = null) { | |||||||
|     return { |     return { | ||||||
|         startNote: startNote, |         startNote: startNote, | ||||||
|         currentNote: currentNote, |         currentNote: currentNote, | ||||||
|         workNote: workNote, |         workEntity: workEntity, | ||||||
|         addButtonToToolbar, |         addButtonToToolbar, | ||||||
|         activateNote, |         activateNote, | ||||||
|         getInstanceName: () => window.glob.instanceName, |         getInstanceName: () => window.glob.instanceName, | ||||||
|   | |||||||
| @@ -1,13 +1,13 @@ | |||||||
| import ScriptApi from './script_api.js'; | import ScriptApi from './script_api.js'; | ||||||
| import utils from './utils.js'; | import utils from './utils.js'; | ||||||
|  |  | ||||||
| function ScriptContext(startNote, allNotes, workNote = null) { | function ScriptContext(startNote, allNotes, workEntity = null) { | ||||||
|     const modules = {}; |     const modules = {}; | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|         modules: modules, |         modules: modules, | ||||||
|         notes: utils.toObject(allNotes, note => [note.noteId, note]), |         notes: utils.toObject(allNotes, note => [note.noteId, note]), | ||||||
|         apis: utils.toObject(allNotes, note => [note.noteId, ScriptApi(startNote, note, workNote)]), |         apis: utils.toObject(allNotes, note => [note.noteId, ScriptApi(startNote, note, workEntity)]), | ||||||
|         require: moduleNoteIds => { |         require: moduleNoteIds => { | ||||||
|             return moduleName => { |             return moduleName => { | ||||||
|                 const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId)); |                 const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId)); | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ const repository = require('../../services/repository'); | |||||||
|  |  | ||||||
| async function exec(req) { | async function exec(req) { | ||||||
|     const result = await scriptService.executeScript(req.body.script, req.body.params, req.body.startNoteId, |     const result = await scriptService.executeScript(req.body.script, req.body.params, req.body.startNoteId, | ||||||
|         req.body.currentNoteId, req.body.workNoteId); |         req.body.currentNoteId, req.body.workEntityName, req.body.workEntityId); | ||||||
|  |  | ||||||
|     return { executionResult: result }; |     return { executionResult: result }; | ||||||
| } | } | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							| @@ -2,16 +2,21 @@ 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'); | ||||||
|  |  | ||||||
| eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => { | async function runAttachedRelations(note, relationName, workEntity) { | ||||||
|     const attributes = await note.getAttributes(); |     const attributes = await note.getAttributes(); | ||||||
|     const runRelations = attributes.filter(relation => relation.type === 'relation' && relation.name === 'runOnNoteTitleChange'); |     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(); | ||||||
|  |  | ||||||
|         await scriptService.executeNote(scriptNote, scriptNote, note); |         await scriptService.executeNote(scriptNote, scriptNote, workEntity); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => { | ||||||
|  |     await runAttachedRelations(note, 'runOnNoteTitleChange', note); | ||||||
|  |  | ||||||
|     if (!note.isRoot()) { |     if (!note.isRoot()) { | ||||||
|         const parents = await note.getParentNotes(); |         const parents = await note.getParentNotes(); | ||||||
| @@ -25,3 +30,11 @@ eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | eventService.subscribe(eventService.ENTITY_CHANGED, async ({ entityId, entityName }) => { | ||||||
|  |     if (entityName === 'attributes') { | ||||||
|  |         const attribute = await repository.getEntityFromName(entityName, entityId); | ||||||
|  |  | ||||||
|  |         await runAttachedRelations(await attribute.getNote(), 'runOnAttributeChange', attribute); | ||||||
|  |     } | ||||||
|  | }); | ||||||
| @@ -9,10 +9,16 @@ async function setEntityConstructor(constructor) { | |||||||
|     entityConstructor = constructor; |     entityConstructor = constructor; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | async function getEntityFromName(entityName, entityId) { | ||||||
|  |     const entityConstructor = entityConstructor.getEntityFromTableName(entityName); | ||||||
|  |  | ||||||
|  |     return await getEntity(`SELECT * FROM ${entityConstructor.tableName()} WHERE ${entityConstructor.primaryKeyName()} = ?`, [entityId]); | ||||||
|  | } | ||||||
|  |  | ||||||
| async function getEntities(query, params = []) { | async function getEntities(query, params = []) { | ||||||
|     const rows = await sql.getRows(query, params); |     const rows = await sql.getRows(query, params); | ||||||
|  |  | ||||||
|     return rows.map(entityConstructor); |     return rows.map(entityConstructor.createEntityFromRow); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function getEntity(query, params = []) { | async function getEntity(query, params = []) { | ||||||
| @@ -22,7 +28,7 @@ async function getEntity(query, params = []) { | |||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return entityConstructor(row); |     return entityConstructor.createEntityFromRow(row); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function getNote(noteId) { | async function getNote(noteId) { | ||||||
| @@ -73,6 +79,7 @@ async function updateEntity(entity) { | |||||||
| } | } | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|  |     getEntityFromName, | ||||||
|     getEntities, |     getEntities, | ||||||
|     getEntity, |     getEntity, | ||||||
|     getNote, |     getNote, | ||||||
|   | |||||||
| @@ -4,17 +4,17 @@ const repository = require('./repository'); | |||||||
| const cls = require('./cls'); | const cls = require('./cls'); | ||||||
| const sourceIdService = require('./source_id'); | const sourceIdService = require('./source_id'); | ||||||
|  |  | ||||||
| async function executeNote(note, targetNote) { | async function executeNote(note, workEntity) { | ||||||
|     if (!note.isJavaScript()) { |     if (!note.isJavaScript()) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const bundle = await getScriptBundle(note); |     const bundle = await getScriptBundle(note); | ||||||
|  |  | ||||||
|     await executeBundle(bundle, note, targetNote); |     await executeBundle(bundle, note, workEntity); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function executeBundle(bundle, startNote, workNote = null) { | async function executeBundle(bundle, startNote, workEntity = null) { | ||||||
|     if (!startNote) { |     if (!startNote) { | ||||||
|         // this is the default case, the only exception is when we want to preserve frontend startNote |         // this is the default case, the only exception is when we want to preserve frontend startNote | ||||||
|         startNote = bundle.note; |         startNote = bundle.note; | ||||||
| @@ -23,7 +23,7 @@ async function executeBundle(bundle, startNote, workNote = null) { | |||||||
|     // last \r\n is necessary if script contains line comment on its last line |     // last \r\n is necessary if script contains line comment on its last line | ||||||
|     const script = "async function() {\r\n" + bundle.script + "\r\n}"; |     const script = "async function() {\r\n" + bundle.script + "\r\n}"; | ||||||
|  |  | ||||||
|     const ctx = new ScriptContext(startNote, bundle.allNotes, workNote); |     const ctx = new ScriptContext(startNote, bundle.allNotes, workEntity); | ||||||
|  |  | ||||||
|     if (await bundle.note.hasLabel('manualTransactionHandling')) { |     if (await bundle.note.hasLabel('manualTransactionHandling')) { | ||||||
|         return await execute(ctx, script, ''); |         return await execute(ctx, script, ''); | ||||||
| @@ -37,10 +37,10 @@ async function executeBundle(bundle, startNote, workNote = null) { | |||||||
|  * This method preserves frontend startNode - that's why we start execution from currentNote and override |  * This method preserves frontend startNode - that's why we start execution from currentNote and override | ||||||
|  * bundle's startNote. |  * bundle's startNote. | ||||||
|  */ |  */ | ||||||
| async function executeScript(script, params, startNoteId, currentNoteId, targetNoteId) { | async function executeScript(script, params, startNoteId, currentNoteId, workEntityName, workEntityId) { | ||||||
|     const startNote = await repository.getNote(startNoteId); |     const startNote = await repository.getNote(startNoteId); | ||||||
|     const currentNote = await repository.getNote(currentNoteId); |     const currentNote = await repository.getNote(currentNoteId); | ||||||
|     const targetNote = await repository.getNote(targetNoteId); |     const workEntity = await repository.getEntityFromName(workEntityName, workEntityId); | ||||||
|  |  | ||||||
|     currentNote.content = `return await (${script}\r\n)(${getParams(params)})`; |     currentNote.content = `return await (${script}\r\n)(${getParams(params)})`; | ||||||
|     currentNote.type = 'code'; |     currentNote.type = 'code'; | ||||||
| @@ -48,7 +48,7 @@ async function executeScript(script, params, startNoteId, currentNoteId, targetN | |||||||
|  |  | ||||||
|     const bundle = await getScriptBundle(currentNote); |     const bundle = await getScriptBundle(currentNote); | ||||||
|  |  | ||||||
|     return await executeBundle(bundle, startNote, targetNote); |     return await executeBundle(bundle, startNote, workEntity); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function execute(ctx, script, paramsStr) { | async function execute(ctx, script, paramsStr) { | ||||||
|   | |||||||
| @@ -10,10 +10,10 @@ const config = require('./config'); | |||||||
| const repository = require('./repository'); | const repository = require('./repository'); | ||||||
| const axios = require('axios'); | const axios = require('axios'); | ||||||
|  |  | ||||||
| function ScriptContext(startNote, allNotes, workNote = null) { | function ScriptContext(startNote, allNotes, workEntity = null) { | ||||||
|     this.modules = {}; |     this.modules = {}; | ||||||
|     this.notes = utils.toObject(allNotes, note => [note.noteId, note]); |     this.notes = utils.toObject(allNotes, note => [note.noteId, note]); | ||||||
|     this.apis = utils.toObject(allNotes, note => [note.noteId, new ScriptApi(startNote, note, workNote)]); |     this.apis = utils.toObject(allNotes, note => [note.noteId, new ScriptApi(startNote, note, workEntity)]); | ||||||
|     this.require = moduleNoteIds => { |     this.require = moduleNoteIds => { | ||||||
|         return moduleName => { |         return moduleName => { | ||||||
|             const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId)); |             const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId)); | ||||||
| @@ -28,10 +28,10 @@ function ScriptContext(startNote, allNotes, workNote = null) { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| function ScriptApi(startNote, currentNote, workNote) { | function ScriptApi(startNote, currentNote, workEntity) { | ||||||
|     this.startNote = startNote; |     this.startNote = startNote; | ||||||
|     this.currentNote = currentNote; |     this.currentNote = currentNote; | ||||||
|     this.workNote = workNote; |     this.workEntity = workEntity; | ||||||
|  |  | ||||||
|     this.axios = axios; |     this.axios = axios; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user