@5minds/node-red-contrib-processcube 1.14.0 → 1.15.0-develop-ce7b3c-mahv7gq1
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/package.json +5 -2
- package/processcube-google-docs-mail-template.html +46 -0
- package/processcube-google-docs-mail-template.js +124 -0
- package/processinstance-delete-advanced.html +79 -0
- package/processinstance-delete-advanced.js +33 -0
- package/processinstance-delete.html +1 -1
- package/usertask-event-listener.html +2 -1
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@5minds/node-red-contrib-processcube",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.15.0-develop-ce7b3c-mahv7gq1",
|
4
4
|
"license": "MIT",
|
5
5
|
"description": "Node-RED nodes for ProcessCube",
|
6
6
|
"scripts": {
|
@@ -48,6 +48,7 @@
|
|
48
48
|
"processcubeEngineConfig": "processcube-engine-config.js",
|
49
49
|
"ProcessinstanceQuery": "processinstance-query.js",
|
50
50
|
"ProcessinstanceDelete": "processinstance-delete.js",
|
51
|
+
"ProcessinstanceDeleteAdvanced": "processinstance-delete-advanced.js",
|
51
52
|
"ProcessdefinitionDeploy": "processdefinition-deploy.js",
|
52
53
|
"ProcessdefinitionQuery": "processdefinition-query.js",
|
53
54
|
"messageEventTrigger": "message-event-trigger.js",
|
@@ -55,12 +56,14 @@
|
|
55
56
|
"userTaskEventListener": "usertask-event-listener.js",
|
56
57
|
"UserTaskInput": "usertask-input.js",
|
57
58
|
"UserTaskOutput": "usertask-output.js",
|
58
|
-
"WaitForUsertask": "wait-for-usertask.js"
|
59
|
+
"WaitForUsertask": "wait-for-usertask.js",
|
60
|
+
"ProcesssCubeGoogleDocsMailTemplate": "processcube-google-docs-mail-template.js"
|
59
61
|
},
|
60
62
|
"examples": "examples"
|
61
63
|
},
|
62
64
|
"dependencies": {
|
63
65
|
"@5minds/processcube_engine_client": "6.0.0-beta.7",
|
66
|
+
"adm-zip": "^0.5.16",
|
64
67
|
"jwt-decode": "^4.0.0",
|
65
68
|
"openid-client": "^5.5.0"
|
66
69
|
},
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<script type="text/javascript">
|
2
|
+
RED.nodes.registerType('processcube-google-docs-mail-template', {
|
3
|
+
category: 'ProcessCube Tools',
|
4
|
+
color: '#02AFD6',
|
5
|
+
defaults: {
|
6
|
+
name: { value: '' },
|
7
|
+
template_link: { value: '', type: 'str' }
|
8
|
+
},
|
9
|
+
inputs: 1,
|
10
|
+
outputs: 1,
|
11
|
+
icon: 'font-awesome/fa-sign-in',
|
12
|
+
label: function () {
|
13
|
+
return this.name || 'processcube-google-docs-mail-template';
|
14
|
+
},
|
15
|
+
});
|
16
|
+
</script>
|
17
|
+
|
18
|
+
<script type="text/html" data-template-name="processcube-google-docs-mail-template">
|
19
|
+
<div class="form-row">
|
20
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
21
|
+
<input type="text" id="node-input-name" placeholder="Name" />
|
22
|
+
</div>
|
23
|
+
<div class="form-row">
|
24
|
+
<label for="node-input-template_link"><i class="fa fa-tag"></i> Template Link</label>
|
25
|
+
<input type="text" id="node-input-template_link" placeholder="Template Link" />
|
26
|
+
</div>
|
27
|
+
</script>
|
28
|
+
|
29
|
+
<script type="text/markdown" data-help-name="processcube-google-docs-mail-template">
|
30
|
+
Download the zip folder from the link and include the Images with cid-Tags.
|
31
|
+
Replace the field {{field}} or ///field/// with the content from the payload.
|
32
|
+
|
33
|
+
## Inputs
|
34
|
+
|
35
|
+
: payload (json): Field for replace within the template ({{field}} or ///field///).
|
36
|
+
|
37
|
+
## Outputs
|
38
|
+
|
39
|
+
: payload (str): The renderted template.
|
40
|
+
: attachments (array): The attachments of the email.
|
41
|
+
|
42
|
+
### References
|
43
|
+
|
44
|
+
- [The ProcessCube Developer Network](https://processcube.io) - All documentation for the ProcessCube© platform
|
45
|
+
- [Node-RED Integration in ProcessCube©](https://processcube.io/docs/node-red) - Node-RED integration in ProcessCube©
|
46
|
+
</script>
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module.exports = function (RED) {
|
2
|
+
|
3
|
+
const fs = require('fs');
|
4
|
+
const path = require('path');
|
5
|
+
const os = require('os');
|
6
|
+
const { pipeline } = require('stream');
|
7
|
+
const { promisify } = require('util');
|
8
|
+
const AdmZip = require('adm-zip');
|
9
|
+
|
10
|
+
const streamPipeline = promisify(pipeline);
|
11
|
+
|
12
|
+
function ProcesssCubeGoogleDocsMailTemplate(config) {
|
13
|
+
RED.nodes.createNode(this, config);
|
14
|
+
const node = this;
|
15
|
+
|
16
|
+
const template_link = config.template_link;
|
17
|
+
|
18
|
+
node.on('input', async function (msg) {
|
19
|
+
|
20
|
+
try {
|
21
|
+
|
22
|
+
function convertGoogleDriveLink(link) {
|
23
|
+
// Format 1: https://drive.google.com/file/d/FILE_ID/view?usp=sharing
|
24
|
+
const fileMatch = link.match(/https:\/\/drive\.google\.com\/file\/d\/([^/]+)\/view/);
|
25
|
+
if (fileMatch) {
|
26
|
+
return `https://drive.google.com/uc?export=download&id=${fileMatch[1]}`;
|
27
|
+
}
|
28
|
+
|
29
|
+
// Format 2: https://drive.google.com/open?id=FILE_ID
|
30
|
+
const openMatch = link.match(/https:\/\/drive\.google\.com\/open\?id=([^&]+)/);
|
31
|
+
if (openMatch) {
|
32
|
+
return `https://drive.google.com/uc?export=download&id=${openMatch[1]}`;
|
33
|
+
}
|
34
|
+
|
35
|
+
// Anderenfalls unverändert zurückgeben
|
36
|
+
return link;
|
37
|
+
}
|
38
|
+
|
39
|
+
function renderTemplate(html, payload) {
|
40
|
+
// Ersetze {{feld}} und ///feld///
|
41
|
+
return html.replace(/({{([^}]+)}}|\/\/\/([^/]+)\/\/\/)/g, (match, _, field1, field2) => {
|
42
|
+
const key = field1 || field2;
|
43
|
+
return payload[key.trim()] ?? match; // fallback: Platzhalter bleibt bestehen
|
44
|
+
});
|
45
|
+
}
|
46
|
+
|
47
|
+
function removeGoogleRedirects(html) {
|
48
|
+
return html.replace(/https:\/\/www\.google\.com\/url\?q=([^"&]+)[^"]*/g, (match, actualUrl) => {
|
49
|
+
try {
|
50
|
+
// Google-URLs sind URL-encoded – dekodieren
|
51
|
+
return decodeURIComponent(actualUrl);
|
52
|
+
} catch {
|
53
|
+
return actualUrl;
|
54
|
+
}
|
55
|
+
});
|
56
|
+
}
|
57
|
+
|
58
|
+
const customRoot = path.resolve(RED.settings.userDir, 'tmp/processcube-google-docs-mail-template');
|
59
|
+
fs.mkdirSync(customRoot, { recursive: true });
|
60
|
+
const tempDir = fs.mkdtempSync(path.join(customRoot, 'run-'));
|
61
|
+
const zipPath = path.join(tempDir, 'downloaded.zip');
|
62
|
+
|
63
|
+
const url = convertGoogleDriveLink(template_link);
|
64
|
+
|
65
|
+
const response = await fetch(url);
|
66
|
+
if (!response.ok) {
|
67
|
+
throw new Error(`Fehler beim Herunterladen: ${response.status} ${response.statusText}`);
|
68
|
+
}
|
69
|
+
await streamPipeline(response.body, fs.createWriteStream(zipPath));
|
70
|
+
|
71
|
+
const zip = new AdmZip(zipPath);
|
72
|
+
zip.extractAllTo(tempDir, true);
|
73
|
+
|
74
|
+
// === HTML-Datei im obersten Verzeichnis finden ===
|
75
|
+
const topLevelFiles = fs.readdirSync(tempDir, { withFileTypes: true });
|
76
|
+
const htmlEntry = topLevelFiles.find(file =>
|
77
|
+
file.isFile() && file.name.toLowerCase().endsWith('.html')
|
78
|
+
);
|
79
|
+
|
80
|
+
if (!htmlEntry) {
|
81
|
+
throw new Error('Keine HTML-Datei im ZIP-Hauptverzeichnis gefunden.');
|
82
|
+
}
|
83
|
+
|
84
|
+
const htmlPath = path.join(tempDir, htmlEntry.name);
|
85
|
+
let html = fs.readFileSync(htmlPath, 'utf8');
|
86
|
+
|
87
|
+
// === Bilder durch CID ersetzen ===
|
88
|
+
const imgRegex = /src="images\/([^"]+)"/g;
|
89
|
+
const attachments = [];
|
90
|
+
let match;
|
91
|
+
|
92
|
+
while ((match = imgRegex.exec(html)) !== null) {
|
93
|
+
const fileName = match[1];
|
94
|
+
const cidName = path.parse(fileName).name;
|
95
|
+
const imgPath = path.join(tempDir, 'images', fileName);
|
96
|
+
|
97
|
+
if (fs.existsSync(imgPath)) {
|
98
|
+
attachments.push({
|
99
|
+
filename: fileName,
|
100
|
+
path: imgPath,
|
101
|
+
cid: cidName
|
102
|
+
});
|
103
|
+
|
104
|
+
html = html.replace(`src="images/${fileName}"`, `src="cid:${cidName}"`);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
let new_payload = renderTemplate(html, msg.payload);
|
109
|
+
|
110
|
+
// ggf. mit schalter
|
111
|
+
new_payload = removeGoogleRedirects(new_payload);
|
112
|
+
|
113
|
+
msg.payload = new_payload;
|
114
|
+
msg.attachments = attachments;
|
115
|
+
|
116
|
+
node.send(msg);
|
117
|
+
} catch (queryError) {
|
118
|
+
node.error(`Generate the content: ${queryError.message}`, msg);
|
119
|
+
}
|
120
|
+
});
|
121
|
+
}
|
122
|
+
|
123
|
+
RED.nodes.registerType('processcube-google-docs-mail-template', ProcesssCubeGoogleDocsMailTemplate);
|
124
|
+
};
|
@@ -0,0 +1,79 @@
|
|
1
|
+
<script type="text/javascript">
|
2
|
+
RED.nodes.registerType('processinstance-delete-advanced', {
|
3
|
+
category: 'ProcessCube DevOps',
|
4
|
+
color: '#02AFD6',
|
5
|
+
defaults: {
|
6
|
+
name: { value: '' },
|
7
|
+
engine: { value: '', type: 'processcube-engine-config' },
|
8
|
+
query: { value: 'payload' },
|
9
|
+
query_type: { value: 'msg' },
|
10
|
+
delete_releated: { value: false },
|
11
|
+
},
|
12
|
+
inputs: 1,
|
13
|
+
outputs: 1,
|
14
|
+
icon: 'font-awesome/fa-sign-in',
|
15
|
+
label: function () {
|
16
|
+
return this.name || 'processinstance-delete-advanced';
|
17
|
+
},
|
18
|
+
oneditprepare: function () {
|
19
|
+
$('#node-input-query').typedInput({
|
20
|
+
default: 'msg',
|
21
|
+
types: ['msg', 'json'],
|
22
|
+
});
|
23
|
+
|
24
|
+
$('#node-input-query').typedInput('value', this.query);
|
25
|
+
$('#node-input-query').typedInput('type', this.query_type);
|
26
|
+
},
|
27
|
+
oneditsave: function () {
|
28
|
+
(this.query = $('#node-input-query').typedInput('value')),
|
29
|
+
(this.query_type = $('#node-input-query').typedInput('type'));
|
30
|
+
},
|
31
|
+
});
|
32
|
+
</script>
|
33
|
+
|
34
|
+
<script type="text/html" data-template-name="processinstance-delete-advanced">
|
35
|
+
<div class="form-row">
|
36
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
37
|
+
<input type="text" id="node-input-name" placeholder="Name" />
|
38
|
+
</div>
|
39
|
+
<div class="form-row">
|
40
|
+
<label for="node-input-engine"><i class="fa fa-tag"></i> Engine-URL</label>
|
41
|
+
<input type="text" id="node-input-engine" placeholder="Engine-URL" />
|
42
|
+
</div>
|
43
|
+
<div class="form-row">
|
44
|
+
<label for="node-input-query"><i class="fa fa-tag"></i> Query</label>
|
45
|
+
<input type="text" id="node-input-query" />
|
46
|
+
</div>
|
47
|
+
<div class="form-row" style="margin-bottom: 3px;">
|
48
|
+
<input type="checkbox" checked id="node-input-delete_releated" style="display: inline-block; width: auto; vertical-align: top; margin-left: 30px; margin-right: 5px;">
|
49
|
+
<label style="width:auto" for="node-input-delete_releated">Delete related</label>
|
50
|
+
</div>
|
51
|
+
</script>
|
52
|
+
|
53
|
+
<script type="text/markdown" data-help-name="processinstance-delete-advanced">
|
54
|
+
Delete old instances of a process model in the ProcessCube.
|
55
|
+
|
56
|
+
Only engines version >= 19 are supported.
|
57
|
+
|
58
|
+
## Inputs
|
59
|
+
|
60
|
+
: payload (json): The Parameter to delete the instances.
|
61
|
+
|
62
|
+
|
63
|
+
## Outputs
|
64
|
+
|
65
|
+
: Explanation of the payload:
|
66
|
+
|
67
|
+
Object
|
68
|
+
{
|
69
|
+
processInstanceId: The IDs of the ProcessInstances to delete (Example : 12345678,87654321).
|
70
|
+
processModelId: The ID of the ProcessModel to delete the instances from.
|
71
|
+
finishedBefore: The date before which the instances should be deleted.
|
72
|
+
finshedAfter: The date after which the instances should be deleted.
|
73
|
+
}
|
74
|
+
|
75
|
+
### References
|
76
|
+
|
77
|
+
- [The ProcessCube Developer Network](https://processcube.io) - All documentation for the ProcessCube© platform
|
78
|
+
- [Node-RED Integration in ProcessCube©](https://processcube.io/docs/node-red) - Node-RED integration in ProcessCube©
|
79
|
+
</script>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module.exports = function (RED) {
|
2
|
+
function ProcessInstanceDeleteAdvanced(config) {
|
3
|
+
RED.nodes.createNode(this, config);
|
4
|
+
const node = this;
|
5
|
+
|
6
|
+
node.on('input', async function (msg) {
|
7
|
+
node.engine = RED.nodes.getNode(config.engine);
|
8
|
+
const client = node.engine ? node.engine.engineClient : null;
|
9
|
+
|
10
|
+
let query = RED.util.evaluateNodeProperty(config.query, config.query_type, node, msg);
|
11
|
+
|
12
|
+
const isUser = !!msg._client?.user && !!msg._client.user.accessToken;
|
13
|
+
const userIdentity = isUser ? { userId: msg._client.user.id, token: msg._client.user.accessToken } : null;
|
14
|
+
|
15
|
+
if (!client || !client.processInstances) {
|
16
|
+
node.error('No engine or processInstances API configured.', msg);
|
17
|
+
return;
|
18
|
+
}
|
19
|
+
|
20
|
+
try {
|
21
|
+
const result = await client.processInstances.delete(query, config.delete_releated, userIdentity)
|
22
|
+
|
23
|
+
msg.payload = result
|
24
|
+
|
25
|
+
node.send(msg);
|
26
|
+
} catch (queryError) {
|
27
|
+
node.error(`Failed to delete process instances Error: ${queryError.message}`, msg);
|
28
|
+
}
|
29
|
+
});
|
30
|
+
}
|
31
|
+
|
32
|
+
RED.nodes.registerType('processinstance-delete-advanced', ProcessInstanceDeleteAdvanced);
|
33
|
+
};
|
@@ -14,7 +14,8 @@
|
|
14
14
|
outputs: 1,
|
15
15
|
icon: 'usertask_event_listener.svg',
|
16
16
|
label: function () {
|
17
|
-
return this.name || 'usertask-event-listener';
|
17
|
+
return this.name || (this.eventtype ? `usertask: ${this.eventtype}` : 'usertask-event-listener');
|
18
|
+
|
18
19
|
},
|
19
20
|
oneditprepare: function () {
|
20
21
|
$('#node-input-query').typedInput({
|