@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@5minds/node-red-contrib-processcube",
3
- "version": "1.14.0",
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&copy; platform
45
+ - [Node-RED Integration in ProcessCube&copy;](https://processcube.io/docs/node-red) - Node-RED integration in ProcessCube&copy;
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&copy; platform
78
+ - [Node-RED Integration in ProcessCube&copy;](https://processcube.io/docs/node-red) - Node-RED integration in ProcessCube&copy;
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
+ };
@@ -50,7 +50,7 @@
50
50
  </script>
51
51
 
52
52
  <script type="text/markdown" data-help-name="processinstance-delete">
53
- Delete old instances of a process model in the ProcessCube.
53
+ Delete old instances of a process model in the ProcessCube.
54
54
 
55
55
  ## Inputs
56
56
 
@@ -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({