@nocobase/plugin-workflow-custom-action-trigger 2.1.0-alpha.20 → 2.1.0-alpha.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/externalVersion.js +11 -10
- package/dist/node_modules/joi/lib/index.js +1 -1
- package/dist/node_modules/joi/package.json +1 -1
- package/dist/server/CustomActionTrigger.d.ts +5 -4
- package/dist/server/CustomActionTrigger.js +38 -8
- package/dist/server/migrations/20260416000000-grant-trigger-action-to-roles.d.ts +40 -0
- package/dist/server/migrations/20260416000000-grant-trigger-action-to-roles.js +120 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"joi","description":"Object schema validation","version":"17.13.3","repository":"git://github.com/hapijs/joi","main":"lib/index.js","types":"lib/index.d.ts","browser":"dist/joi-browser.min.js","files":["lib/**/*","dist/*"],"keywords":["schema","validation"],"dependencies":{"@hapi/hoek":"^9.3.0","@hapi/topo":"^5.1.0","@sideway/address":"^4.1.5","@sideway/formula":"^3.0.1","@sideway/pinpoint":"^2.0.0"},"devDependencies":{"@hapi/bourne":"2.x.x","@hapi/code":"8.x.x","@hapi/joi-legacy-test":"npm:@hapi/joi@15.x.x","@hapi/lab":"^25.1.3","@types/node":"^14.18.63","typescript":"4.3.x"},"scripts":{"prepublishOnly":"cd browser && npm install && npm run build","test":"lab -t 100 -a @hapi/code -L -Y","test-cov-html":"lab -r html -o coverage.html -a @hapi/code"},"license":"BSD-3-Clause","_lastModified":"2026-04-
|
|
1
|
+
{"name":"joi","description":"Object schema validation","version":"17.13.3","repository":"git://github.com/hapijs/joi","main":"lib/index.js","types":"lib/index.d.ts","browser":"dist/joi-browser.min.js","files":["lib/**/*","dist/*"],"keywords":["schema","validation"],"dependencies":{"@hapi/hoek":"^9.3.0","@hapi/topo":"^5.1.0","@sideway/address":"^4.1.5","@sideway/formula":"^3.0.1","@sideway/pinpoint":"^2.0.0"},"devDependencies":{"@hapi/bourne":"2.x.x","@hapi/code":"8.x.x","@hapi/joi-legacy-test":"npm:@hapi/joi@15.x.x","@hapi/lab":"^25.1.3","@types/node":"^14.18.63","typescript":"4.3.x"},"scripts":{"prepublishOnly":"cd browser && npm install && npm run build","test":"lab -t 100 -a @hapi/code -L -Y","test-cov-html":"lab -r html -o coverage.html -a @hapi/code"},"license":"BSD-3-Clause","_lastModified":"2026-04-24T10:05:36.217Z"}
|
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import Joi from 'joi';
|
|
18
18
|
import WorkflowPluginServer, { EventOptions, Trigger, WorkflowModel } from '@nocobase/plugin-workflow';
|
|
19
|
-
import {
|
|
19
|
+
import { Next } from '@nocobase/actions';
|
|
20
|
+
import { ResourcerContext } from '@nocobase/resourcer';
|
|
20
21
|
export default class CustomActionTrigger extends Trigger {
|
|
21
22
|
static TYPE: string;
|
|
22
23
|
configSchema: Joi.ObjectSchema<any>;
|
|
23
24
|
validateConfig(config: Record<string, any>): Record<string, string>;
|
|
24
|
-
globalTriggerAction(context:
|
|
25
|
-
triggerAction: (context:
|
|
25
|
+
globalTriggerAction(context: ResourcerContext, next: Next): Promise<void>;
|
|
26
|
+
triggerAction: (context: ResourcerContext, next: Next) => Promise<any>;
|
|
26
27
|
constructor(workflow: WorkflowPluginServer);
|
|
27
28
|
private processEvents;
|
|
28
29
|
validateContext(values: any, workflow: WorkflowModel): {
|
|
@@ -32,5 +33,5 @@ export default class CustomActionTrigger extends Trigger {
|
|
|
32
33
|
filterByTk: string;
|
|
33
34
|
data?: undefined;
|
|
34
35
|
};
|
|
35
|
-
execute(workflow: WorkflowModel, values:
|
|
36
|
+
execute(workflow: WorkflowModel, values: Record<string, any>, options: EventOptions): Promise<void | import("@nocobase/plugin-workflow").Processor>;
|
|
36
37
|
}
|
|
@@ -104,8 +104,7 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
104
104
|
const syncGroup = [];
|
|
105
105
|
const asyncGroup = [];
|
|
106
106
|
for (const workflow of workflows) {
|
|
107
|
-
const event = [workflow];
|
|
108
|
-
event.push({ data: values, ...userInfo });
|
|
107
|
+
const event = [workflow, { data: values, ...userInfo }];
|
|
109
108
|
(workflow.sync ? syncGroup : asyncGroup).push(event);
|
|
110
109
|
}
|
|
111
110
|
await this.processEvents({
|
|
@@ -115,7 +114,7 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
115
114
|
});
|
|
116
115
|
}
|
|
117
116
|
triggerAction = async (context, next) => {
|
|
118
|
-
var _a;
|
|
117
|
+
var _a, _b, _c;
|
|
119
118
|
const {
|
|
120
119
|
resourceName,
|
|
121
120
|
params: { filterByTk, values, triggerWorkflows = "", associatedIndex }
|
|
@@ -157,22 +156,26 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
157
156
|
workflows.push(workflow);
|
|
158
157
|
}
|
|
159
158
|
}
|
|
159
|
+
const scopeFilter = (_c = (_b = context.permission) == null ? void 0 : _b.parsedParams) == null ? void 0 : _c.filter;
|
|
160
160
|
const syncGroup = [];
|
|
161
161
|
const asyncGroup = [];
|
|
162
162
|
for (const workflow of workflows) {
|
|
163
|
-
const event = [workflow];
|
|
163
|
+
const event = [workflow, {}];
|
|
164
164
|
const { appends = [] } = workflow.config;
|
|
165
165
|
const dataPath = triggerWorkflowsMap.get(workflow.key);
|
|
166
166
|
const formData = dataPath ? (0, import_lodash.get)(values, dataPath) : values;
|
|
167
167
|
let data = formData;
|
|
168
168
|
if (filterByTk != null) {
|
|
169
169
|
if (Array.isArray(filterByTk)) {
|
|
170
|
-
data = (await repository.find({ filterByTk, appends, context })).map(
|
|
170
|
+
data = (await repository.find({ filterByTk, appends, context, filter: scopeFilter })).map(
|
|
171
171
|
(item) => Object.assign(item.toJSON(), formData)
|
|
172
172
|
);
|
|
173
173
|
} else {
|
|
174
|
-
data = await repository.findOne({ filterByTk, appends, context });
|
|
174
|
+
data = await repository.findOne({ filterByTk, appends, context, filter: scopeFilter });
|
|
175
175
|
if (!data) {
|
|
176
|
+
if (scopeFilter) {
|
|
177
|
+
return context.throw(403, "No permissions");
|
|
178
|
+
}
|
|
176
179
|
continue;
|
|
177
180
|
}
|
|
178
181
|
if (typeof data.toJSON === "function") {
|
|
@@ -181,7 +184,7 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
}
|
|
184
|
-
event
|
|
187
|
+
event[1] = { data, ...userInfo };
|
|
185
188
|
(workflow.sync ? syncGroup : asyncGroup).push(event);
|
|
186
189
|
}
|
|
187
190
|
await this.processEvents({
|
|
@@ -194,7 +197,34 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
194
197
|
super(workflow);
|
|
195
198
|
this.workflow.app.dataSourceManager.afterAddDataSource((dataSource) => {
|
|
196
199
|
dataSource.resourceManager.registerActionHandler("trigger", this.triggerAction);
|
|
197
|
-
dataSource.acl.
|
|
200
|
+
dataSource.acl.setAvailableAction("trigger", {
|
|
201
|
+
displayName: `{{t("Trigger workflow", { ns: "${import_constants.NAMESPACE}" })}}`
|
|
202
|
+
});
|
|
203
|
+
dataSource.acl.setAvailableAction("triggerNew", {
|
|
204
|
+
displayName: `{{t("Trigger workflow", { ns: "${import_constants.NAMESPACE}" })}}`,
|
|
205
|
+
onNewRecord: true
|
|
206
|
+
});
|
|
207
|
+
dataSource.resourceManager.use(
|
|
208
|
+
async (ctx, next) => {
|
|
209
|
+
const { resourceName, actionName, params } = ctx.action ?? {};
|
|
210
|
+
if (actionName === "trigger" && resourceName !== "workflows" && (params == null ? void 0 : params.filterByTk) == null) {
|
|
211
|
+
ctx.action.actionName = "triggerNew";
|
|
212
|
+
ctx.state.__customActionTriggerRenamed = true;
|
|
213
|
+
}
|
|
214
|
+
await next();
|
|
215
|
+
},
|
|
216
|
+
{ tag: "customActionTriggerRename", before: "acl", after: "auth" }
|
|
217
|
+
);
|
|
218
|
+
dataSource.resourceManager.use(
|
|
219
|
+
async (ctx, next) => {
|
|
220
|
+
if (ctx.state.__customActionTriggerRenamed) {
|
|
221
|
+
ctx.action.actionName = "trigger";
|
|
222
|
+
}
|
|
223
|
+
await next();
|
|
224
|
+
},
|
|
225
|
+
{ tag: "customActionTriggerRestore", after: "acl" }
|
|
226
|
+
);
|
|
227
|
+
dataSource.acl.allow("workflows", ["trigger"], "loggedIn");
|
|
198
228
|
});
|
|
199
229
|
workflow.app.pm.get(import_plugin_error_handler.default).errorHandler.register(
|
|
200
230
|
(err) => err.name === "CustomActionInterceptionError",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Migration } from '@nocobase/server';
|
|
10
|
+
/**
|
|
11
|
+
* Grant `trigger` / `triggerNew` action permissions for every role, mirroring the role's
|
|
12
|
+
* existing `view` / `create` permissions.
|
|
13
|
+
*
|
|
14
|
+
* Background: The `trigger` action on collection resources was previously allowed for all
|
|
15
|
+
* logged-in users via `acl.allow('*', ['trigger'], 'loggedIn')`. After that global bypass
|
|
16
|
+
* was removed, `trigger` goes through normal ACL checks. Custom-action trigger is now
|
|
17
|
+
* modelled as two ACL actions:
|
|
18
|
+
* - `trigger` — invoked on an existing record; follows `view` (with data scope).
|
|
19
|
+
* - `triggerNew` — invoked on a form that hasn't been persisted yet; follows `create`.
|
|
20
|
+
*
|
|
21
|
+
* 1. Per-data-source strategy (`dataSourcesRoles.strategy.actions`, an array):
|
|
22
|
+
* - `trigger` mirrors `view` (including scope suffix like `:own`). No `view` → no trigger.
|
|
23
|
+
* - `triggerNew` is added (unrestricted) if the strategy contains any `create*` entry.
|
|
24
|
+
* No `create` → no triggerNew. Scope suffixes on `create` are ignored because new-data
|
|
25
|
+
* actions have no data scope.
|
|
26
|
+
*
|
|
27
|
+
* 2. Specific resource configs (`dataSourcesRolesResources` with `usingActionsConfig: true`):
|
|
28
|
+
* - If the resource has a `view` action, create a matching `trigger` action that reuses
|
|
29
|
+
* the same `scopeId`. Skip if `trigger` already exists.
|
|
30
|
+
* - If the resource has a `create` action, create a matching `triggerNew` action
|
|
31
|
+
* (scopeId: null — new-data actions carry no scope). Skip if `triggerNew` already
|
|
32
|
+
* exists.
|
|
33
|
+
*
|
|
34
|
+
* This migration runs only during upgrades (appVersion < 2.0.40), not on fresh installs.
|
|
35
|
+
*/
|
|
36
|
+
export default class extends Migration {
|
|
37
|
+
appVersion: string;
|
|
38
|
+
on: string;
|
|
39
|
+
up(): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var grant_trigger_action_to_roles_exports = {};
|
|
28
|
+
__export(grant_trigger_action_to_roles_exports, {
|
|
29
|
+
default: () => grant_trigger_action_to_roles_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(grant_trigger_action_to_roles_exports);
|
|
32
|
+
var import_server = require("@nocobase/server");
|
|
33
|
+
class grant_trigger_action_to_roles_default extends import_server.Migration {
|
|
34
|
+
appVersion = "<2.1.0-alpha.21";
|
|
35
|
+
on = "afterSync";
|
|
36
|
+
async up() {
|
|
37
|
+
const { db } = this.context;
|
|
38
|
+
await db.sequelize.transaction(async (transaction) => {
|
|
39
|
+
const dsRoles = await db.getRepository("dataSourcesRoles").find({ transaction });
|
|
40
|
+
for (const dsRole of dsRoles) {
|
|
41
|
+
const strategy = dsRole.get("strategy") || {};
|
|
42
|
+
const actions = strategy.actions;
|
|
43
|
+
if (!Array.isArray(actions)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
let nextActions = null;
|
|
47
|
+
const ensureNextActions = () => {
|
|
48
|
+
if (!nextActions) nextActions = [...actions];
|
|
49
|
+
return nextActions;
|
|
50
|
+
};
|
|
51
|
+
const viewEntry = actions.find((a) => a === "view" || a.startsWith("view:"));
|
|
52
|
+
if (viewEntry) {
|
|
53
|
+
const targetTrigger = viewEntry === "view" ? "trigger" : `trigger:${viewEntry.slice("view:".length)}`;
|
|
54
|
+
const existingIndex = actions.findIndex((a) => a === "trigger" || a.startsWith("trigger:"));
|
|
55
|
+
if (existingIndex === -1) {
|
|
56
|
+
ensureNextActions().push(targetTrigger);
|
|
57
|
+
} else if (actions[existingIndex] !== targetTrigger) {
|
|
58
|
+
ensureNextActions()[existingIndex] = targetTrigger;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const hasCreate = actions.some((a) => a === "create" || a.startsWith("create:"));
|
|
62
|
+
if (hasCreate) {
|
|
63
|
+
const existingIndex = actions.findIndex((a) => a === "triggerNew" || a.startsWith("triggerNew:"));
|
|
64
|
+
if (existingIndex === -1) {
|
|
65
|
+
ensureNextActions().push("triggerNew");
|
|
66
|
+
} else if (actions[existingIndex] !== "triggerNew") {
|
|
67
|
+
ensureNextActions()[existingIndex] = "triggerNew";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (nextActions) {
|
|
71
|
+
await db.getRepository("dataSourcesRoles").update({
|
|
72
|
+
filter: {
|
|
73
|
+
roleName: dsRole.get("roleName"),
|
|
74
|
+
dataSourceKey: dsRole.get("dataSourceKey")
|
|
75
|
+
},
|
|
76
|
+
values: {
|
|
77
|
+
strategy: { ...strategy, actions: nextActions }
|
|
78
|
+
},
|
|
79
|
+
hooks: false,
|
|
80
|
+
transaction
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const resources = await db.getRepository("dataSourcesRolesResources").find({
|
|
85
|
+
filter: { usingActionsConfig: true },
|
|
86
|
+
appends: ["actions"],
|
|
87
|
+
transaction
|
|
88
|
+
});
|
|
89
|
+
for (const resource of resources) {
|
|
90
|
+
const actions = resource.get("actions") || [];
|
|
91
|
+
const viewAction = actions.find((a) => a.get("name") === "view");
|
|
92
|
+
const hasTrigger = actions.some((a) => a.get("name") === "trigger");
|
|
93
|
+
if (viewAction && !hasTrigger) {
|
|
94
|
+
await db.getRepository("dataSourcesRolesResourcesActions").create({
|
|
95
|
+
values: {
|
|
96
|
+
rolesResourceId: resource.get("id"),
|
|
97
|
+
name: "trigger",
|
|
98
|
+
scopeId: viewAction.get("scopeId") ?? null,
|
|
99
|
+
fields: []
|
|
100
|
+
},
|
|
101
|
+
transaction
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const createAction = actions.find((a) => a.get("name") === "create");
|
|
105
|
+
const hasTriggerNew = actions.some((a) => a.get("name") === "triggerNew");
|
|
106
|
+
if (createAction && !hasTriggerNew) {
|
|
107
|
+
await db.getRepository("dataSourcesRolesResourcesActions").create({
|
|
108
|
+
values: {
|
|
109
|
+
rolesResourceId: resource.get("id"),
|
|
110
|
+
name: "triggerNew",
|
|
111
|
+
scopeId: null,
|
|
112
|
+
fields: []
|
|
113
|
+
},
|
|
114
|
+
transaction
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/plugin-workflow-custom-action-trigger",
|
|
3
|
-
"version": "2.1.0-alpha.
|
|
3
|
+
"version": "2.1.0-alpha.22",
|
|
4
4
|
"displayName": "Workflow: Custom action event",
|
|
5
5
|
"displayName.zh-CN": "工作流:自定义操作事件",
|
|
6
6
|
"description": "Triggers after click a custom action button.",
|
|
@@ -30,6 +30,6 @@
|
|
|
30
30
|
],
|
|
31
31
|
"editionLevel": 0
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "81ed83f158f172cca607b36beaf8428b14ba16ad",
|
|
34
34
|
"license": "Apache-2.0"
|
|
35
35
|
}
|