From 0aaf00416d7009787e1aedbd8c440e66ce227f33 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Mon, 31 Mar 2025 17:37:35 -0400 Subject: [PATCH 001/142] FOUR-22837 --- config/session.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/session.php b/config/session.php index ed56e8bb35..1452f75dbe 100644 --- a/config/session.php +++ b/config/session.php @@ -170,7 +170,7 @@ | */ - 'secure' => env('SESSION_SECURE_COOKIE'), + 'secure' => env('SESSION_SECURE_COOKIE', false), /* |-------------------------------------------------------------------------- @@ -198,6 +198,6 @@ | */ - 'same_site' => 'lax', + 'same_site' => env('SESSION_SAME_SITE', 'lax'), ]; From 9f58ddafac8cc54b54bbc50a3c278d142632934e Mon Sep 17 00:00:00 2001 From: Fabio Date: Tue, 1 Apr 2025 17:20:21 -0400 Subject: [PATCH 002/142] FOUR-23362:S1: Add in the setting the menu Iframe Whitelist Config --- .../Controllers/Api/SettingController.php | 10 ++ .../settings/components/ModalWhiteList.vue | 104 ++++++++++++++++++ .../settings/components/SettingsListing.vue | 73 +++++++----- routes/api.php | 3 + ..._03_31_140423_add_menu_frame_whitelist.php | 49 +++++++++ 5 files changed, 209 insertions(+), 30 deletions(-) create mode 100644 resources/js/admin/settings/components/ModalWhiteList.vue create mode 100644 upgrades/2025_03_31_140423_add_menu_frame_whitelist.php diff --git a/ProcessMaker/Http/Controllers/Api/SettingController.php b/ProcessMaker/Http/Controllers/Api/SettingController.php index de90e2a461..bde3045d67 100644 --- a/ProcessMaker/Http/Controllers/Api/SettingController.php +++ b/ProcessMaker/Http/Controllers/Api/SettingController.php @@ -265,6 +265,16 @@ public function update(Setting $setting, Request $request) return response([], 204); } + public function store(Request $request) + { + $setting = new Setting(); + + $setting->fill($request->json()->all()); + $setting->saveOrFail(); + + return response([], 201); + } + public function destroy(Setting $setting) { $setting->delete(); diff --git a/resources/js/admin/settings/components/ModalWhiteList.vue b/resources/js/admin/settings/components/ModalWhiteList.vue new file mode 100644 index 0000000000..b0c92e5ad3 --- /dev/null +++ b/resources/js/admin/settings/components/ModalWhiteList.vue @@ -0,0 +1,104 @@ + + + diff --git a/resources/js/admin/settings/components/SettingsListing.vue b/resources/js/admin/settings/components/SettingsListing.vue index f5a0f2a232..5cf91892e5 100644 --- a/resources/js/admin/settings/components/SettingsListing.vue +++ b/resources/js/admin/settings/components/SettingsListing.vue @@ -188,6 +188,7 @@ + @@ -211,8 +212,10 @@ import SettingsRange from './SettingsRange'; import SettingDriverAuthorization from './SettingDriverAuthorization'; import { createUniqIdsMixin } from "vue-uniq-ids"; import SettingsEmpty from "./SettingsEmpty.vue"; +import ModalWhiteList from "./ModalWhiteList.vue"; const uniqIdsMixin = createUniqIdsMixin(); +const whiteListName = "IFrame Whitelist Config"; export default { components: { @@ -232,6 +235,7 @@ export default { SettingsRange, SettingSelect, SettingsEmpty, + ModalWhiteList, }, mixins:[dataLoadingMixin, uniqIdsMixin], props: ['group'], @@ -272,37 +276,10 @@ export default { }, }, mounted() { - this.$on("refresh-menu", this.removeElement); - if (! this.group) { - this.orderBy = "group"; - this.fields.push({ - key: "group", - label: "Group", - sortable: true, - tdClass: "td-group", - }); - } - - this.fields.push({ - key: "name", - label: "Setting", - sortable: true, - tdClass: "align-middle td-name settings-listing-td1", - }); + const that = this; - this.fields.push({ - key: "config", - label: "Configuration", - sortable: false, - tdClass: "align-middle td-config settings-listing-td2", - }); - - this.fields.push({ - key: "actions", - label: "", - sortable: false, - tdClass: "align-middle settings-listing-td3", - }); + this.$on("refresh-menu", this.removeElement); + this.fillFields(); ProcessMaker.EventBus.$on('setting-added-from-modal', () => { this.shouldDisplayNoDataMessage = false; @@ -310,8 +287,44 @@ export default { this.refresh(); }); }); + + window.addWhiteListURL = function (owner) { + that.$refs["modal-whitelist"].show(owner.group); + }; }, methods: { + fillFields() { + if (!this.group) { + this.orderBy = "group"; + this.fields.push({ + key: "group", + label: "Group", + sortable: true, + tdClass: "td-group", + }); + } + + this.fields.push({ + key: "name", + label: this.group === whiteListName ? "Name" : "Setting", + sortable: true, + tdClass: "align-middle td-name settings-listing-td1", + }); + + this.fields.push({ + key: "config", + label: this.group === whiteListName ? "Links" : "Configuration", + sortable: false, + tdClass: "align-middle td-config settings-listing-td2", + }); + + this.fields.push({ + key: "actions", + label: "", + sortable: false, + tdClass: "align-middle settings-listing-td3", + }); + }, loadButtons() { ProcessMaker.apiClient.get(`/settings/group/${this.group}/buttons`) .then((response) => { diff --git a/routes/api.php b/routes/api.php index 1d48e244e3..6b7c23450a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,5 +1,6 @@ name('settings.menu_groups')->middleware($viewSettings); Route::post('settings/import', [SettingController::class, 'import']) ->name('settings.import')->middleware($updateSettings); + Route::post('settings', [SettingController::class, 'store']) + ->name('settings.store')->middleware($updateSettings); Route::delete('settings/{setting}', [SettingController::class, 'destroy']) ->name('settings.destroy')->middleware($updateSettings); Route::put('settings/{setting}', [SettingController::class, 'update']) diff --git a/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php b/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php new file mode 100644 index 0000000000..3e4990346f --- /dev/null +++ b/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php @@ -0,0 +1,49 @@ + 'white_list_frame', + ]; + $whiteListOption = [ + 'format' => 'button', + 'group' => 'IFrame Whitelist Config', + 'group_id' => 3, + 'helper' => null, + 'config' => false, + 'name' => 'Add URL', + 'hidden' => true, + 'ui' => [ + "props" => [ + "variant" => "primary", + "position" => "top", + "order" => "100", + "icon" => "fas fa-plus" + ], + "handler" => "addWhiteListURL" + ] + ]; + + Setting::firstOrCreate($whiteListKey, $whiteListOption); + } + + /** + * Reverse the upgrade migration. + * + * @return void + */ + public function down() + { + // + } +} From 0728ebb7a87fec295325a1ef65d042aab4cb4d8f Mon Sep 17 00:00:00 2001 From: Fabio Date: Tue, 1 Apr 2025 17:25:14 -0400 Subject: [PATCH 003/142] Update Model Setting --- ProcessMaker/Models/Setting.php | 1 - 1 file changed, 1 deletion(-) diff --git a/ProcessMaker/Models/Setting.php b/ProcessMaker/Models/Setting.php index 0646dd6e71..21307649e7 100644 --- a/ProcessMaker/Models/Setting.php +++ b/ProcessMaker/Models/Setting.php @@ -403,7 +403,6 @@ public static function groupsByMenu($menuId) ->groupBy('group') ->where('group_id', $menuId) ->orderBy('group', 'ASC') - ->notHidden() ->pluck('group'); $response = $query->toArray(); $result = []; From a439060d5c19a57c7c848e7261da837117ec509a Mon Sep 17 00:00:00 2001 From: Fabio Date: Wed, 2 Apr 2025 11:55:50 -0400 Subject: [PATCH 004/142] CR --- resources/js/admin/settings/components/ModalWhiteList.vue | 8 ++++++++ upgrades/2025_03_31_140423_add_menu_frame_whitelist.php | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/resources/js/admin/settings/components/ModalWhiteList.vue b/resources/js/admin/settings/components/ModalWhiteList.vue index b0c92e5ad3..15b6f6c639 100644 --- a/resources/js/admin/settings/components/ModalWhiteList.vue +++ b/resources/js/admin/settings/components/ModalWhiteList.vue @@ -81,6 +81,14 @@ export default { this.url = ""; }, addWhiteListURL() { + if (!this.siteName) { + this.stateSiteName = false; + return; + } + if (!this.url) { + this.stateURL = false; + return; + } const site = this.siteName.toLocaleLowerCase().trim().replaceAll(" ", "_"); const data = { key: `whiteList.${site}`, diff --git a/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php b/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php index 3e4990346f..e707cc8882 100644 --- a/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php +++ b/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php @@ -12,13 +12,14 @@ class AddMenuFrameWhitelist extends Upgrade */ public function up() { + $group_id = SettingsMenus::getId(SettingsMenus::LOG_IN_AUTH_MENU_GROUP); $whiteListKey = [ 'key' => 'white_list_frame', ]; $whiteListOption = [ 'format' => 'button', 'group' => 'IFrame Whitelist Config', - 'group_id' => 3, + 'group_id' => $group_id, 'helper' => null, 'config' => false, 'name' => 'Add URL', From ebcc48267a2da99da303c0acd6956639d45661b7 Mon Sep 17 00:00:00 2001 From: Fabio Rodolfo Guachalla Blanco Date: Wed, 2 Apr 2025 15:08:56 -0400 Subject: [PATCH 005/142] add model in upgrade --- upgrades/2025_03_31_140423_add_menu_frame_whitelist.php | 1 + 1 file changed, 1 insertion(+) diff --git a/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php b/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php index e707cc8882..a74b9816cf 100644 --- a/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php +++ b/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php @@ -1,6 +1,7 @@ Date: Thu, 3 Apr 2025 17:31:52 -0400 Subject: [PATCH 006/142] FOUR-23128 --- routes/api.php | 1 - ..._03_31_140423_add_menu_frame_whitelist.php | 21 +++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/routes/api.php b/routes/api.php index 6b7c23450a..051616cb12 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,5 @@ 'Add URL', 'hidden' => true, 'ui' => [ - "props" => [ - "variant" => "primary", - "position" => "top", - "order" => "100", - "icon" => "fas fa-plus" + 'props' => [ + 'variant' => 'primary', + 'position' => 'top', + 'order' => '100', + 'icon' => 'fas fa-plus', ], - "handler" => "addWhiteListURL" - ] - ]; + 'handler' => 'addWhiteListURL', + ], + ]; Setting::firstOrCreate($whiteListKey, $whiteListOption); } @@ -46,6 +46,9 @@ public function up() */ public function down() { - // + $whiteListKey = [ + 'key' => 'white_list_frame', + ]; + Setting::where($whiteListKey)->delete(); } } From 2cc444a65048354e64df61a9783f86d91e7ca673 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Fri, 4 Apr 2025 18:53:58 -0400 Subject: [PATCH 007/142] Update the key --- resources/js/admin/settings/components/ModalWhiteList.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/js/admin/settings/components/ModalWhiteList.vue b/resources/js/admin/settings/components/ModalWhiteList.vue index 15b6f6c639..889585520d 100644 --- a/resources/js/admin/settings/components/ModalWhiteList.vue +++ b/resources/js/admin/settings/components/ModalWhiteList.vue @@ -91,7 +91,7 @@ export default { } const site = this.siteName.toLocaleLowerCase().trim().replaceAll(" ", "_"); const data = { - key: `whiteList.${site}`, + key: `white_list.${site}`, format: "text", config: this.url, name: this.siteName, From cc89f5a2343e0edfe6ccf10febca5f8e498c6c91 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Wed, 9 Apr 2025 18:51:34 -0400 Subject: [PATCH 008/142] FOUR-23765 --- .../Controllers/Api/SettingController.php | 19 ++++++- tests/Feature/Api/SettingsTest.php | 55 +++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/ProcessMaker/Http/Controllers/Api/SettingController.php b/ProcessMaker/Http/Controllers/Api/SettingController.php index bde3045d67..cb337000dd 100644 --- a/ProcessMaker/Http/Controllers/Api/SettingController.php +++ b/ProcessMaker/Http/Controllers/Api/SettingController.php @@ -3,6 +3,7 @@ namespace ProcessMaker\Http\Controllers\Api; use DB; +use Illuminate\Database\QueryException; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Storage; @@ -269,10 +270,22 @@ public function store(Request $request) { $setting = new Setting(); - $setting->fill($request->json()->all()); - $setting->saveOrFail(); + try { + $setting->fill($request->json()->all()); + $setting->saveOrFail(); + + return response([], 201); + } catch (QueryException $e) { + // Check for duplicate entry error code + if ($e->errorInfo[1] == 1062) { + return response()->json([ + 'message' => 'The "Site Name" you\'re trying to add already exists. Please select a different name or review the existing entries to avoid duplication.', + ], 409); + } - return response([], 201); + // Handle other query exceptions + return response()->json(['message' => 'An error occurred while saving the setting.'], 500); + } } public function destroy(Setting $setting) diff --git a/tests/Feature/Api/SettingsTest.php b/tests/Feature/Api/SettingsTest.php index cba31f9226..73534fb5cb 100644 --- a/tests/Feature/Api/SettingsTest.php +++ b/tests/Feature/Api/SettingsTest.php @@ -174,4 +174,59 @@ public function testUpdateExtendedPropertiesWithInvalidVariableName() //Verify variable were not updated $this->assertDatabaseMissing('settings', ['config' => '{"1myVar":"This is my variable 1","myVar space":"This is my variable 2"}']); } + + public function test_it_can_create_a_setting() + { + $menu = SettingsMenus::create([ + 'menu_group' => 'Log-In & Auth', + ]); + $data = [ + 'key' => 'white_list.drive', + 'format' => 'text', + 'config' => 'https://drive.google.com', + 'name' => 'Drive', + 'group' => 'IFrame Whitelist Config', + 'group_id' => $menu->id, + 'hidden' => false, + 'ui' => null, + ]; + $route = route('api.settings.store'); + $response = $this->apiCall('POST', $route, $data); + //Verify the status + $response->assertStatus(201); + } + + public function test_it_returns_error_for_duplicate_entry() + { + $menu = SettingsMenus::create([ + 'menu_group' => 'Log-In & Auth', + ]); + // Create a setting first + Setting::create([ + 'key' => 'white_list.drive', + 'format' => 'text', + 'config' => 'https://drive.google.com', + 'name' => 'Drive', + 'group' => 'IFrame Whitelist Config', + 'group_id' => $menu->id, + 'hidden' => false, + 'ui' => null, + ]); + // Data to create + $data = [ + 'key' => 'white_list.drive', + 'format' => 'text', + 'config' => 'https://drive.google.com', + 'name' => 'Drive', + 'group' => 'IFrame Whitelist Config', + 'group_id' => $menu->id, + 'hidden' => false, + 'ui' => null, + ]; + $route = route('api.settings.store'); + $response = $this->apiCall('POST', $route, $data); + //Verify the status + $response->assertStatus(409) + ->assertJson(['message' => 'The "Site Name" you\'re trying to add already exists. Please select a different name or review the existing entries to avoid duplication.']); + } } From 4f0a2603b77c602b48f8258b34d956711b058dd0 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Wed, 9 Apr 2025 19:26:28 -0400 Subject: [PATCH 009/142] FOUR-23805 --- .../settings/components/ModalWhiteList.vue | 25 ++++++++++++++++--- resources/lang/en.json | 2 ++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/resources/js/admin/settings/components/ModalWhiteList.vue b/resources/js/admin/settings/components/ModalWhiteList.vue index 889585520d..6efcb3aa78 100644 --- a/resources/js/admin/settings/components/ModalWhiteList.vue +++ b/resources/js/admin/settings/components/ModalWhiteList.vue @@ -6,10 +6,10 @@ >
@@ -31,16 +31,19 @@
+
+ {{ urlError }} +
@@ -68,6 +71,7 @@ export default { stateURL: null, groupName: "", group_id: 3, + urlError: "", }; }, methods: { @@ -79,6 +83,11 @@ export default { clear() { this.siteName = ""; this.url = ""; + this.urlError = ""; + }, + validateURL(url) { + const pattern = /^(https?:\/\/)?(\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(:\d+)?(\/.*)?$/; + return pattern.test(url); }, addWhiteListURL() { if (!this.siteName) { @@ -89,6 +98,14 @@ export default { this.stateURL = false; return; } + // Validate the URL using the regex pattern + if (!this.validateURL(this.url)) { + this.stateURL = false; + this.urlError = __("Please enter a valid URL."); + return; + } else { + this.urlError = ""; + } const site = this.siteName.toLocaleLowerCase().trim().replaceAll(" ", "_"); const data = { key: `white_list.${site}`, diff --git a/resources/lang/en.json b/resources/lang/en.json index 01be7b7ec0..95ae00ab3d 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -444,6 +444,7 @@ "Configure Script": "Configure Script", "Configure Template": "Configure Template", "Configure the authenticator app": "Configure the authenticator app", + "Configure URL Parents for Embedding": "Configure URL Parents for Embedding", "Configure": "Configure", "Confirm and Save": "Confirm and Save", "Confirm New Password": "Confirm New Password", @@ -1549,6 +1550,7 @@ "Please choose the tasks in your inbox that this new rule should apply to. Use the column filters to achieve this.": "Please choose the tasks in your inbox that this new rule should apply to. Use the column filters to achieve this.", "Please contact your administrator to get started.": "Please contact your administrator to get started.", "Please enter Tab Name": "Please enter Tab Name", + "Please provide a valid URL (e.g., https://example.com ) to specify the allowed origin(s) permitted to embed ProcessMaker.": "Please provide a valid URL (e.g., https://example.com ) to specify the allowed origin(s) permitted to embed ProcessMaker.", "Please log in to continue your work on this page.": "Please log in to continue your work on this page.", "Please select a Saved Search": "Please select a Saved Search", "Please take a look at it in the 'Rules' section located within your Inbox.": "Please take a look at it in the 'Rules' section located within your Inbox.", From 83d55966d5f43f5b36603f81e4c9dd9c5269b263 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Thu, 10 Apr 2025 10:15:05 -0400 Subject: [PATCH 010/142] solvin obs about remove the else --- resources/js/admin/settings/components/ModalWhiteList.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/js/admin/settings/components/ModalWhiteList.vue b/resources/js/admin/settings/components/ModalWhiteList.vue index 6efcb3aa78..96e7f54688 100644 --- a/resources/js/admin/settings/components/ModalWhiteList.vue +++ b/resources/js/admin/settings/components/ModalWhiteList.vue @@ -103,8 +103,6 @@ export default { this.stateURL = false; this.urlError = __("Please enter a valid URL."); return; - } else { - this.urlError = ""; } const site = this.siteName.toLocaleLowerCase().trim().replaceAll(" ", "_"); const data = { From f5ae5855ca6b60411cb5f34eae1a433e36847c97 Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Fri, 11 Apr 2025 15:07:20 -0400 Subject: [PATCH 011/142] Add test for updating case started script task status without logging errors --- tests/Feature/Cases/CasesTaskTest.php | 29 +++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/Feature/Cases/CasesTaskTest.php b/tests/Feature/Cases/CasesTaskTest.php index 44733cd896..626e4e4d8a 100644 --- a/tests/Feature/Cases/CasesTaskTest.php +++ b/tests/Feature/Cases/CasesTaskTest.php @@ -3,6 +3,9 @@ namespace Tests\Feature\Cases; use Database\Factories\CaseStartedFactory; +use Illuminate\Log\Logger; +use Illuminate\Support\Facades\Log; +use Mockery; use ProcessMaker\Models\Process; use ProcessMaker\Models\ProcessRequest; use ProcessMaker\Models\ProcessRequestToken; @@ -140,6 +143,32 @@ public function test_update_case_started_task_status_exception() ]); } + public function test_update_case_started_script_task_status_do_not_show_error() + { + // Mock the Log facade to check if no error is logged + $mock = Mockery::mock(Logger::class); + $mock->shouldReceive('error')->never(); + Log::swap($mock); + + // Change the element type to script + $this->token->element_type = 'scriptTask'; + $this->token->save(); + + $repo = new CaseRepository(); + $repo->create($this->instance); + $repo->update($this->instance, $this->token); + + $this->assertDatabaseCount('cases_started', 1); + $this->assertDatabaseHas('cases_started', [ + 'case_number' => $this->instance->case_number, + 'request_tokens->[0]' => $this->token->id, + ]); + + $this->token->status = 'COMPLETED'; + $taskRepo = new CaseTaskRepository(9999, $this->token); + $taskRepo->updateCaseStartedTaskStatus(); + } + public function test_find_case_by_task_id_case_found() { $taskRepo = new CaseTaskRepository($this->caseNumber, $this->task); From 84ea8dec63022099a4c963e28b49fb8356dd26e3 Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Fri, 11 Apr 2025 15:07:33 -0400 Subject: [PATCH 012/142] Implement check to skip non-user tasks in updateTaskStatus method --- ProcessMaker/Repositories/CaseTaskRepository.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ProcessMaker/Repositories/CaseTaskRepository.php b/ProcessMaker/Repositories/CaseTaskRepository.php index 653dfff4fd..4808647baa 100644 --- a/ProcessMaker/Repositories/CaseTaskRepository.php +++ b/ProcessMaker/Repositories/CaseTaskRepository.php @@ -44,6 +44,13 @@ public function updateCaseParticipatedTaskStatus() */ public function updateTaskStatus() { + // Skip non-user tasks (e.g. script task, sub-process, etc.) + // tasks column contains only user tasks + $isUserTask = ($this->task->element_type ?? null) === 'task'; + if (!$isUserTask) { + return; + } + try { $case = $this->findCaseByTaskId($this->caseNumber, (string) $this->task->id); From 7ba2a0ba96f5ffc4e311112e5f759b13535c1c83 Mon Sep 17 00:00:00 2001 From: "Marco A. Nina Mena" Date: Mon, 14 Apr 2025 11:51:35 -0400 Subject: [PATCH 013/142] change the order for filtering --- .../Api/V1_1/ProcessVariableController.php | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php index d57af422fb..b170f626dc 100644 --- a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php +++ b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php @@ -225,13 +225,7 @@ public function getProcessesVariables(array $processIds, $excludeSavedSearch, $p ) { $paginator = $this->getProcessesVariablesFrom($processIds); if ($request->has('onlyAvailable')) { - $availableColumns = $this->mergeAvailableColumns($savedSearch); - $availableColumns = $this->filterActiveColumns($availableColumns, $activeColumns); - $paginator->setCollection( - $availableColumns->merge($paginator->items()) - ); - - return $paginator; + return $this->mergeOnlyAvailableColumns($paginator, $savedSearch, $activeColumns); } return $paginator; @@ -265,18 +259,31 @@ public function getProcessesVariables(array $processIds, $excludeSavedSearch, $p $paginator = $query->paginate($perPage, ['*'], 'page', $page); if ($request->has('onlyAvailable')) { - $availableColumns = $this->mergeAvailableColumns($savedSearch); - $availableColumns = $this->filterActiveColumns($availableColumns, $activeColumns); - $paginator->setCollection( - $availableColumns->merge($paginator->items()) - ); - - return $paginator; + return $this->mergeOnlyAvailableColumns($paginator, $savedSearch, $activeColumns); } return $query->paginate($perPage, ['*'], 'page', $page); } + /** + * Merge only available columns with collection items + * + * @param LengthAwarePaginator $paginator + * @param SavedSearch|null $savedSearch + * @param array $activeColumns + * + * @return LengthAwarePaginator + */ + private function mergeOnlyAvailableColumns($paginator, $savedSearch, $activeColumns) + { + $availableColumns = $this->mergeAvailableColumns($savedSearch); + $availableColumns = $availableColumns->merge($paginator->items()); + $availableColumns = $this->filterActiveColumns($availableColumns, $activeColumns); + $paginator->setCollection($availableColumns); + + return $paginator; + } + /** * Merge available columns with collection items * From 028f8a0acb4575f40c75396cb4724feb8f51dd19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Bascop=C3=A9?= Date: Mon, 14 Apr 2025 15:07:47 -0400 Subject: [PATCH 014/142] fixed the add settings API when changing the settings from a bundle --- .../Http/Controllers/Api/DevLinkController.php | 7 ++++++- ProcessMaker/Models/Bundle.php | 14 +++++++++----- .../devlink/components/BundleSettingsModal.vue | 3 ++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/ProcessMaker/Http/Controllers/Api/DevLinkController.php b/ProcessMaker/Http/Controllers/Api/DevLinkController.php index 7b23ec3032..a5f0c63e31 100644 --- a/ProcessMaker/Http/Controllers/Api/DevLinkController.php +++ b/ProcessMaker/Http/Controllers/Api/DevLinkController.php @@ -283,7 +283,12 @@ public function addAsset(Request $request, Bundle $bundle) public function addSettings(Request $request, Bundle $bundle) { - $bundle->addSettings($request->input('setting'), $request->input('config'), $request->input('type')); + $bundle->addSettings( + $request->input('setting'), + $request->input('config'), + $request->input('type'), + $request->input('replaceIds', false) + ); } public function addAssetToBundles(Request $request) diff --git a/ProcessMaker/Models/Bundle.php b/ProcessMaker/Models/Bundle.php index 9d2b017d2d..a62545e499 100644 --- a/ProcessMaker/Models/Bundle.php +++ b/ProcessMaker/Models/Bundle.php @@ -158,7 +158,7 @@ public function addAsset(ProcessMakerModel $asset) ]); } - public function addSettings($setting, $newId, $type = null) + public function addSettings($setting, $newId, $type = null, $replaceIds = false) { $existingSetting = $this->settings()->where('setting', $setting)->first(); @@ -169,7 +169,7 @@ public function addSettings($setting, $newId, $type = null) $decodedNewId = $this->parseNewId($newId); if ($existingSetting) { - return $this->updateExistingSetting($existingSetting, $decodedNewId); + return $this->updateExistingSetting($existingSetting, $decodedNewId, $replaceIds); } $this->createNewSetting($setting, $decodedNewId, $type); @@ -210,7 +210,7 @@ private function parseNewId($newId) return ['id' => [$newId]]; } - private function updateExistingSetting($existingSetting, $decodedNewId) + private function updateExistingSetting($existingSetting, $decodedNewId, $replaceIds = false) { if (isset($decodedNewId['id']) && $decodedNewId['id'] === []) { $existingSetting->delete(); @@ -224,8 +224,12 @@ private function updateExistingSetting($existingSetting, $decodedNewId) $config['id'] = []; } - foreach ($decodedNewId['id'] as $id) { - $config['id'][] = $id; + if ($replaceIds) { + $config['id'] = $decodedNewId['id']; + } else { + foreach ($decodedNewId['id'] as $id) { + $config['id'][] = $id; + } } $config['id'] = array_values(array_unique($config['id'])); diff --git a/resources/js/admin/devlink/components/BundleSettingsModal.vue b/resources/js/admin/devlink/components/BundleSettingsModal.vue index 7f4c18f7a9..ad17d88aa8 100644 --- a/resources/js/admin/devlink/components/BundleSettingsModal.vue +++ b/resources/js/admin/devlink/components/BundleSettingsModal.vue @@ -91,7 +91,8 @@ const onOk = async () => { await window.ProcessMaker.apiClient.post(`devlink/local-bundles/${bundleId}/add-settings`, { setting: settingKey.value, config: JSON.stringify(configs.value), - type: null + type: null, + replaceIds: true }); window.ProcessMaker.alert('Settings saved', 'success'); emit('settings-saved'); From 042b15701a9b8e22355a396104622700867005e4 Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Wed, 16 Apr 2025 14:13:50 -0400 Subject: [PATCH 015/142] Replicate issue: DOMDocument::loadXML(): Entity 'nbsp' not defined in Entity --- tests/Feature/ProcessHtmlTest.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/Feature/ProcessHtmlTest.php diff --git a/tests/Feature/ProcessHtmlTest.php b/tests/Feature/ProcessHtmlTest.php new file mode 100644 index 0000000000..8921ec8413 --- /dev/null +++ b/tests/Feature/ProcessHtmlTest.php @@ -0,0 +1,29 @@ +user = User::factory()->create([ + 'is_administrator' => false, + ]); + $process = $this->createProcessFromBPMN('tests/Fixtures/process_with_html.bpmn'); + + $definitions = $process->getDefinitions(true); + + $this->assertNotEmpty($definitions, 'The process could not be loaded correctly'); + } +} From d90b0876bdeec40b0f2ab0387d52db11873842b8 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Thu, 17 Apr 2025 15:02:03 -0400 Subject: [PATCH 016/142] FOUR-23895 --- ProcessMaker/Models/Setting.php | 1 + 1 file changed, 1 insertion(+) diff --git a/ProcessMaker/Models/Setting.php b/ProcessMaker/Models/Setting.php index 21307649e7..0646dd6e71 100644 --- a/ProcessMaker/Models/Setting.php +++ b/ProcessMaker/Models/Setting.php @@ -403,6 +403,7 @@ public static function groupsByMenu($menuId) ->groupBy('group') ->where('group_id', $menuId) ->orderBy('group', 'ASC') + ->notHidden() ->pluck('group'); $response = $query->toArray(); $result = []; From f3c203b0bdf4e1d0a7044112dc92e5d2341ce65c Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Thu, 17 Apr 2025 19:20:34 -0400 Subject: [PATCH 017/142] Add tests for verify process endpoints --- tests/Feature/ProcessHtmlTest.php | 106 ++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/tests/Feature/ProcessHtmlTest.php b/tests/Feature/ProcessHtmlTest.php index 8921ec8413..5358528169 100644 --- a/tests/Feature/ProcessHtmlTest.php +++ b/tests/Feature/ProcessHtmlTest.php @@ -2,7 +2,9 @@ namespace Tests\Feature; +use ProcessMaker\Models\Process; use ProcessMaker\Models\User; +use ProcessMaker\Nayra\Storage\BpmnDocument; use Tests\Feature\Shared\RequestHelper; use Tests\TestCase; @@ -10,6 +12,7 @@ class ProcessHtmlTest extends TestCase { use RequestHelper; + protected static $DO_NOT_SEND = 'DO_NOT_SEND'; /** * A process with html entities in the documentation field should be able to be loaded. @@ -26,4 +29,107 @@ public function test_process_with_html_can_be_loaded() $this->assertNotEmpty($definitions, 'The process could not be loaded correctly'); } + + /** + * A process with html entities in the documentation field should be able to be stored. + */ + public function test_store_process_with_html_entities() + { + $route = route('api.processes.store'); + $base = Process::factory()->make([ + 'user_id' => static::$DO_NOT_SEND, + 'process_category_id' => static::$DO_NOT_SEND, + ]); + $array = array_diff($base->toArray(), [static::$DO_NOT_SEND]); + // Add a bpmn content + $bpmn = file_get_contents(base_path('tests/Fixtures/process_with_html.bpmn')); + $array['bpmn'] = $bpmn; + $response = $this->apiCall('POST', $route, $array); + $response->assertStatus(201); + $data = $response->json(); + $process = Process::where('id', $data['id'])->first(); + + // Fix bpmn content to remove the html entities + $fixedBpmn = BpmnDocument::replaceHtmlEntities($process->bpmn); + $this->assertEquals($fixedBpmn, $process->bpmn); + } + + /** + * A process with html entities in the documentation field should be able to be updated. + */ + public function test_update_process_with_html_entities() + { + // First create a process + $route = route('api.processes.store'); + $base = Process::factory()->make([ + 'user_id' => static::$DO_NOT_SEND, + 'process_category_id' => static::$DO_NOT_SEND, + ]); + $array = array_diff($base->toArray(), [static::$DO_NOT_SEND]); + $response = $this->apiCall('POST', $route, $array); + $response->assertStatus(201); + $data = $response->json(); + $process = Process::where('id', $data['id'])->first(); + + // Now update the process + $bpmn = file_get_contents(base_path('tests/Fixtures/process_with_html.bpmn')); + $updateRoute = route('api.processes.update', ['process' => $process->id]); + $updateData = [ + 'name' => $process->name . ' Updated', + 'description' => $process->description . ' Updated', + 'bpmn' => $bpmn, + ]; + $updateResponse = $this->apiCall('PUT', $updateRoute, $updateData); + $updateResponse->assertStatus(200); + + // Reload the process from database + $updatedProcess = Process::where('id', $process->id)->first(); + + // Check if the process was updated correctly + $this->assertEquals($process->name . ' Updated', $updatedProcess->name); + + // Fix bpmn content to remove the html entities + $fixedBpmn = BpmnDocument::replaceHtmlEntities($updatedProcess->bpmn); + $this->assertEquals($fixedBpmn, $updatedProcess->bpmn); + } + + /** + * Test updating BPMN content directly via the updateBpmn endpoint. + * This endpoint allows updating only the BPMN content, not the other process attributes. + */ + public function test_update_bpmn_endpoint_with_html_entities() + { + // First create a process + $route = route('api.processes.store'); + $base = Process::factory()->make([ + 'user_id' => static::$DO_NOT_SEND, + 'process_category_id' => static::$DO_NOT_SEND, + ]); + $array = array_diff($base->toArray(), [static::$DO_NOT_SEND]); + $response = $this->apiCall('POST', $route, $array); + $response->assertStatus(201); + $data = $response->json(); + $process = Process::where('id', $data['id'])->first(); + + // Now update the process + $bpmn = file_get_contents(base_path('tests/Fixtures/process_with_html.bpmn')); + $updateRoute = route('api.processes.update_bpmn', ['process' => $process->id]); + $updateData = [ + 'name' => $process->name . ' Updated', + 'description' => $process->description . ' Updated', + 'bpmn' => $bpmn, + ]; + $updateResponse = $this->apiCall('PUT', $updateRoute, $updateData); + $updateResponse->assertStatus(200); + + // Reload the process from database + $updatedProcess = Process::where('id', $process->id)->first(); + + // Check if the process was updated correctly + $this->assertEquals($process->name . ' Updated', $updatedProcess->name); + + // Fix bpmn content to remove the html entities + $fixedBpmn = BpmnDocument::replaceHtmlEntities($updatedProcess->bpmn); + $this->assertEquals($fixedBpmn, $updatedProcess->bpmn); + } } From 69562e459db4522954ecd5f20b1378e7b100e43d Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Thu, 17 Apr 2025 19:21:15 -0400 Subject: [PATCH 018/142] Implement HTML entity replacement for BPMN data in ProcessController --- .../Http/Controllers/Api/ProcessController.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ProcessMaker/Http/Controllers/Api/ProcessController.php b/ProcessMaker/Http/Controllers/Api/ProcessController.php index b782c2cf0b..0c223f70da 100644 --- a/ProcessMaker/Http/Controllers/Api/ProcessController.php +++ b/ProcessMaker/Http/Controllers/Api/ProcessController.php @@ -385,6 +385,11 @@ public function store(Request $request) $processCreated = ProcessCreated::BPMN_CREATION; } + // Replace html entities with the correct characters + if (isset($data['bpmn'])) { + $data['bpmn'] = BpmnDocument::replaceHtmlEntities($data['bpmn']); + } + if ($schemaErrors = $this->validateBpmn($request)) { return response( ['message' => __('The bpm definition is not valid'), @@ -482,6 +487,11 @@ public function update(Request $request, Process $process) $request->validate($rules); $original = $process->getOriginal(); + // Replace html entities with the correct characters + if ($request->has('bpmn')) { + $request->merge(['bpmn' => BpmnDocument::replaceHtmlEntities($request->input('bpmn'))]); + } + // bpmn validation if ($schemaErrors = $this->validateBpmn($request)) { $warnings = []; @@ -599,7 +609,8 @@ public function updateBpmn(Request $request, Process $process) $process->alternative = $request->input('alternative'); } - $process->bpmn = $request->input('bpmn'); + $bpmn = BpmnDocument::replaceHtmlEntities($request->input('bpmn')); + $process->bpmn = $bpmn; $process->name = $request->input('name'); $process->description = $request->input('description'); $process->saveOrFail(); From cf8c4da0725eb88edcf3b3efb023ad41d362ed57 Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Mon, 28 Apr 2025 17:39:39 -0400 Subject: [PATCH 019/142] Add test file process_with_html.bpmn --- tests/Fixtures/process_with_html.bpmn | 51 +++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/Fixtures/process_with_html.bpmn diff --git a/tests/Fixtures/process_with_html.bpmn b/tests/Fixtures/process_with_html.bpmn new file mode 100644 index 0000000000..cc4e694a01 --- /dev/null +++ b/tests/Fixtures/process_with_html.bpmn @@ -0,0 +1,51 @@ + + + + + node_23 + + + node_23 + node_32 + + + <p>dfgdf g gdf gdf. rt fdg dfg f gs gdfh j ghj. ghk gh j75u y u57 uyh jikuilopiuo ityu rt yertqwerq aasd x czx. bdfhg ty gfh nvb nhjtyu uyijknmb. jkluop tyuyu etgfgcvbsrf werrt gb dr ger t dfgdf g gdf gdf. rt fdg dfg f gs gdfh j ghj. ghk gh j75u y u57 uyh jikuilopiuo ityu rt yertqwerq aasd x czx. bdfhg ty gfh nvb nhjtyu uyijknmb. jkluop tyuyu etgfgcvbsrf werrt gb dr ger t dfgdf g gdf gdf. rt fdg dfg f gs gdfh j ghj. ghk gh j75u y u57 uyh jikuilopiuo ityu rt yertqwerq aasd x czx. bdfhg ty gfh nvb nhjtyu uyijknmb. jkluop tyuyu etgfgcvbsrf werrt gb dr ger t dfgdf g gdf gdf. rt fdg dfg f gs gdfh j ghj. ghk gh j75u y u57 uyh jikuilopiuo ityu rt yertqwerq aasd x czx. bdfhg ty gfh nvb nhjtyu uyijknmb. jkluop tyuyu etgfgcvbsrf werrt gb dr ger t dfgdf g gdf gdf. rt fdg dfg f gs gdfh j ghj. ghk gh j75u y u57 uyh jikuilopiuo ityu rt yertqwerq aasd x czx. bdfhg ty gfh nvb nhjtyu uyijknmb. jkluop tyuyu etgfgcvbsrf werrt gb dr ger t </p> + node_32 + node_36 + + + node_36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 83c6ed48d7a33f41d6a4c81beecdca337c432b54 Mon Sep 17 00:00:00 2001 From: Ryan Cooley Date: Fri, 9 May 2025 10:12:53 -0700 Subject: [PATCH 020/142] ci[veracode]: add DAST scan to dev pushes --- .github/workflows/veracode.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/veracode.yml diff --git a/.github/workflows/veracode.yml b/.github/workflows/veracode.yml new file mode 100644 index 0000000000..2279d3c3a1 --- /dev/null +++ b/.github/workflows/veracode.yml @@ -0,0 +1,28 @@ +name: Veracode DAST Essentials + +on: + push: + branches: + - develop + workflow_dispatch: + workflow_call: + +jobs: + veracode_security_scan: + runs-on: ubuntu-latest + name: Run Veracode DAST Essentials Scan + steps: + - name: Veracode Action Step + id: Veracode + uses: veracode/veracode-dast-essentials-action@v1.0.1 + with: + VERACODE_WEBHOOK: '${{ secrets.VERACODE_WEBHOOK }}' + VERACODE_SECRET_ID: '${{ secrets.VERACODE_SECRET_ID }}' + VERACODE_SECRET_ID_KEY: '${{ secrets.VERACODE_SECRET_ID_KEY }}' + REGION: 'us' + pull-report: 'true' + - name: Publish Test Report + uses: mikepenz/action-junit-report@v1 + with: + report_paths: 'report.xml' + github_token: ${{ secrets.GITHUB_TOKEN }} From d144765992c2cc7ef5a283cc53040da506a2ae4f Mon Sep 17 00:00:00 2001 From: Ryan Cooley Date: Fri, 9 May 2025 10:18:35 -0700 Subject: [PATCH 021/142] ci[veracode]: update to new workflow version --- .github/workflows/veracode.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/veracode.yml b/.github/workflows/veracode.yml index 2279d3c3a1..ac809aba71 100644 --- a/.github/workflows/veracode.yml +++ b/.github/workflows/veracode.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Veracode Action Step id: Veracode - uses: veracode/veracode-dast-essentials-action@v1.0.1 + uses: veracode/veracode-dast-essentials-action@v1.0.2 with: VERACODE_WEBHOOK: '${{ secrets.VERACODE_WEBHOOK }}' VERACODE_SECRET_ID: '${{ secrets.VERACODE_SECRET_ID }}' From 0f0dba7d0293799d1627f400b9bd59f7837077ec Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Fri, 9 May 2025 16:55:10 -0400 Subject: [PATCH 022/142] FOUR-24286 --- ProcessMaker/Filters/BaseFilter.php | 8 +++++-- tests/Feature/Api/TasksTest.php | 33 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/ProcessMaker/Filters/BaseFilter.php b/ProcessMaker/Filters/BaseFilter.php index 66b245d06a..3014261b34 100644 --- a/ProcessMaker/Filters/BaseFilter.php +++ b/ProcessMaker/Filters/BaseFilter.php @@ -29,6 +29,8 @@ abstract class BaseFilter public const TYPE_PROCESS_NAME = 'ProcessName'; + public const PROCESS_NAME_IN_REQUEST = 'process_request.name'; + public const TYPE_RELATIONSHIP = 'Relationship'; public string|null $subjectValue; @@ -98,6 +100,8 @@ private function apply($query): void $this->valueAliasAdapter($valueAliasMethod, $query); } elseif ($this->subjectType === self::TYPE_PROCESS) { $this->filterByProcessId($query); + } elseif ($this->subjectValue === self::PROCESS_NAME_IN_REQUEST) { + $this->filterByProcessName($query); } elseif ($this->subjectType === self::TYPE_PROCESS_NAME) { $this->filterByProcessName($query); } elseif ($this->subjectType === self::TYPE_RELATIONSHIP) { @@ -310,8 +314,8 @@ private function filterByProcessName(Builder $query): void { if ($query->getModel() instanceof ProcessRequestToken) { $query->whereIn('process_request_id', function ($query) { - $query->select('id')->from('process_requests'); - $this->applyQueryBuilderMethod($query); + $query->select('id')->from('process_requests') + ->where('name', '=', $this->value()); }); } else { $query->whereIn('name', (array) $this->value()); diff --git a/tests/Feature/Api/TasksTest.php b/tests/Feature/Api/TasksTest.php index 0c3e727f1d..4a45517f22 100644 --- a/tests/Feature/Api/TasksTest.php +++ b/tests/Feature/Api/TasksTest.php @@ -788,6 +788,39 @@ public function testAdvancedFilter() $this->assertEquals($hitTask->id, $json['data'][0]['id']); } + public function testAdvancedFilterByProcessRequestName() + { + $hitProcess = Process::factory()->create(['name' => 'foo']); + $missProcess = Process::factory()->create(['name' => 'bar']); + $hitRequest = ProcessRequest::factory()->create([ + 'process_id' => $hitProcess->id, + 'name' => $hitProcess->name, + ]); + $missRequest = ProcessRequest::factory()->create([ + 'process_id' => $missProcess->id, + ]); + $hitTask = ProcessRequestToken::factory()->create([ + 'process_request_id' => $hitRequest->id, + ]); + ProcessRequestToken::factory()->create([ + 'process_request_id' => $missRequest->id, + ]); + + $filterString = json_encode([ + [ + 'subject' => ['type' => 'Field', 'value' => 'process_request.name'], + 'operator' => '=', + 'value' => $hitProcess->name, + + ], + ]); + + $response = $this->apiCall('GET', '/tasks', ['advanced_filter' => $filterString]); + $json = $response->json(); + + $this->assertEquals($hitTask->id, $json['data'][0]['id']); + } + public function testGetScreenFields() { $this->be($this->user); From 4b0ae2a8f139ec92a09c310b098b63ea240f17f3 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Mon, 12 May 2025 14:47:29 -0400 Subject: [PATCH 023/142] FOUR-24286: adding filter contains --- ProcessMaker/Filters/BaseFilter.php | 5 +++-- tests/Feature/Api/TasksTest.php | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ProcessMaker/Filters/BaseFilter.php b/ProcessMaker/Filters/BaseFilter.php index 3014261b34..40d063fb6f 100644 --- a/ProcessMaker/Filters/BaseFilter.php +++ b/ProcessMaker/Filters/BaseFilter.php @@ -101,6 +101,7 @@ private function apply($query): void } elseif ($this->subjectType === self::TYPE_PROCESS) { $this->filterByProcessId($query); } elseif ($this->subjectValue === self::PROCESS_NAME_IN_REQUEST) { + $this->subjectType = self::TYPE_PROCESS_NAME; $this->filterByProcessName($query); } elseif ($this->subjectType === self::TYPE_PROCESS_NAME) { $this->filterByProcessName($query); @@ -314,8 +315,8 @@ private function filterByProcessName(Builder $query): void { if ($query->getModel() instanceof ProcessRequestToken) { $query->whereIn('process_request_id', function ($query) { - $query->select('id')->from('process_requests') - ->where('name', '=', $this->value()); + $query->select('id')->from('process_requests'); + $this->applyQueryBuilderMethod($query); }); } else { $query->whereIn('name', (array) $this->value()); diff --git a/tests/Feature/Api/TasksTest.php b/tests/Feature/Api/TasksTest.php index 4a45517f22..d4dfce338a 100644 --- a/tests/Feature/Api/TasksTest.php +++ b/tests/Feature/Api/TasksTest.php @@ -806,6 +806,7 @@ public function testAdvancedFilterByProcessRequestName() 'process_request_id' => $missRequest->id, ]); + // Filter by operator = $filterString = json_encode([ [ 'subject' => ['type' => 'Field', 'value' => 'process_request.name'], @@ -818,6 +819,20 @@ public function testAdvancedFilterByProcessRequestName() $response = $this->apiCall('GET', '/tasks', ['advanced_filter' => $filterString]); $json = $response->json(); + $this->assertEquals($hitTask->id, $json['data'][0]['id']); + // Filter by operator contains + $filterString = json_encode([ + [ + 'subject' => ['type' => 'Field', 'value' => 'process_request.name'], + 'operator' => 'contains', + 'value' => $hitProcess->name, + + ], + ]); + + $response = $this->apiCall('GET', '/tasks', ['advanced_filter' => $filterString]); + $json = $response->json(); + $this->assertEquals($hitTask->id, $json['data'][0]['id']); } From 59c2f8faed2f9010f748bdc1146296118cf1b0b0 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Tue, 13 May 2025 13:53:53 -0400 Subject: [PATCH 024/142] Adding the comments about the change --- ProcessMaker/Filters/BaseFilter.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ProcessMaker/Filters/BaseFilter.php b/ProcessMaker/Filters/BaseFilter.php index 40d063fb6f..247cb770d4 100644 --- a/ProcessMaker/Filters/BaseFilter.php +++ b/ProcessMaker/Filters/BaseFilter.php @@ -101,6 +101,8 @@ private function apply($query): void } elseif ($this->subjectType === self::TYPE_PROCESS) { $this->filterByProcessId($query); } elseif ($this->subjectValue === self::PROCESS_NAME_IN_REQUEST) { + // For performance reasons, the task list uses the column process_request.name + // But the filters must use the Process table, for this reason the subjectType is updated $this->subjectType = self::TYPE_PROCESS_NAME; $this->filterByProcessName($query); } elseif ($this->subjectType === self::TYPE_PROCESS_NAME) { From 2aab4e1ffd7b9de06326d244ec6bf8a438318c0f Mon Sep 17 00:00:00 2001 From: Eleazar Resendez Date: Fri, 16 May 2025 11:43:36 -0600 Subject: [PATCH 025/142] fix(sync-default-templates): handle GitHub fetch failures gracefully to avoid pipeline breakage - Added timeout and warning log when fetching individual template files from GitHub fails. - Replaced exception with `continue` to skip failed templates without stopping execution. - Ensures that transient GitHub issues do not interrupt the entire CI/CD pipeline. --- ProcessMaker/Jobs/SyncDefaultTemplates.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/ProcessMaker/Jobs/SyncDefaultTemplates.php b/ProcessMaker/Jobs/SyncDefaultTemplates.php index dc052e991c..c94774bdab 100644 --- a/ProcessMaker/Jobs/SyncDefaultTemplates.php +++ b/ProcessMaker/Jobs/SyncDefaultTemplates.php @@ -3,14 +3,13 @@ namespace ProcessMaker\Jobs; use Exception; -use Facades\ProcessMaker\JsonColumnIndex; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Log; use ProcessMaker\ImportExport\Importer; use ProcessMaker\ImportExport\Options; use ProcessMaker\Models\ProcessCategory; @@ -22,8 +21,6 @@ class SyncDefaultTemplates implements ShouldQueue /** * Create a new job instance. - * - * @return void */ public function __construct() { @@ -34,7 +31,6 @@ public function __construct() * Execute the job. * Function to handle the execution of this job when it is run. * Here the function fetches default templates list from Github and saves them to the database. - * @return void */ public function handle() { @@ -54,10 +50,11 @@ public function handle() )->getKey(); // Get the default template list from Github. - $response = Http::get($url); - + $response = Http::timeout(10)->get($url); if (!$response->successful()) { - throw new Exception('Unable to fetch default template list.'); + Log::warning("[SyncDefaultTemplates] Failed to fetch template index from GitHub: {$url} - Status: {$response->status()}"); + + return; // skip the job gracefully } // Extract the json data from the response and iterate over the categories and templates to retrieve them. @@ -75,9 +72,10 @@ public function handle() $relativePath = ltrim($template['relative_path'], './'); $url = $config['base_url'] . $config['template_repo'] . '/' . $config['template_branch'] . '/' . $relativePath; - $response = Http::get($url); + $response = Http::timeout(10)->get($url); if (!$response->successful()) { - throw new Exception("Unable to fetch default template {$template['name']}."); + Log::warning("[SyncDefaultTemplates] Skipped template due to failed fetch: {$template['name']} ({$template['uuid']}) - Status: {$response->status()}"); + continue; } $payload = $response->json(); data_set($payload, 'export.' . $payload['root'] . '.attributes.process_category_id', $processCategoryId); @@ -87,7 +85,7 @@ public function handle() 'saveAssetsMode' => 'saveAllAssets', ]); $importer = new Importer($payload, $options); - $manifest = $importer->doImport(); + $importer->doImport(); } } } From c2209f95acc90a78d300d10a8a9b79ad184bb39a Mon Sep 17 00:00:00 2001 From: Eleazar Resendez Date: Fri, 16 May 2025 12:02:14 -0600 Subject: [PATCH 026/142] add test for skipping failed template fetches and logging warnings --- tests/Jobs/SyncDefaultTemplatesTest.php | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tests/Jobs/SyncDefaultTemplatesTest.php diff --git a/tests/Jobs/SyncDefaultTemplatesTest.php b/tests/Jobs/SyncDefaultTemplatesTest.php new file mode 100644 index 0000000000..dc55549aa3 --- /dev/null +++ b/tests/Jobs/SyncDefaultTemplatesTest.php @@ -0,0 +1,48 @@ + 'https://fake.test/', + 'template_repo' => 'repo', + 'template_branch' => 'main', + 'template_categories' => 'all', + ]); + + // Fake HTTP responses. + Http::fake([ + 'https://fake.test/repo/main/index.json' => Http::response([ + 'default' => [ + [ + 'uuid' => 'template-uuid', + 'name' => 'Broken Template', + 'relative_path' => './broken-template.json', + ], + ], + ]), + 'https://fake.test/repo/main/broken-template.json' => Http::response(null, 500), + ]); + + // Fake logging. + Log::shouldReceive('warning') + ->once() + ->withArgs(function ($message) { + return str_contains($message, 'Skipped template due to failed fetch'); + }); + + // Run the job. + $job = new SyncDefaultTemplates(); + $job->handle(); + } +} From 1c718e473903daa0511ee58aad868502a5c98db4 Mon Sep 17 00:00:00 2001 From: Nolan Ehrstrom Date: Mon, 19 May 2025 12:57:51 -0700 Subject: [PATCH 027/142] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bfe7ae46c6..400ed4f0f9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +Second CI Instance # ProcessMaker 4 Documentation From 45d13058dda1818b809a01b11ba22411adaea85f Mon Sep 17 00:00:00 2001 From: Nolan Ehrstrom Date: Mon, 19 May 2025 14:41:48 -0700 Subject: [PATCH 028/142] Update cors.php --- config/cors.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/cors.php b/config/cors.php index 6d0570dac6..6c9621254a 100644 --- a/config/cors.php +++ b/config/cors.php @@ -23,6 +23,7 @@ 'http://localhost:4200', 'https://legendary-adventure-2n21ppv.pages.github.io', 'https://processmaker.github.io', + 'https://pm-apps.pages.dev' ], 'allowed_origins_patterns' => [], From 78a58a704888a8176a019b400ce8467a0cc1fef7 Mon Sep 17 00:00:00 2001 From: Ryan Cooley Date: Wed, 21 May 2025 17:53:36 -0700 Subject: [PATCH 029/142] Update enterprise packages --- composer.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 907fe921eb..8b5689d986 100644 --- a/composer.json +++ b/composer.json @@ -153,23 +153,23 @@ "package-ai": "1.16.4", "package-analytics-reporting": "1.11.0", "package-auth": "1.24.2", - "package-collections": "2.25.3", + "package-collections": "2.25.4", "package-comments": "1.16.0", "package-conversational-forms": "1.15.0", "package-data-sources": "1.34.0", "package-decision-engine": "1.16.1", - "package-dynamic-ui": "1.27.2", + "package-dynamic-ui": "1.27.3", "package-files": "1.23.0", "package-googleplaces": "1.12.0", "package-photo-video": "1.6.0", "package-pm-blocks": "1.12.1", "package-process-documenter": "1.12.0", "package-process-optimization": "1.10.0", - "package-product-analytics": "1.5.8", + "package-product-analytics": "1.5.9", "package-projects": "1.12.1", "package-rpa": "1.1.1", - "package-savedsearch": "1.42.3", - "package-slideshow": "1.4.0", + "package-savedsearch": "1.42.4", + "package-slideshow": "1.4.1", "package-sentry": "1.12.0", "package-signature": "1.15.0", "package-testing": "1.8.0", @@ -177,7 +177,7 @@ "package-versions": "1.13.0", "package-vocabularies": "2.17.0", "package-webentry": "2.29.0", - "package-api-testing": "1.3.0", + "package-api-testing": "1.3.1", "package-variable-finder": "1.0.3", "packages": "^0" }, @@ -227,4 +227,4 @@ "tbachert/spi": true } } -} \ No newline at end of file +} From 71537c7ff9a1a87c10742f31e9750932098f7194 Mon Sep 17 00:00:00 2001 From: Ryan Cooley Date: Wed, 21 May 2025 17:55:53 -0700 Subject: [PATCH 030/142] Update Nayra --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 8b5689d986..c26e5f37d9 100644 --- a/composer.json +++ b/composer.json @@ -48,7 +48,7 @@ "processmaker/docker-executor-node": "1.1.0", "processmaker/docker-executor-php": "1.2.0", "processmaker/laravel-i18next": "dev-master", - "processmaker/nayra": "1.12.0", + "processmaker/nayra": "1.12.1", "processmaker/pmql": "1.13.1", "promphp/prometheus_client_php": "^2.12", "psr/http-message": "^1.1", diff --git a/composer.lock b/composer.lock index 6b8bedc206..efde5ca97c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "79f6d31afab1b6fd6e8e9cb5e0174daf", + "content-hash": "b68265a79f0eacb47c9bd51ef0c31b37", "packages": [ { "name": "aws/aws-crt-php", @@ -7910,16 +7910,16 @@ }, { "name": "processmaker/nayra", - "version": "1.12.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/ProcessMaker/nayra.git", - "reference": "07bc74a8f21f09bb80985f80a925ee364dc1ef7b" + "reference": "7bb56c6cf3f952a538ebd2f0f2fd22503049bf62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ProcessMaker/nayra/zipball/07bc74a8f21f09bb80985f80a925ee364dc1ef7b", - "reference": "07bc74a8f21f09bb80985f80a925ee364dc1ef7b", + "url": "https://api.github.com/repos/ProcessMaker/nayra/zipball/7bb56c6cf3f952a538ebd2f0f2fd22503049bf62", + "reference": "7bb56c6cf3f952a538ebd2f0f2fd22503049bf62", "shasum": "" }, "require-dev": { @@ -7938,9 +7938,9 @@ "description": "BPMN compliant engine", "support": { "issues": "https://github.com/ProcessMaker/nayra/issues", - "source": "https://github.com/ProcessMaker/nayra/tree/v1.12.0" + "source": "https://github.com/ProcessMaker/nayra/tree/v1.12.1" }, - "time": "2024-07-01T02:14:36+00:00" + "time": "2025-05-22T00:52:45+00:00" }, { "name": "processmaker/pmql", From 0f6dfacc0d4a7fe7165826cdb8775b7ae84b2a32 Mon Sep 17 00:00:00 2001 From: Ryan Cooley Date: Wed, 21 May 2025 17:57:04 -0700 Subject: [PATCH 031/142] Update SSR executor --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c26e5f37d9..855695775f 100644 --- a/composer.json +++ b/composer.json @@ -146,7 +146,7 @@ "connector-pdf-print": "1.22.0", "connector-send-email": "1.32.3", "connector-slack": "1.9.0", - "docker-executor-node-ssr": "1.7.0", + "docker-executor-node-ssr": "1.7.1", "package-ab-testing": "1.3.0", "package-actions-by-email": "1.22.2", "package-advanced-user-manager": "1.13.0", From fa5c478f3fdcbe2d815c8289a8d32cfe41727f80 Mon Sep 17 00:00:00 2001 From: ProcessMaker Bot <206180840+processmaker-bot@users.noreply.github.com> Date: Thu, 22 May 2025 01:05:58 +0000 Subject: [PATCH 032/142] Version 4.15.0+beta-1 --- composer.json | 6 +++--- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 855695775f..0031f79fcf 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "processmaker/processmaker", - "version": "4.14.2", + "version": "4.15.0+beta-1", "description": "BPM PHP Software", "keywords": [ "php bpm processmaker" @@ -105,7 +105,7 @@ "Gmail" ], "processmaker": { - "build": "91ada418", + "build": "6905a009", "cicd-enabled": true, "custom": { "package-ellucian-ethos": "1.19.3", @@ -227,4 +227,4 @@ "tbachert/spi": true } } -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6a66e7324f..a67d550234 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@processmaker/processmaker", - "version": "4.14.2", + "version": "4.15.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@processmaker/processmaker", - "version": "4.14.2", + "version": "4.15.0", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 897089782d..bb5e286de8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@processmaker/processmaker", - "version": "4.14.2", + "version": "4.15.0", "description": "ProcessMaker 4", "author": "DevOps ", "license": "ISC", From ee0009068cc09ded4eb94f05c6cb2d55c99b5fa7 Mon Sep 17 00:00:00 2001 From: Ryan Cooley Date: Wed, 21 May 2025 19:05:49 -0700 Subject: [PATCH 033/142] Update enterprise package --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0031f79fcf..ee198d14eb 100644 --- a/composer.json +++ b/composer.json @@ -158,7 +158,7 @@ "package-conversational-forms": "1.15.0", "package-data-sources": "1.34.0", "package-decision-engine": "1.16.1", - "package-dynamic-ui": "1.27.3", + "package-dynamic-ui": "1.28.0", "package-files": "1.23.0", "package-googleplaces": "1.12.0", "package-photo-video": "1.6.0", From 4667293105a7558cc494676dbd66656290a65010 Mon Sep 17 00:00:00 2001 From: ProcessMaker Bot <206180840+processmaker-bot@users.noreply.github.com> Date: Thu, 22 May 2025 02:07:32 +0000 Subject: [PATCH 034/142] Version 4.15.0+beta-1 --- composer.json | 2 +- composer.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index ee198d14eb..06c0ba27d5 100644 --- a/composer.json +++ b/composer.json @@ -105,7 +105,7 @@ "Gmail" ], "processmaker": { - "build": "6905a009", + "build": "af762a33", "cicd-enabled": true, "custom": { "package-ellucian-ethos": "1.19.3", diff --git a/composer.lock b/composer.lock index efde5ca97c..f7bcba50d6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b68265a79f0eacb47c9bd51ef0c31b37", + "content-hash": "bc4e1d6ad038a94b5d18d580b703cd2c", "packages": [ { "name": "aws/aws-crt-php", From 01e7a7767544fcfbded9639362518953cb4c4e61 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Wed, 21 May 2025 22:35:57 -0400 Subject: [PATCH 035/142] FOUR-13976 --- .../Http/Controllers/Auth/LoginController.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ProcessMaker/Http/Controllers/Auth/LoginController.php b/ProcessMaker/Http/Controllers/Auth/LoginController.php index 190475ec1b..6be170ac5b 100644 --- a/ProcessMaker/Http/Controllers/Auth/LoginController.php +++ b/ProcessMaker/Http/Controllers/Auth/LoginController.php @@ -80,7 +80,22 @@ public function showLoginForm(Request $request) $driver = $this->getDefaultSSO($arrayAddons); // If a default SSO was defined we will to redirect if (!empty($driver)) { - return redirect()->route('sso.redirect', ['driver' => $driver]); + // Store the current full URL as the intended URL before redirecting to SSO. + $intendedUrl = $request->session()->get('url.intended', url()->full()); + $ssoIntendedCookie = cookie( + 'processmaker_intended', + $intendedUrl, + 10, + '/', + null, + true, + true, + false, + 'none' + ); + + // Redirect to SSO and attach the cookie + return redirect()->route('sso.redirect', ['driver' => $driver])->withCookie($ssoIntendedCookie); } } $block = $manager->getBlock(); From a831407ce9a6ed61bad16f95d8912db6a641232d Mon Sep 17 00:00:00 2001 From: Henry Jonas Date: Thu, 22 May 2025 12:38:30 -0400 Subject: [PATCH 036/142] FOUR-23828:It is possible to create Site name with blank spaces in IFrame white list --- resources/js/admin/settings/components/ModalWhiteList.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/js/admin/settings/components/ModalWhiteList.vue b/resources/js/admin/settings/components/ModalWhiteList.vue index 96e7f54688..6c7e8c9e8e 100644 --- a/resources/js/admin/settings/components/ModalWhiteList.vue +++ b/resources/js/admin/settings/components/ModalWhiteList.vue @@ -78,6 +78,7 @@ export default { show(groupName) { this.clear(); this.groupName = groupName; + this.stateSiteName = null; return this.$refs["bv-modal-whitelist"].show(); }, clear() { @@ -90,7 +91,7 @@ export default { return pattern.test(url); }, addWhiteListURL() { - if (!this.siteName) { + if (!this.siteName.trim()) { this.stateSiteName = false; return; } From a0383b42c795c835db7b989a31e41635aba710f2 Mon Sep 17 00:00:00 2001 From: Paula Quispe Date: Thu, 22 May 2025 13:41:59 -0400 Subject: [PATCH 037/142] FOUR-24545 --- ..._03_31_140423_add_menu_frame_whitelist.php | 8 ++-- ...40424_add_menu_frame_whitelist_default.php | 45 +++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 upgrades/2025_03_31_140424_add_menu_frame_whitelist_default.php diff --git a/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php b/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php index 89f7eb440e..7b4ac746ca 100644 --- a/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php +++ b/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php @@ -13,14 +13,14 @@ class AddMenuFrameWhitelist extends Upgrade */ public function up() { - $group_id = SettingsMenus::getId(SettingsMenus::LOG_IN_AUTH_MENU_GROUP); + $groupId = SettingsMenus::getId(SettingsMenus::LOG_IN_AUTH_MENU_GROUP); $whiteListKey = [ 'key' => 'white_list_frame', ]; - $whiteListOption = [ + $whiteListButtonOption = [ 'format' => 'button', 'group' => 'IFrame Whitelist Config', - 'group_id' => $group_id, + 'group_id' => $groupId, 'helper' => null, 'config' => false, 'name' => 'Add URL', @@ -36,7 +36,7 @@ public function up() ], ]; - Setting::firstOrCreate($whiteListKey, $whiteListOption); + Setting::firstOrCreate($whiteListKey, $whiteListButtonOption); } /** diff --git a/upgrades/2025_03_31_140424_add_menu_frame_whitelist_default.php b/upgrades/2025_03_31_140424_add_menu_frame_whitelist_default.php new file mode 100644 index 0000000000..eb2f9154ff --- /dev/null +++ b/upgrades/2025_03_31_140424_add_menu_frame_whitelist_default.php @@ -0,0 +1,45 @@ + 'white_list_frame.default', + ]; + $whiteListDefaultOption = [ + 'format' => 'text', + 'group' => 'IFrame Whitelist Config', + 'group_id' => $groupId, + 'helper' => null, + 'config' => null, + 'name' => 'Default URL', + 'hidden' => false, + ]; + + Setting::firstOrCreate($whiteListKey, $whiteListDefaultOption); + } + + /** + * Reverse the upgrade migration. + * + * @return void + */ + public function down() + { + $whiteListKey = [ + 'key' => 'white_list_frame.default', + ]; + Setting::where($whiteListKey)->delete(); + } +} From e730c06e3198d4ff653e8c76e281d9a62474720b Mon Sep 17 00:00:00 2001 From: Henry Jonas Date: Thu, 22 May 2025 15:56:23 -0400 Subject: [PATCH 038/142] FOUR-23828:It is possible to create Site name with blank spaces in IFrame white list --- resources/js/admin/settings/components/ModalWhiteList.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/js/admin/settings/components/ModalWhiteList.vue b/resources/js/admin/settings/components/ModalWhiteList.vue index 6c7e8c9e8e..6de0649f05 100644 --- a/resources/js/admin/settings/components/ModalWhiteList.vue +++ b/resources/js/admin/settings/components/ModalWhiteList.vue @@ -79,6 +79,7 @@ export default { this.clear(); this.groupName = groupName; this.stateSiteName = null; + this.stateURL = null; return this.$refs["bv-modal-whitelist"].show(); }, clear() { From bd402aca2544213335c7197f0975d4fc4d77178c Mon Sep 17 00:00:00 2001 From: danloa Date: Fri, 23 May 2025 15:18:41 -0400 Subject: [PATCH 039/142] Make AI env. variables accessibles to the UI --- resources/views/layouts/layout.blade.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/views/layouts/layout.blade.php b/resources/views/layouts/layout.blade.php index bbc3b5d9b5..898f8d80ec 100644 --- a/resources/views/layouts/layout.blade.php +++ b/resources/views/layouts/layout.blade.php @@ -135,6 +135,10 @@ class="main flex-grow-1 h-100 @include('shared.monaco') From 7535e6aa78c4d1c631e68165d0f1303d4e1be97e Mon Sep 17 00:00:00 2001 From: danloa Date: Fri, 23 May 2025 15:42:34 -0400 Subject: [PATCH 040/142] Add ai config for genie timeout --- config/ai.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/ai.php b/config/ai.php index d2bdfbd0c9..7b89cfeb1c 100644 --- a/config/ai.php +++ b/config/ai.php @@ -12,4 +12,7 @@ 'rag_collections' => [ 'enabled' => env('AI_RAG_COLLECTIONS_ENABLED', false), ], + 'genie_client' => [ + 'timeout' => (int) env('AI_GENIE_CLIENT_TIMEOUT', env('API_TIMEOUT', 60000)), + ], ]; From fad2e59349abfdece1046e9315acfdc282eae5aa Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Fri, 23 May 2025 20:37:57 -0400 Subject: [PATCH 041/142] Replicate issue FOUR-24548 --- .../Migration/ExtendedMigrateCommand.php | 4 + tests/Feature/Screens/ScreenCacheTest.php | 144 ++++++++++ .../Fixtures/processes/screen_process_v1.json | 214 ++++++++++++++ .../Fixtures/processes/screen_process_v2.json | 272 ++++++++++++++++++ .../Fixtures/processes/screen_process_v3.json | 272 ++++++++++++++++++ .../Fixtures/processes/screen_process_v4.json | 272 ++++++++++++++++++ 6 files changed, 1178 insertions(+) create mode 100644 tests/Feature/Screens/ScreenCacheTest.php create mode 100644 tests/Fixtures/processes/screen_process_v1.json create mode 100644 tests/Fixtures/processes/screen_process_v2.json create mode 100644 tests/Fixtures/processes/screen_process_v3.json create mode 100644 tests/Fixtures/processes/screen_process_v4.json diff --git a/ProcessMaker/Console/Migration/ExtendedMigrateCommand.php b/ProcessMaker/Console/Migration/ExtendedMigrateCommand.php index 9272209c5d..0b577b4b71 100644 --- a/ProcessMaker/Console/Migration/ExtendedMigrateCommand.php +++ b/ProcessMaker/Console/Migration/ExtendedMigrateCommand.php @@ -4,6 +4,8 @@ use Illuminate\Database\Console\Migrations\MigrateCommand as BaseMigrateCommand; use Illuminate\Support\Facades\Cache; +use ProcessMaker\Cache\Screens\ScreenCacheFactory; +use ProcessMaker\Cache\Settings\SettingCacheFactory; use ProcessMaker\Helpers\CachedSchema; use ProcessMaker\Models\ProcessMakerModel; @@ -13,6 +15,8 @@ public function handle(): void { Cache::tags(ProcessMakerModel::MIGRATION_COLUMNS_CACHE_KEY)->flush(); Cache::tags(CachedSchema::CACHE_TAG)->flush(); + SettingCacheFactory::getSettingsCache()->clear(); + ScreenCacheFactory::getScreenCache()->clearCompiledAssets(); parent::handle(); } diff --git a/tests/Feature/Screens/ScreenCacheTest.php b/tests/Feature/Screens/ScreenCacheTest.php new file mode 100644 index 0000000000..27d151bea7 --- /dev/null +++ b/tests/Feature/Screens/ScreenCacheTest.php @@ -0,0 +1,144 @@ +createProcessFromJSON( + base_path('tests/Fixtures/processes/screen_process_v1.json'), + [ + 'name' => 'Screen Process Test', + 'status' => 'ACTIVE' + ] + ); + + // Run the process (trigger start event) + $route = route('api.process_events.trigger', [$process->id, 'event' => 'node_1']); + $data = []; + $response = $this->apiCall('POST', $route, $data); + $requestJson = $response->json(); + $request = ProcessRequest::find($requestJson['id']); + + // Get the current active task for the process request + $task = ProcessRequestToken::where([ + 'process_request_id' => $request->id, + 'status' => 'ACTIVE', + 'element_type' => 'task', + ])->firstOrFail(); + + // Get the screen of the process using the route api.1.1.show.screen + $response = $this->getJson(route('api.1.1.tasks.show.screen', ['taskId' => $task->id, 'include' => 'screen,nested'])); + + // Add your test assertions here + $response->assertStatus(200); + // Save the latest version id of the screen + $screenId = $response->json()['screen_id']; + $latestScreenVersionId = Screen::find($screenId)->getLatestVersion()->id; + $this->assertCount(2, $response->json()['config'][0]['items'], 'The screen imported first time should have 2 items'); + // Verify label of the first item + $this->assertEquals('Line Input v1', $response->json()['config'][0]['items'][0]['label'], 'The label of the first item should be "Line Input v1"'); + + // 2. Import a process with a screen with a nested pointing to an empty screen (config = null in the database) + $process = $this->createProcessFromJSON( + base_path('tests/Fixtures/processes/screen_process_v2.json'), + [ + 'name' => 'Screen Process Test', + 'status' => 'ACTIVE' + ] + ); + + // Get the screen of the process using the route api.1.1.show.screen + $response = $this->getJson(route('api.1.1.tasks.show.screen', ['taskId' => $task->id, 'include' => 'screen,nested'])); + + // Add your test assertions here + $response->assertStatus(200); + // There is nested screen with an empty config + $this->assertCount(1, $response->json()['nested'], 'The screen imported first time should have 1 nested screen'); + $this->assertCount(0, $response->json()['nested'][0]['config'], 'The nested screen should have an empty config'); + // Verify label of the first item was updated + $this->assertEquals('Line Input v2', $response->json()['config'][0]['items'][0]['label'], 'The label of the first item should be "Line Input v2"'); + // Verify the rendered screen version is newer than the latest version id + $currentScreenVersionId = Screen::find($screenId)->getLatestVersion()->id; + $this->assertGreaterThan($latestScreenVersionId, $currentScreenVersionId, 'The current screen version should be newer than the latest version id'); + $latestScreenVersionId = $currentScreenVersionId; + + // 3. Import a process with a screen with a nested pointing to a screen with one item + $process = $this->createProcessFromJSON( + base_path('tests/Fixtures/processes/screen_process_v3.json'), + [ + 'name' => 'Screen Process Test', + 'status' => 'ACTIVE' + ] + ); + + // Get the screen of the process using the route api.1.1.show.screen + $response = $this->getJson(route('api.1.1.tasks.show.screen', ['taskId' => $task->id, 'include' => 'screen,nested'])); + + // Add your test assertions here + $response->assertStatus(200); + // There is nested screen with 1 item + $this->assertCount(1, $response->json()['nested'], 'The screen imported first time should have 1 nested screen'); + $this->assertCount(1, $response->json()['nested'][0]['config'][0]['items'], 'The nested screen should have 1 item'); + // Verify the rendered screen version is newer than the latest version id + $currentScreenVersionId = Screen::find($screenId)->getLatestVersion()->id; + $this->assertGreaterThan($latestScreenVersionId, $currentScreenVersionId, 'The current screen version should be newer than the latest version id'); + $latestScreenVersionId = $currentScreenVersionId; + + // 4. Import a process that updates the nested screen + $process = $this->createProcessFromJSON( + base_path('tests/Fixtures/processes/screen_process_v4.json'), + [ + 'name' => 'Screen Process Test', + 'status' => 'ACTIVE' + ] + ); + + // Get the screen of the process using the route api.1.1.show.screen + $response = $this->getJson(route('api.1.1.tasks.show.screen', ['taskId' => $task->id, 'include' => 'screen,nested'])); + + // Add your test assertions here + $response->assertStatus(200); + // There is nested screen with 1 item + $this->assertCount(1, $response->json()['nested'], 'The screen imported first time should have 1 nested screen'); + $this->assertCount(1, $response->json()['nested'][0]['config'][0]['items'], 'The nested screen should have 1 item'); + // Verify the rendered screen version is the same + $currentScreenVersionId = Screen::find($screenId)->getLatestVersion()->id; + $this->assertGreaterThan($latestScreenVersionId, $currentScreenVersionId, 'The current screen version should be newer than the latest version id'); + // Verify the nested screen was updated + $this->assertEquals('New Textarea v4', $response->json()['nested'][0]['config'][0]['items'][0]['config']['label'], 'The nested screen should have the new label "New Textarea v4"'); + + // 4. Import a process that updates the nested screen + $process = $this->createProcessFromJSON( + base_path('tests/Fixtures/processes/screen_process_v4.json'), + [ + 'name' => 'Screen Process Test', + 'status' => 'ACTIVE' + ] + ); + + // Get the screen of the process using the route api.1.1.show.screen + $response = $this->getJson(route('api.1.1.tasks.show.screen', ['taskId' => $task->id, 'include' => 'screen,nested'])); + + // Add your test assertions here + $response->assertStatus(200); + // There is nested screen with 1 item + $this->assertCount(1, $response->json()['nested'], 'The screen imported first time should have 1 nested screen'); + $this->assertCount(1, $response->json()['nested'][0]['config'][0]['items'], 'The nested screen should have 1 item'); + // Verify the rendered screen version is the same + $currentScreenVersionId = Screen::find($screenId)->getLatestVersion()->id; + $this->assertGreaterThan($latestScreenVersionId, $currentScreenVersionId, 'The current screen version should be newer than the latest version id'); + // Verify the nested screen was updated + $this->assertEquals('New Textarea v4', $response->json()['nested'][0]['config'][0]['items'][0]['config']['label'], 'The nested screen should have the new label "New Textarea v4"'); + } +} diff --git a/tests/Fixtures/processes/screen_process_v1.json b/tests/Fixtures/processes/screen_process_v1.json new file mode 100644 index 0000000000..0597aa7e31 --- /dev/null +++ b/tests/Fixtures/processes/screen_process_v1.json @@ -0,0 +1,214 @@ +{ + "type": "process_package", + "version": "2", + "root": "9efb5c96-f795-40e9-90a0-ce9ebe246cfd", + "name": "Simple Process", + "export": { + "9efb5c96-f795-40e9-90a0-ce9ebe246cfd": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ProcessExporter", + "type": "Process", + "type_human": "Process", + "type_plural": "Processes", + "type_human_plural": "Processes", + "last_modified_by": "Admin User", + "last_modified_by_id": 1, + "model": "ProcessMaker\\Models\\Process", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "user", + "uuid": "9ef10b5b-c0b8-4327-8c26-533c0e0c25b6", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\UserExporter", + "modelClass": "ProcessMaker\\Models\\User", + "fallbackMatches": { + "email": "admin@processmaker.com", + "username": "admin" + }, + "name": "", + "discard": false + }, + { + "type": "screens", + "uuid": "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b", + "meta": { + "path": "/bpmn:definitions/bpmn:process/bpmn:task" + }, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "modelClass": "ProcessMaker\\Models\\Screen", + "fallbackMatches": { + "key": null, + "title": "Screen with Nested" + }, + "name": "Screen with Nested", + "discard": false + }, + { + "type": "process_launchpad", + "uuid": "9efb6041-6bb5-4544-86b2-3a3a12d43f0a", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ProcessLaunchpadExporter", + "modelClass": "ProcessMaker\\Models\\ProcessLaunchpad", + "fallbackMatches": [], + "name": "Setting", + "discard": false + } + ], + "name": "Simple Process", + "description": "Simple Process", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 25, + "uuid": "9efb5c96-f795-40e9-90a0-ce9ebe246cfd", + "process_category_id": 2, + "user_id": 1, + "bpmn": "\n\n \n \n node_18\n \n \n node_18\n node_27\n \n \n node_27\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n", + "description": "Simple Process", + "name": "Simple Process", + "cancel_screen_id": null, + "request_detail_screen_id": null, + "status": "ACTIVE", + "is_valid": 1, + "package_key": null, + "pause_timer_start": 0, + "deleted_at": null, + "created_at": "2025-05-23 18:17:53", + "updated_at": "2025-05-23 18:28:05", + "updated_by": 1, + "start_events": "[{\"id\": \"node_1\", \"name\": \"Start Event\", \"config\": \"{\\\"web_entry\\\":null}\", \"ownerProcessId\": \"ProcessId\", \"eventDefinitions\": [], \"ownerProcessName\": \"ProcessName\", \"allowInterstitial\": \"true\", \"interstitialScreenRef\": \"\"}]", + "warnings": null, + "self_service_tasks": "[]", + "svg": "Start EventEnd EventForm Task", + "signal_events": "[]", + "conditional_events": "[]", + "properties": "{\"manager_id\": \"undefined\"}", + "is_template": 0, + "asset_type": null, + "case_title": null, + "launchpad_properties": null, + "alternative": "A", + "default_anon_web_language": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "bpmn-elements": [], + "abe_servers": [], + "uncategorized-category": true, + "signals": [], + "hasCustomDashboardRedirect": true, + "notification_settings": [ + { + "process_id": 25, + "element_id": "node_2", + "notifiable_type": "assignee", + "notification_type": "assigned" + }, + { + "process_id": 25, + "element_id": "node_2", + "notifiable_type": "assignee", + "notification_type": "due" + } + ] + } + }, + "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "type": "Screen", + "type_human": "Screen", + "type_plural": "Screens", + "type_human_plural": "Screens", + "last_modified_by": "Admin User", + "last_modified_by_id": 1, + "model": "ProcessMaker\\Models\\Screen", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [], + "name": "Screen with Nested", + "description": "Screen with Nested", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 36, + "uuid": "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b", + "title": "Screen with Nested", + "description": "Screen with Nested", + "type": "FORM", + "config": "[{\"name\": \"Screen with Nested\", \"items\": [{\"uuid\": \"045ae13d-634c-42ae-81a5-ca0bc2c7e30d\", \"label\": \"Line Input v1\", \"config\": {\"icon\": \"far fa-square\", \"name\": \"form_input_1\", \"type\": \"text\", \"label\": \"New Input v1\", \"helper\": null, \"dataFormat\": \"string\", \"validation\": null, \"placeholder\": null}, \"component\": \"FormInput\", \"inspector\": [{\"type\": \"FormInput\", \"field\": \"name\", \"config\": {\"name\": \"Variable Name\", \"label\": \"Variable Name\", \"helper\": \"A variable name is a symbolic name to reference information.\", \"validation\": \"dot_notation|required|not_in:null,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,const,enum,export,extends,import,super,true,false\"}}, {\"type\": \"FormInput\", \"field\": \"label\", \"config\": {\"label\": \"Label\", \"helper\": \"The label describes the field's name\"}}, {\"type\": \"FormMultiselect\", \"field\": \"dataFormat\", \"config\": {\"name\": \"Data Type\", \"label\": \"Data Type\", \"helper\": \"The data type specifies what kind of data is stored in the variable.\", \"options\": [{\"value\": \"string\", \"content\": \"Text\"}, {\"value\": \"int\", \"content\": \"Integer\"}, {\"value\": \"currency\", \"content\": \"Currency\"}, {\"value\": \"percentage\", \"content\": \"Percentage\"}, {\"value\": \"float\", \"content\": \"Decimal\"}, {\"value\": \"datetime\", \"content\": \"Datetime\"}, {\"value\": \"date\", \"content\": \"Date\"}, {\"value\": \"password\", \"content\": \"Password\"}], \"validation\": \"required\"}}, {\"type\": \"SelectDataTypeMask\", \"field\": \"dataMask\", \"config\": {\"name\": \"Data Format\", \"label\": \"Data Format\", \"helper\": \"The data format for the selected type.\"}}, {\"type\": \"ValidationSelect\", \"field\": \"validation\", \"config\": {\"label\": \"Validation Rules\", \"helper\": \"The validation rules needed for this field\"}}, {\"type\": \"FormInput\", \"field\": \"placeholder\", \"config\": {\"label\": \"Placeholder Text\", \"helper\": \"The placeholder is what is shown in the field when no value is provided yet\"}}, {\"type\": \"FormInput\", \"field\": \"helper\", \"config\": {\"label\": \"Helper Text\", \"helper\": \"Help text is meant to provide additional guidance on the field's value\"}}, {\"type\": \"FormCheckbox\", \"field\": \"readonly\", \"config\": {\"label\": \"Read Only\"}}, {\"type\": \"ColorSelect\", \"field\": \"color\", \"config\": {\"label\": \"Text Color\", \"helper\": \"Set the element's text color\", \"options\": [{\"value\": \"text-primary\", \"content\": \"primary\"}, {\"value\": \"text-secondary\", \"content\": \"secondary\"}, {\"value\": \"text-success\", \"content\": \"success\"}, {\"value\": \"text-danger\", \"content\": \"danger\"}, {\"value\": \"text-warning\", \"content\": \"warning\"}, {\"value\": \"text-info\", \"content\": \"info\"}, {\"value\": \"text-light\", \"content\": \"light\"}, {\"value\": \"text-dark\", \"content\": \"dark\"}]}}, {\"type\": \"ColorSelect\", \"field\": \"bgcolor\", \"config\": {\"label\": \"Background Color\", \"helper\": \"Set the element's background color\", \"options\": [{\"value\": \"alert alert-primary\", \"content\": \"primary\"}, {\"value\": \"alert alert-secondary\", \"content\": \"secondary\"}, {\"value\": \"alert alert-success\", \"content\": \"success\"}, {\"value\": \"alert alert-danger\", \"content\": \"danger\"}, {\"value\": \"alert alert-warning\", \"content\": \"warning\"}, {\"value\": \"alert alert-info\", \"content\": \"info\"}, {\"value\": \"alert alert-light\", \"content\": \"light\"}, {\"value\": \"alert alert-dark\", \"content\": \"dark\"}]}}, {\"type\": \"default-value-editor\", \"field\": \"defaultValue\", \"config\": {\"label\": \"Default Value\", \"helper\": \"The default value is pre populated using the existing request data. This feature will allow you to modify the value displayed on screen load if needed.\"}}, {\"type\": \"FormInput\", \"field\": \"conditionalHide\", \"config\": {\"label\": \"Visibility Rule\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"DeviceVisibility\", \"field\": \"deviceVisibility\", \"config\": {\"label\": \"Device Visibility\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"FormInput\", \"field\": \"customFormatter\", \"config\": {\"label\": \"Custom Format String\", \"helper\": \"Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormInput\", \"editor-component\": \"FormInput\"}, {\"uuid\": \"66699e0a-1cd4-4ebf-8987-7a4564742186\", \"label\": \"Submit Button\", \"config\": {\"icon\": \"fas fa-share-square\", \"name\": null, \"event\": \"submit\", \"label\": \"New Submit\", \"loading\": false, \"tooltip\": [], \"variant\": \"primary\", \"fieldValue\": null, \"loadingLabel\": \"Loading...\", \"defaultSubmit\": true}, \"component\": \"FormButton\", \"inspector\": [{\"type\": \"FormInput\", \"field\": \"label\", \"config\": {\"label\": \"Label\", \"helper\": \"The label describes the button's text\"}}, {\"type\": \"FormInput\", \"field\": \"name\", \"config\": {\"name\": \"Variable Name\", \"label\": \"Variable Name\", \"helper\": \"A variable name is a symbolic name to reference information.\", \"validation\": \"regex:/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*(? Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormSubmit\", \"editor-component\": \"FormButton\"}], \"order\": 1}]", + "computed": "[]", + "custom_css": null, + "created_at": "2025-05-23 18:21:11", + "updated_at": "2025-05-23 18:27:28", + "status": "ACTIVE", + "key": null, + "watchers": "[]", + "translations": null, + "is_template": 0, + "asset_type": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "uncategorized-category": true + } + }, + "9efb6041-6bb5-4544-86b2-3a3a12d43f0a": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ProcessLaunchpadExporter", + "type": "LaunchpadSetting", + "type_human": "Launchpad Setting", + "type_plural": "LaunchpadSettings", + "type_human_plural": "Launchpad Settings", + "last_modified_by": "", + "last_modified_by_id": null, + "model": "ProcessMaker\\Models\\ProcessLaunchpad", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "user", + "uuid": "9ef10b5b-c0b8-4327-8c26-533c0e0c25b6", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\UserExporter", + "modelClass": "ProcessMaker\\Models\\User", + "fallbackMatches": { + "email": "admin@processmaker.com", + "username": "admin" + }, + "name": "", + "discard": false + } + ], + "name": "Setting", + "description": null, + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 10, + "uuid": "9efb6041-6bb5-4544-86b2-3a3a12d43f0a", + "process_id": 25, + "user_id": 1, + "properties": "{\"icon\": \"Default Icon\", \"tabs\": [], \"screen_id\": 0, \"icon_label\": \"Default Icon\", \"screen_uuid\": \"\", \"screen_title\": \"Default Launchpad\", \"saved_chart_id\": 0, \"my_tasks_columns\": [], \"saved_chart_title\": \"Default Launchpad Chart\"}", + "created_at": "2025-05-23 18:28:08", + "updated_at": "2025-05-23 18:28:08" + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": [] + } + } +} \ No newline at end of file diff --git a/tests/Fixtures/processes/screen_process_v2.json b/tests/Fixtures/processes/screen_process_v2.json new file mode 100644 index 0000000000..4448ec61eb --- /dev/null +++ b/tests/Fixtures/processes/screen_process_v2.json @@ -0,0 +1,272 @@ +{ + "type": "process_package", + "version": "2", + "root": "9efb5c96-f795-40e9-90a0-ce9ebe246cfd", + "name": "Simple Process", + "export": { + "9efb5c96-f795-40e9-90a0-ce9ebe246cfd": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ProcessExporter", + "type": "Process", + "type_human": "Process", + "type_plural": "Processes", + "type_human_plural": "Processes", + "last_modified_by": "Admin User", + "last_modified_by_id": 1, + "model": "ProcessMaker\\Models\\Process", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "user", + "uuid": "9ef10b5b-c0b8-4327-8c26-533c0e0c25b6", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\UserExporter", + "modelClass": "ProcessMaker\\Models\\User", + "fallbackMatches": { + "email": "admin@processmaker.com", + "username": "admin" + }, + "name": "", + "discard": false + }, + { + "type": "screens", + "uuid": "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b", + "meta": { + "path": "/bpmn:definitions/bpmn:process/bpmn:task" + }, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "modelClass": "ProcessMaker\\Models\\Screen", + "fallbackMatches": { + "key": null, + "title": "Screen with Nested" + }, + "name": "Screen with Nested", + "discard": false + }, + { + "type": "process_launchpad", + "uuid": "9efb6041-6bb5-4544-86b2-3a3a12d43f0a", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ProcessLaunchpadExporter", + "modelClass": "ProcessMaker\\Models\\ProcessLaunchpad", + "fallbackMatches": [], + "name": "Setting", + "discard": false + } + ], + "name": "Simple Process", + "description": "Simple Process", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 25, + "uuid": "9efb5c96-f795-40e9-90a0-ce9ebe246cfd", + "process_category_id": 2, + "user_id": 1, + "bpmn": "\n\n \n \n node_18\n \n \n node_18\n node_27\n \n \n node_27\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n", + "description": "Simple Process", + "name": "Simple Process", + "cancel_screen_id": null, + "request_detail_screen_id": null, + "status": "ACTIVE", + "is_valid": 1, + "package_key": null, + "pause_timer_start": 0, + "deleted_at": null, + "created_at": "2025-05-23 18:17:53", + "updated_at": "2025-05-23 19:43:39", + "updated_by": 1, + "start_events": "[{\"id\": \"node_1\", \"name\": \"Start Event\", \"config\": \"{\\\"web_entry\\\":null}\", \"ownerProcessId\": \"ProcessId\", \"eventDefinitions\": [], \"ownerProcessName\": \"ProcessName\", \"allowInterstitial\": \"true\", \"interstitialScreenRef\": \"\"}]", + "warnings": null, + "self_service_tasks": "[]", + "svg": "Start EventEnd EventForm Task", + "signal_events": "[]", + "conditional_events": "[]", + "properties": "{\"manager_id\": \"undefined\"}", + "is_template": 0, + "asset_type": null, + "case_title": null, + "launchpad_properties": null, + "alternative": "A", + "default_anon_web_language": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "bpmn-elements": [], + "abe_servers": [], + "uncategorized-category": true, + "signals": [], + "hasCustomDashboardRedirect": true, + "notification_settings": [ + { + "process_id": 25, + "element_id": "node_2", + "notifiable_type": "assignee", + "notification_type": "assigned" + }, + { + "process_id": 25, + "element_id": "node_2", + "notifiable_type": "assignee", + "notification_type": "due" + } + ] + } + }, + "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "type": "Screen", + "type_human": "Screen", + "type_plural": "Screens", + "type_human_plural": "Screens", + "last_modified_by": "Admin User", + "last_modified_by_id": 1, + "model": "ProcessMaker\\Models\\Screen", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "screens", + "uuid": "9efb5e32-6db1-47c9-8d50-a81278831832", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "modelClass": "ProcessMaker\\Models\\Screen", + "fallbackMatches": { + "key": null, + "title": "Nested" + }, + "name": "Nested", + "discard": false + } + ], + "name": "Screen with Nested", + "description": "Screen with Nested", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 36, + "uuid": "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b", + "title": "Screen with Nested", + "description": "Screen with Nested", + "type": "FORM", + "config": "[{\"name\": \"Screen with Nested\", \"items\": [{\"uuid\": \"045ae13d-634c-42ae-81a5-ca0bc2c7e30d\", \"label\": \"Line Input v2\", \"config\": {\"icon\": \"far fa-square\", \"name\": \"form_input_1\", \"type\": \"text\", \"label\": \"New Input v2\", \"helper\": null, \"dataFormat\": \"string\", \"validation\": null, \"placeholder\": null}, \"component\": \"FormInput\", \"inspector\": [{\"type\": \"FormInput\", \"field\": \"name\", \"config\": {\"name\": \"Variable Name\", \"label\": \"Variable Name\", \"helper\": \"A variable name is a symbolic name to reference information.\", \"validation\": \"dot_notation|required|not_in:null,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,const,enum,export,extends,import,super,true,false\"}}, {\"type\": \"FormInput\", \"field\": \"label\", \"config\": {\"label\": \"Label\", \"helper\": \"The label describes the field's name\"}}, {\"type\": \"FormMultiselect\", \"field\": \"dataFormat\", \"config\": {\"name\": \"Data Type\", \"label\": \"Data Type\", \"helper\": \"The data type specifies what kind of data is stored in the variable.\", \"options\": [{\"value\": \"string\", \"content\": \"Text\"}, {\"value\": \"int\", \"content\": \"Integer\"}, {\"value\": \"currency\", \"content\": \"Currency\"}, {\"value\": \"percentage\", \"content\": \"Percentage\"}, {\"value\": \"float\", \"content\": \"Decimal\"}, {\"value\": \"datetime\", \"content\": \"Datetime\"}, {\"value\": \"date\", \"content\": \"Date\"}, {\"value\": \"password\", \"content\": \"Password\"}], \"validation\": \"required\"}}, {\"type\": \"SelectDataTypeMask\", \"field\": \"dataMask\", \"config\": {\"name\": \"Data Format\", \"label\": \"Data Format\", \"helper\": \"The data format for the selected type.\"}}, {\"type\": \"ValidationSelect\", \"field\": \"validation\", \"config\": {\"label\": \"Validation Rules\", \"helper\": \"The validation rules needed for this field\"}}, {\"type\": \"FormInput\", \"field\": \"placeholder\", \"config\": {\"label\": \"Placeholder Text\", \"helper\": \"The placeholder is what is shown in the field when no value is provided yet\"}}, {\"type\": \"FormInput\", \"field\": \"helper\", \"config\": {\"label\": \"Helper Text\", \"helper\": \"Help text is meant to provide additional guidance on the field's value\"}}, {\"type\": \"FormCheckbox\", \"field\": \"readonly\", \"config\": {\"label\": \"Read Only\"}}, {\"type\": \"ColorSelect\", \"field\": \"color\", \"config\": {\"label\": \"Text Color\", \"helper\": \"Set the element's text color\", \"options\": [{\"value\": \"text-primary\", \"content\": \"primary\"}, {\"value\": \"text-secondary\", \"content\": \"secondary\"}, {\"value\": \"text-success\", \"content\": \"success\"}, {\"value\": \"text-danger\", \"content\": \"danger\"}, {\"value\": \"text-warning\", \"content\": \"warning\"}, {\"value\": \"text-info\", \"content\": \"info\"}, {\"value\": \"text-light\", \"content\": \"light\"}, {\"value\": \"text-dark\", \"content\": \"dark\"}]}}, {\"type\": \"ColorSelect\", \"field\": \"bgcolor\", \"config\": {\"label\": \"Background Color\", \"helper\": \"Set the element's background color\", \"options\": [{\"value\": \"alert alert-primary\", \"content\": \"primary\"}, {\"value\": \"alert alert-secondary\", \"content\": \"secondary\"}, {\"value\": \"alert alert-success\", \"content\": \"success\"}, {\"value\": \"alert alert-danger\", \"content\": \"danger\"}, {\"value\": \"alert alert-warning\", \"content\": \"warning\"}, {\"value\": \"alert alert-info\", \"content\": \"info\"}, {\"value\": \"alert alert-light\", \"content\": \"light\"}, {\"value\": \"alert alert-dark\", \"content\": \"dark\"}]}}, {\"type\": \"default-value-editor\", \"field\": \"defaultValue\", \"config\": {\"label\": \"Default Value\", \"helper\": \"The default value is pre populated using the existing request data. This feature will allow you to modify the value displayed on screen load if needed.\"}}, {\"type\": \"FormInput\", \"field\": \"conditionalHide\", \"config\": {\"label\": \"Visibility Rule\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"DeviceVisibility\", \"field\": \"deviceVisibility\", \"config\": {\"label\": \"Device Visibility\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"FormInput\", \"field\": \"customFormatter\", \"config\": {\"label\": \"Custom Format String\", \"helper\": \"Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormInput\", \"editor-component\": \"FormInput\"}, {\"uuid\": \"a6171091-ecff-4c73-ba38-0a6acec8b133\", \"label\": \"Nested Screen\", \"config\": {\"icon\": \"fas fa-file-invoice\", \"name\": \"Nested Screen\", \"label\": \"Nested Screen\", \"value\": null, \"screen\": 37, \"variant\": \"primary\"}, \"component\": \"FormNestedScreen\", \"inspector\": [{\"type\": \"ScreenSelector\", \"field\": \"screen\", \"config\": {\"name\": \"SelectScreen\", \"label\": \"Screen\", \"helper\": \"Select a screen\", \"validate-nested\": false}}, {\"type\": \"FormInput\", \"field\": \"conditionalHide\", \"config\": {\"label\": \"Visibility Rule\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"DeviceVisibility\", \"field\": \"deviceVisibility\", \"config\": {\"label\": \"Device Visibility\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"FormInput\", \"field\": \"customFormatter\", \"config\": {\"label\": \"Custom Format String\", \"helper\": \"Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormNestedScreen\", \"editor-component\": \"FormNestedScreen\"}, {\"uuid\": \"66699e0a-1cd4-4ebf-8987-7a4564742186\", \"label\": \"Submit Button\", \"config\": {\"icon\": \"fas fa-share-square\", \"name\": null, \"event\": \"submit\", \"label\": \"New Submit\", \"loading\": false, \"tooltip\": [], \"variant\": \"primary\", \"fieldValue\": null, \"loadingLabel\": \"Loading...\", \"defaultSubmit\": true}, \"component\": \"FormButton\", \"inspector\": [{\"type\": \"FormInput\", \"field\": \"label\", \"config\": {\"label\": \"Label\", \"helper\": \"The label describes the button's text\"}}, {\"type\": \"FormInput\", \"field\": \"name\", \"config\": {\"name\": \"Variable Name\", \"label\": \"Variable Name\", \"helper\": \"A variable name is a symbolic name to reference information.\", \"validation\": \"regex:/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*(? Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormSubmit\", \"editor-component\": \"FormButton\"}], \"order\": 1}]", + "computed": "[]", + "custom_css": null, + "created_at": "2025-05-23 18:21:11", + "updated_at": "2025-05-23 19:43:26", + "status": "ACTIVE", + "key": null, + "watchers": "[]", + "translations": null, + "is_template": 0, + "asset_type": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "uncategorized-category": true + } + }, + "9efb5e32-6db1-47c9-8d50-a81278831832": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "type": "Screen", + "type_human": "Screen", + "type_plural": "Screens", + "type_human_plural": "Screens", + "last_modified_by": "", + "last_modified_by_id": null, + "model": "ProcessMaker\\Models\\Screen", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [], + "name": "Nested", + "description": "Nested", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 37, + "uuid": "9efb5e32-6db1-47c9-8d50-a81278831832", + "title": "Nested", + "description": "Nested", + "type": "FORM", + "config": null, + "computed": null, + "custom_css": null, + "created_at": "2025-05-23 18:22:23", + "updated_at": "2025-05-23 18:22:23", + "status": "ACTIVE", + "key": null, + "watchers": null, + "translations": null, + "is_template": 0, + "asset_type": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "uncategorized-category": true + } + }, + "9efb6041-6bb5-4544-86b2-3a3a12d43f0a": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ProcessLaunchpadExporter", + "type": "LaunchpadSetting", + "type_human": "Launchpad Setting", + "type_plural": "LaunchpadSettings", + "type_human_plural": "Launchpad Settings", + "last_modified_by": "", + "last_modified_by_id": null, + "model": "ProcessMaker\\Models\\ProcessLaunchpad", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "user", + "uuid": "9ef10b5b-c0b8-4327-8c26-533c0e0c25b6", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\UserExporter", + "modelClass": "ProcessMaker\\Models\\User", + "fallbackMatches": { + "email": "admin@processmaker.com", + "username": "admin" + }, + "name": "", + "discard": false + } + ], + "name": "Setting", + "description": null, + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 10, + "uuid": "9efb6041-6bb5-4544-86b2-3a3a12d43f0a", + "process_id": 25, + "user_id": 1, + "properties": "{\"icon\": \"Default Icon\", \"tabs\": [], \"screen_id\": 0, \"icon_label\": \"Default Icon\", \"screen_uuid\": \"\", \"screen_title\": \"Default Launchpad\", \"saved_chart_id\": 0, \"my_tasks_columns\": [], \"saved_chart_title\": \"Default Launchpad Chart\"}", + "created_at": "2025-05-23 18:28:08", + "updated_at": "2025-05-23 18:28:08" + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": [] + } + } +} \ No newline at end of file diff --git a/tests/Fixtures/processes/screen_process_v3.json b/tests/Fixtures/processes/screen_process_v3.json new file mode 100644 index 0000000000..2fab281224 --- /dev/null +++ b/tests/Fixtures/processes/screen_process_v3.json @@ -0,0 +1,272 @@ +{ + "type": "process_package", + "version": "2", + "root": "9efb5c96-f795-40e9-90a0-ce9ebe246cfd", + "name": "Simple Process", + "export": { + "9efb5c96-f795-40e9-90a0-ce9ebe246cfd": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ProcessExporter", + "type": "Process", + "type_human": "Process", + "type_plural": "Processes", + "type_human_plural": "Processes", + "last_modified_by": "Admin User", + "last_modified_by_id": 1, + "model": "ProcessMaker\\Models\\Process", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "user", + "uuid": "9ef10b5b-c0b8-4327-8c26-533c0e0c25b6", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\UserExporter", + "modelClass": "ProcessMaker\\Models\\User", + "fallbackMatches": { + "email": "admin@processmaker.com", + "username": "admin" + }, + "name": "", + "discard": false + }, + { + "type": "screens", + "uuid": "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b", + "meta": { + "path": "/bpmn:definitions/bpmn:process/bpmn:task" + }, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "modelClass": "ProcessMaker\\Models\\Screen", + "fallbackMatches": { + "key": null, + "title": "Screen with Nested" + }, + "name": "Screen with Nested", + "discard": false + }, + { + "type": "process_launchpad", + "uuid": "9efb6041-6bb5-4544-86b2-3a3a12d43f0a", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ProcessLaunchpadExporter", + "modelClass": "ProcessMaker\\Models\\ProcessLaunchpad", + "fallbackMatches": [], + "name": "Setting", + "discard": false + } + ], + "name": "Simple Process", + "description": "Simple Process", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 25, + "uuid": "9efb5c96-f795-40e9-90a0-ce9ebe246cfd", + "process_category_id": 2, + "user_id": 1, + "bpmn": "\n\n \n \n node_18\n \n \n node_18\n node_27\n \n \n node_27\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n", + "description": "Simple Process", + "name": "Simple Process", + "cancel_screen_id": null, + "request_detail_screen_id": null, + "status": "ACTIVE", + "is_valid": 1, + "package_key": null, + "pause_timer_start": 0, + "deleted_at": null, + "created_at": "2025-05-23 18:17:53", + "updated_at": "2025-05-23 19:43:39", + "updated_by": 1, + "start_events": "[{\"id\": \"node_1\", \"name\": \"Start Event\", \"config\": \"{\\\"web_entry\\\":null}\", \"ownerProcessId\": \"ProcessId\", \"eventDefinitions\": [], \"ownerProcessName\": \"ProcessName\", \"allowInterstitial\": \"true\", \"interstitialScreenRef\": \"\"}]", + "warnings": null, + "self_service_tasks": "[]", + "svg": "Start EventEnd EventForm Task", + "signal_events": "[]", + "conditional_events": "[]", + "properties": "{\"manager_id\": \"undefined\"}", + "is_template": 0, + "asset_type": null, + "case_title": null, + "launchpad_properties": null, + "alternative": "A", + "default_anon_web_language": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "bpmn-elements": [], + "abe_servers": [], + "uncategorized-category": true, + "signals": [], + "hasCustomDashboardRedirect": true, + "notification_settings": [ + { + "process_id": 25, + "element_id": "node_2", + "notifiable_type": "assignee", + "notification_type": "assigned" + }, + { + "process_id": 25, + "element_id": "node_2", + "notifiable_type": "assignee", + "notification_type": "due" + } + ] + } + }, + "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "type": "Screen", + "type_human": "Screen", + "type_plural": "Screens", + "type_human_plural": "Screens", + "last_modified_by": "Admin User", + "last_modified_by_id": 1, + "model": "ProcessMaker\\Models\\Screen", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "screens", + "uuid": "9efb5e32-6db1-47c9-8d50-a81278831832", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "modelClass": "ProcessMaker\\Models\\Screen", + "fallbackMatches": { + "key": null, + "title": "Nested" + }, + "name": "Nested", + "discard": false + } + ], + "name": "Screen with Nested", + "description": "Screen with Nested", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 36, + "uuid": "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b", + "title": "Screen with Nested", + "description": "Screen with Nested", + "type": "FORM", + "config": "[{\"name\": \"Screen with Nested\", \"items\": [{\"uuid\": \"045ae13d-634c-42ae-81a5-ca0bc2c7e30d\", \"label\": \"Line Input v3\", \"config\": {\"icon\": \"far fa-square\", \"name\": \"form_input_1\", \"type\": \"text\", \"label\": \"New Input v3\", \"helper\": null, \"dataFormat\": \"string\", \"validation\": null, \"placeholder\": null}, \"component\": \"FormInput\", \"inspector\": [{\"type\": \"FormInput\", \"field\": \"name\", \"config\": {\"name\": \"Variable Name\", \"label\": \"Variable Name\", \"helper\": \"A variable name is a symbolic name to reference information.\", \"validation\": \"dot_notation|required|not_in:null,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,const,enum,export,extends,import,super,true,false\"}}, {\"type\": \"FormInput\", \"field\": \"label\", \"config\": {\"label\": \"Label\", \"helper\": \"The label describes the field's name\"}}, {\"type\": \"FormMultiselect\", \"field\": \"dataFormat\", \"config\": {\"name\": \"Data Type\", \"label\": \"Data Type\", \"helper\": \"The data type specifies what kind of data is stored in the variable.\", \"options\": [{\"value\": \"string\", \"content\": \"Text\"}, {\"value\": \"int\", \"content\": \"Integer\"}, {\"value\": \"currency\", \"content\": \"Currency\"}, {\"value\": \"percentage\", \"content\": \"Percentage\"}, {\"value\": \"float\", \"content\": \"Decimal\"}, {\"value\": \"datetime\", \"content\": \"Datetime\"}, {\"value\": \"date\", \"content\": \"Date\"}, {\"value\": \"password\", \"content\": \"Password\"}], \"validation\": \"required\"}}, {\"type\": \"SelectDataTypeMask\", \"field\": \"dataMask\", \"config\": {\"name\": \"Data Format\", \"label\": \"Data Format\", \"helper\": \"The data format for the selected type.\"}}, {\"type\": \"ValidationSelect\", \"field\": \"validation\", \"config\": {\"label\": \"Validation Rules\", \"helper\": \"The validation rules needed for this field\"}}, {\"type\": \"FormInput\", \"field\": \"placeholder\", \"config\": {\"label\": \"Placeholder Text\", \"helper\": \"The placeholder is what is shown in the field when no value is provided yet\"}}, {\"type\": \"FormInput\", \"field\": \"helper\", \"config\": {\"label\": \"Helper Text\", \"helper\": \"Help text is meant to provide additional guidance on the field's value\"}}, {\"type\": \"FormCheckbox\", \"field\": \"readonly\", \"config\": {\"label\": \"Read Only\"}}, {\"type\": \"ColorSelect\", \"field\": \"color\", \"config\": {\"label\": \"Text Color\", \"helper\": \"Set the element's text color\", \"options\": [{\"value\": \"text-primary\", \"content\": \"primary\"}, {\"value\": \"text-secondary\", \"content\": \"secondary\"}, {\"value\": \"text-success\", \"content\": \"success\"}, {\"value\": \"text-danger\", \"content\": \"danger\"}, {\"value\": \"text-warning\", \"content\": \"warning\"}, {\"value\": \"text-info\", \"content\": \"info\"}, {\"value\": \"text-light\", \"content\": \"light\"}, {\"value\": \"text-dark\", \"content\": \"dark\"}]}}, {\"type\": \"ColorSelect\", \"field\": \"bgcolor\", \"config\": {\"label\": \"Background Color\", \"helper\": \"Set the element's background color\", \"options\": [{\"value\": \"alert alert-primary\", \"content\": \"primary\"}, {\"value\": \"alert alert-secondary\", \"content\": \"secondary\"}, {\"value\": \"alert alert-success\", \"content\": \"success\"}, {\"value\": \"alert alert-danger\", \"content\": \"danger\"}, {\"value\": \"alert alert-warning\", \"content\": \"warning\"}, {\"value\": \"alert alert-info\", \"content\": \"info\"}, {\"value\": \"alert alert-light\", \"content\": \"light\"}, {\"value\": \"alert alert-dark\", \"content\": \"dark\"}]}}, {\"type\": \"default-value-editor\", \"field\": \"defaultValue\", \"config\": {\"label\": \"Default Value\", \"helper\": \"The default value is pre populated using the existing request data. This feature will allow you to modify the value displayed on screen load if needed.\"}}, {\"type\": \"FormInput\", \"field\": \"conditionalHide\", \"config\": {\"label\": \"Visibility Rule\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"DeviceVisibility\", \"field\": \"deviceVisibility\", \"config\": {\"label\": \"Device Visibility\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"FormInput\", \"field\": \"customFormatter\", \"config\": {\"label\": \"Custom Format String\", \"helper\": \"Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormInput\", \"editor-component\": \"FormInput\"}, {\"uuid\": \"a6171091-ecff-4c73-ba38-0a6acec8b133\", \"label\": \"Nested Screen\", \"config\": {\"icon\": \"fas fa-file-invoice\", \"name\": \"Nested Screen\", \"label\": \"Nested Screen\", \"value\": null, \"screen\": 37, \"variant\": \"primary\"}, \"component\": \"FormNestedScreen\", \"inspector\": [{\"type\": \"ScreenSelector\", \"field\": \"screen\", \"config\": {\"name\": \"SelectScreen\", \"label\": \"Screen\", \"helper\": \"Select a screen\", \"validate-nested\": false}}, {\"type\": \"FormInput\", \"field\": \"conditionalHide\", \"config\": {\"label\": \"Visibility Rule\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"DeviceVisibility\", \"field\": \"deviceVisibility\", \"config\": {\"label\": \"Device Visibility\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"FormInput\", \"field\": \"customFormatter\", \"config\": {\"label\": \"Custom Format String\", \"helper\": \"Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormNestedScreen\", \"editor-component\": \"FormNestedScreen\"}, {\"uuid\": \"66699e0a-1cd4-4ebf-8987-7a4564742186\", \"label\": \"Submit Button\", \"config\": {\"icon\": \"fas fa-share-square\", \"name\": null, \"event\": \"submit\", \"label\": \"New Submit\", \"loading\": false, \"tooltip\": [], \"variant\": \"primary\", \"fieldValue\": null, \"loadingLabel\": \"Loading...\", \"defaultSubmit\": true}, \"component\": \"FormButton\", \"inspector\": [{\"type\": \"FormInput\", \"field\": \"label\", \"config\": {\"label\": \"Label\", \"helper\": \"The label describes the button's text\"}}, {\"type\": \"FormInput\", \"field\": \"name\", \"config\": {\"name\": \"Variable Name\", \"label\": \"Variable Name\", \"helper\": \"A variable name is a symbolic name to reference information.\", \"validation\": \"regex:/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*(? Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormSubmit\", \"editor-component\": \"FormButton\"}], \"order\": 1}]", + "computed": "[]", + "custom_css": null, + "created_at": "2025-05-23 18:21:11", + "updated_at": "2025-05-23 19:43:26", + "status": "ACTIVE", + "key": null, + "watchers": "[]", + "translations": null, + "is_template": 0, + "asset_type": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "uncategorized-category": true + } + }, + "9efb5e32-6db1-47c9-8d50-a81278831832": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "type": "Screen", + "type_human": "Screen", + "type_plural": "Screens", + "type_human_plural": "Screens", + "last_modified_by": "Admin User", + "last_modified_by_id": 1, + "model": "ProcessMaker\\Models\\Screen", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [], + "name": "Nested", + "description": "Nested", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 37, + "uuid": "9efb5e32-6db1-47c9-8d50-a81278831832", + "title": "Nested", + "description": "Nested", + "type": "FORM", + "config": "[{\"name\": \"Nested\", \"items\": [{\"uuid\": \"5c01f614-fc83-48b4-8a42-0e77c16fb334\", \"label\": \"Textarea\", \"config\": {\"icon\": \"fas fa-paragraph\", \"name\": \"form_text_area_1\", \"rows\": 2, \"label\": \"New Textarea\", \"helper\": null, \"currency\": {\"code\": \"USD\", \"name\": \"US Dollar\", \"format\": \"#,###.##\", \"symbol\": \"$\"}, \"richtext\": false, \"placeholder\": null}, \"component\": \"FormTextArea\", \"inspector\": [{\"type\": \"FormInput\", \"field\": \"name\", \"config\": {\"name\": \"Variable Name\", \"label\": \"Variable Name\", \"helper\": \"A variable name is a symbolic name to reference information.\", \"validation\": \"dot_notation|required|not_in:null,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,const,enum,export,extends,import,super,true,false\"}}, {\"type\": \"FormInput\", \"field\": \"label\", \"config\": {\"label\": \"Label\", \"helper\": \"The label describes the field's name\"}}, {\"type\": \"FormInput\", \"field\": \"placeholder\", \"config\": {\"label\": \"Placeholder Text\", \"helper\": \"The placeholder is what is shown in the field when no value is provided yet\"}}, {\"type\": \"FormInput\", \"field\": \"helper\", \"config\": {\"label\": \"Helper Text\", \"helper\": \"Help text is meant to provide additional guidance on the field's value\"}}, {\"type\": \"FormCheckbox\", \"field\": \"richtext\", \"config\": {\"label\": \"Rich Text\", \"helper\": null}}, {\"type\": \"ValidationSelect\", \"field\": \"validation\", \"config\": {\"label\": \"Validation Rules\", \"helper\": \"The validation rules needed for this field\"}}, {\"type\": \"FormInput\", \"field\": \"rows\", \"config\": {\"label\": \"Rows\", \"helper\": \"The number of rows to provide for input\", \"validation\": \"integer\"}}, {\"type\": \"FormCheckbox\", \"field\": \"readonly\", \"config\": {\"label\": \"Read Only\"}}, {\"type\": \"ColorSelect\", \"field\": \"color\", \"config\": {\"label\": \"Text Color\", \"helper\": \"Set the element's text color\", \"options\": [{\"value\": \"text-primary\", \"content\": \"primary\"}, {\"value\": \"text-secondary\", \"content\": \"secondary\"}, {\"value\": \"text-success\", \"content\": \"success\"}, {\"value\": \"text-danger\", \"content\": \"danger\"}, {\"value\": \"text-warning\", \"content\": \"warning\"}, {\"value\": \"text-info\", \"content\": \"info\"}, {\"value\": \"text-light\", \"content\": \"light\"}, {\"value\": \"text-dark\", \"content\": \"dark\"}]}}, {\"type\": \"ColorSelect\", \"field\": \"bgcolor\", \"config\": {\"label\": \"Background Color\", \"helper\": \"Set the element's background color\", \"options\": [{\"value\": \"alert alert-primary\", \"content\": \"primary\"}, {\"value\": \"alert alert-secondary\", \"content\": \"secondary\"}, {\"value\": \"alert alert-success\", \"content\": \"success\"}, {\"value\": \"alert alert-danger\", \"content\": \"danger\"}, {\"value\": \"alert alert-warning\", \"content\": \"warning\"}, {\"value\": \"alert alert-info\", \"content\": \"info\"}, {\"value\": \"alert alert-light\", \"content\": \"light\"}, {\"value\": \"alert alert-dark\", \"content\": \"dark\"}]}}, {\"type\": \"default-value-editor\", \"field\": \"defaultValue\", \"config\": {\"label\": \"Default Value\", \"helper\": \"The default value is pre populated using the existing request data. This feature will allow you to modify the value displayed on screen load if needed.\"}}, {\"type\": \"FormInput\", \"field\": \"conditionalHide\", \"config\": {\"label\": \"Visibility Rule\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"DeviceVisibility\", \"field\": \"deviceVisibility\", \"config\": {\"label\": \"Device Visibility\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"FormInput\", \"field\": \"customFormatter\", \"config\": {\"label\": \"Custom Format String\", \"helper\": \"Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormTextArea\", \"editor-component\": \"FormTextArea\"}], \"order\": 1}]", + "computed": "[]", + "custom_css": null, + "created_at": "2025-05-23 18:22:23", + "updated_at": "2025-05-23 19:52:19", + "status": "ACTIVE", + "key": null, + "watchers": "[]", + "translations": null, + "is_template": 0, + "asset_type": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "uncategorized-category": true + } + }, + "9efb6041-6bb5-4544-86b2-3a3a12d43f0a": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ProcessLaunchpadExporter", + "type": "LaunchpadSetting", + "type_human": "Launchpad Setting", + "type_plural": "LaunchpadSettings", + "type_human_plural": "Launchpad Settings", + "last_modified_by": "", + "last_modified_by_id": null, + "model": "ProcessMaker\\Models\\ProcessLaunchpad", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "user", + "uuid": "9ef10b5b-c0b8-4327-8c26-533c0e0c25b6", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\UserExporter", + "modelClass": "ProcessMaker\\Models\\User", + "fallbackMatches": { + "email": "admin@processmaker.com", + "username": "admin" + }, + "name": "", + "discard": false + } + ], + "name": "Setting", + "description": null, + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 10, + "uuid": "9efb6041-6bb5-4544-86b2-3a3a12d43f0a", + "process_id": 25, + "user_id": 1, + "properties": "{\"icon\": \"Default Icon\", \"tabs\": [], \"screen_id\": 0, \"icon_label\": \"Default Icon\", \"screen_uuid\": \"\", \"screen_title\": \"Default Launchpad\", \"saved_chart_id\": 0, \"my_tasks_columns\": [], \"saved_chart_title\": \"Default Launchpad Chart\"}", + "created_at": "2025-05-23 18:28:08", + "updated_at": "2025-05-23 18:28:08" + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": [] + } + } +} \ No newline at end of file diff --git a/tests/Fixtures/processes/screen_process_v4.json b/tests/Fixtures/processes/screen_process_v4.json new file mode 100644 index 0000000000..32506e1c63 --- /dev/null +++ b/tests/Fixtures/processes/screen_process_v4.json @@ -0,0 +1,272 @@ +{ + "type": "process_package", + "version": "2", + "root": "9efb5c96-f795-40e9-90a0-ce9ebe246cfd", + "name": "Simple Process", + "export": { + "9efb5c96-f795-40e9-90a0-ce9ebe246cfd": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ProcessExporter", + "type": "Process", + "type_human": "Process", + "type_plural": "Processes", + "type_human_plural": "Processes", + "last_modified_by": "Admin User", + "last_modified_by_id": 1, + "model": "ProcessMaker\\Models\\Process", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "user", + "uuid": "9ef10b5b-c0b8-4327-8c26-533c0e0c25b6", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\UserExporter", + "modelClass": "ProcessMaker\\Models\\User", + "fallbackMatches": { + "email": "admin@processmaker.com", + "username": "admin" + }, + "name": "", + "discard": false + }, + { + "type": "screens", + "uuid": "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b", + "meta": { + "path": "/bpmn:definitions/bpmn:process/bpmn:task" + }, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "modelClass": "ProcessMaker\\Models\\Screen", + "fallbackMatches": { + "key": null, + "title": "Screen with Nested" + }, + "name": "Screen with Nested", + "discard": false + }, + { + "type": "process_launchpad", + "uuid": "9efb6041-6bb5-4544-86b2-3a3a12d43f0a", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ProcessLaunchpadExporter", + "modelClass": "ProcessMaker\\Models\\ProcessLaunchpad", + "fallbackMatches": [], + "name": "Setting", + "discard": false + } + ], + "name": "Simple Process", + "description": "Simple Process", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 25, + "uuid": "9efb5c96-f795-40e9-90a0-ce9ebe246cfd", + "process_category_id": 2, + "user_id": 1, + "bpmn": "\n\n \n \n node_18\n \n \n node_18\n node_27\n \n \n node_27\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n", + "description": "Simple Process", + "name": "Simple Process", + "cancel_screen_id": null, + "request_detail_screen_id": null, + "status": "ACTIVE", + "is_valid": 1, + "package_key": null, + "pause_timer_start": 0, + "deleted_at": null, + "created_at": "2025-05-23 18:17:53", + "updated_at": "2025-05-23 23:40:56", + "updated_by": 1, + "start_events": "[{\"id\": \"node_1\", \"name\": \"Start Event\", \"config\": \"{\\\"web_entry\\\":null}\", \"ownerProcessId\": \"ProcessId\", \"eventDefinitions\": [], \"ownerProcessName\": \"ProcessName\", \"allowInterstitial\": \"true\", \"interstitialScreenRef\": \"\"}]", + "warnings": null, + "self_service_tasks": "[]", + "svg": "Start EventEnd EventForm Task--", + "signal_events": "[]", + "conditional_events": "[]", + "properties": "{\"manager_id\": \"undefined\"}", + "is_template": 0, + "asset_type": null, + "case_title": null, + "launchpad_properties": null, + "alternative": "A", + "default_anon_web_language": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "bpmn-elements": [], + "abe_servers": [], + "uncategorized-category": true, + "signals": [], + "hasCustomDashboardRedirect": true, + "notification_settings": [ + { + "process_id": 25, + "element_id": "node_2", + "notifiable_type": "assignee", + "notification_type": "assigned" + }, + { + "process_id": 25, + "element_id": "node_2", + "notifiable_type": "assignee", + "notification_type": "due" + } + ] + } + }, + "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "type": "Screen", + "type_human": "Screen", + "type_plural": "Screens", + "type_human_plural": "Screens", + "last_modified_by": "Admin User", + "last_modified_by_id": 1, + "model": "ProcessMaker\\Models\\Screen", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "screens", + "uuid": "9efb5e32-6db1-47c9-8d50-a81278831832", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "modelClass": "ProcessMaker\\Models\\Screen", + "fallbackMatches": { + "key": null, + "title": "Nested" + }, + "name": "Nested", + "discard": false + } + ], + "name": "Screen with Nested", + "description": "Screen with Nested", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 36, + "uuid": "9efb5dc4-bda4-4e49-8991-e66f8d19fd6b", + "title": "Screen with Nested", + "description": "Screen with Nested", + "type": "FORM", + "config": "[{\"name\": \"Screen with Nested\", \"items\": [{\"uuid\": \"045ae13d-634c-42ae-81a5-ca0bc2c7e30d\", \"label\": \"Line Input\", \"config\": {\"icon\": \"far fa-square\", \"name\": \"form_input_1\", \"type\": \"text\", \"label\": \"New Input\", \"helper\": null, \"dataFormat\": \"string\", \"validation\": null, \"placeholder\": null}, \"component\": \"FormInput\", \"inspector\": [{\"type\": \"FormInput\", \"field\": \"name\", \"config\": {\"name\": \"Variable Name\", \"label\": \"Variable Name\", \"helper\": \"A variable name is a symbolic name to reference information.\", \"validation\": \"dot_notation|required|not_in:null,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,const,enum,export,extends,import,super,true,false\"}}, {\"type\": \"FormInput\", \"field\": \"label\", \"config\": {\"label\": \"Label\", \"helper\": \"The label describes the field's name\"}}, {\"type\": \"FormMultiselect\", \"field\": \"dataFormat\", \"config\": {\"name\": \"Data Type\", \"label\": \"Data Type\", \"helper\": \"The data type specifies what kind of data is stored in the variable.\", \"options\": [{\"value\": \"string\", \"content\": \"Text\"}, {\"value\": \"int\", \"content\": \"Integer\"}, {\"value\": \"currency\", \"content\": \"Currency\"}, {\"value\": \"percentage\", \"content\": \"Percentage\"}, {\"value\": \"float\", \"content\": \"Decimal\"}, {\"value\": \"datetime\", \"content\": \"Datetime\"}, {\"value\": \"date\", \"content\": \"Date\"}, {\"value\": \"password\", \"content\": \"Password\"}], \"validation\": \"required\"}}, {\"type\": \"SelectDataTypeMask\", \"field\": \"dataMask\", \"config\": {\"name\": \"Data Format\", \"label\": \"Data Format\", \"helper\": \"The data format for the selected type.\"}}, {\"type\": \"ValidationSelect\", \"field\": \"validation\", \"config\": {\"label\": \"Validation Rules\", \"helper\": \"The validation rules needed for this field\"}}, {\"type\": \"FormInput\", \"field\": \"placeholder\", \"config\": {\"label\": \"Placeholder Text\", \"helper\": \"The placeholder is what is shown in the field when no value is provided yet\"}}, {\"type\": \"FormInput\", \"field\": \"helper\", \"config\": {\"label\": \"Helper Text\", \"helper\": \"Help text is meant to provide additional guidance on the field's value\"}}, {\"type\": \"FormCheckbox\", \"field\": \"readonly\", \"config\": {\"label\": \"Read Only\"}}, {\"type\": \"ColorSelect\", \"field\": \"color\", \"config\": {\"label\": \"Text Color\", \"helper\": \"Set the element's text color\", \"options\": [{\"value\": \"text-primary\", \"content\": \"primary\"}, {\"value\": \"text-secondary\", \"content\": \"secondary\"}, {\"value\": \"text-success\", \"content\": \"success\"}, {\"value\": \"text-danger\", \"content\": \"danger\"}, {\"value\": \"text-warning\", \"content\": \"warning\"}, {\"value\": \"text-info\", \"content\": \"info\"}, {\"value\": \"text-light\", \"content\": \"light\"}, {\"value\": \"text-dark\", \"content\": \"dark\"}]}}, {\"type\": \"ColorSelect\", \"field\": \"bgcolor\", \"config\": {\"label\": \"Background Color\", \"helper\": \"Set the element's background color\", \"options\": [{\"value\": \"alert alert-primary\", \"content\": \"primary\"}, {\"value\": \"alert alert-secondary\", \"content\": \"secondary\"}, {\"value\": \"alert alert-success\", \"content\": \"success\"}, {\"value\": \"alert alert-danger\", \"content\": \"danger\"}, {\"value\": \"alert alert-warning\", \"content\": \"warning\"}, {\"value\": \"alert alert-info\", \"content\": \"info\"}, {\"value\": \"alert alert-light\", \"content\": \"light\"}, {\"value\": \"alert alert-dark\", \"content\": \"dark\"}]}}, {\"type\": \"default-value-editor\", \"field\": \"defaultValue\", \"config\": {\"label\": \"Default Value\", \"helper\": \"The default value is pre populated using the existing request data. This feature will allow you to modify the value displayed on screen load if needed.\"}}, {\"type\": \"FormInput\", \"field\": \"conditionalHide\", \"config\": {\"label\": \"Visibility Rule\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"DeviceVisibility\", \"field\": \"deviceVisibility\", \"config\": {\"label\": \"Device Visibility\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"FormInput\", \"field\": \"customFormatter\", \"config\": {\"label\": \"Custom Format String\", \"helper\": \"Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormInput\", \"editor-component\": \"FormInput\"}, {\"uuid\": \"a6171091-ecff-4c73-ba38-0a6acec8b133\", \"label\": \"Nested Screen\", \"config\": {\"icon\": \"fas fa-file-invoice\", \"name\": \"Nested Screen\", \"label\": \"Nested Screen\", \"value\": null, \"screen\": 37, \"variant\": \"primary\"}, \"component\": \"FormNestedScreen\", \"inspector\": [{\"type\": \"ScreenSelector\", \"field\": \"screen\", \"config\": {\"name\": \"SelectScreen\", \"label\": \"Screen\", \"helper\": \"Select a screen\", \"validate-nested\": false}}, {\"type\": \"FormInput\", \"field\": \"conditionalHide\", \"config\": {\"label\": \"Visibility Rule\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"DeviceVisibility\", \"field\": \"deviceVisibility\", \"config\": {\"label\": \"Device Visibility\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"FormInput\", \"field\": \"customFormatter\", \"config\": {\"label\": \"Custom Format String\", \"helper\": \"Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormNestedScreen\", \"editor-component\": \"FormNestedScreen\"}, {\"uuid\": \"66699e0a-1cd4-4ebf-8987-7a4564742186\", \"label\": \"Submit Button\", \"config\": {\"icon\": \"fas fa-share-square\", \"name\": null, \"event\": \"submit\", \"label\": \"New Submit\", \"loading\": false, \"tooltip\": [], \"variant\": \"primary\", \"fieldValue\": null, \"loadingLabel\": \"Loading...\", \"defaultSubmit\": true}, \"component\": \"FormButton\", \"inspector\": [{\"type\": \"FormInput\", \"field\": \"label\", \"config\": {\"label\": \"Label\", \"helper\": \"The label describes the button's text\"}}, {\"type\": \"FormInput\", \"field\": \"name\", \"config\": {\"name\": \"Variable Name\", \"label\": \"Variable Name\", \"helper\": \"A variable name is a symbolic name to reference information.\", \"validation\": \"regex:/^(?:[A-Za-z])(?:[0-9A-Z_.a-z])*(? Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormSubmit\", \"editor-component\": \"FormButton\"}], \"order\": 1}]", + "computed": "[]", + "custom_css": null, + "created_at": "2025-05-23 18:21:11", + "updated_at": "2025-05-23 23:39:17", + "status": "ACTIVE", + "key": null, + "watchers": "[]", + "translations": null, + "is_template": 0, + "asset_type": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "uncategorized-category": true + } + }, + "9efb5e32-6db1-47c9-8d50-a81278831832": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ScreenExporter", + "type": "Screen", + "type_human": "Screen", + "type_plural": "Screens", + "type_human_plural": "Screens", + "last_modified_by": "Admin User", + "last_modified_by_id": 1, + "model": "ProcessMaker\\Models\\Screen", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [], + "name": "Nested", + "description": "Nested", + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 37, + "uuid": "9efb5e32-6db1-47c9-8d50-a81278831832", + "title": "Nested", + "description": "Nested", + "type": "FORM", + "config": "[{\"name\": \"Nested\", \"items\": [{\"uuid\": \"5c01f614-fc83-48b4-8a42-0e77c16fb334\", \"label\": \"Textarea\", \"config\": {\"icon\": \"fas fa-paragraph\", \"name\": \"form_text_area_1\", \"rows\": 2, \"label\": \"New Textarea v4\", \"helper\": null, \"currency\": {\"code\": \"USD\", \"name\": \"US Dollar\", \"format\": \"#,###.##\", \"symbol\": \"$\"}, \"readonly\": false, \"richtext\": false, \"validation\": [], \"placeholder\": null}, \"component\": \"FormTextArea\", \"inspector\": [{\"type\": \"FormInput\", \"field\": \"name\", \"config\": {\"name\": \"Variable Name\", \"label\": \"Variable Name\", \"helper\": \"A variable name is a symbolic name to reference information.\", \"validation\": \"dot_notation|required|not_in:null,break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,class,const,enum,export,extends,import,super,true,false\"}}, {\"type\": \"FormInput\", \"field\": \"label\", \"config\": {\"label\": \"Label\", \"helper\": \"The label describes the field's name\"}}, {\"type\": \"FormInput\", \"field\": \"placeholder\", \"config\": {\"label\": \"Placeholder Text\", \"helper\": \"The placeholder is what is shown in the field when no value is provided yet\"}}, {\"type\": \"FormInput\", \"field\": \"helper\", \"config\": {\"label\": \"Helper Text\", \"helper\": \"Help text is meant to provide additional guidance on the field's value\"}}, {\"type\": \"FormCheckbox\", \"field\": \"richtext\", \"config\": {\"label\": \"Rich Text\", \"helper\": null}}, {\"type\": \"ValidationSelect\", \"field\": \"validation\", \"config\": {\"label\": \"Validation Rules\", \"helper\": \"The validation rules needed for this field\"}}, {\"type\": \"FormInput\", \"field\": \"rows\", \"config\": {\"label\": \"Rows\", \"helper\": \"The number of rows to provide for input\", \"validation\": \"integer\"}}, {\"type\": \"FormCheckbox\", \"field\": \"readonly\", \"config\": {\"label\": \"Read Only\", \"helper\": null}}, {\"type\": \"ColorSelect\", \"field\": \"color\", \"config\": {\"label\": \"Text Color\", \"helper\": \"Set the element's text color\", \"options\": [{\"value\": \"text-primary\", \"content\": \"primary\"}, {\"value\": \"text-secondary\", \"content\": \"secondary\"}, {\"value\": \"text-success\", \"content\": \"success\"}, {\"value\": \"text-danger\", \"content\": \"danger\"}, {\"value\": \"text-warning\", \"content\": \"warning\"}, {\"value\": \"text-info\", \"content\": \"info\"}, {\"value\": \"text-light\", \"content\": \"light\"}, {\"value\": \"text-dark\", \"content\": \"dark\"}]}}, {\"type\": \"ColorSelect\", \"field\": \"bgcolor\", \"config\": {\"label\": \"Background Color\", \"helper\": \"Set the element's background color\", \"options\": [{\"value\": \"alert alert-primary\", \"content\": \"primary\"}, {\"value\": \"alert alert-secondary\", \"content\": \"secondary\"}, {\"value\": \"alert alert-success\", \"content\": \"success\"}, {\"value\": \"alert alert-danger\", \"content\": \"danger\"}, {\"value\": \"alert alert-warning\", \"content\": \"warning\"}, {\"value\": \"alert alert-info\", \"content\": \"info\"}, {\"value\": \"alert alert-light\", \"content\": \"light\"}, {\"value\": \"alert alert-dark\", \"content\": \"dark\"}]}}, {\"type\": \"default-value-editor\", \"field\": \"defaultValue\", \"config\": {\"label\": \"Default Value\", \"helper\": \"The default value is pre populated using the existing request data. This feature will allow you to modify the value displayed on screen load if needed.\"}}, {\"type\": \"FormInput\", \"field\": \"conditionalHide\", \"config\": {\"label\": \"Visibility Rule\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"DeviceVisibility\", \"field\": \"deviceVisibility\", \"config\": {\"label\": \"Device Visibility\", \"helper\": \"This control is hidden until this expression is true\"}}, {\"type\": \"FormInput\", \"field\": \"customFormatter\", \"config\": {\"label\": \"Custom Format String\", \"helper\": \"Use the Mask Pattern format
Date ##/##/####
SSN ###-##-####
Phone (###) ###-####\", \"validation\": null}}, {\"type\": \"FormInput\", \"field\": \"customCssSelector\", \"config\": {\"label\": \"CSS Selector Name\", \"helper\": \"Use this in your custom css rules\", \"validation\": \"regex: [-?[_a-zA-Z]+[_-a-zA-Z0-9]*]\"}}, {\"type\": \"FormInput\", \"field\": \"ariaLabel\", \"config\": {\"label\": \"Aria Label\", \"helper\": \"Attribute designed to help assistive technology (e.g. screen readers) attach a label\"}}, {\"type\": \"FormInput\", \"field\": \"tabindex\", \"config\": {\"label\": \"Tab Order\", \"helper\": \"Order in which a user will move focus from one control to another by pressing the Tab key\", \"validation\": \"regex: [0-9]*\"}}, {\"type\": \"EncryptedConfig\", \"field\": \"encryptedConfig\", \"config\": {\"label\": \"Encrypted\", \"helper\": null}}], \"editor-control\": \"FormTextArea\", \"editor-component\": \"FormTextArea\"}], \"order\": 1}]", + "computed": "[]", + "custom_css": null, + "created_at": "2025-05-23 18:22:23", + "updated_at": "2025-05-23 23:39:47", + "status": "ACTIVE", + "key": null, + "watchers": "[]", + "translations": null, + "is_template": 0, + "asset_type": null + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": { + "uncategorized-category": true + } + }, + "9efb6041-6bb5-4544-86b2-3a3a12d43f0a": { + "exporter": "ProcessMaker\\ImportExport\\Exporters\\ProcessLaunchpadExporter", + "type": "LaunchpadSetting", + "type_human": "Launchpad Setting", + "type_plural": "LaunchpadSettings", + "type_human_plural": "Launchpad Settings", + "last_modified_by": "", + "last_modified_by_id": null, + "model": "ProcessMaker\\Models\\ProcessLaunchpad", + "force_password_protect": false, + "hidden": false, + "mode": "update", + "saveAssetsMode": "saveAllAssets", + "explicit_discard": false, + "dependents": [ + { + "type": "user", + "uuid": "9ef10b5b-c0b8-4327-8c26-533c0e0c25b6", + "meta": null, + "exporterClass": "ProcessMaker\\ImportExport\\Exporters\\UserExporter", + "modelClass": "ProcessMaker\\Models\\User", + "fallbackMatches": { + "email": "admin@processmaker.com", + "username": "admin" + }, + "name": "", + "discard": false + } + ], + "name": "Setting", + "description": null, + "process_manager": "", + "process_manager_id": null, + "attributes": { + "id": 10, + "uuid": "9efb6041-6bb5-4544-86b2-3a3a12d43f0a", + "process_id": 25, + "user_id": 1, + "properties": "{\"icon\": \"Default Icon\", \"tabs\": [], \"screen_id\": 0, \"icon_label\": \"Default Icon\", \"screen_uuid\": \"\", \"screen_title\": \"Default Launchpad\", \"saved_chart_id\": 0, \"my_tasks_columns\": [], \"saved_chart_title\": \"Default Launchpad Chart\"}", + "created_at": "2025-05-23 18:28:08", + "updated_at": "2025-05-23 18:28:08" + }, + "extraAttributes": { + "translatedLanguages": [] + }, + "references": [] + } + } +} \ No newline at end of file From c16015e8a0e87d4148271ded8a48a7e1637079b1 Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Fri, 23 May 2025 20:46:01 -0400 Subject: [PATCH 042/142] Fix handling of empty non published nested screens --- ProcessMaker/Http/Resources/V1_1/TaskScreen.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ProcessMaker/Http/Resources/V1_1/TaskScreen.php b/ProcessMaker/Http/Resources/V1_1/TaskScreen.php index e053bc6d63..93a7e2d593 100644 --- a/ProcessMaker/Http/Resources/V1_1/TaskScreen.php +++ b/ProcessMaker/Http/Resources/V1_1/TaskScreen.php @@ -50,7 +50,8 @@ private function includeScreen($request) if (array_key_exists('nested', $array['screen'])) { foreach ($array['screen']['nested'] as &$nestedScreen) { $nestedScreen['config'] = $screenTranslation->applyTranslations(new ScreenVersionModel($nestedScreen)); - $nestedScreen['config'] = $this->removeInspectorMetadata($nestedScreen['config']); + // When Nested points to an empty screen (config = null in the database) we need to use an empty array + $nestedScreen['config'] = $this->removeInspectorMetadata($nestedScreen['config'] ?? []); } } } From b0587ade052808719257e346a4f77ebc280a64de Mon Sep 17 00:00:00 2001 From: David Callizaya Date: Fri, 23 May 2025 20:54:28 -0400 Subject: [PATCH 043/142] Add console command to clear screens cache --- .../Console/Commands/CacheScreensClear.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 ProcessMaker/Console/Commands/CacheScreensClear.php diff --git a/ProcessMaker/Console/Commands/CacheScreensClear.php b/ProcessMaker/Console/Commands/CacheScreensClear.php new file mode 100644 index 0000000000..e6bd54cb5f --- /dev/null +++ b/ProcessMaker/Console/Commands/CacheScreensClear.php @@ -0,0 +1,33 @@ +clearCompiledAssets(); + $this->info('Cache for screens cleared'); + } +} From c7db2d3d06667449931a27de34b7bded532b3479 Mon Sep 17 00:00:00 2001 From: Eleazar Resendez Date: Tue, 27 May 2025 13:07:41 -0600 Subject: [PATCH 044/142] fix(guided-template): override button width for deep-nested "btn-bx1" selector using ::v-deep --- resources/js/components/templates/WizardTemplateDetails.vue | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/js/components/templates/WizardTemplateDetails.vue b/resources/js/components/templates/WizardTemplateDetails.vue index c6c0aac979..28d39a1971 100644 --- a/resources/js/components/templates/WizardTemplateDetails.vue +++ b/resources/js/components/templates/WizardTemplateDetails.vue @@ -155,4 +155,8 @@ export default { margin-top: 2rem; margin-bottom: 2rem; } + +::v-deep(.custom-css-scope [selector="btn-bx1"] button) { + width: auto !important; +} \ No newline at end of file From 2259448da9b0b0ad536616e780d0147b932ce715 Mon Sep 17 00:00:00 2001 From: danloa Date: Wed, 28 May 2025 10:10:25 -0400 Subject: [PATCH 045/142] Filter available variable in saved search --- .../Http/Controllers/Api/V1_1/ProcessVariableController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php index d57af422fb..5e940ea759 100644 --- a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php +++ b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php @@ -210,6 +210,7 @@ public function getProcessesVariables(array $processIds, $excludeSavedSearch, $p { // Determine which columns to exclude based on the saved search $activeColumns = []; + $savedSearch = null; if ($excludeSavedSearch) { $savedSearch = SavedSearch::find($excludeSavedSearch); if ($savedSearch && $savedSearch->current_columns) { @@ -222,6 +223,7 @@ public function getProcessesVariables(array $processIds, $excludeSavedSearch, $p !class_exists(ProcessVariable::class) || !Schema::hasTable('process_variables') || !self::$useVarFinder + || $savedSearch ) { $paginator = $this->getProcessesVariablesFrom($processIds); if ($request->has('onlyAvailable')) { From 91cd020c690ccbe9bfd3d76a171662a96d570697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Bascop=C3=A9?= Date: Wed, 28 May 2025 12:50:01 -0400 Subject: [PATCH 046/142] added a fix upgrade to change nullable the encrypted field --- ...25_02_25_215239_encrypt_devlink_tokens.php | 4 +- ...259_validate_devlink_tokens_encryption.php | 118 ++++++++++++++++++ 2 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 upgrades/2025_05_27_183259_validate_devlink_tokens_encryption.php diff --git a/upgrades/2025_02_25_215239_encrypt_devlink_tokens.php b/upgrades/2025_02_25_215239_encrypt_devlink_tokens.php index 4d57db7742..7caecd7ee5 100644 --- a/upgrades/2025_02_25_215239_encrypt_devlink_tokens.php +++ b/upgrades/2025_02_25_215239_encrypt_devlink_tokens.php @@ -29,7 +29,7 @@ public function up() { // Change the column type to TEXT Schema::table('dev_links', function ($table) { - $table->text('client_secret')->change(); + $table->text('client_secret')->change()->nullable(); }); $devlinks = DB::table('dev_links')->get(); @@ -79,7 +79,7 @@ public function down() // Change the column type back to String Schema::table('dev_links', function ($table) { - $table->string('client_secret')->change(); + $table->string('client_secret')->change()->nullable(); }); } diff --git a/upgrades/2025_05_27_183259_validate_devlink_tokens_encryption.php b/upgrades/2025_05_27_183259_validate_devlink_tokens_encryption.php new file mode 100644 index 0000000000..89509321f1 --- /dev/null +++ b/upgrades/2025_05_27_183259_validate_devlink_tokens_encryption.php @@ -0,0 +1,118 @@ +Null === 'YES'; + + // If the column is not nullable, make the change + if (!$isNullable || $columnInfo->Type === 'varchar(255)') { + Schema::table('dev_links', function ($table) { + $table->text('client_secret')->change()->nullable(); + }); + } + + $devlinks = DB::table('dev_links')->get(); + + // Encrypt the client_secret, access_token, and refresh_token columns + foreach ($devlinks as $devlink) { + if (!$this->isEncrypted($devlink->client_secret)) { + DB::table('dev_links') + ->where('id', $devlink->id) + ->update([ + 'client_secret' => $devlink->client_secret ? Crypt::encrypt($devlink->client_secret) : null, + 'access_token' => $devlink->access_token ? Crypt::encrypt($devlink->access_token) : null, + 'refresh_token' => $devlink->refresh_token ? Crypt::encrypt($devlink->refresh_token) : null, + ]); + } + } + } + + /** + * Reverse the upgrade migration. + * + * @return void + */ + public function down() + { + // first decrypt the data + $devlinks = DB::table('dev_links')->get(); + + foreach ($devlinks as $devlink) { + try { + // try to decrypt the data + $decrypted = Crypt::decryptString($devlink->client_secret); + $decryptedAccess = Crypt::decryptString($devlink->access_token); + $decryptedRefresh = Crypt::decryptString($devlink->refresh_token); + DB::table('dev_links') + ->where('id', $devlink->id) + ->update([ + 'client_secret' => $decrypted, + 'access_token' => $decryptedAccess, + 'refresh_token' => $decryptedRefresh, + ]); + } catch (Exception $e) { + // if the decryption fails, assume the data is already decrypted + continue; + } + } + + // Change the column type back to String + Schema::table('dev_links', function ($table) { + $table->string('client_secret')->change()->nullable(); + }); + } + + /** + * Checks if the value is encrypted + * + * @param string $value + * @return bool + */ + private function isEncrypted($value) + { + if (empty($value)) { + return true; + } + + try { + Crypt::decryptString($value); + + return true; + } catch (Exception $e) { + return false; + } + } +} From ba13e1fef7dfad505443694b976449c926e86e3b Mon Sep 17 00:00:00 2001 From: danloa Date: Wed, 28 May 2025 14:09:54 -0400 Subject: [PATCH 047/142] Group logic to get variables when savedsearches are used --- .../Api/V1_1/ProcessVariableController.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php index 5e940ea759..f8d851b771 100644 --- a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php +++ b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php @@ -219,11 +219,22 @@ public function getProcessesVariables(array $processIds, $excludeSavedSearch, $p } // If the classes or tables do not exist, fallback to a saved search approach. + + if ($savedSearch && $request->has('onlyAvailable')) { + $paginator = $this->getProcessesVariablesFrom($processIds); + $availableColumns = $this->mergeAvailableColumns($savedSearch); + $availableColumns = $this->filterActiveColumns($availableColumns, $activeColumns); + $paginator->setCollection( + $availableColumns->merge($paginator->items()) + ); + + return $paginator; + } + if ( !class_exists(ProcessVariable::class) || !Schema::hasTable('process_variables') || !self::$useVarFinder - || $savedSearch ) { $paginator = $this->getProcessesVariablesFrom($processIds); if ($request->has('onlyAvailable')) { From 179bfcf8e33b67e813562f56ede9ac4ef9f681a5 Mon Sep 17 00:00:00 2001 From: sanja <52755494+sanjacornelius@users.noreply.github.com> Date: Wed, 28 May 2025 14:00:09 -0700 Subject: [PATCH 048/142] Ensure only admin roles can assign super admins and all permissions --- resources/views/admin/users/edit.blade.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/views/admin/users/edit.blade.php b/resources/views/admin/users/edit.blade.php index a437c46e22..aee8bc4df0 100644 --- a/resources/views/admin/users/edit.blade.php +++ b/resources/views/admin/users/edit.blade.php @@ -77,6 +77,7 @@