Skip to content

Commit 68d9aa5

Browse files
committed
updated with develop
2 parents 95d3a5e + af79ee3 commit 68d9aa5

16 files changed

+238
-91
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@processmaker/screen-builder",
3-
"version": "3.8.11",
3+
"version": "3.8.13",
44
"scripts": {
55
"dev": "VITE_COVERAGE=true vite",
66
"build": "vite build",

src/bootstrap.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ const cacheEnabled = document.head.querySelector(
5050
const cacheTimeout = document.head.querySelector(
5151
"meta[name='screen-cache-timeout']"
5252
);
53+
const secureHandlerToggleVisibleMeta = document.head.querySelector(
54+
"meta[name='screen-secure-handler-toggle-visible']"
55+
);
5356

5457
// Get the current protocol, hostname, and port
5558
const { protocol, hostname, port } = window.location;
@@ -73,7 +76,10 @@ window.ProcessMaker = {
7376
alert(message, variant) {},
7477
screen: {
7578
cacheEnabled: cacheEnabled ? cacheEnabled.content === "true" : false,
76-
cacheTimeout: cacheTimeout ? Number(cacheTimeout.content) : 0
79+
cacheTimeout: cacheTimeout ? Number(cacheTimeout.content) : 0,
80+
secureHandlerToggleVisible: !!Number(
81+
secureHandlerToggleVisibleMeta?.content
82+
)
7783
}
7884
};
7985
window.Echo = {
Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
export const handlerEventProperty = {
2-
type: 'CodeEditor',
3-
field: 'handler',
2+
type: "CodeEditor",
3+
field: "handler",
44
config: {
5-
label: 'Click Handler',
6-
helper: 'The handler is a JavaScript function that will be executed when the button is clicked.',
7-
dataFeature: 'i1177',
8-
},
5+
label: "Click Handler",
6+
helper:
7+
"The handler is a JavaScript function that will be executed when the button is clicked.",
8+
dataFeature: "i1177"
9+
}
10+
};
11+
12+
export const handlerSecurityProperty = {
13+
type: "FormCheckbox",
14+
field: "handlerSecurityEnabled",
15+
config: {
16+
label: "Secure Handler Execution",
17+
toggle: true,
18+
helper:
19+
"When enabled, the handler runs inside a sandboxed worker. Disable to allow full JavaScript access."
20+
}
921
};

src/components/inspector/validation-select.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ export default {
442442
field: "after_or_equal:",
443443
content: this.$t("After or Equal to Date"),
444444
helper: this.$t(
445-
"The field unter validation must be after or equal to the given field."
445+
"The field under validation must be after or equal to the given field."
446446
),
447447
visible: true,
448448
configs: [
@@ -459,7 +459,7 @@ export default {
459459
field: "before:",
460460
content: this.$t("Before Date"),
461461
helper: this.$t(
462-
"The field unter validation must be before the given date."
462+
"The field under validation must be before the given date."
463463
),
464464
visible: true,
465465
configs: [
@@ -476,7 +476,7 @@ export default {
476476
field: "before_or_equal:",
477477
content: this.$t("Before or Equal to Date"),
478478
helper: this.$t(
479-
"The field unter validation must be before or equal to the given field."
479+
"The field under validation must be before or equal to the given field."
480480
),
481481
visible: true,
482482
configs: [

src/components/renderer/form-button.vue

Lines changed: 146 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,29 @@
22
<div class="form-group" style="overflow-x: hidden">
33
<button
44
v-b-tooltip="options"
5-
@click="click"
65
:class="classList"
76
:name="name"
87
:aria-label="$attrs['aria-label']"
98
:tabindex="$attrs['tabindex']"
109
:disabled="showSpinner"
10+
@click="click"
1111
>
1212
<b-spinner v-if="showSpinner" small></b-spinner>
1313
{{ showSpinner ? (!loadingLabel ? "Loading..." : loadingLabel) : label }}
1414
</button>
1515
</div>
1616
</template>
1717

18+
<!-- eslint-disable import/no-extraneous-dependencies -->
19+
<!-- eslint-disable import/no-unresolved -->
20+
<!-- eslint-disable import/extensions -->
1821
<script>
19-
import Mustache from 'mustache';
20-
import { mapActions, mapState } from "vuex";
21-
import { getValidPath } from '@/mixins';
22+
import Mustache from "mustache";
23+
import { mapState } from "vuex";
24+
import { stringify } from "flatted";
25+
import { getValidPath } from "@/mixins";
2226
import Worker from "@/workers/worker.js?worker&inline";
2327
import { findRootScreen } from "@/mixins/DataReference";
24-
import { stringify } from 'flatted';
2528
2629
export default {
2730
mixins: [getValidPath],
@@ -37,7 +40,8 @@ export default {
3740
"transientData",
3841
"loading",
3942
"loadingLabel",
40-
"handler"
43+
"handler",
44+
"handlerSecurityEnabled"
4145
],
4246
data() {
4347
return {
@@ -47,30 +51,35 @@ export default {
4751
computed: {
4852
...mapState("globalErrorsModule", ["valid"]),
4953
classList() {
50-
let variant = this.variant || 'primary';
54+
const variant = this.variant || "primary";
5155
return {
5256
btn: true,
53-
['btn-' + variant]: true,
54-
disabled: this.event === 'submit' && !this.valid
57+
[`btn-${variant}`]: true,
58+
disabled: this.event === "submit" && !this.valid
5559
};
5660
},
5761
options() {
58-
if (!this.tooltip || this.event === 'submit') {
62+
if (!this.tooltip || this.event === "submit") {
5963
return {};
6064
}
6165
62-
let content = '';
66+
let content = "";
6367
try {
64-
content = Mustache.render(this.tooltip.content || '', this.transientData);
65-
} catch (error) { error; }
68+
content = Mustache.render(
69+
this.tooltip.content || "",
70+
this.transientData
71+
);
72+
} catch (error) {
73+
console.error(error);
74+
}
6675
6776
return {
6877
title: content,
6978
html: true,
70-
placement: this.tooltip.position || '',
71-
trigger: 'hover',
72-
variant: this.tooltip.variant || '',
73-
boundary: 'window',
79+
placement: this.tooltip.position || "",
80+
trigger: "hover",
81+
variant: this.tooltip.variant || "",
82+
boundary: "window"
7483
};
7584
},
7685
buttonInfo() {
@@ -92,74 +101,150 @@ export default {
92101
}
93102
},
94103
async click() {
95-
if (this.event === 'script') {
96-
const trueValue = this.fieldValue || '1';
97-
const value = (this.value == trueValue) ? null : trueValue;
98-
this.$emit('input', value);
104+
if (this.event === "script") {
105+
const trueValue = this.fieldValue || "1";
106+
// eslint-disable-next-line eqeqeq
107+
const value = this.value == trueValue ? null : trueValue;
108+
this.$emit("input", value);
99109
// Run handler after setting the value
100110
await this.runHandler();
101111
}
102-
if (this.event !== 'pageNavigate' && this.name) {
112+
if (this.event !== "pageNavigate" && this.name) {
103113
this.setValue(this.$parent, this.name, this.fieldValue);
104114
}
105-
if (this.event === 'submit') {
115+
if (this.event === "submit") {
106116
if (this.loading && this.valid) {
107117
this.showSpinner = true;
108118
}
109-
this.$emit('input', this.fieldValue);
119+
this.$emit("input", this.fieldValue);
110120
// Run handler after setting the value
111121
await this.runHandler();
112122
this.$nextTick(() => {
113-
this.$emit('submit', this.eventData, this.loading, this.buttonInfo);
123+
this.$emit("submit", this.eventData, this.loading, this.buttonInfo);
114124
});
115125
return;
116126
}
117-
if (this.event === 'pageNavigate') {
127+
if (this.event === "pageNavigate") {
118128
// Run handler for page navigate
119129
await this.runHandler();
120130
}
121131
this.$emit(this.event, this.eventData);
122-
if (this.event === 'pageNavigate') {
123-
this.$emit('page-navigate', this.eventData);
132+
if (this.event === "pageNavigate") {
133+
this.$emit("page-navigate", this.eventData);
124134
}
125135
},
126136
runHandler() {
127-
if (this.handler) {
128-
return new Promise((resolve, reject) => {
129-
try {
130-
const rootScreen = findRootScreen(this);
131-
const data = rootScreen.vdata;
132-
const scope = this.transientData;
137+
if (!this.handler) {
138+
return Promise.resolve();
139+
}
133140
134-
const worker = new Worker();
135-
// Send the handler code to the worker
136-
worker.postMessage({
137-
fn: this.handler,
138-
dataRefs: stringify({data, scope}),
139-
});
141+
const rootScreen = findRootScreen(this);
142+
const data = rootScreen.vdata;
143+
const scope = this.transientData;
140144
141-
// Listen for the result from the worker
142-
worker.onmessage = (e) => {
143-
if (e.data.error) {
144-
reject(e.data.error);
145-
} else if (e.data.result) {
146-
// Update the data with the result
147-
Object.keys(e.data.result).forEach(key => {
148-
if (key === '_root') {
149-
Object.assign(data, e.data.result[key]);
150-
} else {
151-
scope[key] = e.data.result[key];
152-
}
153-
});
154-
resolve();
155-
}
156-
};
157-
} catch (error) {
158-
console.error("❌ There is an error in the button handler", error);
159-
}
160-
});
145+
if (this.handlerSecurityEnabled === false) {
146+
return this.executeHandlerWithoutWorker(data, scope);
161147
}
148+
149+
return this.executeHandlerWithWorker(data, scope);
150+
},
151+
executeHandlerWithWorker(data, scope) {
152+
return new Promise((resolve, reject) => {
153+
try {
154+
const worker = new Worker();
155+
worker.postMessage({
156+
fn: this.handler,
157+
dataRefs: stringify({ data, scope })
158+
});
159+
160+
worker.onmessage = (e) => {
161+
worker.terminate();
162+
if (e.data.error) {
163+
console.error(
164+
"There is an error in the button handler",
165+
e.data.error
166+
);
167+
reject(e.data.error);
168+
return;
169+
}
170+
this.applyHandlerResult(e.data.result, data, scope);
171+
resolve();
172+
};
173+
174+
worker.onerror = (errorEvent) => {
175+
worker.terminate();
176+
console.error(
177+
"There is an error in the button handler",
178+
errorEvent
179+
);
180+
reject(errorEvent);
181+
};
182+
} catch (error) {
183+
console.error("There is an error in the button handler", error);
184+
reject(error);
185+
}
186+
});
187+
},
188+
executeHandlerWithoutWorker(data, scope) {
189+
const hasDataReferenceHelper =
190+
typeof this.getScreenDataReference === "function";
191+
const dataReference = hasDataReferenceHelper
192+
? this.getScreenDataReference(null, (screen, name, value) => {
193+
screen.$set(screen.vdata, name, value);
194+
})
195+
: data;
196+
const parentReference =
197+
hasDataReferenceHelper && dataReference
198+
? dataReference._parent
199+
: undefined;
200+
const context = scope || dataReference;
201+
const toRaw = (item) => (item && item[Symbol.for("__v_raw")]) || item;
202+
const functionBody = `return (async () => { ${this.handler} })();`;
203+
204+
try {
205+
// eslint-disable-next-line no-new-func, max-len
206+
const userFunc = new Function("data", "parent", "toRaw", functionBody); // NOSONAR. This dynamic code execution is safe because it only occurs when the user has explicitly disabled the security worker.
207+
const result = userFunc.apply(context, [
208+
dataReference,
209+
parentReference,
210+
toRaw
211+
]);
212+
return this.resolveHandlerResult(result, data, scope);
213+
} catch (error) {
214+
console.error("There is an error in the button handler", error);
215+
return Promise.reject(error);
216+
}
217+
},
218+
resolveHandlerResult(result, data, scope) {
219+
if (result && typeof result.then === "function") {
220+
return result
221+
.then((resolved) => {
222+
this.applyHandlerResult(resolved, data, scope);
223+
})
224+
.catch((error) => {
225+
console.error("There is an error in the button handler", error);
226+
throw error;
227+
});
228+
}
229+
230+
this.applyHandlerResult(result, data, scope);
231+
return Promise.resolve();
232+
},
233+
applyHandlerResult(result, data, scope) {
234+
if (!result || typeof result !== "object") {
235+
return;
236+
}
237+
238+
const targetScope = scope || this.transientData || {};
239+
240+
Object.keys(result).forEach((key) => {
241+
if (key === "_root") {
242+
Object.assign(data, result[key]);
243+
} else {
244+
this.$set(targetScope, key, result[key]);
245+
}
246+
});
162247
}
163-
},
248+
}
164249
};
165250
</script>

src/components/task.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ export default {
531531
const url = `?user_id=${this.userId}&status=ACTIVE&process_request_id=${requestId}&include_sub_tasks=1${timestamp}`;
532532
return this.$dataProvider
533533
.getTasks(url).then((response) => {
534+
this.$emit("load-data-task", response);
534535
if (response.data.data.length > 0) {
535536
let task = response.data.data[0];
536537
if (task.process_request_id !== this.requestId) {

0 commit comments

Comments
 (0)