Repository: ProcessMaker/processmaker
Pull request #8163: FOUR-23362:S1 : Add in the setting the menu Iframe Whitelist Config
Author: login: fagubla
Labels: name: merge-epic
Created: 2025-04-01T21:25:37Z | Merged: 2025-04-02T22:15:55Z

## Issue & Reproduction Steps
Add the menu for white list

## Related Tickets & Packages
- https://processmaker.atlassian.net/browse/FOUR-23362
- https://processmaker.atlassian.net/browse/FOUR-23130
- https://processmaker.atlassian.net/browse/FOUR-23131

## Code Review Checklist
- [ ] I have pulled this code locally and tested it on my instance, along with any associated packages.
- [ ] This code adheres to [ProcessMaker Coding Guidelines](https://github.com/ProcessMaker/processmaker/wiki/Coding-Guidelines).
- [ ] This code includes a unit test or an E2E test that tests its functionality, or is covered by an existing test.
- [ ] This solution fixes the bug reported in the original ticket.
- [ ] This solution does not alter the expected output of a component in a way that would break existing Processes.
- [ ] This solution does not implement any breaking changes that would invalidate documentation or cause existing Processes to fail.
- [ ] This solution has been tested with enterprise packages that rely on its functionality and does not introduce bugs in those packages.
- [ ] This code does not duplicate functionality that already exists in the framework or in ProcessMaker.
- [ ] This ticket conforms to the PRD associated with this part of ProcessMaker.

ci:deploy


Diff:
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/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 = [];
diff --git a/resources/js/admin/settings/components/ModalWhiteList.vue b/resources/js/admin/settings/components/ModalWhiteList.vue
new file mode 100644
index 0000000000..15b6f6c639
--- /dev/null
+++ b/resources/js/admin/settings/components/ModalWhiteList.vue
@@ -0,0 +1,112 @@
+<template>
+  <b-modal
+    id="bv-modal-whitelist"
+    ref="bv-modal-whitelist"
+    hide-footer
+  >
+    <template #modal-title>
+      <div class="tw-self-stretch tw-text-base tw-font-medium tw-text-[#20242A]">
+        {{ $t("URL Creation") }}
+      </div>
+      <div class="tw-self-stretch tw-text-sm tw-text-[#596372]">
+        {{ $t("Please provide your information to create an account.") }}
+      </div>
+    </template>
+    <div class="tw-block">
+      <div class="form-group col-md-12">
+        <div class="d-flex flex-column">
+          <label for="site-name">
+            {{ $t("Site Name") }}
+          </label>
+          <b-form-input
+            id="site-name"
+            v-model="siteName"
+            type="text"
+            required
+            :state="stateSiteName"
+            :placeholder="$t('Site Name')"
+          />
+        </div>
+      </div>
+      <div class="form-group col-md-12">
+        <div class="d-flex flex-column">
+          <label for="url">
+            {{ $t("Site Name") }}
+          </label>
+          <b-form-input
+            id="url"
+            v-model="url"
+            type="text"
+            required
+            placeholder="http://www.sample.org/head"
+            :state="stateURL"
+          />
+        </div>
+      </div>
+    </div>
+    <div class="tw-block tw-mt-8">
+      <b-button
+        id="confirm"
+        type="submit"
+        variant="primary"
+        block
+        @click="addWhiteListURL"
+      >
+        {{ $t('Create') }}
+      </b-button>
+    </div>
+  </b-modal>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      siteName: "",
+      url: "",
+      stateSiteName: null,
+      stateURL: null,
+      groupName: "",
+      group_id: 3,
+    };
+  },
+  methods: {
+    show(groupName) {
+      this.clear();
+      this.groupName = groupName;
+      return this.$refs["bv-modal-whitelist"].show();
+    },
+    clear() {
+      this.siteName = "";
+      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}`,
+        format: "text",
+        config: this.url,
+        name: this.siteName,
+        group: this.groupName,
+        group_id: this.group_id,
+        hidden: false,
+        ui: null,
+      };
+      ProcessMaker.apiClient
+        .post("settings", data)
+        .then(() => {
+          this.$parent.refresh();
+          this.$refs["bv-modal-whitelist"].hide();
+        });
+    },
+  },
+};
+</script>
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 @@
         </div>
       </div>
     </div>
+    <modal-white-list ref="modal-whitelist" />
   </div>
 </template>
 
@@ -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 @@
 <?php
 
+use Box\Spout\Common\Entity\Row;
 use Illuminate\Support\Facades\Route;
 use ProcessMaker\Http\Controllers\Api\BookmarkController;
 use ProcessMaker\Http\Controllers\Api\ChangePasswordController;
@@ -321,6 +322,8 @@
         ->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..a74b9816cf
--- /dev/null
+++ b/upgrades/2025_03_31_140423_add_menu_frame_whitelist.php
@@ -0,0 +1,51 @@
+<?php
+
+use ProcessMaker\Models\Setting;
+use ProcessMaker\Models\SettingsMenus;
+use ProcessMaker\Upgrades\UpgradeMigration as Upgrade;
+
+class AddMenuFrameWhitelist extends Upgrade
+{
+    /**
+     * Run the upgrade migration.
+     *
+     * @return void
+     */
+    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' => $group_id,
+            '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()
+    {
+        //
+    }
+}


Reviews:
user: login: pmPaulis
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T00:06:09Z
user: login: fagubla
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T16:07:22Z
user: login: pmPaulis
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T19:03:03Z
user: login: fagubla
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T19:09:31Z
user: login: pmPaulis
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T22:12:42Z

Review comments:
diff_hunk: @@ -0,0 +1,49 @@
+<?php
+
+use ProcessMaker\Models\Setting;
+use ProcessMaker\Upgrades\UpgradeMigration as Upgrade;
+
+class AddMenuFrameWhitelist extends Upgrade
+{
+    /**
+     * Run the upgrade migration.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        $whiteListKey = [
+            'key' => 'white_list_frame',
+        ];
+        $whiteListOption = [
+            'format' => 'button',
+            'group' => 'IFrame Whitelist Config',
+            'group_id' => 3,
path: upgrades/2025_03_31_140423_add_menu_frame_whitelist.php
user: login: pmPaulis
body: Could you use this:
```
$groupId = SettingsMenus::getId(SettingsMenus::LOG_IN_AUTH_MENU_ORDER);
'group_id' => $groupId,
```

created_at: 2025-04-02T00:06:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,49 @@
+<?php
+
+use ProcessMaker\Models\Setting;
+use ProcessMaker\Upgrades\UpgradeMigration as Upgrade;
+
+class AddMenuFrameWhitelist extends Upgrade
+{
+    /**
+     * Run the upgrade migration.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        $whiteListKey = [
+            'key' => 'white_list_frame',
+        ];
+        $whiteListOption = [
+            'format' => 'button',
+            'group' => 'IFrame Whitelist Config',
+            'group_id' => 3,
path: upgrades/2025_03_31_140423_add_menu_frame_whitelist.php
user: login: fagubla
body: Solved
created_at: 2025-04-02T16:07:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,50 @@
+<?php
+
+use ProcessMaker\Models\Setting;
path: upgrades/2025_03_31_140423_add_menu_frame_whitelist.php
user: login: pmPaulis
body: add use use ProcessMaker\Models\SettingsMenus; for avoid errors
created_at: 2025-04-02T19:02:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,50 @@
+<?php
+
+use ProcessMaker\Models\Setting;
path: upgrades/2025_03_31_140423_add_menu_frame_whitelist.php
user: login: fagubla
body: done
created_at: 2025-04-02T19:09:31Z
author_association: CONTRIBUTOR

PR comments:
user: login: pmPaulis
created_at: 2025-04-02T15:40:50Z
body: http://ci-c5e7b0640e.engk8s.processmaker.net/login
author_association: CONTRIBUTOR
user: login: processmaker-sonarqube[bot]
created_at: 2025-04-02T19:54:13Z
body: ## [![Quality Gate passed](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v104/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarqube.processmaker.net/dashboard?id=ProcessMaker_processmaker_AYkzkfKexYvY_isvKt07&pullRequest=8163) **Quality Gate passed**  
Issues  
[![](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v104/common/passed-16px.png '')](https://sonarqube.processmaker.net/project/issues?id=ProcessMaker_processmaker_AYkzkfKexYvY_isvKt07&pullRequest=8163&resolved=false) [0 New issues](https://sonarqube.processmaker.net/project/issues?id=ProcessMaker_processmaker_AYkzkfKexYvY_isvKt07&pullRequest=8163&resolved=false)  
[![](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v104/common/accepted-16px.png '')](https://sonarqube.processmaker.net/project/issues?id=ProcessMaker_processmaker_AYkzkfKexYvY_isvKt07&pullRequest=8163&issueStatuses=ACCEPTED) [0 Accepted issues](https://sonarqube.processmaker.net/project/issues?id=ProcessMaker_processmaker_AYkzkfKexYvY_isvKt07&pullRequest=8163&issueStatuses=ACCEPTED)

Measures  
[![](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v104/common/passed-16px.png '')](https://sonarqube.processmaker.net/security_hotspots?id=ProcessMaker_processmaker_AYkzkfKexYvY_isvKt07&pullRequest=8163) [0 Security Hotspots](https://sonarqube.processmaker.net/security_hotspots?id=ProcessMaker_processmaker_AYkzkfKexYvY_isvKt07&pullRequest=8163)  
![](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v104/common/no-data-16px.png '') No data about Coverage  
![](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v104/common/no-data-16px.png '') No data about Duplication  
  
[See analysis details on SonarQube](https://sonarqube.processmaker.net/dashboard?id=ProcessMaker_processmaker_AYkzkfKexYvY_isvKt07&pullRequest=8163)


author_association: NONE