From 1972b461e7be8e5d69ef8f95624ce81f9006fe3c Mon Sep 17 00:00:00 2001 From: chfhtw Date: Mon, 23 Mar 2026 11:21:15 +0100 Subject: [PATCH] replace controllers/dashboard/Config.php with controllers/api/frontend/v1/dashboard/User.php & controllers/api/frontend/v1/dashboard/DashboardAdmin.php --- .../frontend/v1/dashboard/DashboardAdmin.php | 6 - .../api/frontend/v1/dashboard/User.php | 152 ++++++++++++ application/controllers/dashboard/Config.php | 216 ------------------ public/js/api/factory/dashboard/user.js | 45 ++++ public/js/components/Dashboard/Dashboard.js | 194 +++++++--------- 5 files changed, 285 insertions(+), 328 deletions(-) create mode 100644 application/controllers/api/frontend/v1/dashboard/User.php delete mode 100644 application/controllers/dashboard/Config.php create mode 100644 public/js/api/factory/dashboard/user.js diff --git a/application/controllers/api/frontend/v1/dashboard/DashboardAdmin.php b/application/controllers/api/frontend/v1/dashboard/DashboardAdmin.php index 31e73b6ba..af844b29e 100644 --- a/application/controllers/api/frontend/v1/dashboard/DashboardAdmin.php +++ b/application/controllers/api/frontend/v1/dashboard/DashboardAdmin.php @@ -32,15 +32,9 @@ class DashboardAdmin extends FHCAPI_Controller 'createDashboard' => 'dashboard/admin:rw', 'updateDashboard' => 'dashboard/admin:rw', 'deleteDashboard' => 'dashboard/admin:rw', - 'index' => 'dashboard/benutzer:r', - 'dummy' => 'dashboard/benutzer:r', - 'genWidgetId' => 'dashboard/benutzer:rw', 'addWidgetsToPreset' => 'dashboard/admin:rw', 'removeWidgetFromPreset' => 'dashboard/admin:rw', - 'addWidgetsToUserOverride' => 'dashboard/benutzer:rw', - 'removeWidgetFromUserOverride' => 'dashboard/benutzer:rw', 'funktionen' => 'dashboard/admin:r', - 'preset' => 'dashboard/admin:r', 'presetBatch' => 'dashboard/admin:r' ]); diff --git a/application/controllers/api/frontend/v1/dashboard/User.php b/application/controllers/api/frontend/v1/dashboard/User.php new file mode 100644 index 000000000..1242232a2 --- /dev/null +++ b/application/controllers/api/frontend/v1/dashboard/User.php @@ -0,0 +1,152 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * This controller operates between (interface) the JS (GUI) and the back-end + * Provides data to the ajax get calls about the users dashboard + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class User extends FHCAPI_Controller +{ + public function __construct() + { + parent::__construct([ + 'get' => 'dashboard/benutzer:r', + 'addWidget' => 'dashboard/benutzer:rw', + 'removeWidget' => 'dashboard/benutzer:rw' + ]); + + // Libraries + $this->load->library('dashboard/DashboardLib'); + + // Models + $this->load->model('ressource/Funktion_model', 'FunktionModel'); + } + + public function get($dashboard_kurzbz) + { + $dashboard = $this->dashboardlib->getDashboardByKurzbz($dashboard_kurzbz); + if (!$dashboard) + show_404(); + + $uid = $this->authlib->getAuthObj()->username; + + /*$mergedconfig = $this->dashboardlib->getMergedConfig($dashboard->dashboard_id, $uid); + + $this->terminateWithSuccess([ + 'general' => call_user_func_array( + 'array_merge_recursive', + $mergedconfig + ) + ]);*/ + $defaultconfig = $this->dashboardlib->getDefaultConfig($dashboard->dashboard_id, $uid); + $userconfig = $this->dashboardlib->getUserConfig($dashboard->dashboard_id, $uid); + + $defaultconfig_squashed = $defaultconfig ? call_user_func_array('array_merge_recursive', $defaultconfig) : []; + $userconfig_squashed = $userconfig ? call_user_func_array('array_merge_recursive', $userconfig) : []; + + $mergedconfig = array_replace_recursive($defaultconfig_squashed, $userconfig_squashed); + + $this->terminateWithSuccess([ + DashboardLib::SECTION_IF_FUNKTION_KURZBZ_IS_NULL => $mergedconfig + ]); + } + + public function addWidget() + { + $this->terminateWithValidationErrors(['test' => 'Test']); + $this->load->library('form_validation'); + + $this->form_validation->set_rules('dashboard', 'Dashboard', 'required'); + $this->form_validation->set_rules('widget[widget]', 'Widget', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $widget = $this->input->post('widget'); + $dashboard_kurzbz = $this->input->post('dashboard'); + $uid = $this->authlib->getAuthObj()->username; + + if (!isset($widget['widgetid'])) + $widget['widgetid'] = $this->dashboardlib->generateWidgetId($dashboard_kurzbz); + + $override = $this->dashboardlib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid); + + $override_decoded = json_decode($override->override, true); + + if (!isset($override_decoded['general']) || !is_array($override_decoded['general'])) + $override_decoded['general'] = []; + + if (!isset($override_decoded['general']['widgets'])) + $override_decoded['general']['widgets'] = []; + + $override_decoded['general']['widgets'][$widget['widgetid']] = $widget; + + $override->override = json_encode($override_decoded); + + $result = $this->dashboardlib->insertOrUpdateOverride($override); + + $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($widget['widgetid']); + } + + public function removeWidget() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('dashboard', 'Dashboard', 'required'); + $this->form_validation->set_rules('widget', 'Widget', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $widget_id = $this->input->post('widget'); + $dashboard_kurzbz = $this->input->post('dashboard'); + $uid = $this->authlib->getAuthObj()->username; + + $override = $this->dashboardlib->getOverride($dashboard_kurzbz, $uid); + if (empty($override)) + show_404(); + + $override_decoded = json_decode($override->override, true); + + foreach (array_keys($override_decoded) as $k) { + if (!isset($override_decoded[$k]["widgets"])) { + unset($override_decoded[$k]); + continue; + } + if (isset($override_decoded[$k]["widgets"][$widget_id])) { + unset($override_decoded[$k]["widgets"][$widget_id]); + } + if (!$override_decoded[$k]["widgets"]) { + unset($override_decoded[$k]); + } + } + + $override->override = json_encode($override_decoded); + + $result = $this->dashboardlib->insertOrUpdateOverride($override); + + $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess(); + } +} diff --git a/application/controllers/dashboard/Config.php b/application/controllers/dashboard/Config.php deleted file mode 100644 index f6db9509f..000000000 --- a/application/controllers/dashboard/Config.php +++ /dev/null @@ -1,216 +0,0 @@ - 'dashboard/benutzer:r', - 'dummy' => 'dashboard/benutzer:r', - 'genWidgetId' => 'dashboard/benutzer:rw', - 'addWidgetsToPreset' => 'dashboard/admin:rw', - 'removeWidgetFromPreset' => 'dashboard/admin:rw', - 'addWidgetsToUserOverride' => 'dashboard/benutzer:rw', - 'removeWidgetFromUserOverride' => 'dashboard/benutzer:rw', - 'funktionen' => 'dashboard/admin:r', - 'preset' => 'dashboard/admin:r', - 'presetBatch' => 'dashboard/admin:r' - ) - ); - - $this->load->library('dashboard/DashboardLib', null, 'DashboardLib'); - $this->load->library('AuthLib', null, 'AuthLib'); - $this->load->model('ressource/Funktion_model', 'FunktionModel'); - } - - public function index() - { - $dashboard_kurzbz = $this->input->get('db'); - $uid = $this->AuthLib->getAuthObj()->username; - - $dashboard = $this->DashboardLib->getDashboardByKurzbz($dashboard_kurzbz); - if(!$dashboard) { - http_response_code(404); - $this->terminateWithJsonError(array( - 'error' => 'Dashboard ' . $dashboard_kurzbz . ' not found.' - )); - } - - $mergedconfig = $this->DashboardLib->getMergedConfig($dashboard->dashboard_id, $uid); - $this->outputJsonSuccess($mergedconfig); - } - - public function genWidgetId() - { - $dashboard_kurzbz = $this->input->get('db'); - $widgetid = $this->DashboardLib->generateWidgetId($dashboard_kurzbz); - $this->outputJsonSuccess(array( - 'widgetid' => $widgetid - )); - } - - public function addWidgetsToPreset() - { - $input = json_decode($this->input->raw_input_stream); - $dashboard_kurzbz = $input->db; - $funktion_kurzbz = $input->funktion_kurzbz; - - $preset = $this->DashboardLib->getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz); - - $preset_decoded = json_decode($preset->preset, true); - - $this->DashboardLib->addWidgetsToWidgets($preset_decoded, $dashboard_kurzbz, $funktion_kurzbz, $input->widgets); - - $preset->preset = json_encode($preset_decoded); - - $result = $this->DashboardLib->insertOrUpdatePreset($preset); - if (isError($result)) { - http_response_code(500); - $this->terminateWithJsonError('preset could not be saved'); - } - - $this->outputJsonSuccess(array('msg' => 'preset successfully stored.', 'data' => $preset_decoded)); - } - - public function removeWidgetFromPreset() - { - $input = json_decode($this->input->raw_input_stream); - $dashboard_kurzbz = $input->db; - $funktion_kurzbz = $input->funktion_kurzbz; - $widgetid = $input->widgetid; - - $preset = $this->DashboardLib->getPreset($dashboard_kurzbz, $funktion_kurzbz); - if ($preset === null) { - http_response_code(404); - $this->terminateWithJsonError('preset for dashboard ' . $dashboard_kurzbz . ' and funktion ' . $funktion_kurzbz . ' not found.'); - } - - $preset_decoded = json_decode($preset->preset, true); - if (!$this->DashboardLib->removeWidgetFromWidgets($preset_decoded, $funktion_kurzbz, $widgetid)) - { - http_response_code(404); - $this->terminateWithJsonError('widgetid ' . $widgetid . ' not found'); - } - - $preset->preset = json_encode($preset_decoded); - $result = $this->DashboardLib->insertOrUpdatePreset($preset); - if (isError($result)) - { - http_response_code(500); - $this->terminateWithJsonError('failed to remove widget'); - } - $this->outputJsonSuccess(array('msg' => 'preset successfully updated.')); - } - - public function addWidgetsToUserOverride() - { - $input = json_decode($this->input->raw_input_stream); - $dashboard_kurzbz = $input->db; - $funktion_kurzbz = $input->funktion_kurzbz; - $uid = $this->AuthLib->getAuthObj()->username; - - $override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid); - - $override_decoded = json_decode($override->override, true); - - $this->DashboardLib->addWidgetsToWidgets($override_decoded, $dashboard_kurzbz, $funktion_kurzbz, $input->widgets); - - $override->override = json_encode($override_decoded); - - $result = $this->DashboardLib->insertOrUpdateOverride($override); - if (isError($result)) { - http_response_code(500); - $this->terminateWithJsonError('override could not be saved'); - } - - $this->outputJsonSuccess(array('msg' => 'override successfully stored.', 'data' => $override_decoded)); - } - - public function removeWidgetFromUserOverride() - { - $input = json_decode($this->input->raw_input_stream); - $dashboard_kurzbz = $input->db; - $funktion_kurzbz = $input->funktion_kurzbz; - $uid = $this->AuthLib->getAuthObj()->username; - $widgetid = $input->widgetid; - - $override = $this->DashboardLib->getOverride($dashboard_kurzbz, $uid); - if (empty($override)) { - http_response_code(404); - $this->terminateWithJsonError('userconfig for dashboard ' . $dashboard_kurzbz . ' not found.'); - } - - $override_decoded = json_decode($override->override, true); - - if (!$this->DashboardLib->removeWidgetFromWidgets($override_decoded, $funktion_kurzbz, $widgetid)) - { - http_response_code(404); - $this->terminateWithJsonError('widgetid ' . $widgetid . ' not found'); - } - - $override->override = json_encode($override_decoded); - $result = $this->DashboardLib->insertOrUpdateOverride($override, $uid); - if (isError($result)) - { - http_response_code(500); - $this->terminateWithJsonError('failed to remove widget'); - } - $this->outputJsonSuccess(array('msg' => 'override successfully updated.')); - } - - public function funktionen() - { - $funktionen = $this->FunktionModel->load(); - - if (isError($funktionen)) { - http_response_code(404); - $this->terminateWithJsonError([ - 'error' => getError($funktionen) - ]); - } - - return $this->outputJsonSuccess(getData($funktionen) ?: []); - } - - public function preset() - { - $db = $this->input->get('db'); - $funktion = $this->input->get('funktion'); - - $conf = $this->DashboardLib->getPreset($db, $funktion); - - if (!$conf) - return $this->outputJsonSuccess(['widgets' => [$funktion => []]]); - - return $this->outputJsonSuccess(json_decode($conf->preset, true)); - } - - public function presetBatch() - { - $db = $this->input->get('db'); - $funktionen = $this->input->get('funktionen'); - $result = []; - - foreach ($funktionen as $funktion) { - $conf = $this->DashboardLib->getPreset($db, $funktion); - if ($conf) - { - $preset = json_decode($conf->preset, true); - if (!isset($preset[$funktion]) || !isset($preset[$funktion]['widgets'])) - $result[$funktion] = []; - else - $result[$funktion] = $preset[$funktion]['widgets']; - } - else - $result[$funktion] = []; - } - - return $this->outputJsonSuccess($result); - } -} diff --git a/public/js/api/factory/dashboard/user.js b/public/js/api/factory/dashboard/user.js new file mode 100644 index 000000000..e660d077e --- /dev/null +++ b/public/js/api/factory/dashboard/user.js @@ -0,0 +1,45 @@ +/** + * Copyright (C) 2026 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export default { + get(dashboard) { + return { + method: 'get', + url: '/api/frontend/v1/dashboard/user/get/' + dashboard + }; + }, + addWidget(dashboard, widget) { + return { + method: 'post', + url: '/api/frontend/v1/dashboard/user/addWidget', + params: { + dashboard, + widget + } + }; + }, + removeWidget(dashboard, widget) { + return { + method: 'post', + url: '/api/frontend/v1/dashboard/user/removeWidget', + params: { + dashboard, + widget + } + }; + } +}; \ No newline at end of file diff --git a/public/js/components/Dashboard/Dashboard.js b/public/js/components/Dashboard/Dashboard.js index 3ed8aac09..497564fa5 100644 --- a/public/js/components/Dashboard/Dashboard.js +++ b/public/js/components/Dashboard/Dashboard.js @@ -3,6 +3,7 @@ import DashboardWidgetPicker from "./Widget/Picker.js"; import ObjectUtils from "../../helpers/ObjectUtils.js"; import ApiDashboardWidget from '../../api/factory/dashboard/widget.js'; +import ApiDashboardUser from '../../api/factory/dashboard/user.js'; export default { name: 'Dashboard', @@ -26,9 +27,10 @@ export default { }, data() { return { - sections: [], + widgets: [], + originalWidgets: {}, widgetsSetup: null, - editMode: false, + editMode: false } }, provide() { @@ -38,98 +40,83 @@ export default { timezone: Vue.computed(() => this.viewData.timezone) } }, - computed: { - apiurl() { - return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard'; - } - }, methods: { widgetAdd(section_name, widget) { - this.$refs.widgetpicker.getWidget().then(widget_id => { - widget.widget = widget_id; - widget.id = 'loading_' + String((new Date()).valueOf()); - let loading = {...widget}; - loading.loading = true; - this.sections.forEach(section => { - if (section.name == section_name) - section.widgets.push(loading); - }); - - axios.post(this.apiurl + '/Config/addWidgetsToUserOverride', { - db: this.dashboard, - funktion_kurzbz: section_name, - widgets: [widget] - }).then(result => { - let newId = Object.keys(result.data.retval.data[section_name].widgets).pop(); - widget.id = newId; - this.sections.forEach(section => { - if (section.name == section_name) { - section.widgets.splice(section.widgets.indexOf(loading),1); - section.widgets.push(widget); - } - }); - }).catch(error => { - console.error('ERROR: ', error); - alert('ERROR: ' + error.response.data.retval); - }); - }).catch(() => {}); + // TODO(chris): remove section_name? (change order of params => get rid of it) + this.$refs.widgetpicker + .getWidget() + .then(widget_id => { + widget.widget = widget_id; + widget.id = 'loading_' + String((new Date()).valueOf()); + let loading = { ...widget }; + loading.loading = true; + this.widgets.push(loading); + + this.$api + .call(ApiDashboardUser.addWidget(this.dashboard, widget)) + .then(result => { + widget.id = result.data; + this.widgets.splice(this.widgets.indexOf(loading), 1); + this.widgets.push(widget); + this.originalWidgets[widget.id] = structuredClone(ObjectUtils.deepToRaw(widget)); + }) + .catch(this.$fhcAlert.handleSystemError); + }) + .catch(() => {}); }, widgetUpdate(section_name, payload) { payload = payload[section_name]; for (var k in payload) { - for (var i in this.sections) { - if (this.sections[i].name == section_name) { - for (var wid in this.sections[i].widgets) { - if (this.sections[i].widgets[wid].id == k) { - payload[k] = ObjectUtils.mergeDeep(this.sections[i].widgets[wid], payload[k]); - // NOTE(chris): remove internal props - for (var prop in {_x:1,_y:1,_w:1,_h:1,index:1,id:1,preset:1}) - if (payload[k][prop]) - delete payload[k][prop]; - break; - } - } + for (var wid in this.widgets) { + if (this.widgets[wid].id == k) { + payload[k] = ObjectUtils.mergeDeep(this.widgets[wid], payload[k]); + // NOTE(chris): remove internal props + for (var prop of ['_x','_y','_w','_h','index','id','preset']) + if (payload[k][prop]) + delete payload[k][prop]; break; } } payload[k].widgetid = k; } - axios.post(this.apiurl + '/Config/addWidgetsToUserOverride', { - db: this.dashboard, - funktion_kurzbz: section_name, - widgets: payload - }).then(() => { - this.sections.forEach(section => { - if (section.name == section_name) { - section.widgets.forEach((widget, i) => { - if (payload[widget.id]) { - payload[widget.id].id = widget.id; - payload[widget.id].index = widget.index; - section.widgets[i] = payload[widget.id]; + this.$api + .call(Object.entries(payload).map(([key, widget]) => [key, ApiDashboardUser.addWidget(this.dashboard, widget)])) + .then(result => { + const failed = result + .filter(o => o.status == 'rejected') + .map(o => o.reason.config.errorHeader); + + this.widgets.forEach((widget, i) => { + if (failed.includes(widget.id)) { + this.widgets[i] = structuredClone(ObjectUtils.deepToRaw(this.originalWidgets[widget.id])); + /** NOTE(chris): if you wanna hide or unhide a + * preset and it fails: switch around the hidden + * value to revert it properly (checkboxes can't + * really handle it otherwise) + */ + if (payload[widget.id].hidden !== undefined) { + this.widgets[i].hidden = payload[widget.id].hidden; + this.$nextTick(() => { + this.widgets[i] = structuredClone(ObjectUtils.deepToRaw(this.originalWidgets[widget.id])); + }); } - }); - } - }); - }).catch(error => { - // TODO(chris): revert placement on failure - console.error('ERROR: ', error); - alert('ERROR: ' + error.response.data.retval); - }); + } else if (payload[widget.id]) { + payload[widget.id].id = widget.id; + payload[widget.id].index = widget.index; + this.widgets[i] = payload[widget.id]; + this.originalWidgets[widget.id] = structuredClone(ObjectUtils.deepToRaw(this.widgets[i])); + } + }); + }) + .catch(this.$fhcAlert.handleSystemError); }, widgetRemove(section_name, id) { - axios.post(this.apiurl + '/Config/removeWidgetFromUserOverride', { - db: this.dashboard, - funktion_kurzbz: section_name, - widgetid: id - }).then(() => { - this.sections.forEach(section => { - if (section.name == section_name) - section.widgets = section.widgets.filter(widget => widget.id != id); - }); - }).catch(error => { - console.error('ERROR: ', error); - alert('ERROR: ' + error.response.data.retval); - }); + this.$api + .call(ApiDashboardUser.removeWidget(this.dashboard, id)) + .then(() => { + this.widgets = this.widgets.filter(widget => widget.id != id); + }) + .catch(this.$fhcAlert.handleSystemError); } }, created() { @@ -142,33 +129,28 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); - axios.get(this.apiurl + '/Config', {params:{ - db: this.dashboard - }}).then(res => { - for (var name in res.data.retval) { - let widgets = []; - let remove = []; - for (var wid in res.data.retval[name].widgets) { - res.data.retval[name].widgets[wid].id = wid; - if (res.data.retval[name].widgets[wid].custom || res.data.retval[name].widgets[wid].preset) - widgets.push(res.data.retval[name].widgets[wid]); - else + this.$api + .call(ApiDashboardUser.get(this.dashboard)) + .then(res => { + const widgets = []; + const remove = []; + + for (var wid in res.data.general.widgets) { + let widget = res.data.general.widgets[wid]; + widget.id = wid; + if (widget.custom || widget.preset) { + widgets.push(widget); + this.originalWidgets[wid] = structuredClone(widget); + } else { remove.push(wid); + } } - this.sections.push({ - name: name, - widgets: widgets - }); - remove.forEach(wid => this.widgetRemove(name, wid)); - } - this.sections = this.sections.sort((section1, section2) => { - if(section1.name == 'custom') - return 1; - if (section2.name == 'custom') - return -1; - return section2.widgets.length - section1.widgets.length; - }); - }).catch(err => console.error('ERROR:', err)); + + remove.forEach(wid => this.widgetRemove('general', wid)); + + this.widgets = widgets; + }) + .catch(this.$fhcAlert.handleSystemError); }, template: `
@@ -176,7 +158,7 @@ export default { {{ $p.t('global/personalGreeting', [ viewData?.name ]) }} - +
` }