From ff93c957cdaa0f2cc72429d7c1ab94bc8dd32eed Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Wed, 14 Sep 2022 19:05:29 +0200 Subject: [PATCH 0001/1461] dashboard config api first steps --- application/controllers/dashboard/Config.php | 136 ++++++++++++++++++ .../dashboard/Dashboard_Override_model.php | 26 ++++ .../dashboard/Dashboard_Preset_model.php | 26 ++++ 3 files changed, 188 insertions(+) create mode 100644 application/controllers/dashboard/Config.php create mode 100644 application/models/dashboard/Dashboard_Override_model.php create mode 100644 application/models/dashboard/Dashboard_Preset_model.php diff --git a/application/controllers/dashboard/Config.php b/application/controllers/dashboard/Config.php new file mode 100644 index 000000000..30c066467 --- /dev/null +++ b/application/controllers/dashboard/Config.php @@ -0,0 +1,136 @@ + 'basis/mitarbeiter:r', + 'dummy' => 'basis/mitarbeiter:r', + ) + ); + + $this->load->model('dashboard/Dashboard_Preset_model', 'DashboardPresetModel'); + $this->load->model('dashboard/Dashboard_Override_model', 'DashboardOverrideModel'); + } + + public function index() + { + $res_presets = $this->DashboardPresetModel->getPresets(1, 'ma0080'); + $defaultconfig = array(); + + if( isSuccess($res_presets) && hasData($res_presets) ) + { + $presets = getData($res_presets); + foreach ($presets as $presetobj) + { + if( null !== ($preset = json_decode($presetobj->preset, true)) ) + { + $defaultconfig = array_replace_recursive($defaultconfig, + $preset); + } + } + } + + $res_userconfig = $this->DashboardOverrideModel->getOverride(1, 'ma0080'); + $mergedconfig = array(); + if( isSuccess($res_userconfig) && hasData($res_userconfig) ) + { + $data = getData($res_userconfig); + if( null !== ($userconfig = json_decode($data[0]->override, true)) ) + { + $mergedconfig = array_replace_recursive($defaultconfig, $userconfig); + } + } +/* + header('Content-Type: text/plain'); + print_r($defaultconfig); + print_r($userconfig); + print_r($mergedconfig); + die(); +*/ +/* + $ret = array( + 'defaultconfig' => $defaultconfig, + 'userconfig' => $userconfig, + 'mergedconfig' => $mergedconfig + ); + $this->outputJsonSuccess($ret); + */ + $this->outputJsonSuccess($mergedconfig); + } + + public function dummy() + { + $defaultconfig = array( + 'title' => 'CIS Dashboard', + 'widgets' => array( + 'd39ba153ac9e60a21ed694cc3728f4dd' => array( + 'title' => 'test1', + 'type' => 'kpi', + 'config' => array(), + 'place' => array( + 'row' => 1, + 'col' => 3, + 'width' => 1, + 'height' => 1 + ) + ), + 'c6c526b78a0e4bc3a0b67e00f983fc33' => array( + 'title' => 'test2', + 'type' => 'url', + 'config' => array(), + 'place' => array( + 'row' => 3, + 'col' => 1, + 'width' => 1, + 'height' => 1 + ) + ), + '3f1ebb24bdaa2b82fbdacf7d55977412' => array( + 'title' => 'test3', + 'type' => 'chart', + 'config' => array(), + 'place' => array( + 'row' => 2, + 'col' => 1, + 'width' => 3, + 'height' => 3 + ) + ) + ) + ); + + $userconfig = array( + 'widgets' => array( + 'd39ba153ac9e60a21ed694cc3728f4dd' => array( + 'modifier' => 'bold', + 'visible' => false, + 'place' => array( + 'width' => 2, + 'height' => 2 + ) + ), + 'c6c526b78a0e4bc3a0b67e00f983fc33' => array( + 'type' => 'test' + ) + ) + ); + + $merged = array_replace_recursive($defaultconfig, $userconfig); + + $ret = array( + 'defaultconfig' => $defaultconfig, + 'userconfig' => $userconfig, + 'merged' => $merged + ); + + $this->outputJsonSuccess($ret); + } +} diff --git a/application/models/dashboard/Dashboard_Override_model.php b/application/models/dashboard/Dashboard_Override_model.php new file mode 100644 index 000000000..d7a12bb42 --- /dev/null +++ b/application/models/dashboard/Dashboard_Override_model.php @@ -0,0 +1,26 @@ +dbTable = 'dashboard.tbl_dashboard_benutzer_override'; + $this->pk = 'override_id'; + } + + + /** + * Get Overrides of given uid. + * @param integer dashboard_id + * @param string $uid + * @return array + */ + public function getOverride($dashboard_id, $uid) + { + return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'uid'=> $uid)); + } +} diff --git a/application/models/dashboard/Dashboard_Preset_model.php b/application/models/dashboard/Dashboard_Preset_model.php new file mode 100644 index 000000000..444869fe6 --- /dev/null +++ b/application/models/dashboard/Dashboard_Preset_model.php @@ -0,0 +1,26 @@ +dbTable = 'dashboard.tbl_dashboard_preset'; + $this->pk = 'preset_id'; + } + + + /** + * Get Presets of given uid. + * @param integer dashboard_id + * @param string $uid + * @return array + */ + public function getPresets($dashboard_id, $uid) + { + return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'funktion_kurzbz'=> null)); + } +} From 5c9c4b7e2dd4041d5db9bb157d97d76d73ec3d90 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Fri, 16 Sep 2022 10:46:29 +0200 Subject: [PATCH 0002/1461] store configs as json in database and merge them --- application/controllers/dashboard/Config.php | 63 ++++-------- .../libraries/dashboard/DashboardLib.php | 97 +++++++++++++++++++ .../models/dashboard/Dashboard_model.php | 25 +++++ 3 files changed, 143 insertions(+), 42 deletions(-) create mode 100644 application/libraries/dashboard/DashboardLib.php create mode 100644 application/models/dashboard/Dashboard_model.php diff --git a/application/controllers/dashboard/Config.php b/application/controllers/dashboard/Config.php index 30c066467..5c6ad8040 100644 --- a/application/controllers/dashboard/Config.php +++ b/application/controllers/dashboard/Config.php @@ -11,61 +11,40 @@ class Config extends Auth_Controller { parent::__construct( array( - 'index' => 'basis/mitarbeiter:r', - 'dummy' => 'basis/mitarbeiter:r', + 'index' => 'basis/mitarbeiter:r', + 'dummy' => 'basis/mitarbeiter:r', + 'genWidgetId' => 'basis/mitarbeiter:r' ) ); - $this->load->model('dashboard/Dashboard_Preset_model', 'DashboardPresetModel'); - $this->load->model('dashboard/Dashboard_Override_model', 'DashboardOverrideModel'); + $this->load->library('dashboard/DashboardLib', null, 'DashboardLib'); } public function index() { - $res_presets = $this->DashboardPresetModel->getPresets(1, 'ma0080'); - $defaultconfig = array(); + $dashboard_kurzbz = $this->input->get('db'); + $uid = $this->input->get('uid'); - if( isSuccess($res_presets) && hasData($res_presets) ) - { - $presets = getData($res_presets); - foreach ($presets as $presetobj) - { - if( null !== ($preset = json_decode($presetobj->preset, true)) ) - { - $defaultconfig = array_replace_recursive($defaultconfig, - $preset); - } - } + $dashboard = $this->DashboardLib->getDashboardByKurzbz($dashboard_kurzbz); + if(!$dashboard) { + $this->outputJsonError(array( + 'error' => 'Dashboard ' . $dashboard_kurzbz . ' not found.' + )); } - $res_userconfig = $this->DashboardOverrideModel->getOverride(1, 'ma0080'); - $mergedconfig = array(); - if( isSuccess($res_userconfig) && hasData($res_userconfig) ) - { - $data = getData($res_userconfig); - if( null !== ($userconfig = json_decode($data[0]->override, true)) ) - { - $mergedconfig = array_replace_recursive($defaultconfig, $userconfig); - } - } -/* - header('Content-Type: text/plain'); - print_r($defaultconfig); - print_r($userconfig); - print_r($mergedconfig); - die(); -*/ -/* - $ret = array( - 'defaultconfig' => $defaultconfig, - 'userconfig' => $userconfig, - 'mergedconfig' => $mergedconfig - ); - $this->outputJsonSuccess($ret); - */ + $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['widgetid'] + )); + } + public function dummy() { $defaultconfig = array( diff --git a/application/libraries/dashboard/DashboardLib.php b/application/libraries/dashboard/DashboardLib.php new file mode 100644 index 000000000..a5a39a3c6 --- /dev/null +++ b/application/libraries/dashboard/DashboardLib.php @@ -0,0 +1,97 @@ +_ci =& get_instance(); + + $this->_ci->load->model('dashboard/Dashboard_model', 'DashboardModel'); + $this->_ci->load->model('dashboard/Dashboard_Preset_model', 'DashboardPresetModel'); + $this->_ci->load->model('dashboard/Dashboard_Override_model', 'DashboardOverrideModel'); + } + + public function generateWidgetId($dashboard_kurzbz='') + { + $dashboard_kurzbz = (!empty($dashboard_kurzbz)) ? $dashboard_kurzbz + : self::DEFAULT_DASHBOARD_KURZBZ; + $widgetid_input = time() . '_' . $dashboard_kurzbz . '_' + . bin2hex(random_bytes(self::WIDGET_ID_RANDOM_BYTES)); + $widgetid = md5($widgetid_input); + return array( + 'widgetid' => $widgetid, + 'widgetid_input' => $widgetid_input + ); + } + + public function getDashboardByKurzbz($dashboard_kurzbz) + { + $dashboard = null; + $result = $this->_ci->DashboardModel->getDashboardByKurzbz($dashboard_kurzbz); + if( isSuccess($result) && ($dashboards = getData($result)) ) + { + $dashboard = $dashboards[0]; + } + return $dashboard; + } + + public function getMergedConfig($dashboard_id, $uid) + { + $defaultconfig = $this->getDefaultConfig($dashboard_id, $uid); + $userconfig = $this->getUserConfig($dashboard_id, $uid); + + $mergedconfig = array_replace_recursive($defaultconfig, $userconfig); + + return $mergedconfig; + } + + public function getDefaultConfig($dashboard_id, $uid) + { + $res_presets = $this->_ci->DashboardPresetModel->getPresets($dashboard_id, $uid); + $defaultconfig = array(); + + if( isSuccess($res_presets) && hasData($res_presets) ) + { + $presets = getData($res_presets); + foreach ($presets as $presetobj) + { + if( null !== ($preset = json_decode($presetobj->preset, true)) ) + { + $defaultconfig = array_replace_recursive($defaultconfig, + $preset); + } + } + } + + return $defaultconfig; + } + + public function getUserConfig($dashboard_id, $uid) + { + $res_userconfig = $this->_ci->DashboardOverrideModel->getOverride($dashboard_id, $uid); + $userconfig = array(); + + if( isSuccess($res_userconfig) && hasData($res_userconfig) ) + { + $data = getData($res_userconfig); + if( null !== ($decodedconfig = json_decode($data[0]->override, true)) ) + { + $userconfig = $decodedconfig; + } + } + + return $userconfig; + } + +} diff --git a/application/models/dashboard/Dashboard_model.php b/application/models/dashboard/Dashboard_model.php new file mode 100644 index 000000000..88946ed83 --- /dev/null +++ b/application/models/dashboard/Dashboard_model.php @@ -0,0 +1,25 @@ +dbTable = 'dashboard.tbl_dashboard'; + $this->pk = 'dashboard_id'; + } + + + /** + * Get Dashboard by kurzbz. + * @param string dashboard_kurzbz + * @return array + */ + public function getDashboardByKurzbz($dashboard_kurzbz) + { + return $this->loadWhere(array('dashboard_kurzbz' => $dashboard_kurzbz)); + } +} From fb7f445e22787dcdf5b941c0fdf9fda804886a77 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Wed, 21 Sep 2022 11:38:16 +0200 Subject: [PATCH 0003/1461] progress on api endpoints to add and remove presets and overrides --- application/controllers/dashboard/Config.php | 135 +++++++++++++++++- .../libraries/dashboard/DashboardLib.php | 91 ++++++++++++ .../dashboard/Dashboard_Preset_model.php | 45 +++++- 3 files changed, 265 insertions(+), 6 deletions(-) diff --git a/application/controllers/dashboard/Config.php b/application/controllers/dashboard/Config.php index 5c6ad8040..0253942a4 100644 --- a/application/controllers/dashboard/Config.php +++ b/application/controllers/dashboard/Config.php @@ -11,9 +11,13 @@ class Config extends Auth_Controller { parent::__construct( array( - 'index' => 'basis/mitarbeiter:r', - 'dummy' => 'basis/mitarbeiter:r', - 'genWidgetId' => 'basis/mitarbeiter:r' + 'index' => 'dashboard/benutzer:r', + 'dummy' => 'dashboard/benutzer:r', + 'genWidgetId' => 'dashboard/benutzer:rw', + 'addWidgetToPreset' => 'dashboard/admin:rw', + 'removeWidgetFromPreset' => 'dashboard/admin:rw', + 'addWidgetToUserOverride' => 'dashboard/benutzer:rw', + 'removeWidgetFromUserOverride' => 'dashboard/benutzer:rw' ) ); @@ -27,7 +31,8 @@ class Config extends Auth_Controller $dashboard = $this->DashboardLib->getDashboardByKurzbz($dashboard_kurzbz); if(!$dashboard) { - $this->outputJsonError(array( + http_response_code(404); + $this->terminateWithJsonError(array( 'error' => 'Dashboard ' . $dashboard_kurzbz . ' not found.' )); } @@ -45,6 +50,128 @@ class Config extends Auth_Controller )); } + public function addWidgetToPreset() + { + $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); + $widgetid = $this->DashboardLib->generateWidgetId($dashboard_kurzbz); + + $preset_decoded['widgets'][$widgetid['widgetid']] = $input->widget; + + $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.')); + } + + 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( isset($preset_decoded['widgets'][$widgetid]) ) + { + unset($preset_decoded['widgets'][$widgetid]); + } + else + { + 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 addWidgetToUserOverride() + { + $input = json_decode($this->input->raw_input_stream); + $dashboard_kurzbz = $input->db; + $uid = $input->uid; + + $override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid); + + $override_decoded = json_decode($override->override, true); + $widgetid = isset($input->widgetid) ? array('widgetid' => $input->widgetid) + : $this->DashboardLib->generateWidgetId($dashboard_kurzbz); + + $override_decoded['widgets'][$widgetid['widgetid']] = $input->widget; + + $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.')); + } + + public function removeWidgetFromUserOverride() + { + $input = json_decode($this->input->raw_input_stream); + $dashboard_kurzbz = $input->db; + $uid = $input->uid; + $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( array_key_exists($widgetid, $override_decoded['widgets']) ) + { + unset($override_decoded['widgets'][$widgetid]); + } + else + { + 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 dummy() { $defaultconfig = array( diff --git a/application/libraries/dashboard/DashboardLib.php b/application/libraries/dashboard/DashboardLib.php index a5a39a3c6..f4edb6490 100644 --- a/application/libraries/dashboard/DashboardLib.php +++ b/application/libraries/dashboard/DashboardLib.php @@ -94,4 +94,95 @@ class DashboardLib return $userconfig; } + public function getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid) + { + $override = $this->getOverride($dashboard_kurzbz, $uid); + if( null !== $override ) { + return $override; + } + + $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); + + $emptyoverride = new stdClass(); + $emptyoverride->dashboard_id = $dashboard->dashboard_id; + $emptyoverride->uid = $uid; + $emptyoverride->override = '{"widgets": {}}'; + + return $emptyoverride; + } + + public function getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz) + { + $preset = $this->getPreset($dashboard_kurzbz, $funktion_kurzbz); + if( null !== $preset ) { + return $preset; + } + + $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); + + $emptypreset = new stdClass(); + $emptypreset->dashboard_id = $dashboard->dashboard_id; + $emptypreset->funktion_kurzbz = $funktion_kurzbz; + $emptypreset->preset = '{"widgets": {}}'; + + return $emptypreset; + } + + public function getPreset($dashboard_kurzbz, $funktion_kurzbz) + { + $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); + $preset = null; + + $result = $this->_ci->DashboardPresetModel + ->getPresetByDashboardAndFunktion($dashboard->dashboard_id, $funktion_kurzbz); + + if( isSuccess($result) && hasData($result) && ($presets = getData($result)) ) + { + $preset = $presets[0]; + } + + return $preset; + } + + public function getOverride($dashboard_kurzbz, $uid) + { + $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); + $override = null; + + $result = $this->_ci->DashboardOverrideModel + ->getOverride($dashboard->dashboard_id, $uid); + + if( isSuccess($result) && hasData($result) && ($overrides = getData($result)) ) + { + $override = $overrides[0]; + } + + return $override; + } + + public function insertOrUpdatePreset($preset) + { + if( isset($preset->preset_id) && $preset->preset_id > 0 ) + { + $result = $this->_ci->DashboardPresetModel->update($preset->preset_id, $preset); + } else + { + $result = $this->_ci->DashboardPresetModel->insert($preset); + } + + return $result; + } + + public function insertOrUpdateOverride($override) + { + if( isset($override->override_id) && $override->override_id > 0 ) + { + $result = $this->_ci->DashboardOverrideModel->update($override->override_id, $override); + } else + { + $result = $this->_ci->DashboardOverrideModel->insert($override); + } + + return $result; + } } diff --git a/application/models/dashboard/Dashboard_Preset_model.php b/application/models/dashboard/Dashboard_Preset_model.php index 444869fe6..c899195d4 100644 --- a/application/models/dashboard/Dashboard_Preset_model.php +++ b/application/models/dashboard/Dashboard_Preset_model.php @@ -12,7 +12,6 @@ class Dashboard_Preset_model extends DB_Model $this->pk = 'preset_id'; } - /** * Get Presets of given uid. * @param integer dashboard_id @@ -21,6 +20,48 @@ class Dashboard_Preset_model extends DB_Model */ public function getPresets($dashboard_id, $uid) { - return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'funktion_kurzbz'=> null)); + // TODO: get Funktionen for uid and load all preset for all funktionen for uid + //return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'funktion_kurzbz'=> null)); + $sql = <<execQuery($sql, array($dashboard_id, $uid)); + } + + /** + * Get Preset by Dashboard and Funktion + * @param integer dashboard_id + * @param string funktion_kurzbz + * @return array + */ + public function getPresetByDashboardAndFunktion($dashboard_id, $funktion_kurzbz) + { + return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'funktion_kurzbz' => $funktion_kurzbz)); } } From 252abeafcbe870735aedb2dcfb4d1dd389a6e67b Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Wed, 28 Sep 2022 15:26:45 +0200 Subject: [PATCH 0004/1461] dummy config changed to test function level in config --- application/controllers/dashboard/Config.php | 84 +++++++++++--------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/application/controllers/dashboard/Config.php b/application/controllers/dashboard/Config.php index 0253942a4..6ace1f464 100644 --- a/application/controllers/dashboard/Config.php +++ b/application/controllers/dashboard/Config.php @@ -177,37 +177,41 @@ class Config extends Auth_Controller $defaultconfig = array( 'title' => 'CIS Dashboard', 'widgets' => array( - 'd39ba153ac9e60a21ed694cc3728f4dd' => array( - 'title' => 'test1', - 'type' => 'kpi', - 'config' => array(), - 'place' => array( - 'row' => 1, - 'col' => 3, - 'width' => 1, - 'height' => 1 - ) + 'nofunction' => array( + 'd39ba153ac9e60a21ed694cc3728f4dd' => array( + 'title' => 'test1', + 'type' => 'kpi', + 'config' => array(), + 'place' => array( + 'row' => 1, + 'col' => 3, + 'width' => 1, + 'height' => 1 + ) + ), + 'c6c526b78a0e4bc3a0b67e00f983fc33' => array( + 'title' => 'test2', + 'type' => 'url', + 'config' => array(), + 'place' => array( + 'row' => 3, + 'col' => 1, + 'width' => 1, + 'height' => 1 + ) + ), ), - 'c6c526b78a0e4bc3a0b67e00f983fc33' => array( - 'title' => 'test2', - 'type' => 'url', - 'config' => array(), - 'place' => array( - 'row' => 3, - 'col' => 1, - 'width' => 1, - 'height' => 1 - ) - ), - '3f1ebb24bdaa2b82fbdacf7d55977412' => array( - 'title' => 'test3', - 'type' => 'chart', - 'config' => array(), - 'place' => array( - 'row' => 2, - 'col' => 1, - 'width' => 3, - 'height' => 3 + 'leitung' => array( + '3f1ebb24bdaa2b82fbdacf7d55977412' => array( + 'title' => 'test3', + 'type' => 'chart', + 'config' => array(), + 'place' => array( + 'row' => 2, + 'col' => 1, + 'width' => 3, + 'height' => 3 + ) ) ) ) @@ -215,16 +219,18 @@ class Config extends Auth_Controller $userconfig = array( 'widgets' => array( - 'd39ba153ac9e60a21ed694cc3728f4dd' => array( - 'modifier' => 'bold', - 'visible' => false, - 'place' => array( - 'width' => 2, - 'height' => 2 + 'nofunction' => array( + 'd39ba153ac9e60a21ed694cc3728f4dd' => array( + 'modifier' => 'bold', + 'visible' => false, + 'place' => array( + 'width' => 2, + 'height' => 2 + ) + ), + 'c6c526b78a0e4bc3a0b67e00f983fc33' => array( + 'type' => 'test' ) - ), - 'c6c526b78a0e4bc3a0b67e00f983fc33' => array( - 'type' => 'test' ) ) ); From ad369bbe4465ae0a43f43be7cac90169807b186c Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Wed, 28 Sep 2022 16:53:48 +0200 Subject: [PATCH 0005/1461] add dashboard schema and tables create script --- system/dashboard/dbupdate_dashboard.php | 429 ++++++++++++++++++++++++ system/dbupdate_3.3.php | 3 + 2 files changed, 432 insertions(+) create mode 100644 system/dashboard/dbupdate_dashboard.php diff --git a/system/dashboard/dbupdate_dashboard.php b/system/dashboard/dbupdate_dashboard.php new file mode 100644 index 000000000..3e73de3b9 --- /dev/null +++ b/system/dashboard/dbupdate_dashboard.php @@ -0,0 +1,429 @@ +, + * + * Beschreibung: + * Dashboard DB Aenderungen + */ + +if (($result = $db->db_query("SELECT schema_name FROM information_schema.schemata WHERE schema_name='dashboard'"))) +{ + if ($db->db_num_rows($result) == 0) + { + $qry = <<db_query($qry)) + { + echo 'Schema Dashboard: '.$db->db_last_error().'
'; + } + else + { + echo '
Neues Schema dashboard hinzugefuegt'; + } + } +} diff --git a/system/dbupdate_3.3.php b/system/dbupdate_3.3.php index b29171f86..fc5e95b35 100644 --- a/system/dbupdate_3.3.php +++ b/system/dbupdate_3.3.php @@ -6521,6 +6521,9 @@ if($result = @$db->db_query("SELECT * FROM information_schema.role_table_grants } } +// add Dashboard Schema and Tables +include __DIR__ . '/dashboard/dbupdate_dashboard.php'; + // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

