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:
+ |
+
+
+ |
+
+
+ |
+
`;
+
+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}`);
+ }
}
}
}