diff --git a/src/public/app/widgets/search_actions/execute_script.js b/src/public/app/widgets/search_actions/execute_script.js new file mode 100644 index 000000000..a0e76dadf --- /dev/null +++ b/src/public/app/widgets/search_actions/execute_script.js @@ -0,0 +1,39 @@ +import SpacedUpdate from "../../services/spaced_update.js"; +import AbstractAction from "./abstract_action.js"; + +const TPL = ` + + + Execute script: + + +
+
Script:
+ + +
+ + + + +`; + +export default class ExecuteScriptSearchAction extends AbstractAction { + static get actionName() { return "executeScript"; } + + doRender() { + const $action = $(TPL); + const $script = $action.find('.script'); + $script.val(this.actionDef.script || ""); + + const spacedUpdate = new SpacedUpdate(async () => { + await this.saveAction({ script: $script.val() }); + }, 1000) + + $script.on('input', () => spacedUpdate.scheduleUpdate()); + + return $action; + } +} diff --git a/src/public/app/widgets/search_actions/rename_relation.js b/src/public/app/widgets/search_actions/rename_relation.js new file mode 100644 index 000000000..8ec6ed41a --- /dev/null +++ b/src/public/app/widgets/search_actions/rename_relation.js @@ -0,0 +1,59 @@ +import SpacedUpdate from "../../services/spaced_update.js"; +import AbstractAction from "./abstract_action.js"; + +const TPL = ` + + + Rename relation: + + +
+
+
From:
+ + + +
To:
+ + +
+
+ + + + +`; + +export default class RenameRelationSearchAction extends AbstractAction { + static get actionName() { return "renameRelation"; } + + doRender() { + const $action = $(TPL); + + const $oldRelationName = $action.find('.old-relation-name'); + $oldRelationName.val(this.actionDef.oldRelationName || ""); + + const $newRelationName = $action.find('.new-relation-name'); + $newRelationName.val(this.actionDef.newRelationName || ""); + + const spacedUpdate = new SpacedUpdate(async () => { + await this.saveAction({ + oldRelationName: $oldRelationName.val(), + newRelationName: $newRelationName.val() + }); + }, 1000) + + $oldRelationName.on('input', () => spacedUpdate.scheduleUpdate()); + $newRelationName.on('input', () => spacedUpdate.scheduleUpdate()); + + return $action; + } +} diff --git a/src/public/app/widgets/search_definition.js b/src/public/app/widgets/search_definition.js index 01ec82f65..e67ee9b52 100644 --- a/src/public/app/widgets/search_definition.js +++ b/src/public/app/widgets/search_definition.js @@ -12,6 +12,8 @@ import DeleteRelationSearchAction from "./search_actions/delete_relation.js"; import RenameLabelSearchAction from "./search_actions/rename_label.js"; import SetLabelValueSearchAction from "./search_actions/set_label_value.js"; import SetRelationTargetSearchAction from "./search_actions/set_relation_target.js"; +import RenameRelationSearchAction from "./search_actions/rename_relation.js"; +import ExecuteScriptSearchAction from "./search_actions/execute_script.js"; const TPL = `
@@ -205,8 +207,10 @@ for (const clazz of [ DeleteLabelSearchAction, DeleteRelationSearchAction, RenameLabelSearchAction, + RenameRelationSearchAction, SetLabelValueSearchAction, - SetRelationTargetSearchAction + SetRelationTargetSearchAction, + ExecuteScriptSearchAction ]) { ACTION_CLASSES[clazz.actionName] = clazz; } diff --git a/src/routes/api/search.js b/src/routes/api/search.js index ddd9cdc73..f1580e60f 100644 --- a/src/routes/api/search.js +++ b/src/routes/api/search.js @@ -71,14 +71,46 @@ const ACTION_HANDLERS = { note.isDeleted; note.save(); }, + deleteLabel: (action, note) => { + for (const label of note.getOwnedLabels(action.labelName)) { + label.isDeleted = true; + label.save(); + } + }, + deleteRelation: (action, note) => { + for (const relation of note.getOwnedRelations(action.relationName)) { + relation.isDeleted = true; + relation.save(); + } + }, renameLabel: (action, note) => { for (const label of note.getOwnedLabels(action.oldLabelName)) { label.name = action.newLabelName; label.save(); } }, + renameRelation: (action, note) => { + for (const relation of note.getOwnedRelations(action.oldRelationName)) { + relation.name = action.newRelationName; + relation.save(); + } + }, setLabelValue: (action, note) => { note.setLabel(action.labelName, action.labelValue); + }, + setRelationTarget: (action, note) => { + note.setRelation(action.relationName, action.targetNoteId); + }, + executeScript: (action, note) => { + if (!action.script || !action.script.trim()) { + log.info("Ignoring executeScript since the script is empty.") + return; + } + + const scriptFunc = new Function("note", action.script); + scriptFunc(note); + + note.save(); } }; @@ -132,9 +164,14 @@ async function searchAndExecute(req) { } for (const action of actions) { - ACTION_HANDLERS[action.name](action, resultNote); + try { + log.info(`Applying action handler to note ${resultNote.noteId}: ${JSON.stringify(action)}`); - log.info(`Applying action handler to note ${resultNote.noteId}: ${JSON.stringify(action)}`); + ACTION_HANDLERS[action.name](action, resultNote); + } + catch (e) { + log.error(`ExecuteScript search action failed with ${e.message}`); + } } } }