Skip to content

Commit 7646f30

Browse files
authored
Merge pull request #8398 from ProcessMaker/bugfix/FOUR-25187
FOUR-25187: Processes cannot be created with BPMN files
2 parents 4f89578 + 1e28a94 commit 7646f30

5 files changed

Lines changed: 372 additions & 0 deletions

File tree

ProcessMaker/Http/Controllers/Api/ProcessController.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
use ProcessMaker\Models\Script;
3939
use ProcessMaker\Models\Template;
4040
use ProcessMaker\Nayra\Exceptions\ElementNotFoundException;
41+
use ProcessMaker\Nayra\Services\FixBpmnSchemaService;
4142
use ProcessMaker\Nayra\Storage\BpmnDocument;
4243
use ProcessMaker\Package\Translations\Models\Language;
4344
use ProcessMaker\Package\WebEntry\Models\WebentryRoute;
@@ -389,6 +390,12 @@ public function store(Request $request)
389390
// Validate if exists file bpmn
390391
if ($request->has('file')) {
391392
$data['bpmn'] = $request->file('file')->get();
393+
394+
// Some BPMN definitions created in others modelers doesn't comply BPMN 2.0
395+
// So, should be fixed
396+
$fixBpmnSchemaService = app(FixBpmnSchemaService::class);
397+
$data['bpmn'] = $fixBpmnSchemaService->fix($data['bpmn']);
398+
392399
$request->merge(['bpmn' => $data['bpmn']]);
393400
$request->offsetUnset('file');
394401
unset($data['file']);
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php
2+
3+
namespace ProcessMaker\Nayra\Services;
4+
5+
use DOMDocument;
6+
use DOMXPath;
7+
use Exception;
8+
9+
class FixBpmnSchemaService
10+
{
11+
/**
12+
* Fix BPMN definition if is necesasary
13+
*
14+
* @param string $bpmn
15+
* @return string
16+
* @throws \Exception
17+
*/
18+
public static function fix(string $bpmn): string
19+
{
20+
try {
21+
// Create main object
22+
$document = new DOMDocument();
23+
$document->preserveWhiteSpace = false;
24+
$document->loadXml($bpmn);
25+
26+
// Get root node
27+
$root = $document->documentElement;
28+
29+
// Set "targetNamespace" attribute always
30+
$root->setAttribute('targetNamespace', 'http://www.omg.org/spec/BPMN/20100524/MODEL');
31+
32+
// Create XPath object
33+
$xpath = new DOMXPath($document);
34+
35+
// Register all namespaces, including default
36+
foreach ($document->documentElement->attributes as $attr) {
37+
if (strpos($attr->nodeName, 'xmlns:') === 0) {
38+
$prefix = substr($attr->nodeName, 6);
39+
$xpath->registerNamespace($prefix, $attr->nodeValue);
40+
} elseif ($attr->nodeName === 'xmlns') {
41+
// Register default namespace as "def"
42+
$xpath->registerNamespace('def', $attr->nodeValue);
43+
}
44+
}
45+
46+
// Find all task nodes
47+
$taskNodes = $xpath->query('//*[local-name()="task"]');
48+
49+
foreach ($taskNodes as $task) {
50+
$taskId = $task->getAttribute("id");
51+
$taskPrefix = !empty($task->prefix) ? "$task->prefix:" : '';
52+
$taskNS = $task->namespaceURI;
53+
54+
$dataInputAssociation = $xpath->query('./*[local-name()="dataInputAssociation"]', $task)->item(0);
55+
if (!$dataInputAssociation) {
56+
continue;
57+
}
58+
59+
// Skip if targetRef already exists
60+
$targetRefExists = $xpath->query('./*[local-name()="targetRef"]', $dataInputAssociation)->length > 0;
61+
if ($targetRefExists) {
62+
continue;
63+
}
64+
65+
// Extract sourceRef
66+
$sourceRef = $xpath->query('./*[local-name()="sourceRef"]', $dataInputAssociation)->item(0);
67+
if (!$sourceRef) {
68+
throw new Exception("sourceRef not found in dataInputAssociation for task $taskId");
69+
}
70+
$sourceId = $sourceRef->nodeValue;
71+
72+
// Create ioSpecification and children
73+
$ioSpec = $document->createElementNS($taskNS, "{$taskPrefix}ioSpecification");
74+
$ioSpecId = "{$taskId}_inner_" . round(microtime(true) * 1000);
75+
$ioSpec->setAttribute("id", $ioSpecId);
76+
77+
$dataInputId = "data_input_{$sourceId}";
78+
$dataInput = $document->createElementNS($taskNS, "{$taskPrefix}dataInput");
79+
$dataInput->setAttribute("id", $dataInputId);
80+
$dataInput->setAttribute("name", "Template for protocol");
81+
$ioSpec->appendChild($dataInput);
82+
83+
$inputSet = $document->createElementNS($taskNS, "{$taskPrefix}inputSet");
84+
$inputSet->setAttribute("id", "{$taskId}_inner_" . (round(microtime(true) * 1000) + 2));
85+
$dataInputRefs = $document->createElementNS($taskNS, "{$taskPrefix}dataInputRefs", $dataInputId);
86+
$inputSet->appendChild($dataInputRefs);
87+
$ioSpec->appendChild($inputSet);
88+
89+
$outputSet = $document->createElementNS($taskNS, "{$taskPrefix}outputSet");
90+
$outputSet->setAttribute("id", "{$taskId}_inner_" . (round(microtime(true) * 1000) + 3));
91+
$ioSpec->appendChild($outputSet);
92+
93+
$task->insertBefore($ioSpec, $dataInputAssociation);
94+
95+
// Add targetRef
96+
$targetRef = $document->createElementNS($taskNS, "{$taskPrefix}targetRef", $dataInputId);
97+
$dataInputAssociation->appendChild($targetRef);
98+
99+
// Add BPMNEdge to BPMNDiagram
100+
$diagramNodes = $xpath->query('//*[local-name() = "BPMNDiagram"]');
101+
if ($diagramNodes->length === 0) {
102+
throw new Exception("No BPMNDiagram node found in the BPMN file.");
103+
}
104+
$bpmnDiagram = $diagramNodes->item(0);
105+
$diagramPrefix = $bpmnDiagram->prefix;
106+
$diagramNS = $bpmnDiagram->namespaceURI;
107+
108+
$bpmnPlaneNodes = $xpath->query('.//*[local-name() = "BPMNPlane"]', $bpmnDiagram);
109+
if ($bpmnPlaneNodes->length === 0) {
110+
throw new Exception("No BPMNPlane found inside BPMNDiagram.");
111+
}
112+
$bpmnPlane = $bpmnPlaneNodes->item(0);
113+
114+
$edgeId = 'BPMNEdge_' . $dataInputAssociation->getAttribute("id");
115+
$bpmnEdge = $document->createElementNS($diagramNS, "{$diagramPrefix}:BPMNEdge");
116+
$bpmnEdge->setAttribute("id", $edgeId);
117+
$bpmnEdge->setAttribute("bpmnElement", $dataInputAssociation->getAttribute("id"));
118+
119+
$diNS = 'http://www.omg.org/spec/DD/20100524/DI';
120+
$waypoint1 = $document->createElementNS($diNS, "di:waypoint");
121+
$waypoint1->setAttribute("x", "100");
122+
$waypoint1->setAttribute("y", "100");
123+
124+
$waypoint2 = $document->createElementNS($diNS, "di:waypoint");
125+
$waypoint2->setAttribute("x", "200");
126+
$waypoint2->setAttribute("y", "200");
127+
128+
$bpmnEdge->appendChild($waypoint1);
129+
$bpmnEdge->appendChild($waypoint2);
130+
$bpmnPlane->appendChild($bpmnEdge);
131+
}
132+
133+
return $document->saveXml();
134+
} catch (Exception $e) {
135+
throw $e;
136+
}
137+
}
138+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:pm="http://processmaker.com/BPMN/2.0/Schema.xsd" xmlns:tns="http://sourceforge.net/bpmn/definitions/_1530553328908" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://bpmn.io/schema/bpmn" exporter="ProcessMaker Modeler" exporterVersion="1.0" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://bpmn.sourceforge.net/schemas/BPMN20.xsd">
3+
<bpmn:process id="ProcessId" name="ProcessName" isExecutable="true">
4+
<bpmn:task id="node_1" name="Form Task" pm:allowInterstitial="false" pm:assignment="requester" pm:assignmentLock="false" pm:allowReassignment="false" pm:elementDestination="{&#34;type&#34;:&#34;taskSource&#34;,&#34;value&#34;:null}">
5+
<bpmn:ioSpecification id="node_1_inner_1753895979606">
6+
<bpmn:dataInput id="data_input_node_16" name="Data Object" />
7+
<bpmn:inputSet id="node_1_inner_1753895979608">
8+
<bpmn:dataInputRefs>data_input_node_16</bpmn:dataInputRefs>
9+
</bpmn:inputSet>
10+
<bpmn:outputSet id="node_1_inner_1753895979609" />
11+
</bpmn:ioSpecification>
12+
<bpmn:dataInputAssociation id="node_17">
13+
<bpmn:sourceRef>node_16</bpmn:sourceRef>
14+
<bpmn:targetRef>data_input_node_16</bpmn:targetRef>
15+
</bpmn:dataInputAssociation>
16+
</bpmn:task>
17+
<bpmn:dataObjectReference id="node_16" name="Data Object" />
18+
</bpmn:process>
19+
<bpmndi:BPMNDiagram id="BPMNDiagramId">
20+
<bpmndi:BPMNPlane id="BPMNPlaneId" bpmnElement="ProcessId">
21+
<bpmndi:BPMNShape id="node_1_di" bpmnElement="node_1">
22+
<dc:Bounds x="320" y="200" width="116" height="76" />
23+
</bpmndi:BPMNShape>
24+
<bpmndi:BPMNShape id="node_16_di" bpmnElement="node_16">
25+
<dc:Bounds x="350" y="400" width="36" height="50" />
26+
</bpmndi:BPMNShape>
27+
<bpmndi:BPMNEdge id="node_17_di" bpmnElement="node_17">
28+
<di:waypoint x="368" y="425" />
29+
<di:waypoint x="378" y="238" />
30+
</bpmndi:BPMNEdge>
31+
</bpmndi:BPMNPlane>
32+
</bpmndi:BPMNDiagram>
33+
</bpmn:definitions>
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<ns4:definitions xmlns="http://www.omg.org/spec/DD/20100524/DC" xmlns:ns2="http://www.omg.org/spec/DD/20100524/DI" xmlns:ns3="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:ns4="http://www.omg.org/spec/BPMN/20100524/MODEL" exporter="CWA SmartProcess" exporterVersion="25.3">
3+
<ns4:process name="Test Process" id="_1446983">
4+
<ns4:laneSet id="tls_1446983">
5+
<ns4:lane name="Lane 1" id="_1446983_4">
6+
<ns4:flowNodeRef>_1446983_5</ns4:flowNodeRef>
7+
<ns4:flowNodeRef>_1446983_8</ns4:flowNodeRef>
8+
<ns4:flowNodeRef>_1446983_20</ns4:flowNodeRef>
9+
<ns4:flowNodeRef>_1446983_29</ns4:flowNodeRef>
10+
</ns4:lane>
11+
<ns4:lane name="Lane 2" id="_1446983_6">
12+
<ns4:flowNodeRef>_1446983_12</ns4:flowNodeRef>
13+
<ns4:flowNodeRef>_1446983_14</ns4:flowNodeRef>
14+
</ns4:lane>
15+
</ns4:laneSet>
16+
<ns4:dataObject name="Data Input" id="_1446983_36"/>
17+
<ns4:dataObjectReference dataObjectRef="_1446983_36" name="Data Input" id="dor_1446983_36"/>
18+
<ns4:dataObject name="Data Output" id="_1446983_38"/>
19+
<ns4:dataObjectReference dataObjectRef="_1446983_38" name="Data Output" id="dor_1446983_38"/>
20+
<ns4:startEvent name="Start Process" id="_1446983_5"/>
21+
<ns4:task name="Task 1" id="_1446983_8">
22+
<ns4:dataInputAssociation id="_1446983_40">
23+
<ns4:sourceRef>dor_1446983_36</ns4:sourceRef>
24+
</ns4:dataInputAssociation>
25+
</ns4:task>
26+
<ns4:task name="Task 4" id="_1446983_20"/>
27+
<ns4:endEvent name="End Process" id="_1446983_29"/>
28+
<ns4:task name="Task 2" id="_1446983_12"/>
29+
<ns4:task name="Task 3" id="_1446983_14">
30+
<ns4:dataOutputAssociation id="_1446983_39">
31+
<ns4:targetRef>dor_1446983_38</ns4:targetRef>
32+
</ns4:dataOutputAssociation>
33+
</ns4:task>
34+
<ns4:sequenceFlow sourceRef="_1446983_8" targetRef="_1446983_12" id="_1446983_13"/>
35+
<ns4:sequenceFlow sourceRef="_1446983_14" targetRef="_1446983_20" id="_1446983_21"/>
36+
<ns4:sequenceFlow sourceRef="_1446983_5" targetRef="_1446983_8" id="_1446983_9"/>
37+
<ns4:sequenceFlow sourceRef="_1446983_12" targetRef="_1446983_14" id="_1446983_15"/>
38+
</ns4:process>
39+
<ns3:BPMNDiagram name="Test Process" resolution="10.0">
40+
<ns3:BPMNPlane xmlns:ns5="http://www.omg.org/spec/DD/20100524/DC" xmlns="" bpmnElement="_1446983" id="bp_1446983">
41+
<ns3:BPMNShape bpmnElement="_1446983_4" isHorizontal="true">
42+
<ns5:Bounds x="10.0" y="60.0" width="1150.0" height="210.0"/>
43+
<ns3:BPMNLabel>
44+
<ns5:Bounds x="10.0" y="60.0" width="23.0" height="210.0"/>
45+
</ns3:BPMNLabel>
46+
</ns3:BPMNShape>
47+
<ns3:BPMNShape bpmnElement="_1446983_6" isHorizontal="true">
48+
<ns5:Bounds x="10.0" y="270.0" width="1150.0" height="210.0"/>
49+
<ns3:BPMNLabel>
50+
<ns5:Bounds x="10.0" y="270.0" width="23.0" height="210.0"/>
51+
</ns3:BPMNLabel>
52+
</ns3:BPMNShape>
53+
<ns3:BPMNShape bpmnElement="dor_1446983_36">
54+
<ns5:Bounds x="225.0" y="490.0" width="30.0" height="40.0"/>
55+
</ns3:BPMNShape>
56+
<ns3:BPMNShape bpmnElement="dor_1446983_38">
57+
<ns5:Bounds x="485.0" y="500.0" width="30.0" height="40.0"/>
58+
</ns3:BPMNShape>
59+
<ns3:BPMNShape bpmnElement="_1446983_5">
60+
<ns5:Bounds x="80.0" y="145.0" width="40.0" height="40.0"/>
61+
</ns3:BPMNShape>
62+
<ns3:BPMNShape bpmnElement="_1446983_8">
63+
<ns5:Bounds x="190.0" y="135.0" width="100.0" height="60.0"/>
64+
</ns3:BPMNShape>
65+
<ns3:BPMNShape bpmnElement="_1446983_20">
66+
<ns5:Bounds x="560.0" y="145.0" width="100.0" height="60.0"/>
67+
</ns3:BPMNShape>
68+
<ns3:BPMNShape bpmnElement="_1446983_29">
69+
<ns5:Bounds x="880.0" y="155.0" width="40.0" height="40.0"/>
70+
</ns3:BPMNShape>
71+
<ns3:BPMNShape bpmnElement="_1446983_12">
72+
<ns5:Bounds x="300.0" y="340.0" width="100.0" height="60.0"/>
73+
</ns3:BPMNShape>
74+
<ns3:BPMNShape bpmnElement="_1446983_14">
75+
<ns5:Bounds x="450.0" y="340.0" width="100.0" height="60.0"/>
76+
</ns3:BPMNShape>
77+
<ns3:BPMNEdge bpmnElement="_1446983_13">
78+
<ns2:waypoint x="240.0" y="195.0"/>
79+
<ns2:waypoint x="240.0" y="370.0"/>
80+
<ns2:waypoint x="300.0" y="370.0"/>
81+
</ns3:BPMNEdge>
82+
<ns3:BPMNEdge bpmnElement="_1446983_21">
83+
<ns2:waypoint x="550.0" y="370.0"/>
84+
<ns2:waypoint x="610.0" y="370.0"/>
85+
<ns2:waypoint x="610.0" y="272.5"/>
86+
<ns2:waypoint x="610.0" y="205.0"/>
87+
</ns3:BPMNEdge>
88+
<ns3:BPMNEdge bpmnElement="_1446983_39">
89+
<ns2:waypoint x="500.0" y="400.0"/>
90+
<ns2:waypoint x="500.0" y="450.0"/>
91+
<ns2:waypoint x="500.0" y="500.0"/>
92+
</ns3:BPMNEdge>
93+
<ns3:BPMNEdge bpmnElement="_1446983_40">
94+
<ns2:waypoint x="240.0" y="490.0"/>
95+
<ns2:waypoint x="240.0" y="342.5"/>
96+
<ns2:waypoint x="240.0" y="195.0"/>
97+
</ns3:BPMNEdge>
98+
<ns3:BPMNEdge bpmnElement="_1446983_9">
99+
<ns2:waypoint x="120.0" y="165.0"/>
100+
<ns2:waypoint x="155.0" y="165.0"/>
101+
<ns2:waypoint x="190.0" y="165.0"/>
102+
</ns3:BPMNEdge>
103+
<ns3:BPMNEdge bpmnElement="_1446983_15">
104+
<ns2:waypoint x="400.0" y="370.0"/>
105+
<ns2:waypoint x="425.0" y="370.0"/>
106+
<ns2:waypoint x="450.0" y="370.0"/>
107+
</ns3:BPMNEdge>
108+
</ns3:BPMNPlane>
109+
</ns3:BPMNDiagram>
110+
</ns4:definitions>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
namespace Tests\Unit\ProcessMaker\Nayra\Services;
4+
5+
use Exception;
6+
use ProcessMaker\Nayra\Services\FixBpmnSchemaService;
7+
use ProcessMaker\Nayra\Storage\BpmnDocument;
8+
use Tests\TestCase;
9+
10+
/**
11+
* Class FixBpmnSchemaServiceTest
12+
*/
13+
class FixBpmnSchemaServiceTest extends TestCase
14+
{
15+
/**
16+
* Test with BPMN definition created in another modeler,
17+
* an Exception should be throwed
18+
*
19+
* @return void
20+
*/
21+
public function testExceptionInIncompleteProcess()
22+
{
23+
$bpmn = file_get_contents(
24+
__DIR__ .
25+
"/../../../../Fixtures/process_data_input_without_targetref.bpmn"
26+
);
27+
28+
$document = new BpmnDocument();
29+
$document->loadXML($bpmn);
30+
31+
$this->expectException(Exception::class);
32+
$validation = $document->validateBPMNSchema(
33+
public_path("definitions/ProcessMaker.xsd")
34+
);
35+
}
36+
37+
/**
38+
* Test fixing incomplete BPMN definition created in another modeler
39+
*
40+
* @return void
41+
*/
42+
public function testFixIncompleteProcess()
43+
{
44+
$bpmn = file_get_contents(
45+
__DIR__ .
46+
"/../../../../Fixtures/process_data_input_without_targetref.bpmn"
47+
);
48+
49+
$fixBpmnSchemaService = app(FixBpmnSchemaService::class);
50+
$bpmn = $fixBpmnSchemaService->fix($bpmn);
51+
52+
$document = new BpmnDocument();
53+
$document->loadXML($bpmn);
54+
$validation = $document->validateBPMNSchema(
55+
public_path("definitions/ProcessMaker.xsd")
56+
);
57+
58+
$this->assertTrue($validation);
59+
}
60+
61+
/**
62+
* Test using a BPMN definition created in PM4
63+
*
64+
* @return void
65+
*/
66+
public function testFixPm4Process()
67+
{
68+
$bpmn = file_get_contents(
69+
__DIR__ .
70+
"/../../../../Fixtures/process_data_input_generated_in_pm4.bpmn"
71+
);
72+
73+
$fixBpmnSchemaService = app(FixBpmnSchemaService::class);
74+
$bpmn = $fixBpmnSchemaService->fix($bpmn);
75+
76+
$document = new BpmnDocument();
77+
$document->loadXML($bpmn);
78+
$validation = $document->validateBPMNSchema(
79+
public_path("definitions/ProcessMaker.xsd")
80+
);
81+
82+
$this->assertTrue($validation);
83+
}
84+
}

0 commit comments

Comments
 (0)