Pruefe Tabellen und Attribute!

'; From d14608ed28b22323e99ee65162f5cdb111f89090 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Mon, 3 Oct 2022 07:59:11 +0200 Subject: [PATCH 0006/1461] level funktion_kurzbz added to presets and overrides --- application/controllers/dashboard/Config.php | 24 ++++++------- .../libraries/dashboard/DashboardLib.php | 36 +++++++++++++++++-- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/application/controllers/dashboard/Config.php b/application/controllers/dashboard/Config.php index 6ace1f464..c03bcdbf3 100644 --- a/application/controllers/dashboard/Config.php +++ b/application/controllers/dashboard/Config.php @@ -61,7 +61,8 @@ class Config extends Auth_Controller $preset_decoded = json_decode($preset->preset, true); $widgetid = $this->DashboardLib->generateWidgetId($dashboard_kurzbz); - $preset_decoded['widgets'][$widgetid['widgetid']] = $input->widget; + $this->DashboardLib->addWidgetToWidgets($preset_decoded['widgets'], + $funktion_kurzbz, $input->widget, $widgetid['widgetid']); $preset->preset = json_encode($preset_decoded); @@ -91,11 +92,8 @@ class Config extends Auth_Controller $preset_decoded = json_decode($preset->preset, true); - if( isset($preset_decoded['widgets'][$widgetid]) ) - { - unset($preset_decoded['widgets'][$widgetid]); - } - else + if( $this->DashboardLib->removeWidgetFromWidgets($preset_decoded['widgets'], + $funktion_kurzbz, $widgetid) ) { http_response_code(404); $this->terminateWithJsonError('widgetid ' . $widgetid . ' not found'); @@ -115,6 +113,7 @@ class Config extends Auth_Controller { $input = json_decode($this->input->raw_input_stream); $dashboard_kurzbz = $input->db; + $funktion_kurzbz = $input->funktion_kurzbz; $uid = $input->uid; $override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid); @@ -122,8 +121,9 @@ class Config extends Auth_Controller $override_decoded = json_decode($override->override, true); $widgetid = isset($input->widgetid) ? array('widgetid' => $input->widgetid) : $this->DashboardLib->generateWidgetId($dashboard_kurzbz); - - $override_decoded['widgets'][$widgetid['widgetid']] = $input->widget; + + $this->DashboardLib->addWidgetToWidgets($override_decoded['widgets'], + $funktion_kurzbz, $input->widget, $widgetid['widgetid']); $override->override = json_encode($override_decoded); @@ -140,6 +140,7 @@ class Config extends Auth_Controller { $input = json_decode($this->input->raw_input_stream); $dashboard_kurzbz = $input->db; + $funktion_kurzbz = $input->funktion_kurzbz; $uid = $input->uid; $widgetid = $input->widgetid; @@ -152,11 +153,8 @@ class Config extends Auth_Controller $override_decoded = json_decode($override->override, true); - if( array_key_exists($widgetid, $override_decoded['widgets']) ) - { - unset($override_decoded['widgets'][$widgetid]); - } - else + if( !$this->DashboardLib->removeWidgetFromWidgets($override_decoded['widgets'], + $funktion_kurzbz, $widgetid) ) { http_response_code(404); $this->terminateWithJsonError('widgetid ' . $widgetid . ' not found'); diff --git a/application/libraries/dashboard/DashboardLib.php b/application/libraries/dashboard/DashboardLib.php index f4edb6490..ba8469bfd 100644 --- a/application/libraries/dashboard/DashboardLib.php +++ b/application/libraries/dashboard/DashboardLib.php @@ -9,6 +9,8 @@ class DashboardLib { const WIDGET_ID_RANDOM_BYTES = 16; const DEFAULT_DASHBOARD_KURZBZ = 'fhcomplete'; + const SECTION_IF_FUNKTION_KURZBZ_IS_NULL = 'general'; + const USEROVERRIDE_SECTION = 'custom'; private $_ci; // CI instance @@ -106,7 +108,7 @@ class DashboardLib $emptyoverride = new stdClass(); $emptyoverride->dashboard_id = $dashboard->dashboard_id; $emptyoverride->uid = $uid; - $emptyoverride->override = '{"widgets": {}}'; + $emptyoverride->override = '{"widgets": {"' . self::USEROVERRIDE_SECTION . '": {}}}'; return $emptyoverride; } @@ -123,7 +125,8 @@ class DashboardLib $emptypreset = new stdClass(); $emptypreset->dashboard_id = $dashboard->dashboard_id; $emptypreset->funktion_kurzbz = $funktion_kurzbz; - $emptypreset->preset = '{"widgets": {}}'; + $section = ($funktion_kurzbz !== null) ? $funktion_kurzbz : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; + $emptypreset->preset = '{"widgets": {"' . $funktion_kurzbz . '": {}}}'; return $emptypreset; } @@ -184,5 +187,32 @@ class DashboardLib } return $result; - } + } + + public function addWidgetToWidgets(&$widgets, $section, $widget, $widgetid) + { + $section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; + if( !isset($widgets[$section]) || !is_array($widgets[$section]) ) + { + $widgets[$section] = array(); + } + + $widgets[$section][$widgetid] = $widget; + } + + public function removeWidgetFromWidgets(&$widgets, $section, $widgetid) + { + $section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; + if(isset($widgets[$section]) && isset($widgets[$section][$widgetid]) ) + { + unset($widgets[$section][$widgetid]); + if(empty($widgets[$section])) { + unset($widgets[$section]); + } + return true; + } + else { + return false; + } + } } From 90e006feb5957978feb7ea224cbd06ad9a5b5647 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Tue, 4 Oct 2022 08:22:27 +0200 Subject: [PATCH 0007/1461] allow widgets array for add operations --- application/controllers/dashboard/Config.php | 21 ++++++++---------- .../libraries/dashboard/DashboardLib.php | 22 ++++++++++++++----- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/application/controllers/dashboard/Config.php b/application/controllers/dashboard/Config.php index c03bcdbf3..60225fa11 100644 --- a/application/controllers/dashboard/Config.php +++ b/application/controllers/dashboard/Config.php @@ -14,9 +14,9 @@ class Config extends Auth_Controller 'index' => 'dashboard/benutzer:r', 'dummy' => 'dashboard/benutzer:r', 'genWidgetId' => 'dashboard/benutzer:rw', - 'addWidgetToPreset' => 'dashboard/admin:rw', + 'addWidgetsToPreset' => 'dashboard/admin:rw', 'removeWidgetFromPreset' => 'dashboard/admin:rw', - 'addWidgetToUserOverride' => 'dashboard/benutzer:rw', + 'addWidgetsToUserOverride' => 'dashboard/benutzer:rw', 'removeWidgetFromUserOverride' => 'dashboard/benutzer:rw' ) ); @@ -46,11 +46,11 @@ class Config extends Auth_Controller $dashboard_kurzbz = $this->input->get('db'); $widgetid = $this->DashboardLib->generateWidgetId($dashboard_kurzbz); $this->outputJsonSuccess(array( - 'widgetid' => $widgetid['widgetid'] + 'widgetid' => $widgetid )); } - public function addWidgetToPreset() + public function addWidgetsToPreset() { $input = json_decode($this->input->raw_input_stream); $dashboard_kurzbz = $input->db; @@ -59,10 +59,9 @@ class Config extends Auth_Controller $preset = $this->DashboardLib->getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz); $preset_decoded = json_decode($preset->preset, true); - $widgetid = $this->DashboardLib->generateWidgetId($dashboard_kurzbz); - $this->DashboardLib->addWidgetToWidgets($preset_decoded['widgets'], - $funktion_kurzbz, $input->widget, $widgetid['widgetid']); + $this->DashboardLib->addWidgetsToWidgets($preset_decoded['widgets'], + $dashboard_kurzbz, $funktion_kurzbz, $input->widgets); $preset->preset = json_encode($preset_decoded); @@ -109,7 +108,7 @@ class Config extends Auth_Controller $this->outputJsonSuccess(array('msg' => 'preset successfully updated.')); } - public function addWidgetToUserOverride() + public function addWidgetsToUserOverride() { $input = json_decode($this->input->raw_input_stream); $dashboard_kurzbz = $input->db; @@ -119,11 +118,9 @@ class Config extends Auth_Controller $override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid); $override_decoded = json_decode($override->override, true); - $widgetid = isset($input->widgetid) ? array('widgetid' => $input->widgetid) - : $this->DashboardLib->generateWidgetId($dashboard_kurzbz); - $this->DashboardLib->addWidgetToWidgets($override_decoded['widgets'], - $funktion_kurzbz, $input->widget, $widgetid['widgetid']); + $this->DashboardLib->addWidgetsToWidgets($override_decoded['widgets'], + $dashboard_kurzbz, $funktion_kurzbz, $input->widgets); $override->override = json_encode($override_decoded); diff --git a/application/libraries/dashboard/DashboardLib.php b/application/libraries/dashboard/DashboardLib.php index ba8469bfd..3f189fc03 100644 --- a/application/libraries/dashboard/DashboardLib.php +++ b/application/libraries/dashboard/DashboardLib.php @@ -31,10 +31,7 @@ class DashboardLib $widgetid_input = time() . '_' . $dashboard_kurzbz . '_' . bin2hex(random_bytes(self::WIDGET_ID_RANDOM_BYTES)); $widgetid = md5($widgetid_input); - return array( - 'widgetid' => $widgetid, - 'widgetid_input' => $widgetid_input - ); + return $widgetid; } public function getDashboardByKurzbz($dashboard_kurzbz) @@ -131,11 +128,12 @@ class DashboardLib return $emptypreset; } - public function getPreset($dashboard_kurzbz, $funktion_kurzbz) + public function getPreset($dashboard_kurzbz, $section) { $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); $preset = null; + $funktion_kurzbz = ($section === self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL) ? null : $section; $result = $this->_ci->DashboardPresetModel ->getPresetByDashboardAndFunktion($dashboard->dashboard_id, $funktion_kurzbz); @@ -189,6 +187,18 @@ class DashboardLib return $result; } + public function addWidgetsToWidgets(&$widgets, $dashboard_kurzbz, $section, $addwigets) + { + foreach ($addwigets as $widget) + { + if(!isset($widget->widgetid)) + { + $widget->widgetid = $this->generateWidgetId($dashboard_kurzbz); + } + $this->addWidgetToWidgets($widgets, $section, $widget, $widget->widgetid); + } + } + public function addWidgetToWidgets(&$widgets, $section, $widget, $widgetid) { $section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; @@ -206,7 +216,7 @@ class DashboardLib if(isset($widgets[$section]) && isset($widgets[$section][$widgetid]) ) { unset($widgets[$section][$widgetid]); - if(empty($widgets[$section])) { + if(empty($widgets[$section]) && $section !== self::USEROVERRIDE_SECTION) { unset($widgets[$section]); } return true; From 4f3e8dcb3c8ccfceac18b3eac4f7297c5b25f20b Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Wed, 5 Oct 2022 10:43:43 +0200 Subject: [PATCH 0008/1461] Basic VUE Components & Test WidgetController --- application/controllers/dashboard/Config.php | 2 +- application/controllers/dashboard/Widget.php | 162 +++++++ public/css/components/dashboard.css | 16 + public/js/components/Dashboard/Dashboard.js | 170 +++++++ public/js/components/Dashboard/Item.js | 138 ++++++ public/js/components/Dashboard/Section.js | 417 ++++++++++++++++++ .../js/components/DashboardWidget/Abstract.js | 12 + .../js/components/DashboardWidget/Default.js | 14 + .../Dashboard/CachedWidgetLoader.js | 29 ++ 9 files changed, 959 insertions(+), 1 deletion(-) create mode 100644 application/controllers/dashboard/Widget.php create mode 100644 public/css/components/dashboard.css create mode 100644 public/js/components/Dashboard/Dashboard.js create mode 100644 public/js/components/Dashboard/Item.js create mode 100644 public/js/components/Dashboard/Section.js create mode 100644 public/js/components/DashboardWidget/Abstract.js create mode 100644 public/js/components/DashboardWidget/Default.js create mode 100644 public/js/composables/Dashboard/CachedWidgetLoader.js diff --git a/application/controllers/dashboard/Config.php b/application/controllers/dashboard/Config.php index 60225fa11..ae177091e 100644 --- a/application/controllers/dashboard/Config.php +++ b/application/controllers/dashboard/Config.php @@ -130,7 +130,7 @@ class Config extends Auth_Controller $this->terminateWithJsonError('override could not be saved'); } - $this->outputJsonSuccess(array('msg' => 'override successfully stored.')); + $this->outputJsonSuccess(array('msg' => 'override successfully stored.', 'data' => $override_decoded)); } public function removeWidgetFromUserOverride() diff --git a/application/controllers/dashboard/Widget.php b/application/controllers/dashboard/Widget.php new file mode 100644 index 000000000..f1f0614d7 --- /dev/null +++ b/application/controllers/dashboard/Widget.php @@ -0,0 +1,162 @@ + 1, + "name" => 'Widget 1', + "description" => 'Das ist ein Test Widget', + "icon" => 'https://upload.wikimedia.org/wikipedia/commons/8/8a/Farben-Testbild.svg', + "file" => 'DashboardWidget/Widget1.js', + "arguments" => [ + "test" => 2 + ], + "size" => [ + "width" => [ "max" => 3 ], + "height" => [ "max" => 3 ] + ] + ] + ]; + + public function __construct() + { + parent::__construct( + array( + 'index' => 'dashboard/benutzer:r', + 'getWidgetsForDashboard' => 'dashboard/benutzer:rw', + 'addWidgetToUserOverride' => 'dashboard/benutzer:rw', + 'updateWidgetsToUserOverride' => 'dashboard/benutzer:rw', + 'removeWidgetFromUserOverride' => 'dashboard/benutzer:rw' + ) + ); + + $this->load->library('dashboard/DashboardLib', null, 'DashboardLib'); + } + + public function index() + { + $widget_id = $this->input->get('id'); + + foreach ($this->demoData as $widget) { + if ($widget["id"] == $widget_id) + return $this->outputJsonSuccess($widget); + } + return $this->outputJsonSuccess([ + "id" => 0, + "name" => 'Widget Not Found', + "file" => 'DashboardWidget/Default.js', + "arguments" => [ + "className" => 'alert-danger', + "title" => 'Widget Not Found', + "msg" => 'The widget with the id ' . $widget_id . ' could not be found' + ], + "size" => [ + "width" => 1, + "height" => 1 + ] + ]); + } + + public function getWidgetsForDashboard() + { + $db = $this->input->get('db'); + + $this->outputJsonSuccess($this->demoData); + } + + public function addWidgetToUserOverride() + { + $input = json_decode($this->input->raw_input_stream); + $dashboard_kurzbz = $input->db; + $uid = $input->uid; + $funktion = $input->funktion; + + $override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid); + + $override_decoded = json_decode($override->override, true); + $widgetid = isset($input->widgetid) ? array('widgetid' => $input->widgetid) + : $this->DashboardLib->generateWidgetId($dashboard_kurzbz); + + $override_decoded[$funktion][$widgetid['widgetid']] = $input->widget; + + $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($widgetid['widgetid']); + } + + public function updateWidgetsToUserOverride() + { + $input = json_decode($this->input->raw_input_stream, true); + $dashboard_kurzbz = $input['db']; + $uid = $input['uid']; + $funktion = $input['funktion']; + + $override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid); + + $override_decoded = json_decode($override->override, true); + if ($override_decoded[$funktion]) + $override_decoded[$funktion] = array_replace_recursive($override_decoded[$funktion], $input['widgets']); + else + $override_decoded[$funktion] = $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(['msg' => 'override successfully updated.']); + } + + public function removeWidgetFromUserOverride() + { + $input = json_decode($this->input->raw_input_stream); + $dashboard_kurzbz = $input->db; + $uid = $input->uid; + $funktion = $input->funktion; + $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( array_key_exists($widgetid, $override_decoded[$funktion]) ) + { + unset($override_decoded[$funktion][$widgetid]); + } + else + { + http_response_code(404); + $this->terminateWithJsonError('widgetid ' . $widgetid . ' not found in funktion ' . $funktion); + } + + $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.')); + } + +} diff --git a/public/css/components/dashboard.css b/public/css/components/dashboard.css new file mode 100644 index 000000000..91379200c --- /dev/null +++ b/public/css/components/dashboard.css @@ -0,0 +1,16 @@ +.alert-danger .form-check-input:checked { + border-color: #842029; + background-color: #842029; +} + +.draganddropcontainer { + grid-template-columns:repeat(4,1fr); + gap: 1rem; + place-items: stretch; + place-content: stretch; +} +@media(max-width: 577px) { + .draganddropcontainer { + grid-template-columns:repeat(2,1fr); + } +} diff --git a/public/js/components/Dashboard/Dashboard.js b/public/js/components/Dashboard/Dashboard.js new file mode 100644 index 000000000..04976da9c --- /dev/null +++ b/public/js/components/Dashboard/Dashboard.js @@ -0,0 +1,170 @@ +import DashboardSection from "./Section.js"; +import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js"; + +export default { + props: [ + "dashboard", + "apiurl" + ], + data: () => ({ + sections: [], + widgets: [], + isLoading: 0, + tmpCreate: null, + }), + components: { + DashboardSection + }, + computed: { + listReady() { + return this.widgets.length && !this.isLoading; + } + }, + methods: { + widgetAdd(section_name, widget) { + if (!this.widgets.length) { + axios.get(this.apiurl + '/Widget/getWidgetsForDashboard', {params:{ + db: this.dashboard + }}).then(res => { + //console.log(res.data.retval); + this.widgets = res.data.retval; + }).catch(err => console.error('ERROR:', err)); + } + this.tmpCreate = {section_name,widget}; + this.listModal.show(); + }, + widgetCreate(widget) { + this.isLoading = 1; + this.tmpCreate.widget.widget = widget; + axios.post(this.apiurl + '/Config/addWidgetsToUserOverride', { + db: this.dashboard, + uid: 'ma0168', + funktion_kurzbz: this.tmpCreate.section_name, + widgets: [this.tmpCreate.widget] + }).then(result => { + let newId = 0; + let sec = result.data.retval.data.widgets[this.tmpCreate.section_name]; + for (var i in sec) { + newId = i; + break; + } + console.log(newId); + this.tmpCreate.widget.id = newId; + this.sections.forEach(section => { + if (section.name == this.tmpCreate.section_name) + section.widgets.push(this.tmpCreate.widget); + }); + }).catch(error => { + console.error('ERROR: ', error); + alert('ERROR: ' + error.response.data.retval); + }).finally(() => { + this.listModal.hide(); + this.isLoading = 0; + }); + }, + 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] = {...this.sections[i].widgets[wid], ...payload[k]}; + break; + } + } + break; + } + } + payload[k].widgetid = k; + } + return axios.post(this.apiurl + '/Config/addWidgetsToUserOverride', { + db: this.dashboard, + uid: 'ma0168', + funktion_kurzbz: section_name, + widgets: payload + }).then(result => { + this.sections.forEach(section => { + if (section.name == section_name) { + section.widgets.forEach((widget, i) => { + if (payload[widget.id]) { + // TODO(chris): revert placement on failure + delete payload[widget.id].place; // TODO(chris): find out why overwriting place bugs out + section.widgets[i] = {...widget, ...payload[widget.id]}; + } + }); + } + }); + }).catch(error => { + console.error('ERROR: ', error); + alert('ERROR: ' + error.response.data.retval); + }); + }, + widgetRemove(section_name, id) { + axios.post(this.apiurl + '/Config/removeWidgetFromUserOverride', { + db: this.dashboard, + uid: 'ma0168', + funktion_kurzbz: section_name, + widgetid: id + }).then(result => { + 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); + }); + } + }, + created() { + CachedWidgetLoader.setPath(this.apiurl + '/Widget'); + axios.get(this.apiurl + '/Config', {params:{ + db: this.dashboard, + uid: 'ma0168' + }}).then(res => { + //console.log(res.data.retval); + for (var name in res.data.retval.widgets) { + let widgets = []; + for (var wid in res.data.retval.widgets[name]) { + res.data.retval.widgets[name][wid].id = wid; + widgets.push(res.data.retval.widgets[name][wid]); + } + this.sections.push({ + name: name, + widgets: widgets + }); + } + }).catch(err => console.error('ERROR:', err)); + }, + mounted() { + this.listModal = new bootstrap.Modal(this.$refs.widgetlist); + }, + template: `
+ + +
` +} \ No newline at end of file diff --git a/public/js/components/Dashboard/Item.js b/public/js/components/Dashboard/Item.js new file mode 100644 index 000000000..4434311ff --- /dev/null +++ b/public/js/components/Dashboard/Item.js @@ -0,0 +1,138 @@ +import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js"; + +export default { + components: {}, + data: () => ({ + component: '', + arguments: null, + configModal: null, + target: false, + widget: null, + tmpConfig: {}, + isLoading: false, + hasConfig: true + }), + emits: [ + "change", + "remove", + "dragstart", + "resizestart", + ], + props: [ + "id", + "config", + "width", + "height", + "custom", + "hidden", + "editMode" + ], + computed: { + isResizeable() { + if (!this.widget) + return false; + return this.widget.size.width.max || this.widget.size.height.max; + }, + ready() { + return this.component && this.arguments !== null + } + }, + methods: { + mouseDown(e) { + this.target = e.target; + }, + startDrag(e) { + if (this.$refs.dragHandle.contains(this.target)) { + this.$emit('dragstart', e); + } else if (this.isResizeable && this.$refs.resizeHandle.contains(this.target)) { + if (this.isResizeable) + this.$emit('resizestart', e); + else + e.preventDefault(); + } else { + e.preventDefault(); + } + }, + openConfig() { + this.tmpConfig = {...this.arguments}; + this.configModal.show(); + }, + setConfig(hasConfig) { + this.hasConfig = hasConfig; + }, + changeConfig() { + this.isLoading = true; + let config = {...this.tmpConfig}; + this.sendChangeConfig(config); + }, + changeConfigManually() { + let config = {...this.arguments}; + this.sendChangeConfig(config); + }, + sendChangeConfig(config) { + for (var k in config) { + if (this.widget.arguments[k] == config[k]) { + delete config[k]; + } + } + this.$emit('change', config); + } + }, + watch: { + config() { + this.arguments = {...this.widget.arguments, ...this.config}; + this.tmpConfig = {...this.arguments}; + this.configModal.hide(); + this.isLoading = false; + } + }, + async created() { + this.widget = await CachedWidgetLoader.loadWidget(this.id); + let component = (await import('../' + this.widget.file)).default; + this.$options.components['widget' + this.widget.id] = component; + this.component = 'widget' + this.widget.id; + this.arguments = {...this.widget.arguments, ...this.config}; + this.tmpConfig = {...this.arguments}; + }, + mounted() { + this.configModal = new bootstrap.Modal(this.$refs.config); + }, + template: `
+
+ + {{ widget.name }} + + + + +
+ +
+
+
+ +
+
+ + +
` +} \ No newline at end of file diff --git a/public/js/components/Dashboard/Section.js b/public/js/components/Dashboard/Section.js new file mode 100644 index 000000000..9ea40fdc1 --- /dev/null +++ b/public/js/components/Dashboard/Section.js @@ -0,0 +1,417 @@ +import DashboardItem from "./Item.js"; +import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js"; + +// TODO(chris): handle overflow (moving outside the box) +export default { + data: () => ({ + gridWidth: 0, + containerRect: {top:0,left:0}, + changeHeight: 1, + movedObjects: [], + editMode: 0, + gridXLast: 0, + gridYLast: 0, + }), + props: [ + "name", + "widgets" + ], + emits: [ + "widgetAdd", + "widgetUpdate", + "widgetRemove" + ], + components: { + DashboardItem + }, + computed: { + items() { + this.widgets.forEach((item,i) => item.index = i); + return this.widgets; + }, + itemCoords() { + if (!this.gridWidth) + return []; + let itemCoords = this.items.map(item => item.place[this.gridWidth] || this.createItemPlacement(item)); + // TODO(chris): verify positions & sizes + let occupiers = []; + let wrongPlacedItems = []; + let gridWidth = this.gridWidth; + this.items.forEach(item => { + let x = item._x !== undefined ? item._x : itemCoords[item.index].x; + let y = item._y !== undefined ? item._y : itemCoords[item.index].y; + let w = item._w !== undefined ? item._w : itemCoords[item.index].w; + let h = item._h !== undefined ? item._h : itemCoords[item.index].h; + // TODO(chris): check with and height params here? + for (var i = 0; i < w; i++) { + for (var j = 0; j < h; j++) { + var c = (y+j-1) * gridWidth + (x+i-1); + // NOTE(chris): check for overlaping items + if (occupiers[c] !== undefined) { + //console.log('try to add ' + item.index + ' to ' + x + '/' + y + ', but ' + occupiers[c] + ' is already there'); + // NOTE(chris): remove possible other entries of this item + for (var c2 = c; c2; c2--) + if (occupiers[c2] == item.index) + occupiers[c2] = undefined; + wrongPlacedItems.push(item); + return; + } + occupiers[c] = item.index; + } + } + }); + wrongPlacedItems.forEach(item => { + let w = item._w !== undefined ? item._w : itemCoords[item.index].w; + let h = item._h !== undefined ? item._h : itemCoords[item.index].h; + for (var c = 0; c < occupiers.length + gridWidth; c++) { + if (occupiers[c] === undefined) { + var occupied = false; + for (var i = 0; i < w; i++) { + for (var j = 0; j < h; j++) { + if (occupiers[c + i + j * gridWidth] !== undefined) { + i = w; + occupied = true; + break; + } + } + } + if (!occupied) { + item.place[gridWidth].x = c%gridWidth + 1; + item.place[gridWidth].y = Math.floor(c/gridWidth) + 1; + for (var i = 0; i < w; i++) { + for (var j = 0; j < h; j++) { + occupiers[c + i + j * gridWidth] = item.index; + } + } + return; + } + } + } + }); + return itemCoords; + }, + gridHeight() { + if (!this.gridWidth || !this.changeHeight) + return 0; + let minH = 0; + this.itemCoords.forEach((item,i) => minH = Math.max(minH, (!this.editMode && this.items[i].hidden) ? 0 : item.y + item.h - 1)); + // TODO(chris): the extraline should only be present if all slots are occupied + return minH + this.editMode; + }, + gridOccupiers() { + let occupiers = []; + let gridWidth = this.gridWidth; + this.items.forEach(item => { + let x = item._x !== undefined ? item._x : this.itemCoords[item.index].x; + let y = item._y !== undefined ? item._y : this.itemCoords[item.index].y; + let w = item._w !== undefined ? item._w : this.itemCoords[item.index].w; + let h = item._h !== undefined ? item._h : this.itemCoords[item.index].h; + for (var i = 0; i < w; i++) { + for (var j = 0; j < h; j++) { + var c = (y+j-1) * gridWidth + (x+i-1); + occupiers[c] = item.index; + } + } + }); + return occupiers; + } + }, + methods: { + addWidget(evt) { + if (evt.target != this.$refs.container || !this.editMode) + return; + const rect = this.containerRect; + const gridX = Math.floor(this.gridWidth * (evt.clientX - rect.left) / this.$refs.container.clientWidth) + 1; + const gridY = Math.floor(this.gridHeight * (evt.clientY - rect.top) / this.$refs.container.clientHeight) + 1; + if (this.gridOccupiers[gridY * this.gridWidth + gridX] === undefined) { + let widget = { widget: 1, config: {}, place: {}, custom: 1 }; + widget.place[this.gridWidth] = { + x: gridX, + y: gridY, + w: 1, + h: 1 + }; + this.$emit('widgetAdd', this.name, widget); + } + }, + createItemPlacement(item) { + // TODO(chris): create correct default placement if it is not there + item.place[this.gridWidth] = {x:1,y:1,w:1,h:1}; + /*var freeList = [], nextId = 0; + this.items.forEach(item => { + if (!item.place[this.gridWidth]) { + if (!this.gridWidth) { + item.place[this.gridWidth] = {x:1,y:nextId++,w:1,h:1}; + } else { + // TODO(chris): IMPLEMENT widths & heights + if (freeList[nextId]) + while (freeList[++nextId]); + freeList[nextId] = 1; + item.place[this.gridWidth] = {x:(nextId%this.gridWidth)+1,y:Math.floor(nextId/this.gridWidth)+1,w:1,h:1}; + } + } + });*/ + return item.place[this.gridWidth]; + }, + startDrag(evt, item) { + this.gridXLast = -1; + this.gridYLast = -1; + item._x = this.itemCoords[item.index].x; + item._y = this.itemCoords[item.index].y; + + evt.dataTransfer.dropEffect = 'move'; + evt.dataTransfer.effectAllowed = 'move'; + evt.dataTransfer.setData('itemAction', 'm'); + evt.dataTransfer.setData('itemId', item.index); + evt.dataTransfer.setData('itemW', this.itemCoords[item.index].w); + evt.dataTransfer.setData('itemH', this.itemCoords[item.index].h); + }, + startResize(evt, item) { + this.gridXLast = -1; + this.gridYLast = -1; + item._w = this.itemCoords[item.index].w; + item._h = this.itemCoords[item.index].h; + + evt.dataTransfer.setDragImage(evt.target, -99999, -99999); + evt.dataTransfer.dropEffect = 'move'; + evt.dataTransfer.effectAllowed = 'move'; + evt.dataTransfer.setData('itemAction', 'r'); + evt.dataTransfer.setData('itemId', item.index); + evt.dataTransfer.setData('itemX', this.itemCoords[item.index].x); + evt.dataTransfer.setData('itemY', this.itemCoords[item.index].y); + }, + occupyFields(id, x, y, w, h) { + var c; + while ((c = this.movedObjects.pop())) { + if (this.items[c]._y !== undefined) { + this.items[c].place[this.gridWidth].y = this.items[c]._y; + this.items[c]._y = undefined; + } + } + var move = {}; + move[id] = this.items[id]; + this.getOccupiedItems(x,y,w,h,move); + h = y + h; + y = 0; + for (x in move) { + if (x != id) { + c = move[x]._y !== undefined ? move[x]._y : this.itemCoords[x].y; + if (c < h) + y = Math.max(h-c, y); + } + } + for (x in move) { + if (x != id) { + this.movedObjects.push(x); + if (move[x]._y === undefined) { + move[x]._y = this.itemCoords[x].y; + } + move[x].place[this.gridWidth].y = move[x]._y + y; + } + } + }, + getOccupiedItems(x, y, w, h, move) { + var i, j, c; + for (i = 0; i < w; i++) { + for (j = 0; j < h; j++) { + c = (y+j-1) * this.gridWidth + (x+i-1); + if (this.gridOccupiers[c] !== undefined && !move[this.gridOccupiers[c]]) { + move[this.gridOccupiers[c]] = this.items[this.gridOccupiers[c]]; + c = this.itemCoords[this.gridOccupiers[c]]; + this.getOccupiedItems(c.x, c.y + 1, c.w, c.h, move); + } + } + } + }, + onDragOver(evt) { + let id, x, y, w, h; + const action = evt.dataTransfer.getData('itemAction'); + const rect = this.containerRect; + const gridX = Math.floor(this.gridWidth * (evt.clientX - rect.left) / this.$refs.container.clientWidth); + const gridY = Math.floor(this.gridHeight * (evt.clientY - rect.top) / this.$refs.container.clientHeight); + + if (this.gridXLast == gridX && this.gridYLast == gridY) + return; + this.gridXLast = gridX; + this.gridYLast = gridY; + + if (action == 'm') { + x = gridX + 1; + y = gridY + 1; + w = parseInt(evt.dataTransfer.getData('itemW')); + h = parseInt(evt.dataTransfer.getData('itemH')); + + if (x + w > this.gridWidth + 1) + x = this.gridWidth + 1 - w; + + id = evt.dataTransfer.getData('itemID'); + this.occupyFields(id, x, y, w, h); + + this.itemCoords[id].x = x; + this.itemCoords[id].y = y; + } else if (action == 'r') { + x = parseInt(evt.dataTransfer.getData('itemX')); + y = parseInt(evt.dataTransfer.getData('itemY')); + w = gridX + 2 - x; + h = gridY + 2 - y; + w = Math.max(1, w); + h = Math.max(1, h); + + if (x + w > this.gridWidth + 1) + w = this.gridWidth + 1 - x; + + id = evt.dataTransfer.getData('itemID'); + let widget = CachedWidgetLoader.getWidget(this.items[id].widget); + if (widget) { + let minmaxW = widget.size.width; + if (minmaxW.max) + minmaxW.min = minmaxW.min || 1; + else + minmaxW = {min:minmaxW,max:minmaxW}; + if (w < minmaxW.min) + w = minmaxW.min; + if (w > minmaxW.max) + w = minmaxW.max; + + let minmaxH = widget.size.height; + if (minmaxH.max) + minmaxH.min = minmaxH.min || 1; + else + minmaxH = {min:minmaxH,max:minmaxH}; + if (h < minmaxH.min) + h = minmaxH.min; + if (h > minmaxH.max) + h = minmaxH.max; + } + + this.occupyFields(id, x, y, w, h); + + this.itemCoords[id].w = w; + this.itemCoords[id].h = h; + } + }, + onDrop(evt) { + let id = 0; + let update = {}; + while ((id = this.movedObjects.pop())) { + if (this.items[id]._y !== undefined) { + if (this.itemCoords[id].y != this.items[id]._y) { + update[this.items[id].id] = {place:{}}; + update[this.items[id].id].place[this.gridWidth] = {y:this.itemCoords[id].y}; + } + this.items[id]._y = undefined; + } + } + + id = evt.dataTransfer.getData('itemId'); + + const action = evt.dataTransfer.getData('itemAction'); + update[this.items[id].id] = {place:{}}; + update[this.items[id].id].place[this.gridWidth] = {}; + + if (action == 'm') { + if (this.items[id]._x !== undefined) { + if (this.itemCoords[id].x != this.items[id]._x) { + update[this.items[id].id].place[this.gridWidth].x = this.itemCoords[id].x; + } + this.items[id]._x = undefined; + } + if (this.items[id]._y !== undefined) { + if (this.itemCoords[id].y != this.items[id]._y) { + update[this.items[id].id].place[this.gridWidth].y = this.itemCoords[id].y; + } + this.items[id]._y = undefined; + } + } else if (action == 'r') { + update[this.items[id].id].place[this.gridWidth].w = this.itemCoords[id].w; + update[this.items[id].id].place[this.gridWidth].h = this.itemCoords[id].h; + } + + if (update[this.items[id].id].place[this.gridWidth].x === undefined && + update[this.items[id].id].place[this.gridWidth].y === undefined && + update[this.items[id].id].place[this.gridWidth].w === undefined && + update[this.items[id].id].place[this.gridWidth].h === undefined) { + delete update[this.items[id].id].place[this.gridWidth]; + } + + this.updatePreset(update); + + // TODO(chris): find better way to trigger change for gridHeight + this.changeHeight++ + }, + removeWidget(item, revert) { + if (item.custom) { + if (confirm('Are you sure you want to delete this widget?')) { + this.$emit('widgetRemove', this.name, item.id); + } + } else { + let update = {}; + update[item.id] = { hidden: !revert }; + this.updatePreset(update); + } + }, + saveConfig(config, item) { + let payload = {}; + payload[item.id] = { config };console.log(payload); + this.updatePreset(payload); + }, + updatePreset(update) { + let payload = {}; + payload[this.name] = update; + this.$emit('widgetUpdate', this.name, payload); + } + }, + mounted() { + let self = this; + let cont = self.$refs.container; + self.gridWidth = window.getComputedStyle(cont).getPropertyValue('grid-template-columns').split(" ").length; + self.containerRect = cont.getBoundingClientRect(); + + window.addEventListener('resize', () => { + for (const child of cont.children) { + child.style.display = 'none'; + } + self.gridWidth = window.getComputedStyle(cont).getPropertyValue('grid-template-columns').split(" ").length; + self.containerRect = cont.getBoundingClientRect(); + for (const child of cont.children) { + child.style.display = ''; + } + }); + }, + template: `
+

