From ecc87f2b6a61d437d1366935ac4828934d5379dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Tue, 29 Jul 2025 11:02:45 -0400 Subject: [PATCH 1/7] FOUR-25187 Processes cannot be created with BPMN files --- public/definitions/BPMN20.xsd | 2 +- public/definitions/Semantic.xsd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/definitions/BPMN20.xsd b/public/definitions/BPMN20.xsd index d60695ca38..885c6a65c2 100644 --- a/public/definitions/BPMN20.xsd +++ b/public/definitions/BPMN20.xsd @@ -19,7 +19,7 @@ - + diff --git a/public/definitions/Semantic.xsd b/public/definitions/Semantic.xsd index f6056942aa..9f200727f6 100644 --- a/public/definitions/Semantic.xsd +++ b/public/definitions/Semantic.xsd @@ -440,7 +440,7 @@ - + From a0c7b91ae22883854aa95e9710963a01cf248445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Tue, 29 Jul 2025 19:11:35 -0400 Subject: [PATCH 2/7] FOUR-25187 Processes cannot be created with BPMN files - Seconf round --- .../Controllers/Api/ProcessController.php | 8 +++++ public/definitions/BPMN20.xsd | 2 +- public/definitions/Semantic.xsd | 30 +++++++++---------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/ProcessMaker/Http/Controllers/Api/ProcessController.php b/ProcessMaker/Http/Controllers/Api/ProcessController.php index 0c223f70da..a80b51c751 100644 --- a/ProcessMaker/Http/Controllers/Api/ProcessController.php +++ b/ProcessMaker/Http/Controllers/Api/ProcessController.php @@ -819,6 +819,14 @@ private function validateBpmn(Request $request): array $document = new BpmnDocument(); try { $document->loadXML($data['bpmn']); + + // Get root node + $root = $document->documentElement; + + if ($root) { + // Set "targetNamespace" attribute always + $root->setAttribute('targetNamespace', 'http://bpmn.io/schema/bpmn'); + } } catch (\ErrorException $e) { return [$e->getMessage()]; } diff --git a/public/definitions/BPMN20.xsd b/public/definitions/BPMN20.xsd index 885c6a65c2..d60695ca38 100644 --- a/public/definitions/BPMN20.xsd +++ b/public/definitions/BPMN20.xsd @@ -19,7 +19,7 @@ - + diff --git a/public/definitions/Semantic.xsd b/public/definitions/Semantic.xsd index 9f200727f6..83d4a3e6f3 100644 --- a/public/definitions/Semantic.xsd +++ b/public/definitions/Semantic.xsd @@ -434,20 +434,6 @@ - - - - - - - - - - - - - - @@ -465,7 +451,13 @@ - + + + + + + + @@ -512,7 +504,13 @@ - + + + + + + + From 558aff924d7c1b6c5e30a56685616011dca1292f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Tue, 29 Jul 2025 19:24:20 -0400 Subject: [PATCH 3/7] FOUR-25187 Processes cannot be created with BPMN files - Third round --- public/definitions/Semantic.xsd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/definitions/Semantic.xsd b/public/definitions/Semantic.xsd index 83d4a3e6f3..21a8aa29f2 100644 --- a/public/definitions/Semantic.xsd +++ b/public/definitions/Semantic.xsd @@ -454,6 +454,7 @@ + @@ -506,6 +507,7 @@ + From 28c1c67f733994ff486baca8f063333aeb20bb8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Thu, 31 Jul 2025 12:10:26 -0400 Subject: [PATCH 4/7] FOUR-25187 Processes cannot be created with BPMN files - Fourth round --- .../Controllers/Api/ProcessController.php | 15 +- .../Nayra/Services/FixBpmnSchemaService.php | 139 ++++++++++++++++++ public/definitions/Semantic.xsd | 32 ++-- 3 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 ProcessMaker/Nayra/Services/FixBpmnSchemaService.php diff --git a/ProcessMaker/Http/Controllers/Api/ProcessController.php b/ProcessMaker/Http/Controllers/Api/ProcessController.php index a80b51c751..6db5e3caa0 100644 --- a/ProcessMaker/Http/Controllers/Api/ProcessController.php +++ b/ProcessMaker/Http/Controllers/Api/ProcessController.php @@ -37,6 +37,7 @@ use ProcessMaker\Models\Script; use ProcessMaker\Models\Template; use ProcessMaker\Nayra\Exceptions\ElementNotFoundException; +use ProcessMaker\Nayra\Services\FixBpmnSchemaService; use ProcessMaker\Nayra\Storage\BpmnDocument; use ProcessMaker\Package\Translations\Models\Language; use ProcessMaker\Package\WebEntry\Models\WebentryRoute; @@ -379,6 +380,12 @@ public function store(Request $request) // Validate if exists file bpmn if ($request->has('file')) { $data['bpmn'] = $request->file('file')->get(); + + // Some BPMN definitions created in others modelers doesn't comply BPMN 2.0 + // So, should be fixed + $fixBpmnSchemaService = new FixBpmnSchemaService(); + $data['bpmn'] = $fixBpmnSchemaService->fix($data['bpmn']); + $request->merge(['bpmn' => $data['bpmn']]); $request->offsetUnset('file'); unset($data['file']); @@ -819,14 +826,6 @@ private function validateBpmn(Request $request): array $document = new BpmnDocument(); try { $document->loadXML($data['bpmn']); - - // Get root node - $root = $document->documentElement; - - if ($root) { - // Set "targetNamespace" attribute always - $root->setAttribute('targetNamespace', 'http://bpmn.io/schema/bpmn'); - } } catch (\ErrorException $e) { return [$e->getMessage()]; } diff --git a/ProcessMaker/Nayra/Services/FixBpmnSchemaService.php b/ProcessMaker/Nayra/Services/FixBpmnSchemaService.php new file mode 100644 index 0000000000..88f885c7d5 --- /dev/null +++ b/ProcessMaker/Nayra/Services/FixBpmnSchemaService.php @@ -0,0 +1,139 @@ +preserveWhiteSpace = false; + $document->loadXml($bpmn); + + // Get root node + $root = $document->documentElement; + + // Set "targetNamespace" attribute always + $root->setAttribute('targetNamespace', 'http://www.omg.org/spec/BPMN/20100524/MODEL'); + + // Create XPath object + $xpath = new DOMXPath($document); + + // Register all namespaces, including default + foreach ($document->documentElement->attributes as $attr) { + if (strpos($attr->nodeName, 'xmlns:') === 0) { + $prefix = substr($attr->nodeName, 6); + $xpath->registerNamespace($prefix, $attr->nodeValue); + } elseif ($attr->nodeName === 'xmlns') { + // Register default namespace as "def" + $xpath->registerNamespace('def', $attr->nodeValue); + } + } + + // Find all task nodes + $taskNodes = $xpath->query('//*[local-name()="task"]'); + //$taskNodes = $xpath->query('//*[local-name()="task" or local-name()="userTask" or local-name()="serviceTask" or local-name()="scriptTask" or local-name()="receiveTask" or local-name()="sendTask" or local-name()="businessRuleTask" or local-name()="manualTask" or local-name()="callActivity" or local-name()="subProcess" or local-name()="intermediateThrowEvent"]'); + + foreach ($taskNodes as $task) { + $taskId = $task->getAttribute("id"); + $taskPrefix = !empty($task->prefix) ? "$task->prefix:" : ''; + $taskNS = $task->namespaceURI; + + $dataInputAssociation = $xpath->query('./*[local-name()="dataInputAssociation"]', $task)->item(0); + if (!$dataInputAssociation) { + continue; + } + + // Skip if targetRef already exists + $targetRefExists = $xpath->query('./*[local-name()="targetRef"]', $dataInputAssociation)->length > 0; + if ($targetRefExists) { + continue; + } + + // Extract sourceRef + $sourceRef = $xpath->query('./*[local-name()="sourceRef"]', $dataInputAssociation)->item(0); + if (!$sourceRef) { + throw new Exception("sourceRef not found in dataInputAssociation for task $taskId"); + } + $sourceId = $sourceRef->nodeValue; + + // Create ioSpecification and children + $ioSpec = $document->createElementNS($taskNS, "{$taskPrefix}ioSpecification"); + $ioSpecId = "{$taskId}_inner_" . round(microtime(true) * 1000); + $ioSpec->setAttribute("id", $ioSpecId); + + $dataInputId = "data_input_{$sourceId}"; + $dataInput = $document->createElementNS($taskNS, "{$taskPrefix}dataInput"); + $dataInput->setAttribute("id", $dataInputId); + $dataInput->setAttribute("name", "Template for protocol"); + $ioSpec->appendChild($dataInput); + + $inputSet = $document->createElementNS($taskNS, "{$taskPrefix}inputSet"); + $inputSet->setAttribute("id", "{$taskId}_inner_" . (round(microtime(true) * 1000) + 2)); + $dataInputRefs = $document->createElementNS($taskNS, "{$taskPrefix}dataInputRefs", $dataInputId); + $inputSet->appendChild($dataInputRefs); + $ioSpec->appendChild($inputSet); + + $outputSet = $document->createElementNS($taskNS, "{$taskPrefix}outputSet"); + $outputSet->setAttribute("id", "{$taskId}_inner_" . (round(microtime(true) * 1000) + 3)); + $ioSpec->appendChild($outputSet); + + $task->insertBefore($ioSpec, $dataInputAssociation); + + // Add targetRef + $targetRef = $document->createElementNS($taskNS, "{$taskPrefix}targetRef", $dataInputId); + $dataInputAssociation->appendChild($targetRef); + + // Add BPMNEdge to BPMNDiagram + $diagramNodes = $xpath->query('//*[local-name() = "BPMNDiagram"]'); + if ($diagramNodes->length === 0) { + throw new Exception("No BPMNDiagram node found in the BPMN file."); + } + $bpmnDiagram = $diagramNodes->item(0); + $diagramPrefix = $bpmnDiagram->prefix; + $diagramNS = $bpmnDiagram->namespaceURI; + + $bpmnPlaneNodes = $xpath->query('.//*[local-name() = "BPMNPlane"]', $bpmnDiagram); + if ($bpmnPlaneNodes->length === 0) { + throw new Exception("No BPMNPlane found inside BPMNDiagram."); + } + $bpmnPlane = $bpmnPlaneNodes->item(0); + + $edgeId = 'BPMNEdge_' . $dataInputAssociation->getAttribute("id"); + $bpmnEdge = $document->createElementNS($diagramNS, "{$diagramPrefix}:BPMNEdge"); + $bpmnEdge->setAttribute("id", $edgeId); + $bpmnEdge->setAttribute("bpmnElement", $dataInputAssociation->getAttribute("id")); + + $diNS = 'http://www.omg.org/spec/DD/20100524/DI'; + $waypoint1 = $document->createElementNS($diNS, "di:waypoint"); + $waypoint1->setAttribute("x", "100"); + $waypoint1->setAttribute("y", "100"); + + $waypoint2 = $document->createElementNS($diNS, "di:waypoint"); + $waypoint2->setAttribute("x", "200"); + $waypoint2->setAttribute("y", "200"); + + $bpmnEdge->appendChild($waypoint1); + $bpmnEdge->appendChild($waypoint2); + $bpmnPlane->appendChild($bpmnEdge); + } + + return $document->saveXml(); + } catch (Exception $e) { + throw $e; + } + } +} diff --git a/public/definitions/Semantic.xsd b/public/definitions/Semantic.xsd index 21a8aa29f2..a08ed90a92 100644 --- a/public/definitions/Semantic.xsd +++ b/public/definitions/Semantic.xsd @@ -434,6 +434,20 @@ + + + + + + + + + + + + + + @@ -451,14 +465,7 @@ - - - - - - - - + @@ -505,14 +512,7 @@ - - - - - - - - + From d119f7628961c3bfbd022103007160d272b1f2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Thu, 31 Jul 2025 12:13:52 -0400 Subject: [PATCH 5/7] FOUR-25187 Processes cannot be created with BPMN files - Fifth round --- ProcessMaker/Nayra/Services/FixBpmnSchemaService.php | 1 - public/definitions/Semantic.xsd | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ProcessMaker/Nayra/Services/FixBpmnSchemaService.php b/ProcessMaker/Nayra/Services/FixBpmnSchemaService.php index 88f885c7d5..4c3cf3ffff 100644 --- a/ProcessMaker/Nayra/Services/FixBpmnSchemaService.php +++ b/ProcessMaker/Nayra/Services/FixBpmnSchemaService.php @@ -45,7 +45,6 @@ public static function fix(string $bpmn): string // Find all task nodes $taskNodes = $xpath->query('//*[local-name()="task"]'); - //$taskNodes = $xpath->query('//*[local-name()="task" or local-name()="userTask" or local-name()="serviceTask" or local-name()="scriptTask" or local-name()="receiveTask" or local-name()="sendTask" or local-name()="businessRuleTask" or local-name()="manualTask" or local-name()="callActivity" or local-name()="subProcess" or local-name()="intermediateThrowEvent"]'); foreach ($taskNodes as $task) { $taskId = $task->getAttribute("id"); diff --git a/public/definitions/Semantic.xsd b/public/definitions/Semantic.xsd index a08ed90a92..f6056942aa 100644 --- a/public/definitions/Semantic.xsd +++ b/public/definitions/Semantic.xsd @@ -446,8 +446,8 @@ - - + + From f5d4bf2fa9c39917d2673cf7de7ab71d26b232ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Thu, 31 Jul 2025 13:02:00 -0400 Subject: [PATCH 6/7] FOUR-25187 Processes cannot be created with BPMN files - Sixth round --- ProcessMaker/Http/Controllers/Api/ProcessController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessMaker/Http/Controllers/Api/ProcessController.php b/ProcessMaker/Http/Controllers/Api/ProcessController.php index 6db5e3caa0..6ebd6a4916 100644 --- a/ProcessMaker/Http/Controllers/Api/ProcessController.php +++ b/ProcessMaker/Http/Controllers/Api/ProcessController.php @@ -383,7 +383,7 @@ public function store(Request $request) // Some BPMN definitions created in others modelers doesn't comply BPMN 2.0 // So, should be fixed - $fixBpmnSchemaService = new FixBpmnSchemaService(); + $fixBpmnSchemaService = app(FixBpmnSchemaService::class); $data['bpmn'] = $fixBpmnSchemaService->fix($data['bpmn']); $request->merge(['bpmn' => $data['bpmn']]); From 1e28a948cc3d81e01bfc8a3ade97168cacd48df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Fri, 1 Aug 2025 11:57:03 -0400 Subject: [PATCH 7/7] FOUR-25187 Processes cannot be created with BPMN files - Adding unit test --- .../process_data_input_generated_in_pm4.bpmn | 33 ++++++ .../process_data_input_without_targetref.bpmn | 110 ++++++++++++++++++ .../Services/FixBpmnSchemaServiceTest.php | 84 +++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 tests/Fixtures/process_data_input_generated_in_pm4.bpmn create mode 100644 tests/Fixtures/process_data_input_without_targetref.bpmn create mode 100644 tests/unit/ProcessMaker/Nayra/Services/FixBpmnSchemaServiceTest.php diff --git a/tests/Fixtures/process_data_input_generated_in_pm4.bpmn b/tests/Fixtures/process_data_input_generated_in_pm4.bpmn new file mode 100644 index 0000000000..4a4d0839c8 --- /dev/null +++ b/tests/Fixtures/process_data_input_generated_in_pm4.bpmn @@ -0,0 +1,33 @@ + + + + + + + + data_input_node_16 + + + + + node_16 + data_input_node_16 + + + + + + + + + + + + + + + + + + + diff --git a/tests/Fixtures/process_data_input_without_targetref.bpmn b/tests/Fixtures/process_data_input_without_targetref.bpmn new file mode 100644 index 0000000000..e6c346b2bf --- /dev/null +++ b/tests/Fixtures/process_data_input_without_targetref.bpmn @@ -0,0 +1,110 @@ + + + + + + _1446983_5 + _1446983_8 + _1446983_20 + _1446983_29 + + + _1446983_12 + _1446983_14 + + + + + + + + + + dor_1446983_36 + + + + + + + + dor_1446983_38 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/unit/ProcessMaker/Nayra/Services/FixBpmnSchemaServiceTest.php b/tests/unit/ProcessMaker/Nayra/Services/FixBpmnSchemaServiceTest.php new file mode 100644 index 0000000000..2b3ec5adaf --- /dev/null +++ b/tests/unit/ProcessMaker/Nayra/Services/FixBpmnSchemaServiceTest.php @@ -0,0 +1,84 @@ +loadXML($bpmn); + + $this->expectException(Exception::class); + $validation = $document->validateBPMNSchema( + public_path("definitions/ProcessMaker.xsd") + ); + } + + /** + * Test fixing incomplete BPMN definition created in another modeler + * + * @return void + */ + public function testFixIncompleteProcess() + { + $bpmn = file_get_contents( + __DIR__ . + "/../../../../Fixtures/process_data_input_without_targetref.bpmn" + ); + + $fixBpmnSchemaService = app(FixBpmnSchemaService::class); + $bpmn = $fixBpmnSchemaService->fix($bpmn); + + $document = new BpmnDocument(); + $document->loadXML($bpmn); + $validation = $document->validateBPMNSchema( + public_path("definitions/ProcessMaker.xsd") + ); + + $this->assertTrue($validation); + } + + /** + * Test using a BPMN definition created in PM4 + * + * @return void + */ + public function testFixPm4Process() + { + $bpmn = file_get_contents( + __DIR__ . + "/../../../../Fixtures/process_data_input_generated_in_pm4.bpmn" + ); + + $fixBpmnSchemaService = app(FixBpmnSchemaService::class); + $bpmn = $fixBpmnSchemaService->fix($bpmn); + + $document = new BpmnDocument(); + $document->loadXML($bpmn); + $validation = $document->validateBPMNSchema( + public_path("definitions/ProcessMaker.xsd") + ); + + $this->assertTrue($validation); + } +}