diff --git a/ProcessMaker/Http/Controllers/Api/DevLinkController.php b/ProcessMaker/Http/Controllers/Api/DevLinkController.php index e77f926d3e..a0d06ac138 100644 --- a/ProcessMaker/Http/Controllers/Api/DevLinkController.php +++ b/ProcessMaker/Http/Controllers/Api/DevLinkController.php @@ -223,7 +223,7 @@ public function addAsset(Request $request, Bundle $bundle) public function addSettings(Request $request, Bundle $bundle) { - $bundle->addSettings($request->input('setting'), $request->input('config')); + $bundle->addSettings($request->input('setting'), $request->input('config'), $request->input('type')); } public function addAssetToBundles(Request $request) @@ -238,6 +238,17 @@ public function addAssetToBundles(Request $request) } } + public function addSettingToBundles(Request $request) + { + $bundles = $request->input('bundles'); + foreach ($bundles as $id) { + $bundle = Bundle::find($id); + if ($bundle) { + $bundle->addSettingToBundles($request->input('setting'), $request->input('config')); + } + } + } + public function sharedAssets(Request $request) { return Setting::Where('group', 'Devlink')->get(); diff --git a/ProcessMaker/Models/Bundle.php b/ProcessMaker/Models/Bundle.php index 31112a4a8b..47e77ca390 100644 --- a/ProcessMaker/Models/Bundle.php +++ b/ProcessMaker/Models/Bundle.php @@ -9,6 +9,7 @@ use ProcessMaker\ImportExport\Logger; use ProcessMaker\ImportExport\Options; use ProcessMaker\Models\ProcessMakerModel; +use ProcessMaker\Models\SettingsMenus; use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\InteractsWithMedia; @@ -138,18 +139,52 @@ public function addAsset(ProcessMakerModel $asset) ]); } - public function addSettings($setting, $config) + public function addSettings($setting, $newId, $type = null) { - $exists = $this->settings()->where('setting', $setting)->exists(); - if ($exists) { - $this->settings()->where('setting', $setting)->update([ - 'config' => $config, + $existingSetting = $this->settings()->where('setting', $setting)->first(); + + if ($existingSetting) { + // If the config is null, do not add the new ID + if (is_null($existingSetting->config)) { + return; + } + + // Decode the existing JSON + $config = json_decode($existingSetting->config, true); + + // Ensure 'id' is an array + if (!isset($config['id']) || !is_array($config['id'])) { + $config['id'] = []; + } + + // Add the new ID + $config['id'][] = $newId; + + // Remove duplicates + $config['id'] = array_unique($config['id']); + + // Update the config + $existingSetting->update([ + 'config' => json_encode($config), ]); } else { + // Create a new BundleSetting with the initial ID + $config = ['id' => []]; + if ($newId) { + $config['id'][] = $newId; + } + + if ($type) { + $settingsMenu = SettingsMenus::where('menu_group', $setting)->first(); + if ($settingsMenu) { + $config['id'] = $settingsMenu->id; + } + } + BundleSetting::create([ 'bundle_id' => $this->id, 'setting' => $setting, - 'config' => $config, + 'config' => json_encode($config), ]); } } @@ -166,6 +201,18 @@ public function addAssetToBundles(ProcessMakerModel $asset) return $message; } + public function addSettingToBundles($setting, $newId) + { + $message = null; + try { + $this->addSettings($setting, $newId); + } catch (ValidationException $ve) { + $message = $ve->getMessage(); + } + + return $message; + } + public function validateEditable() { if (!$this->editable()) { diff --git a/ProcessMaker/Models/Setting.php b/ProcessMaker/Models/Setting.php index e6167a2517..9119959002 100644 --- a/ProcessMaker/Models/Setting.php +++ b/ProcessMaker/Models/Setting.php @@ -143,7 +143,7 @@ public static function messages() * * @param string $key * - * @return \ProcessMaker\Models\Setting|null + * @return Setting|null * @throws \Exception */ public static function byKey(string $key) @@ -382,7 +382,7 @@ public static function getFavicon() */ public static function groupsByMenu($menuId) { - $query = Setting::query() + $query = self::query() ->select('group') ->groupBy('group') ->where('group_id', $menuId) @@ -409,7 +409,7 @@ public static function groupsByMenu($menuId) */ public static function updateSettingsGroup($settingsGroup, $id) { - Setting::where('group', $settingsGroup)->whereNull('group_id')->chunk( + self::where('group', $settingsGroup)->whereNull('group_id')->chunk( 50, function ($settings) use ($id) { foreach ($settings as $setting) { @@ -429,7 +429,7 @@ function ($settings) use ($id) { */ public static function updateAllSettingsGroupId() { - Setting::whereNull('group_id')->chunk(100, function ($settings) { + self::whereNull('group_id')->chunk(100, function ($settings) { $defaultId = SettingsMenus::EMAIL_MENU_GROUP; foreach ($settings as $setting) { // Define the value of 'menu_group' based on 'group' @@ -466,6 +466,9 @@ public static function updateAllSettingsGroupId() case 'System': // System not related with settings menu $id = null; break; + case 'Devlink': + $id = null; + break; default: // The default value if (preg_match('/^Email Server/', $setting->group)) { $id = SettingsMenus::getId(SettingsMenus::EMAIL_MENU_GROUP); diff --git a/resources/js/admin/auth-clients/components/AuthClientsListing.vue b/resources/js/admin/auth-clients/components/AuthClientsListing.vue index 34702a07ee..f69e7a6db4 100644 --- a/resources/js/admin/auth-clients/components/AuthClientsListing.vue +++ b/resources/js/admin/auth-clients/components/AuthClientsListing.vue @@ -29,6 +29,7 @@ @navigate="onNavigate" :actions="actions" :data="props.rowData" + :permission="permission" :divider="true" /> @@ -46,6 +47,10 @@ {{ props.rowData.secret.substr(0, 10) }}... + @@ -46,6 +47,10 @@ const props = defineProps({ title: { type: String, required: true + }, + type: { + type: String, + required: false } }); diff --git a/resources/js/admin/devlink/components/BundleDetail.vue b/resources/js/admin/devlink/components/BundleDetail.vue index 8a34d94d81..2909f60486 100644 --- a/resources/js/admin/devlink/components/BundleDetail.vue +++ b/resources/js/admin/devlink/components/BundleDetail.vue @@ -91,6 +91,7 @@ :values="bundle.settings" @config-change="handleConfigChange" title="Settings" + type="settings" /> { ProcessMaker.apiClient.post(`devlink/local-bundles/${bundle.value.id}/add-settings`, { setting: event.key, config: null, + type: event.type, }) .then(() => { loadAssets(); diff --git a/resources/js/admin/devlink/components/platformConfigurations.js b/resources/js/admin/devlink/components/platformConfigurations.js index 1f78d359c4..61d477b21c 100644 --- a/resources/js/admin/devlink/components/platformConfigurations.js +++ b/resources/js/admin/devlink/components/platformConfigurations.js @@ -16,12 +16,16 @@ export default [ name: "UI Settings", }, { - type: "script_executors", - name: "Script Executors", + type: "ui_dashboards", + name: "Dashboards", + }, + { + type: "ui_menus", + name: "Menus", }, { - type: "analytics_settings", - name: "Analytics Settings", + type: "script_executors", + name: "Script Executors", }, { type: "public_files", diff --git a/resources/js/admin/devlink/components/settings.js b/resources/js/admin/devlink/components/settings.js index 3d7593c7da..c961cbefc8 100644 --- a/resources/js/admin/devlink/components/settings.js +++ b/resources/js/admin/devlink/components/settings.js @@ -1,18 +1,18 @@ export default [ { - type: "email_settings", + type: "Email", name: "Email Settings", }, { - type: "log-in_auth", + type: "Log-In & Auth", name: "Log-in & Auth", }, { - type: "user_settings", + type: "User Settings", name: "User Settings", }, { - type: "integrations", + type: "Integrations", name: "Integrations", }, ]; diff --git a/resources/js/admin/groups/components/GroupsListing.vue b/resources/js/admin/groups/components/GroupsListing.vue index 34ab46d279..695ae82fbe 100644 --- a/resources/js/admin/groups/components/GroupsListing.vue +++ b/resources/js/admin/groups/components/GroupsListing.vue @@ -33,6 +33,10 @@ /> + + + + + import datatableMixin from "../../components/common/mixins/datatable"; import dataLoadingMixin from "../../components/common/mixins/apiDataLoading"; +import AddToBundle from "../../components/shared/AddToBundle.vue"; import { createUniqIdsMixin } from "vue-uniq-ids"; const uniqIdsMixin = createUniqIdsMixin(); export default { mixins: [datatableMixin, dataLoadingMixin, uniqIdsMixin], + components: { AddToBundle }, props: ["filter", "permission"], data() { return { @@ -508,6 +523,9 @@ export default { this.loading = false; }); }, + onAddToBundle(data) { + this.$root.$emit('add-to-bundle', data); + }, }, }; diff --git a/resources/js/admin/settings/components/SettingsMenuCollapse.vue b/resources/js/admin/settings/components/SettingsMenuCollapse.vue index 13cec06982..6f5d978ef2 100644 --- a/resources/js/admin/settings/components/SettingsMenuCollapse.vue +++ b/resources/js/admin/settings/components/SettingsMenuCollapse.vue @@ -20,11 +20,18 @@ aria-expanded="false" :aria-controls="`collapseOne${menu.id}`" > + {{ menu.menu_group }} - + @@ -50,11 +57,20 @@ + @@ -223,3 +248,10 @@ export default { font-weight: 700; } + diff --git a/resources/js/admin/users/components/UsersListing.vue b/resources/js/admin/users/components/UsersListing.vue index 313966ada7..c5daaf0af4 100644 --- a/resources/js/admin/users/components/UsersListing.vue +++ b/resources/js/admin/users/components/UsersListing.vue @@ -36,6 +36,10 @@ /> + { selected.value = null; error.value = null; assetId.value = data.id; + assetName.value = data.name || data.title; modal.value.show(); }); const save = (event) => { event.preventDefault(); if (selected.value?.id) { - const asset = { - 'type': props.assetType, - 'id': assetId.value - }; - window.ProcessMaker.apiClient.post(`devlink/local-bundles/${selected.value.id}/add-assets`, asset).then(() => { - modal.value.hide(); - window.ProcessMaker.alert(vue.$t('Asset added to bundle'), 'success'); - }).catch(e => { - error.value = e.response?.data?.error?.message || e.message; - }) + if (props.setting) { + window.ProcessMaker.apiClient.post(`devlink/local-bundles/${selected.value.id}/add-settings`, { + setting: props.assetType, + config: null, + }) + .then(() => { + window.ProcessMaker.alert(vue.$t('Setting added to bundle'), 'success'); + }); + } else { + const asset = { + 'type': props.assetType, + 'id': assetId.value + }; + window.ProcessMaker.apiClient.post(`devlink/local-bundles/${selected.value.id}/add-assets`, asset).then(() => { + modal.value.hide(); + window.ProcessMaker.alert(vue.$t('Asset added to bundle'), 'success'); + }).catch(e => { + error.value = e.response?.data?.error?.message || e.message; + }) + } } if (Array.isArray(selected.value)) { let bundles = []; selected.value.forEach((item) => { bundles.push(item.id); }); - const asset = { - 'type': props.assetType, - 'id': assetId.value, - 'bundles': bundles - }; - window.ProcessMaker.apiClient.post(`devlink/local-bundles/add-asset-to-bundles`, asset).then(() => { - modal.value.hide(); - window.ProcessMaker.alert(vue.$t('Asset added to bundle'), 'success'); - }).catch(e => { - error.value = e.response?.data?.error?.message || e.message; - }); + if (props.setting) { + const setting = { + 'setting': props.assetType, + 'config': assetId.value, + 'bundles': bundles + }; + window.ProcessMaker.apiClient.post(`devlink/local-bundles/add-setting-to-bundles`, setting).then(() => { + modal.value.hide(); + window.ProcessMaker.alert(vue.$t('Setting added to bundle'), 'success'); + }).catch(e => { + error.value = e.response?.data?.error?.message || e.message; + }); + } else { + const asset = { + 'type': props.assetType, + 'id': assetId.value, + 'bundles': bundles + }; + window.ProcessMaker.apiClient.post(`devlink/local-bundles/add-asset-to-bundles`, asset).then(() => { + modal.value.hide(); + window.ProcessMaker.alert(vue.$t('Asset added to bundle'), 'success'); + }).catch(e => { + error.value = e.response?.data?.error?.message || e.message; + }); + } } }; @@ -71,7 +100,7 @@ const save = (event) => { >

{{ vue.$t('The asset') }} - {{ vue.$t('Marketing screens') }} + {{ assetName }} {{ vue.$t('can be added to one or various bundles.') }}

{{ vue.$t('Bundles') }} @@ -96,4 +125,4 @@ const save = (event) => { .add-to-bundle-modal .modal-footer { border-top: none; } - \ No newline at end of file + diff --git a/resources/views/admin/auth-clients/index.blade.php b/resources/views/admin/auth-clients/index.blade.php index 39651acf3c..926a39e92b 100644 --- a/resources/views/admin/auth-clients/index.blade.php +++ b/resources/views/admin/auth-clients/index.blade.php @@ -68,7 +68,7 @@
- +
diff --git a/routes/api.php b/routes/api.php index 9c465e653c..cef69151f8 100644 --- a/routes/api.php +++ b/routes/api.php @@ -399,6 +399,7 @@ Route::post('devlink/local-bundles/{bundle}/add-assets', [DevLinkController::class, 'addAsset'])->name('devlink.add-asset'); Route::post('devlink/local-bundles/{bundle}/add-settings', [DevLinkController::class, 'addSettings'])->name('devlink.add-settings'); Route::post('devlink/local-bundles/add-asset-to-bundles', [DevLinkController::class, 'addAssetToBundles'])->name('devlink.add-asset-to-bundles'); + Route::post('devlink/local-bundles/add-setting-to-bundles', [DevLinkController::class, 'addSettingToBundles'])->name('devlink.add-setting-to-bundles'); Route::delete('devlink/local-bundles/{bundle}', [DevLinkController::class, 'deleteBundle'])->name('devlink.delete-bundle'); Route::delete('devlink/local-bundles/assets/{bundle_asset}', [DevLinkController::class, 'deleteBundleAsset'])->name('devlink.delete-bundle-asset'); Route::delete('devlink/local-bundles/settings/{bundle_setting}', [DevLinkController::class, 'deleteBundleSetting'])->name('devlink.delete-bundle-setting');