+ {{name}} + +

+
+
+ + + + +
+
+
` +} \ No newline at end of file diff --git a/public/js/components/DashboardWidget/Abstract.js b/public/js/components/DashboardWidget/Abstract.js new file mode 100644 index 000000000..8d0416e28 --- /dev/null +++ b/public/js/components/DashboardWidget/Abstract.js @@ -0,0 +1,12 @@ +export default { + props: [ + "config", + "width", + "height", + "setConfig", + "configMode" + ], + emits: [ + "change" // TODO(chris): do we need this? + ] +} diff --git a/public/js/components/DashboardWidget/Default.js b/public/js/components/DashboardWidget/Default.js new file mode 100644 index 000000000..29e3880dd --- /dev/null +++ b/public/js/components/DashboardWidget/Default.js @@ -0,0 +1,14 @@ +import AbstractWidget from './Abstract'; + +export default { + mixins: [ + AbstractWidget + ], + created() { + this.$emit('setConfig', false) + }, + template: `
+
{{ config.title }}
+

{{ config.msg }}

+
` +} \ No newline at end of file diff --git a/public/js/composables/Dashboard/CachedWidgetLoader.js b/public/js/composables/Dashboard/CachedWidgetLoader.js new file mode 100644 index 000000000..df9ff6abe --- /dev/null +++ b/public/js/composables/Dashboard/CachedWidgetLoader.js @@ -0,0 +1,29 @@ +let __widgets = {}; +let __widgetsStarted = {}; +let __path = ''; + +export default { + getWidget(id) { + return __widgets[id]; + }, + loadWidget(id) { + if (__widgets[id]) + return Promise.resolve(__widgets[id]); + if (__widgetsStarted[id]) + return __widgetsStarted[id]; + if (!__path) + return Promise.reject('Widget could not be loaded because there is no path yet!'); + + __widgetsStarted[id] = new Promise((resolve, reject) => { + axios.get(__path, {params:{id}}).then(res => { + __widgets[id] = res.data.retval; + __widgetsStarted[id] = undefined; + resolve(__widgets[id]); + }).catch(error => reject(error.response.data.retval.error)); + }); + return __widgetsStarted[id]; + }, + setPath(path) { + __path = path; + } +} \ No newline at end of file From a3ad8eb337702dd320abe163815d767e1d7af64c Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Tue, 11 Oct 2022 15:41:43 +0200 Subject: [PATCH 0009/1461] dashboard.tbl_widget new fields --- system/dashboard/dbupdate_dashboard.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system/dashboard/dbupdate_dashboard.php b/system/dashboard/dbupdate_dashboard.php index 3e73de3b9..8f3c8ba9a 100644 --- a/system/dashboard/dbupdate_dashboard.php +++ b/system/dashboard/dbupdate_dashboard.php @@ -166,7 +166,9 @@ ALTER TABLE dashboard.tbl_dashboard_widget OWNER TO fhcomplete; CREATE TABLE dashboard.tbl_widget ( widget_id integer NOT NULL, widget_kurzbz character varying(32) NOT NULL, - beschreibung text + beschreibung text, + arguments jsonb NOT NULL, + setup jsonb NOT NULL ); From 39bbcac217037fecdb555ac731c29ef3008e8236 Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Wed, 12 Oct 2022 08:58:48 +0200 Subject: [PATCH 0010/1461] Widget controller & model & table --- application/controllers/dashboard/Widget.php | 44 +++++++++++-------- .../dashboard/Dashboard_Widget_model.php | 24 ++++++++++ public/js/components/Dashboard/Dashboard.js | 14 +++--- public/js/components/Dashboard/Item.js | 12 ++--- public/js/components/Dashboard/Section.js | 4 +- .../Dashboard/CachedWidgetLoader.js | 2 + 6 files changed, 68 insertions(+), 32 deletions(-) create mode 100644 application/models/dashboard/Dashboard_Widget_model.php diff --git a/application/controllers/dashboard/Widget.php b/application/controllers/dashboard/Widget.php index f1f0614d7..6f5bd6a6c 100644 --- a/application/controllers/dashboard/Widget.php +++ b/application/controllers/dashboard/Widget.php @@ -37,37 +37,43 @@ class Widget extends Auth_Controller ); $this->load->library('dashboard/DashboardLib', null, 'DashboardLib'); + $this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel'); } public function index() { $widget_id = $this->input->get('id'); - foreach ($this->demoData as $widget) { - if ($widget["id"] == $widget_id) - return $this->outputJsonSuccess($widget); - } - return $this->outputJsonSuccess([ - "id" => 0, - "name" => 'Widget Not Found', - "file" => 'DashboardWidget/Default.js', - "arguments" => [ - "className" => 'alert-danger', - "title" => 'Widget Not Found', - "msg" => 'The widget with the id ' . $widget_id . ' could not be found' - ], - "size" => [ - "width" => 1, - "height" => 1 - ] - ]); + $widget = $this->DashboardWidgetModel->load($widget_id); + + if (isError($widget) || !getData($widget)) + return $this->outputJsonSuccess([ + "widget_id" => 0, + "widget_kurzbz" => "notfound", + "arguments" => json_encode([ + "className" => 'alert-danger', + "title" => 'Widget Not Found', + "msg" => 'The widget with the id ' . $widget_id . ' could not be found' + ]), + "setup" => json_encode([ + "name" => 'Widget Not Found', + "file" => 'DashboardWidget/Default.js', + "width" => 1, + "height" => 1 + ]) + ]); + return $this->outputJsonSuccess(current(getData($widget))); } public function getWidgetsForDashboard() { $db = $this->input->get('db'); + $result = $this->DashboardWidgetModel->getAllForDashboard($db); - $this->outputJsonSuccess($this->demoData); + if (isError($result)) + return $this->outputJsonError(getError($result)); + + $this->outputJsonSuccess(getData($result)); } public function addWidgetToUserOverride() diff --git a/application/models/dashboard/Dashboard_Widget_model.php b/application/models/dashboard/Dashboard_Widget_model.php new file mode 100644 index 000000000..59aadf8f8 --- /dev/null +++ b/application/models/dashboard/Dashboard_Widget_model.php @@ -0,0 +1,24 @@ +dbTable = 'dashboard.tbl_widget'; + $this->pk = 'widget_id'; + } + + public function getAllForDashboard($db) + { + $this->addSelect($this->dbTable . '.*'); + $this->addJoin('dashboard.tbl_dashboard_widget', 'widget_id'); + $this->addJoin('dashboard.tbl_dashboard', 'dashboard_id'); + + return $this->loadWhere(['dashboard_kurzbz' => $db]); + } + +} diff --git a/public/js/components/Dashboard/Dashboard.js b/public/js/components/Dashboard/Dashboard.js index 04976da9c..345ebd9b1 100644 --- a/public/js/components/Dashboard/Dashboard.js +++ b/public/js/components/Dashboard/Dashboard.js @@ -27,6 +27,10 @@ export default { db: this.dashboard }}).then(res => { //console.log(res.data.retval); + res.data.retval.forEach(widget => { + widget.arguments = JSON.parse(widget.arguments); + widget.setup = JSON.parse(widget.setup); + }); this.widgets = res.data.retval; }).catch(err => console.error('ERROR:', err)); } @@ -151,12 +155,12 @@ export default {