diff --git a/application/config/navigation.php b/application/config/navigation.php index c70aba57c..4d4dcc22a 100644 --- a/application/config/navigation.php +++ b/application/config/navigation.php @@ -208,7 +208,14 @@ $config['navigation_header'] = array( 'expand' => true, 'sort' => 30, 'requiredPermissions' => 'lehre/anrechnungszeitfenster:rw' - ) + ), + 'dashboardadmin' => array( + 'link' => site_url('dashboard/Admin'), + 'description' => 'Dashboard Admin', + 'expand' => true, + 'sort' => 40, + 'requiredPermissions' => 'dashboard/admin:r' + ) ) ) ) diff --git a/application/controllers/api/frontend/v1/Abgabe.php b/application/controllers/api/frontend/v1/Abgabe.php index af598a345..43dc18d1c 100644 --- a/application/controllers/api/frontend/v1/Abgabe.php +++ b/application/controllers/api/frontend/v1/Abgabe.php @@ -511,10 +511,11 @@ class Abgabe extends FHCAPI_Controller return $projektarbeit->projektarbeit_id; }; $projektarbeiten_ids = array_map($mapFunc, $projektarbeiten->retval); - - $ret = $this->ProjektarbeitModel->getProjektarbeitenAbgabetermine($projektarbeiten_ids); - $projektabgaben = $this->getDataOrTerminateWithError($ret, 'general'); + if(count($projektarbeiten_ids) > 0) { + $ret = $this->ProjektarbeitModel->getProjektarbeitenAbgabetermine($projektarbeiten_ids); + $projektabgaben = $this->getDataOrTerminateWithError($ret, 'general'); + } forEach($projektarbeiten->retval as $pa) { @@ -846,9 +847,10 @@ class Abgabe extends FHCAPI_Controller private function getProjektbetreuerEmailByProjektarbeitID($projektarbeit_id) { $this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel'); $result = $this->ProjektarbeitModel->getProjektbetreuerEmail($projektarbeit_id); - $email = $this->getDataOrTerminateWithError($result, 'general'); - - return $email[0]->uid ? $email[0]->uid.'@'.DOMAIN : $email[0]->private_email; + if(count($result->retval) > 0) { + $email = getData($result); + return $email[0]->uid ? $email[0]->uid.'@'.DOMAIN : $email[0]->private_email; + } else return ''; } diff --git a/application/controllers/api/frontend/v1/Documents.php b/application/controllers/api/frontend/v1/Documents.php index 7b2fc4a15..13c0a2eba 100644 --- a/application/controllers/api/frontend/v1/Documents.php +++ b/application/controllers/api/frontend/v1/Documents.php @@ -208,7 +208,6 @@ class Documents extends FHCAPI_Controller $this->load->model('system/Vorlage_model', 'VorlageModel'); $result = $this->VorlageModel->load($xsl); - $this->addMeta("ress", $result); $vorlage = current($this->getDataOrTerminateWithError($result)); if (!$vorlage) show_404(); @@ -221,7 +220,7 @@ class Documents extends FHCAPI_Controller 'gedruckt' => true, 'insertamum' => date('c'), 'insertvon' => getAuthUID(), - 'uid' => $this->input->post_get('uid') ?: '', + 'uid' => $this->input->post_get('uid') ?: null, 'archiv' => true, 'signiert' => !!$sign_user, 'stud_selfservice' => $vorlage->stud_selfservice @@ -251,6 +250,9 @@ class Documents extends FHCAPI_Controller 'studiensemester_kurzbz' => $ss, 'student_uid' => $akteData['uid'] ]); + + if (!hasData($result)) $this->terminateWithError($this->p->t("stv", "error_noLehrverbandAssigned")); + $res = current($this->getDataOrTerminateWithError($result)); $studiengang_kz = $res->studiengang_kz; @@ -332,6 +334,7 @@ class Documents extends FHCAPI_Controller if ($prestudent_id) { $this->load->model('crm/prestudent_model', 'PrestudentModel'); $this->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz', 'LEFT'); + $this->PrestudentModel->addSelect('tbl_prestudent.*, UPPER(typ || kurzbz) AS kuerzel'); $result = $this->PrestudentModel->load($prestudent_id); $prestudent = current($this->getDataOrTerminateWithError($result)); diff --git a/application/controllers/api/frontend/v1/dashboard/Board.php b/application/controllers/api/frontend/v1/dashboard/Board.php new file mode 100644 index 000000000..c50fec128 --- /dev/null +++ b/application/controllers/api/frontend/v1/dashboard/Board.php @@ -0,0 +1,121 @@ +. + */ + +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 addresses + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Board extends FHCAPI_Controller +{ + public function __construct() + { + parent::__construct([ + 'list' => 'dashboard/admin:r', + 'create' => 'dashboard/admin:rw', + 'update' => 'dashboard/admin:rw', + 'delete' => 'dashboard/admin:rw' + ]); + + // Models + $this->load->model('dashboard/Dashboard_model', 'DashboardModel'); + } + + public function list() + { + $result = $this->DashboardModel->load(); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($result); + } + + public function create() + { + $dashboard_kurzbz = $this->input->post('dashboard_kurzbz'); + + $result = $this->DashboardModel->insert([ + 'dashboard_kurzbz' => $dashboard_kurzbz + ]); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function update() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('dashboard_id', 'Dashboard ID', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $dashboard_id = $this->input->post('dashboard_id'); + $dashboard_kurzbz = $this->input->post('dashboard_kurzbz'); + $beschreibung = $this->input->post('beschreibung'); + + $result = $this->DashboardModel->update([ + 'dashboard_id' => $dashboard_id + ], [ + 'dashboard_kurzbz' => $dashboard_kurzbz, + 'beschreibung' => $beschreibung + ]); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($result); + } + + public function delete() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('dashboard_id', 'Dashboard ID', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $dashboard_id = $this->input->post('dashboard_id'); + + //delete all presets + $this->load->model('dashboard/Dashboard_Preset_model', 'DashboardPresetModel'); + + $result = $this->DashboardPresetModel->delete([ + 'dashboard_id' => $dashboard_id + ]); + $this->getDataOrTerminateWithError($result); + + //delete all widgets + $this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel'); + + $result = $this->DashboardWidgetModel->delete([ + 'dashboard_id' => $dashboard_id + ]); + $this->getDataOrTerminateWithError($result); + + $result = $this->DashboardModel->delete($dashboard_id); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($result); + } +} diff --git a/application/controllers/api/frontend/v1/dashboard/Preset.php b/application/controllers/api/frontend/v1/dashboard/Preset.php new file mode 100644 index 000000000..5983d9660 --- /dev/null +++ b/application/controllers/api/frontend/v1/dashboard/Preset.php @@ -0,0 +1,200 @@ +. + */ + +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 addresses + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Preset extends FHCAPI_Controller +{ + public function __construct() + { + parent::__construct([ + 'list' => 'dashboard/admin:r', + 'getBatch' => 'dashboard/admin:r', + 'addWidget' => 'dashboard/admin:rw', + 'removeWidget' => 'dashboard/admin:rw' + ]); + + // Load language phrases + $this->loadPhrases([ + 'ui' + ]); + + // Libraries + $this->load->library('dashboard/DashboardLib'); + + // Models + $this->load->model('ressource/Funktion_model', 'FunktionModel'); + } + + public function list($dashboard_kurzbz) + { + $sql = " + WITH + dashboard_presets AS ( + SELECT + * + FROM + dashboard.tbl_dashboard_preset dp + JOIN + dashboard.tbl_dashboard d ON d.dashboard_id = dp.dashboard_id + WHERE + d.dashboard_kurzbz = {$this->db->escape($dashboard_kurzbz)} + ), + general AS ( + SELECT + 'general' AS funktion_kurzbz, + 'Allgemein' AS beschreibung + ) + + ( + SELECT + f.funktion_kurzbz, + f.beschreibung, + COUNT(p.preset_id) AS has_preset + FROM + general f + LEFT JOIN + dashboard_presets p ON p.funktion_kurzbz IS NULL + GROUP BY + f.funktion_kurzbz, f.beschreibung + ) + UNION ALL + ( + SELECT + f.funktion_kurzbz, + f.beschreibung, + COUNT(p.preset_id) AS has_preset + FROM + public.tbl_funktion f + LEFT JOIN + dashboard_presets p ON p.funktion_kurzbz = f.funktion_kurzbz + GROUP BY + f.funktion_kurzbz, f.beschreibung + ORDER BY + f.beschreibung ASC + ) + "; + + $result = $this->FunktionModel->execReadOnlyQuery($sql); + + $funktionen = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($funktionen); + } + + public function getBatch() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('db', 'Dashboard', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $db = $this->input->post('db'); + $funktionen = $this->input->post('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->terminateWithSuccess($result); + } + + public function addWidget() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('dashboard', 'Dashboard', 'required'); + $this->form_validation->set_rules('funktion_kurzbz', 'Funktion', 'required'); + $this->form_validation->set_rules('widget[widget]', 'Widget', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $dashboard_kurzbz = $this->input->post('dashboard'); + $funktion_kurzbz = $this->input->post('funktion_kurzbz'); + $widget = $this->input->post('widget'); + + if (!isset($widget['widgetid'])) + $widget['widgetid'] = $this->dashboardlib->generateWidgetId($dashboard_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, [$widget]); + + $preset->preset = json_encode($preset_decoded); + + $result = $this->dashboardlib->insertOrUpdatePreset($preset); + + $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($widget['widgetid']); + } + + public function removeWidget() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('db', 'Dashboard', 'required'); + $this->form_validation->set_rules('funktion_kurzbz', 'Funktion', 'required'); + $this->form_validation->set_rules('widgetid', 'Widget', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $dashboard_kurzbz = $this->input->post('db'); + $funktion_kurzbz = $this->input->post('funktion_kurzbz'); + $widgetid = $this->input->post('widgetid'); + + $preset = $this->dashboardlib->getPreset($dashboard_kurzbz, $funktion_kurzbz); + if (!$preset) + show_404(); + + $preset_decoded = json_decode($preset->preset, true); + + if (!$this->dashboardlib->removeWidgetFromWidgets($preset_decoded, $funktion_kurzbz, $widgetid)) + show_404(); + + $preset->preset = json_encode($preset_decoded); + + $result = $this->dashboardlib->insertOrUpdatePreset($preset); + + $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess(array('msg' => $this->p->t('dashboard', 'success_savePreset'))); + } +} 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..9d020649e --- /dev/null +++ b/application/controllers/api/frontend/v1/dashboard/User.php @@ -0,0 +1,159 @@ +. + */ + +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); + $userconfig = $this->dashboardlib->getUserConfig($dashboard->dashboard_id, $uid); + + $defaultconfig_squashed = $defaultconfig ? call_user_func_array('array_replace_recursive', $defaultconfig) : []; + $userconfig_squashed = $userconfig ? call_user_func_array('array_replace_recursive', $userconfig) : []; + + $mergedconfig = array_replace_recursive($defaultconfig_squashed, $userconfig_squashed); + + $this->terminateWithSuccess([ + DashboardLib::SECTION_IF_FUNKTION_KURZBZ_IS_NULL => $mergedconfig + ]); + } + + public function addWidget() + { + $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; + + // NOTE(chris): remove doubles in other funktionen + foreach ($override_decoded as $funktion => $array) { + if ($funktion == 'general') + continue; + if (isset($array['widgets']) && isset($array['widgets'][$widget['widgetid']])) + unset($override_decoded[$funktion]['widgets'][$widget['widgetid']]); + } + + $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 (!$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/api/frontend/v1/dashboard/Widget.php b/application/controllers/api/frontend/v1/dashboard/Widget.php new file mode 100644 index 000000000..ac8c682e8 --- /dev/null +++ b/application/controllers/api/frontend/v1/dashboard/Widget.php @@ -0,0 +1,137 @@ +. + */ + +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 Widget extends FHCAPI_Controller +{ + public function __construct() + { + parent::__construct([ + 'get' => ['dashboard/benutzer:r', 'dashboard/admin:r'], + 'list' => 'dashboard/admin:r', + 'listAllowed' => ['dashboard/benutzer:rw', 'dashboard/admin:r'], + 'setAllowed' => 'dashboard/admin:rw' + ]); + + // Libraries + $this->load->library('dashboard/DashboardLib'); + + // Models + $this->load->model('dashboard/Widget_model', 'WidgetModel'); + } + + public function get($id) + { + $result = $this->WidgetModel->load($id); + + $widget = $this->getDataOrTerminateWithError($result); + + if (!$widget) + return $this->terminateWithSuccess([ + "widget_id" => 0, + "widget_kurzbz" => "notfound", + "arguments" => [ + "className" => 'alert-danger', + "title" => 'Widget Not Found', + "msg" => 'The widget with the id ' . $id . ' could not be found' + ], + "setup" => [ + "name" => 'Widget Not Found', + "file" => absoluteJsImportUrl('public/js/components/DashboardWidget/Default.js'), + "width" => 1, + "height" => 1 + ] + ]); + + $widget = current($widget); + $widget->arguments = json_decode($widget->arguments); + $tmpsetup = json_decode($widget->setup); + $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); + $widget->setup = $tmpsetup; + + $this->terminateWithSuccess($widget); + } + + public function list($dashboard) + { + $result = $this->WidgetModel->getWithAllowedForDashboard($dashboard); + + $widgets = $this->getDataOrTerminateWithError($result); + + $widgets = array_map(function ($widget) { + $widget->arguments = json_decode($widget->arguments); + $tmpsetup = json_decode($widget->setup); + $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); + $widget->setup = $tmpsetup; + return $widget; + }, $widgets); + + $this->terminateWithSuccess($widgets); + } + + public function listAllowed($dashboard) + { + $result = $this->WidgetModel->getForDashboard($dashboard); + + $widgets = $this->getDataOrTerminateWithError($result); + + $widgets = array_map(function ($widget) { + $widget->arguments = json_decode($widget->arguments); + $tmpsetup = json_decode($widget->setup); + $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); + $widget->setup = $tmpsetup; + return $widget; + }, $widgets); + + $this->terminateWithSuccess($widgets); + } + + public function setAllowed() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('dashboard_id', 'Dashboard', 'required'); + $this->form_validation->set_rules('widget_id', 'Widget', 'required'); + $this->form_validation->set_rules('allowed', 'Allowed', 'is_bool'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $data = [ + 'dashboard_id' => $this->input->post('dashboard_id'), + 'widget_id' => $this->input->post('widget_id') + ]; + + $this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel'); + + if ($this->input->post('allowed')) + $result = $this->DashboardWidgetModel->insert($data); + else + $result = $this->DashboardWidgetModel->delete($data); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } +} diff --git a/application/controllers/api/frontend/v1/notiz/NotizPerson.php b/application/controllers/api/frontend/v1/notiz/NotizPerson.php index 7f0645bc6..a047129d7 100644 --- a/application/controllers/api/frontend/v1/notiz/NotizPerson.php +++ b/application/controllers/api/frontend/v1/notiz/NotizPerson.php @@ -24,6 +24,7 @@ class NotizPerson extends Notiz_Controller //Load Models $this->load->model('person/Benutzer_model', 'BenutzerModel'); $this->load->model('crm/Student_model', 'StudentModel'); + $this->load->model('crm/Prestudent_model', 'PrestudentModel'); //Permission checks for allowed Oes if ($this->router->method == 'addNewNotiz') @@ -38,7 +39,7 @@ class NotizPerson extends Notiz_Controller { return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Person ID']), self::ERROR_TYPE_GENERAL); } - $this->_checkIfBerechtigungForOneUidExists($person_id, $allowedStgs); + $this->_checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs); } if ( $this->router->method == 'updateNotiz') @@ -59,7 +60,7 @@ class NotizPerson extends Notiz_Controller $person_id = current($data)->person_id; $allowedStgs = $this->permissionlib->getSTG_isEntitledFor('assistenz') ?: []; - $this->_checkIfBerechtigungForOneUidExists($person_id, $allowedStgs); + $this->_checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs); } if ($this->router->method == 'deleteNotiz' ) @@ -78,7 +79,7 @@ class NotizPerson extends Notiz_Controller } $allowedStgs = $this->permissionlib->getSTG_isEntitledFor('assistenz') ?: []; - $this->_checkIfBerechtigungForOneUidExists($person_id, $allowedStgs); + $this->_checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs); } } @@ -99,44 +100,20 @@ class NotizPerson extends Notiz_Controller } //stv: if person has permission of one studiengang of person -> permission to add/update/delete Note - private function _checkIfBerechtigungForOneUidExists($person_id, $allowedStgs) + private function _checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs) { - //get all studentUids of person_id - $result = $this->BenutzerModel->loadWhere(['person_id' => $person_id]); + $result = $this->PrestudentModel->loadWhere(['person_id' => $person_id]); $data = $this->getDataOrTerminateWithError($result); $checkarray = []; foreach ($data as $item) { - //check if isStudent - $result = $this->StudentModel->isStudent($item->uid); - - $isStudent = $this->getDataOrTerminateWithError($result); - if($isStudent) + if(in_array($item->studiengang_kz, $allowedStgs)) { - $checkarray[] = $this->_checkAllowedStgsFromUid($item->uid, $allowedStgs); + return true; } - } - if (!in_array(1, $checkarray)) - return $this->terminateWithError($this->p->t('ui', 'error_keineBerechtigungStg'), self::ERROR_TYPE_GENERAL); - } - private function _checkAllowedStgsFromUid($student_uid, $allowedStgs) - { - $this->load->model('crm/Student_model', 'StudentModel'); - $result = $this->StudentModel->loadWhere(['student_uid' => $student_uid]); - - $data = $this->getDataOrTerminateWithError($result); - $studiengang_kz = current($data)->studiengang_kz; - - if (!in_array($studiengang_kz, $allowedStgs)) - { - return 0; - } - else - { - return 1; - } + $this->terminateWithError($this->p->t('ui', 'error_keineBerechtigungStg'), self::ERROR_TYPE_GENERAL); } } diff --git a/application/controllers/dashboard/Admin.php b/application/controllers/dashboard/Admin.php new file mode 100644 index 000000000..702c04bab --- /dev/null +++ b/application/controllers/dashboard/Admin.php @@ -0,0 +1,52 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + */ +class Admin extends Auth_Controller +{ + /** + * Constructor + */ + public function __construct() + { + // Set required permissions + parent::__construct( + array( + 'index' => 'dashboard/admin:rw', + 'preview' => 'dashboard/admin:r', + ) + ); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + public function index() + { + $this->load->view('dashboard/admin.php', []); + } + + public function preview($dashboard_kurzbz = 'CIS') + { + $this->load->view('dashboard/preview.php', [ + 'dashboard_kurzbz' => $dashboard_kurzbz + ]); + } +} diff --git a/application/controllers/dashboard/Api.php b/application/controllers/dashboard/Api.php deleted file mode 100644 index 422bf0675..000000000 --- a/application/controllers/dashboard/Api.php +++ /dev/null @@ -1,76 +0,0 @@ - 'dashboard/admin:rw', - 'getNews' => 'dashboard/benutzer:r', - 'getAmpeln' => 'dashboard/benutzer:r', - ) - ); - - $this->load->library('AuthLib', null, 'AuthLib'); - - $this->_setAuthUID(); - } - - public function index() - { - echo 'Dashboard API Controller'; - } - - /** - * Get News. - */ - public function getNews() - { - $limit = $this->input->get('limit'); - - $this->load->model('content/News_model', 'NewsModel'); - - $result = $this->NewsModel->getAll($limit); - - if (hasData($result)) - { - $this->outputJson(getData($result), REST_Controller::HTTP_OK); - } - else - { - $this->terminateWithJsonError('fehler entdeckt'); - } - } - - - /** - * Get Ampeln. - */ - public function getAmpeln() - { - - $this->load->model('content/Ampel_model', 'AmpelModel'); - $result = $this->AmpelModel->getByUser($this->_uid); - - if (hasData($result)) - { - $this->outputJson(getData($result), REST_Controller::HTTP_OK); - } - else - { - $this->terminateWithJsonError('fehler entdeckt'); - } - } - - /** - * Retrieve the UID of the logged user and checks if it is valid - */ - private function _setAuthUID() - { - $this->_uid = getAuthUID(); - - if (!$this->_uid) show_error('User authentification failed'); - } -} 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/application/controllers/dashboard/Dashboard.php b/application/controllers/dashboard/Dashboard.php deleted file mode 100644 index 3773a6d73..000000000 --- a/application/controllers/dashboard/Dashboard.php +++ /dev/null @@ -1,86 +0,0 @@ - 'dashboard/admin:r', - 'create' => 'dashboard/admin:rw', - 'update' => 'dashboard/admin:rw', - 'delete' => 'dashboard/admin:rw' - ) - ); - - $this->load->library('dashboard/DashboardLib', null, 'DashboardLib'); - $this->load->model('dashboard/Dashboard_model', 'DashboardModel'); - } - - public function index() - { - $result = $this->DashboardModel->load(); - - if (isError($result)) { - http_response_code(404); - $this->terminateWithJsonError([ - 'error' => getError($result) - ]); - } - - return $this->outputJsonSuccess(getData($result) ?: []); - } - - public function create() - { - $input = $this->getPostJSON(); - - $result = $this->DashboardModel->insert($input); - - if (isError($result)) { - http_response_code(404); - $this->terminateWithJsonError([ - 'error' => getError($result) - ]); - } - - return $this->outputJsonSuccess(getData($result) ?: []); - } - - public function update() - { - $input = $this->getPostJSON(); - - $result = $this->DashboardModel->update($input->dashboard_id, $input); - - if (isError($result)) { - http_response_code(404); - $this->terminateWithJsonError([ - 'error' => getError($result) - ]); - } - - return $this->outputJsonSuccess(getData($result) ?: []); - } - - public function delete() - { - $input = $this->getPostJSON(); - - $result = $this->DashboardModel->delete($input->dashboard_id); - - if (isError($result)) { - http_response_code(404); - $this->terminateWithJsonError([ - 'error' => getError($result) - ]); - } - - return $this->outputJsonSuccess(getData($result) ?: []); - } -} diff --git a/application/controllers/dashboard/DashboardDemo.php b/application/controllers/dashboard/DashboardDemo.php deleted file mode 100644 index 35d530384..000000000 --- a/application/controllers/dashboard/DashboardDemo.php +++ /dev/null @@ -1,58 +0,0 @@ - 'dashboard/benutzer:r', - 'admin' => 'dashboard/admin:rw' - ) - ); - - $this->load->library('AuthLib'); - $this->load->library('WidgetLib'); - - $this->_setAuthUID(); // sets property uid - - $this->setControllerId(); // sets the controller id - } - - // ----------------------------------------------------------------------------------------------------------------- - // Public methods - public function index() - { - $this->load->view('dashboard/dashboard_demo.php', []); - } - - // ----------------------------------------------------------------------------------------------------------------- - // Public methods - public function admin() - { - $this->load->view('dashboard/dashboard_demo_admin.php', []); - } - - // ----------------------------------------------------------------------------------------------------------------- - // Private methods - - /** - * Retrieve the UID of the logged user and checks if it is valid - */ - private function _setAuthUID() - { - $this->_uid = getAuthUID(); - - if (!$this->_uid) show_error('User authentification failed'); - } -} diff --git a/application/controllers/dashboard/Widget.php b/application/controllers/dashboard/Widget.php deleted file mode 100644 index 9966ddc12..000000000 --- a/application/controllers/dashboard/Widget.php +++ /dev/null @@ -1,134 +0,0 @@ - ['dashboard/benutzer:r', 'dashboard/admin:r'], - 'getAll' => 'dashboard/admin:r', - 'getWidgetsForDashboard' => ['dashboard/benutzer:rw', 'dashboard/admin:r'], - 'setAllowed' => 'dashboard/admin:rw' - ) - ); - - $this->load->library('dashboard/DashboardLib', null, 'DashboardLib'); - $this->load->model('dashboard/Widget_model', 'WidgetModel'); - $this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel'); - } - - public function index() - { - $widget_id = $this->input->get('id'); - - $widget = $this->WidgetModel->load($widget_id); - - if (isError($widget) || !getData($widget)) - return $this->outputJsonSuccess([ - "widget_id" => 0, - "widget_kurzbz" => "notfound", - "arguments" => [ - "className" => 'alert-danger', - "title" => 'Widget Not Found', - "msg" => 'The widget with the id ' . $widget_id . ' could not be found' - ], - "setup" => [ - "name" => 'Widget Not Found', - "file" => absoluteJsImportUrl('public/js/components/DashboardWidget/Default.js'), - "width" => 1, - "height" => 1 - ] - ]); - - $widget = current(getData($widget)); - $widget->arguments = json_decode($widget->arguments); - $tmpsetup = json_decode($widget->setup); - $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); - $widget->setup = $tmpsetup; - - return $this->outputJsonSuccess($widget); - } - - public function getAll() - { - $dashboard_id = $this->input->get('dashboard_id'); - $result = $this->WidgetModel->getWithAllowedForDashboard($dashboard_id); - - if (isError($result)) - return $this->outputJsonError(getError($result)); - - $tmpwidgets = getData($result) ?: []; - $widgets = array_map(function($widget) { - $widget->arguments = json_decode($widget->arguments); - $tmpsetup = json_decode($widget->setup); - $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); - $widget->setup = $tmpsetup; - return $widget; - }, $tmpwidgets); - - $this->outputJsonSuccess($widgets); - } - - public function getWidgetsForDashboard() - { - $db = $this->input->get('db'); - $result = $this->WidgetModel->getForDashboard($db); - - if (isError($result)) { - http_response_code(404); - $this->terminateWithJsonError([ - 'error' => getError($result) - ]); - } - - $tmpwidgets = getData($result) ?: []; - $widgets = array_map(function($widget) { - $widget->arguments = json_decode($widget->arguments); - $tmpsetup = json_decode($widget->setup); - $tmpsetup->file = absoluteJsImportUrl($tmpsetup->file); - $widget->setup = $tmpsetup; - return $widget; - }, $tmpwidgets); - - $this->outputJsonSuccess($widgets); - } - - public function setAllowed() - { - $input = $this->getPostJSON(); - - $dashboard_id = $input->dashboard_id; - $widget_id = $input->widget_id; - $action = $input->action; - - if ($action == 'add') { - $result = $this->DashboardWidgetModel->insert([ - 'dashboard_id' => $dashboard_id, - 'widget_id' => $widget_id - ]); - } elseif ($action == 'delete') { - $result = $this->DashboardWidgetModel->delete([ - 'dashboard_id' => $dashboard_id, - 'widget_id' => $widget_id - ]); - } else { - http_response_code(404); // TODO(chris): 400? - $this->terminateWithJsonError([ - 'error' => 'action value invalid' - ]); - } - if (isError($result)) { - http_response_code(404); - $this->terminateWithJsonError([ - 'error' => getError($result) - ]); - } - return $this->outputJsonSuccess(getData($result)); - } -} diff --git a/application/controllers/jobs/AbgabetoolJob.php b/application/controllers/jobs/AbgabetoolJob.php index 9b59a72e7..b81053032 100644 --- a/application/controllers/jobs/AbgabetoolJob.php +++ b/application/controllers/jobs/AbgabetoolJob.php @@ -495,6 +495,10 @@ class AbgabetoolJob extends JOB_Controller // get all new or changed termine in interval $result = $this->_ci->PaabgabeModel->findAbgabenNewOrUpdatedSince($interval, $relevantTypes); $retval = getData($result); + if(!$retval) { + $this->_ci->logInfo("Keine Emails an Betreuer über neue oder veränderte Termine versandt"); + return; + } // group changed/new abgaben for projektarbeiten $projektarbeiten = []; @@ -557,6 +561,8 @@ class AbgabetoolJob extends JOB_Controller $anredeFillString = $data->anrede == "Herr" ? "r" : ""; $fullFormattedNameString = $data->first; + $relevantCounter = 0; // workaround to check if a betreuer needs to have any notification about relevant + // abgaben at all to avoid sending empty emails since we filter on certain conditions forEach($tupelArr as $tupel) { $projektarbeit_id = $tupel[0]; $betreuerRow = $tupel[1]; @@ -575,6 +581,8 @@ class AbgabetoolJob extends JOB_Controller continue; } + $relevantCounter++; + // format the Student Name $s = $relevantAbgaben[0]; $nameParts = []; @@ -633,6 +641,11 @@ class AbgabetoolJob extends JOB_Controller // done with building the change list, now send it $betreuerRow = $tupelArr[0][1]; + if($relevantCounter == 0) { + $this->_ci->logInfo('No Relevant Abgaben to notify Betreuer PersonID: "'.$betreuerRow->person_id.'".'); + continue; + } + $path = $this->_ci->config->item('URL_MITARBEITER'); $url = CIS_ROOT.$path; diff --git a/application/libraries/PermissionLib.php b/application/libraries/PermissionLib.php index 42502f999..d3fdc6642 100644 --- a/application/libraries/PermissionLib.php +++ b/application/libraries/PermissionLib.php @@ -50,6 +50,7 @@ class PermissionLib const LOGINAS_PERSONIDS_BLACKLIST = 'permission_loginas_personids_blacklist'; private $_ci; // CI instance + private $access_rights; // current users access rights private static $bb; // benutzerberechtigung /** @@ -61,6 +62,8 @@ class PermissionLib // Loads CI instance $this->_ci =& get_instance(); + $this->access_rights = null; + $this->_ci->config->load('permission'); // Loads permission configuration // If it's NOT called from command line @@ -69,8 +72,10 @@ class PermissionLib // API Caller rights initialization $authObj = $this->_ci->authlib->getAuthObj(); self::$bb = new benutzerberechtigung(); - if ($authObj) + if ($authObj) { self::$bb->getBerechtigungen($authObj->{AuthLib::AO_USERNAME}); + $this->access_rights = self::$bb->berechtigungen; + } } } @@ -340,6 +345,16 @@ class PermissionLib } } + /** + * Returns the access rights for the current user + * + * @return array|null + */ + public function getAccessRights() + { + return $this->access_rights; + } + //------------------------------------------------------------------------------------------------------------------ // Private methods diff --git a/application/libraries/dashboard/DashboardLib.php b/application/libraries/dashboard/DashboardLib.php index f6d7d6599..1c3983108 100644 --- a/application/libraries/dashboard/DashboardLib.php +++ b/application/libraries/dashboard/DashboardLib.php @@ -49,7 +49,7 @@ class DashboardLib public function getMergedConfig($dashboard_id, $uid) { - $defaultconfig = $this->getDefaultConfig($dashboard_id, $uid); + $defaultconfig = $this->getDefaultConfig($dashboard_id); $userconfig = $this->getUserConfig($dashboard_id, $uid); $mergedconfig = array_replace_recursive($defaultconfig, $userconfig); @@ -57,14 +57,31 @@ class DashboardLib return $mergedconfig; } - public function getDefaultConfig($dashboard_id, $uid) + public function getDefaultConfig($dashboard_id) { - $res_presets = $this->_ci->DashboardPresetModel->getPresets($dashboard_id, $uid); + $funktion_kurzbzs = []; + $rights = $this->_ci->permissionlib->getAccessRights(); + if ($rights) + $funktion_kurzbzs = array_unique(array_map(function ($right) { + return $right->funktion_kurzbz; + }, $rights)); + + $this->_ci->DashboardPresetModel->db + ->group_start() + ->where_in('funktion_kurzbz', $funktion_kurzbzs) + ->or_where('funktion_kurzbz IS NULL') + ->group_end(); + + $this->_ci->DashboardPresetModel->addOrder('funktion_kurzbz', 'DESC'); + + $result = $this->_ci->DashboardPresetModel->loadWhere([ + 'dashboard_id' => $dashboard_id + ]); $defaultconfig = array(); - if (hasData($res_presets)) + if (hasData($result)) { - $presets = getData($res_presets); + $presets = getData($result); foreach ($presets as $presetobj) { $preset = json_decode($presetobj->preset, true); @@ -137,8 +154,10 @@ class DashboardLib $dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz); $funktion_kurzbz = ($section === self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL) ? null : $section; - $result = $this->_ci->DashboardPresetModel - ->getPresetByDashboardAndFunktion($dashboard->dashboard_id, $funktion_kurzbz); + $result = $this->_ci->DashboardPresetModel->loadWhere([ + 'dashboard_id' => $dashboard->dashboard_id, + 'funktion_kurzbz' => $funktion_kurzbz + ]); if (hasData($result)) { @@ -195,11 +214,11 @@ class DashboardLib { foreach ($addwigets as $widget) { - if(!isset($widget->widgetid)) + if(!isset($widget['widgetid'])) { - $widget->widgetid = $this->generateWidgetId($dashboard_kurzbz); + $widget['widgetid'] = $this->generateWidgetId($dashboard_kurzbz); } - $this->addWidgetToWidgets($widgets, $section, $widget, $widget->widgetid); + $this->addWidgetToWidgets($widgets, $section, $widget, $widget['widgetid']); } } diff --git a/application/models/dashboard/Dashboard_Preset_model.php b/application/models/dashboard/Dashboard_Preset_model.php index ca10ce98a..42570d091 100644 --- a/application/models/dashboard/Dashboard_Preset_model.php +++ b/application/models/dashboard/Dashboard_Preset_model.php @@ -11,57 +11,4 @@ class Dashboard_Preset_model extends DB_Model $this->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) - { - // 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)); - } } diff --git a/application/views/dashboard/dashboard_demo.php b/application/views/dashboard/admin.php similarity index 67% rename from application/views/dashboard/dashboard_demo.php rename to application/views/dashboard/admin.php index 8efc230b7..1e338e125 100644 --- a/application/views/dashboard/dashboard_demo.php +++ b/application/views/dashboard/admin.php @@ -8,9 +8,15 @@ $this->load->view( 'axios027' => true, 'restclient' => true, 'vue3' => true, - 'customJSModules' => ['public/js/apps/Dashboard.js'], + 'primevue3' => true, + 'vuedatepicker11' => true, + 'customJSs' => [ + 'vendor/moment/luxonjs/luxon.min.js' + ], + 'customJSModules' => ['public/js/apps/Dashboard/Admin.js'], 'customCSSs' => [ - 'public/css/components/dashboard.css' + 'public/css/components/dashboard.css', + 'public/css/components/primevue.css', ], 'navigationcomponent' => true ) @@ -25,7 +31,7 @@ $this->load->view(

Dashboard

- + diff --git a/application/views/dashboard/dashboard_demo_admin.php b/application/views/dashboard/preview.php similarity index 67% rename from application/views/dashboard/dashboard_demo_admin.php rename to application/views/dashboard/preview.php index 0d92146a8..f8c37c0c8 100644 --- a/application/views/dashboard/dashboard_demo_admin.php +++ b/application/views/dashboard/preview.php @@ -8,7 +8,12 @@ $this->load->view( 'axios027' => true, 'restclient' => true, 'vue3' => true, - 'customJSModules' => ['public/js/apps/DashboardAdmin.js'], + 'vuedatepicker11' => true, + 'primevue3' => true, + 'customJSs' => [ + 'vendor/moment/luxonjs/luxon.min.js' + ], + 'customJSModules' => ['public/js/apps/Dashboard/Preview.js'], 'customCSSs' => [ 'public/css/components/dashboard.css' ], @@ -23,9 +28,9 @@ $this->load->view(
-

Dashboard

+

Dashboard

- +
diff --git a/content/student/studentoverlay.js.php b/content/student/studentoverlay.js.php index f64ed5f91..9ebe73cfe 100644 --- a/content/student/studentoverlay.js.php +++ b/content/student/studentoverlay.js.php @@ -3550,9 +3550,11 @@ function StudentZeugnisDokumentArchivieren() case 'microcredentialzertifikat_1': case 'microcredentialzertifikat_2': case 'microcredentialzertifikat_3': + case 'microcredentialzertifikat_4': case 'microcredential_1': case 'microcredential_2': case 'microcredential_3': + case 'microcredential_4': xml = 'microcredential.xml.php'; break; diff --git a/include/lehreinheitmitarbeiter.class.php b/include/lehreinheitmitarbeiter.class.php index 80cdd0c8b..b892863e8 100644 --- a/include/lehreinheitmitarbeiter.class.php +++ b/include/lehreinheitmitarbeiter.class.php @@ -552,9 +552,40 @@ class lehreinheitmitarbeiter extends basis_db $beginn = new DateTime($beginn); $ende = new DateTime($ende); + // get relevant Studiensemester + $studiensemester_kurzbz_arr = []; + + $qry = ' + SELECT + studiensemester_kurzbz + FROM + public.tbl_studiensemester + WHERE + start BETWEEN + '. $this->db_add_param($beginn->format('Y-m-d')). ' AND + '. $this->db_add_param($ende->format('Y-m-d')); + + if ($this->db_query($qry)) + { + while($row = $this->db_fetch_object()) + { + $studiensemester_kurzbz_arr[] = $row->studiensemester_kurzbz; + } + } + else + { + $this->errormsg = 'Fehler bei der Datenbankabfrage'; + return false; + } + + $lehrgaengeDistr = $this->_getLehrgaengeForDistribution($studiensemester_kurzbz_arr); + + if (!is_array($studiensemester_kurzbz_arr) || empty($studiensemester_kurzbz_arr)) return true; + $qry = ' WITH semester_sws_tbl AS ( - SELECT DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden, stg.studiengang_kz + SELECT DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden, + stg.studiengang_kz, stg.melde_studiengang_kz, stg.lgartcode FROM lehre.tbl_lehreinheitmitarbeiter lema JOIN lehre.tbl_lehreinheit USING (lehreinheit_id) JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id) @@ -564,38 +595,103 @@ class lehreinheitmitarbeiter extends basis_db JOIN public.tbl_studiengang stg ON stg.studiengang_kz = sto.studiengang_kz JOIN public.tbl_studiensemester ss USING (studiensemester_kurzbz) WHERE mitarbeiter_uid = '. $this->db_add_param($uid). ' - AND ( - ss.start BETWEEN - '. $this->db_add_param($beginn->format('Y-m-d')). ' AND - '. $this->db_add_param($ende->format('Y-m-d')). ') + AND ss.studiensemester_kurzbz IN ('.$this->implode4SQL($studiensemester_kurzbz_arr).') -- nur lehre, die bisgemeldet wird AND lema.bismelden -- keine lehreinheiten ohne semesterstunden - AND lema.semesterstunden != 0 + AND lema.semesterstunden != 0 ) SELECT studiengang_kz, studiensemester_kurzbz, + melde_studiengang_kz, + lgartcode, sum(semesterstunden) AS summe, round(sum(semesterstunden) / 15, 2) AS sws FROM semester_sws_tbl GROUP BY studiengang_kz, - studiensemester_kurzbz + studiensemester_kurzbz, + melde_studiengang_kz, + lgartcode ORDER BY studiengang_kz; '; if ($this->db_query($qry)) { + $additionalLehrgaenge = []; + while($row = $this->db_fetch_object()) { $obj = new StdClass(); $obj->studiengang_kz = $row->studiengang_kz; $obj->studiensemester_kurzbz = $row->studiensemester_kurzbz; + $obj->melde_studiengang_kz = $row->melde_studiengang_kz; + $obj->lgartcode = $row->lgartcode; $obj->sws = $row->sws; + + if (isset($lehrgaengeDistr[$uid][$row->studiensemester_kurzbz])) + { + $lehrgaenge = $lehrgaengeDistr[$uid][$row->studiensemester_kurzbz]; + + foreach ($lehrgaenge as $lehreinheit_id => $lehrgangKzArr) + { + // wenn lehrgang gefunden, zusammenhängende Lehrgaenge holen und sws aufteilen + if (array_key_exists($row->studiengang_kz, $lehrgangKzArr)) + { + foreach ($lehrgangKzArr as $studiengang_kz => $lehrgang) + { + // check: nur eine Studiengangsverknüpfung pro Mitarbeiter, Semester, und Referenzstudiengang + if ( + $studiengang_kz == $row->studiengang_kz + || isset( + $additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz][$studiengang_kz] + ) + ) continue; + + // Lehrgang erstellen + $lg = new StdClass(); + $lg->mitarbeiter_uid = $uid; + $lg->melde_studiengang_kz = $lehrgang->melde_studiengang_kz; + $lg->lgartcode = $lehrgang->lgartcode; + $lg->studiengang_kz = $lehrgang->studiengang_kz; + $lg->studiensemester_kurzbz = $lehrgang->studiensemester_kurzbz; + $lg->summe = $row->summe; + $lg->sws = $row->sws; + // Lehrgang, der mit Ursprungsstudiengang aufgrund lehreinheit "verknüpft" ist, hinzufügen + $additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz][$studiengang_kz] = $lg; + } + } + } + + // ignorieren, wenn für den Studiengang keine verknüpften Lehrgaenge hat + if (isset($additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz])) + { + $addLehrgaenge = $additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz]; + + // sws Durchschnitt über alle verknuepften Lehrgaenge berechnet + $summeSws = $row->summe/(count($addLehrgaenge) + 1); + $sws = $row->sws/(count($addLehrgaenge) + 1); + + // neue sws zuweisen + $obj->summe = $summeSws; + $obj->sws = $sws; + + foreach ($addLehrgaenge as $conn_ws_studiengang_kz => $lehrgang) + { + // sws fuer jeden verknuepften Lehrgang zuweisen + $lehrgang->summe = $summeSws; + $lehrgang->sws = $sws; + + // neue lehrgang sws hinzufuegen + $this->result [] = $lehrgang; + } + } + } + $this->result []= $obj; } return true; @@ -655,4 +751,63 @@ class lehreinheitmitarbeiter extends basis_db return false; } + /** + * Get "connected" Lehrgaenge for equal sws distribution. + * @param $studiensemester_kurzbz_arr all semester for which Lehrgaenge should be retrieved + * @return object success or error + */ + private function _getLehrgaengeForDistribution($studiensemester_kurzbz_arr) + { + if (!is_array($studiensemester_kurzbz_arr) || empty($studiensemester_kurzbz_arr)) return []; + + $qry = " + WITH gruppen AS ( + SELECT + mitarbeiter_uid, lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, sem.start, sem.ende, + lehreinheitgruppe_id, studiengang_kz, melde_studiengang_kz, lgartcode + FROM + lehre.tbl_lehreinheitmitarbeiter lema + JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id) + JOIN lehre.tbl_lehreinheitgruppe legr USING (lehreinheit_id) + JOIN public.tbl_studiengang stg USING (studiengang_kz) + JOIN public.tbl_studiensemester sem USING (studiensemester_kurzbz) + WHERE + bismelden + AND stg.melderelevant + AND stg.typ = 'l' + AND le.studiensemester_kurzbz IN (".$this->implode4SQL($studiensemester_kurzbz_arr).") + ) + SELECT + DISTINCT mitarbeiter_uid, studiensemester_kurzbz, lehreinheit_id, studiengang_kz, melde_studiengang_kz, lgartcode + FROM + gruppen gr + GROUP BY + mitarbeiter_uid, studiensemester_kurzbz, lehreinheit_id, studiengang_kz, melde_studiengang_kz, lgartcode + ORDER BY + mitarbeiter_uid, studiensemester_kurzbz, lehreinheit_id, studiengang_kz, melde_studiengang_kz, lgartcode"; + + $lehrgaengeDistributions = []; + + if($this->db_query($qry)) + { + while($row = $this->db_fetch_object()) + { + // group by properties + $lehrgaengeDistributions + [$row->mitarbeiter_uid] + [$row->studiensemester_kurzbz] + [$row->lehreinheit_id] + [$row->studiengang_kz] + = $row; + } + } + else + { + $this->errormsg = 'Fehler bei der Datenbankabfrage'; + return false; + } + + return $lehrgaengeDistributions; + } + } diff --git a/include/lehrelisthelper.class.php b/include/lehrelisthelper.class.php index b1821391f..f511b27cc 100644 --- a/include/lehrelisthelper.class.php +++ b/include/lehrelisthelper.class.php @@ -270,6 +270,8 @@ class LehreListHelper } else if ($row->bisio_id != '' && $row->status != 'Incoming' && ($row->von > $stsemdatumvon || $row->von == '')) { // if bis datum is not yet known but von is available already $zusatz .= '(o)(ab '.$datum->formatDatum($row->von, 'd.m.Y').')'; + } else if ($row->bisio_id != '' && $row->status != 'Incoming' && ($row->von <= $stsemdatumvon || $row->von == '') && ($row->bis == '' || $row->bis > date('Y-m-d'))){ + $zusatz .= '(o)(ab '.$datum->formatDatum($row->von, 'd.m.Y').')'; } diff --git a/public/css/components/dashboard/news.css b/public/css/components/dashboard/news.css index 4c4616aa5..116c96ddd 100644 --- a/public/css/components/dashboard/news.css +++ b/public/css/components/dashboard/news.css @@ -193,3 +193,6 @@ word-break: break-word; } +.news-list-item p { + word-break: break-word; +} \ No newline at end of file diff --git a/public/js/api/factory/dashboard/board.js b/public/js/api/factory/dashboard/board.js new file mode 100644 index 000000000..b5e9b5be3 --- /dev/null +++ b/public/js/api/factory/dashboard/board.js @@ -0,0 +1,46 @@ +/** + * 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 { + list() { + return { + method: 'get', + url: 'api/frontend/v1/dashboard/board/list' + }; + }, + add(params) { + return { + method: 'post', + url: 'api/frontend/v1/dashboard/board/create', + params + }; + }, + update(params) { + return { + method: 'post', + url: 'api/frontend/v1/dashboard/board/update', + params + }; + }, + delete(dashboard_id) { + return { + method: 'post', + url: 'api/frontend/v1/dashboard/board/delete', + params: { dashboard_id } + }; + } +} \ No newline at end of file diff --git a/public/js/api/factory/dashboard/preset.js b/public/js/api/factory/dashboard/preset.js new file mode 100644 index 000000000..3f380581e --- /dev/null +++ b/public/js/api/factory/dashboard/preset.js @@ -0,0 +1,47 @@ +/** + * 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 { + list(dashboard_kurzbz) { + return { + method: 'get', + url: 'api/frontend/v1/dashboard/preset/list/' + + encodeURIComponent(dashboard_kurzbz) + }; + }, + getBatch(params) { + return { + method: 'post', + url: 'api/frontend/v1/dashboard/preset/getBatch', + params + }; + }, + addWidget(params) { + return { + method: 'post', + url: 'api/frontend/v1/dashboard/preset/addWidget', + params + }; + }, + removeWidget(params) { + return { + method: 'post', + url: 'api/frontend/v1/dashboard/preset/removeWidget', + params + }; + } +}; \ No newline at end of file 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/api/factory/dashboard/widget.js b/public/js/api/factory/dashboard/widget.js new file mode 100644 index 000000000..d7d1bbc86 --- /dev/null +++ b/public/js/api/factory/dashboard/widget.js @@ -0,0 +1,46 @@ +/** + * 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(widget) { + return { + method: 'get', + url: '/api/frontend/v1/dashboard/widget/get/' + widget + }; + }, + list(dashboard) { + return { + method: 'get', + url: '/api/frontend/v1/dashboard/widget/list/' + dashboard + }; + }, + listAllowed(dashboard) { + return { + method: 'get', + url: '/api/frontend/v1/dashboard/widget/listAllowed/' + dashboard + }; + }, + setAllowed(dashboard_id, widget_id, allowed) { + return { + method: 'post', + url: '/api/frontend/v1/dashboard/widget/setAllowed', + params: { + dashboard_id, widget_id, allowed + } + }; + } +}; \ No newline at end of file diff --git a/public/js/apps/Dashboard/Admin.js b/public/js/apps/Dashboard/Admin.js index 4a5fcee2f..32909a50a 100644 --- a/public/js/apps/Dashboard/Admin.js +++ b/public/js/apps/Dashboard/Admin.js @@ -1,16 +1,67 @@ -import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js'; +import { CoreNavigationCmpt } from '../../components/navigation/Navigation.js'; import DashboardAdmin from '../../components/Dashboard/Admin.js'; + import PluginsPhrasen from '../../plugins/Phrasen.js'; +import ApiRenderers from '../../api/factory/renderers.js'; + const app = Vue.createApp({ - name: 'AdminApp', - data: () => ({ - appSideMenuEntries: {} - }), - components: { - CoreNavigationCmpt, - DashboardAdmin - } + name: 'DashboardAdminApp', + data: () => ({ + appSideMenuEntries: {}, + renderers: null + }), + components: { + CoreNavigationCmpt, + DashboardAdmin + }, + provide() { + return { + // TODO(chris): move those two into the components that need it + renderers: Vue.computed(() => this.renderers), + timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone + }; + }, + created() { + this.$api + .call(ApiRenderers.loadRenderers()) + .then(res => { + for (let rendertype of Object.keys(res.data)) { + let modalTitle = null; + let modalContent = null; + let calendarEvent = null; + if (res.data[rendertype].modalTitle) + modalTitle = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].modalTitle))); + if (res.data[rendertype].modalContent) + modalContent = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].modalContent))); + if (res.data[rendertype].calendarEvent) + calendarEvent = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].calendarEvent))); + + if (res.data[rendertype].calendarEventStyles) { + var head = document.head; + if (!head.querySelector(`link[href="${res.data[rendertype].calendarEventStyles}"]`)) { + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = res.data[rendertype].calendarEventStyles; + head.appendChild(link); + } + } + + if (this.renderers === null) { + this.renderers = {}; + } + if (!this.renderers[rendertype]) { + this.renderers[rendertype] = {} + } + this.renderers[rendertype].modalTitle = modalTitle; + this.renderers[rendertype].modalContent = modalContent; + this.renderers[rendertype].calendarEvent = calendarEvent; + } + }) + .catch(this.$fhcAlert.handleSystemErrors); + } }); app.use(PluginsPhrasen); +app.directive('tooltip', primevue.tooltip); app.mount('#main'); \ No newline at end of file diff --git a/public/js/apps/Dashboard/Preview.js b/public/js/apps/Dashboard/Preview.js new file mode 100644 index 000000000..a270c3028 --- /dev/null +++ b/public/js/apps/Dashboard/Preview.js @@ -0,0 +1,17 @@ +import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js'; +import CoreDashboard from '../../components/Dashboard/Dashboard.js'; +import PluginsPhrasen from '../../plugins/Phrasen.js'; + +const app = Vue.createApp({ + name: 'DashboardPreviewApp', + data: () => ({ + appSideMenuEntries: {} + }), + components: { + CoreNavigationCmpt, + CoreDashboard + } +}); +app.use(PluginsPhrasen); +app.directive('tooltip', primevue.tooltip); +app.mount('#main'); \ No newline at end of file diff --git a/public/js/apps/DashboardAdmin.js b/public/js/apps/DashboardAdmin_DEPR.js similarity index 100% rename from public/js/apps/DashboardAdmin.js rename to public/js/apps/DashboardAdmin_DEPR.js diff --git a/public/js/components/Bootstrap/Confirm.js b/public/js/components/Bootstrap/Confirm.js index a56885473..f6ac52abc 100644 --- a/public/js/components/Bootstrap/Confirm.js +++ b/public/js/components/Bootstrap/Confirm.js @@ -17,7 +17,7 @@ export default { ` } diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index d5caf97a6..bb5c6a710 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -180,7 +180,7 @@ export const AbgabetoolAssistenz = { // frozen: true, // width: 40 // }, - {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', headerFilter: false, headerSort: false, formatter: this.formAction, tooltip:false, minWidth: 150, cssClass: 'sticky-col'}, + {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', headerFilter: false, headerSort: false, formatter: this.formAction, tooltip:false, minWidth: 100, cssClass: 'sticky-col'}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'student_vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'student_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1}, @@ -226,7 +226,7 @@ export const AbgabetoolAssistenz = { field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}, ], persistence: false, - persistenceID: "abgabetool_2026_02_26" + persistenceID: "abgabetool_2026_03_16" }, abgabeTableEventHandlers: [ { @@ -645,7 +645,7 @@ export const AbgabetoolAssistenz = { actionButtons.className = "d-flex gap-3"; // you can keep Bootstrap gap if loaded actionButtons.style.display = "flex"; actionButtons.style.alignItems = "stretch"; // buttons stretch to full height - actionButtons.style.justifyContent = "center"; + actionButtons.style.justifyContent = "start"; actionButtons.style.height = "100%"; // full grid cell height const val = cell.getValue(); @@ -675,8 +675,20 @@ export const AbgabetoolAssistenz = { createButton('fa fa-timeline', 'abgabetool/c4termineTimeLine', () => this.openTimeline(val)) ); + if(val.latestTerminWithUpload) { + actionButtons.append( + createButton('fa fa-download', 'abgabetool/c4downloadLatestAbgabe', () => this.downloadAbgabe(val.latestTerminWithUpload.paabgabe_id, val.student_uid, val.projektarbeit_id)) + ) + } + return actionButtons; }, + downloadAbgabe(paabgabe_id, student_uid, projektarbeit_id) { + const url = `/api/frontend/v1/Abgabe/getStudentProjektarbeitAbgabeFile?paabgabe_id=${paabgabe_id}&student_uid=${student_uid}&projektarbeit_id=${projektarbeit_id}`; + + window.open(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + url) + // this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid)) + }, undoSelection(cell) { // checks if cells row is selected and unselects -> imitates columns which dont trigger row selection @@ -780,6 +792,8 @@ export const AbgabetoolAssistenz = { // TODO: mehrsprachig englisch projekt.note_bez = opt.bezeichnung } + + const latestTerminWithUpload = this.findLatestTerminWithUpload(projekt) return { ...projekt, @@ -787,6 +801,7 @@ export const AbgabetoolAssistenz = { details: { student_uid: projekt.student_uid, projektarbeit_id: projekt.projektarbeit_id, + latestTerminWithUpload: latestTerminWithUpload ?? null }, pkz: this.buildPKZ(projekt), beurteilung: projekt.beurteilungLink ?? null, @@ -800,6 +815,15 @@ export const AbgabetoolAssistenz = { } }) }, + findLatestTerminWithUpload(projekt) { + const withAbgabedatumSorted = projekt?.abgabetermine?.filter(t => t.abgabedatum != null)?.sort((a,b) => a < b) + + if(withAbgabedatumSorted.length) { + return withAbgabedatumSorted[0] + } + + return null + }, createInfoString(data) { let str = ''; @@ -1413,9 +1437,12 @@ export const AbgabetoolAssistenz = {
-
+

{{$p.t('abgabetool/abgabetoolTitle')}}

+
+ +
+
+ +
projekarbeit.projektarbeit_id == details.projektarbeit_id) + const projektarbeiten = this.projektarbeiten?.retval ?? this.projektarbeiten + const pa = projektarbeiten.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id) + let paIsBenotet = false if(pa.note !== undefined && pa.note !== null) { // check if the note is not defined as a non final projektarbeit note diff --git a/public/js/components/Dashboard/Admin.js b/public/js/components/Dashboard/Admin.js index f1a837880..ff117a956 100644 --- a/public/js/components/Dashboard/Admin.js +++ b/public/js/components/Dashboard/Admin.js @@ -3,15 +3,20 @@ import DashboardAdminEdit from "./Admin/Edit.js"; import DashboardAdminWidgets from "./Admin/Widgets.js"; import DashboardAdminPresets from "./Admin/Presets.js"; +import ApiDashboardBoard from "../../api/factory/dashboard/board.js"; +import ApiDashboardWidget from "../../api/factory/dashboard/widget.js"; + export default { + name: 'DashboardAdmin', components: { DashboardAdminEdit, DashboardAdminWidgets, - DashboardAdminPresets + DashboardAdminPresets, }, provide() { return { - adminMode: true + adminMode: true, + widgetsSetup: Vue.computed(() => this.dashboards[this.current] ? this.dashboards[this.current].widgetSetup : null) }; }, data() { @@ -22,9 +27,6 @@ export default { }; }, computed: { - apiurl() { - return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard'; - }, dashboard() { return this.dashboards.find(el => el.dashboard_id == this.current); } @@ -35,33 +37,50 @@ export default { BsPrompt.popup('New Dashboard name').then( name => { _name = name; - return axios.post(this.apiurl + '/Dashboard/create', { + const params = { dashboard_kurzbz: name - }) - } - ).then(res => { - let newDashboard = { - dashboard_id: res.data.retval, - dashboard_kurzbz: _name, - beschreibung: '' - }; - this.dashboards.push(newDashboard); - this.current = newDashboard.dashboard_id; - }).catch(err => err !== undefined ? console.error('ERROR:', err) : 0); + }; + return this.$api + .call(ApiDashboardBoard.add(params)) + .then(response =>{ + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); + + let newDashboard = { + dashboard_id: response.data, + dashboard_kurzbz: _name, + beschreibung: '' + }; + this.dashboards.push(newDashboard); + this.current = newDashboard.dashboard_id; + }) + .catch(this.$fhcAlert.handleSystemError); + }); }, dashboardUpdate(dashboard) { - // TODO(chris): Loading or message - axios.post(this.apiurl + '/Dashboard/update', dashboard).then(() => { - let old = this.dashboards.find(el => el.dashboard_id == dashboard.dashboard_id); - old.dashboard_kurzbz = dashboard.dashboard_kurzbz; - old.beschreibung = dashboard.beschreibung; - }).catch(err => console.error('ERROR:', err)); + return this.$api + .call(ApiDashboardBoard.update(dashboard)) + .then(response =>{ + + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); + + let old = this.dashboards.find(el => el.dashboard_id == dashboard.dashboard_id); + old.dashboard_kurzbz = dashboard.dashboard_kurzbz; + old.beschreibung = dashboard.beschreibung; + }) + .catch(this.$fhcAlert.handleSystemError); }, dashboardDelete(dashboard_id) { - axios.post(this.apiurl + '/Dashboard/delete', {dashboard_id}).then(() => { - this.current = -1; - this.dashboards = this.dashboards.filter(el => el.dashboard_id != dashboard_id); - }).catch(err => console.error('ERROR:', err)); + return this.$api + .call(ApiDashboardBoard.delete(dashboard_id)) + .then(response => { + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete')); + + }) + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.current = -1; + this.dashboards = this.dashboards.filter(el => el.dashboard_id != dashboard_id); + }); }, assignWidgets(widgets) { this.widgets = widgets; @@ -72,22 +91,35 @@ export default { } }, created() { - axios.get(this.apiurl + '/Dashboard').then(res => { - this.dashboards = res.data.retval; - }).catch(err => console.error('ERROR:', err)); + this.$api + .call(ApiDashboardBoard.list()) + .then(result => { + this.dashboards = result.data.retval; + for (const dashboard of this.dashboards) { + this.$api + .call(ApiDashboardWidget.list(dashboard.dashboard_id)) + .then(res => { + dashboard.widgetSetup = res.data; + }) + .catch(this.$fhcAlert.handleSystemError); + } + }) + .catch(this.$fhcAlert.handleSystemError); }, template: `
+
- -
+
- +
diff --git a/public/js/components/Dashboard/Admin/Edit.js b/public/js/components/Dashboard/Admin/Edit.js index ec841a9ad..c40d91183 100644 --- a/public/js/components/Dashboard/Admin/Edit.js +++ b/public/js/components/Dashboard/Admin/Edit.js @@ -18,7 +18,8 @@ export default { }, methods: { sendDelete() { - BsConfirm.popup('Sure?').then(() => this.$emit('delete', this.dashboard_id)).catch(); + BsConfirm.popup(this.$p.t('ui', 'confirm_delete') + " " + this.$p.t('ui', 'deleteInfo')) + .then(() => this.$emit('delete', this.dashboard_id)).catch(); } }, template: `
@@ -31,8 +32,8 @@ export default {
- - + +
` } diff --git a/public/js/components/Dashboard/Admin/Presets.js b/public/js/components/Dashboard/Admin/Presets.js index 8d6a07803..ef1c06e00 100644 --- a/public/js/components/Dashboard/Admin/Presets.js +++ b/public/js/components/Dashboard/Admin/Presets.js @@ -1,6 +1,7 @@ import DashboardSection from "../Section.js"; import DashboardWidgetPicker from "../Widget/Picker.js"; import ObjectUtils from "../../../helpers/ObjectUtils.js"; +import ApiDashboardPreset from "../../../api/factory/dashboard/preset.js"; export default { components: { @@ -17,9 +18,6 @@ export default { tmpLoading: '' }), computed: { - apiurl() { - return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard'; - }, pickerWidgets() { return this.widgets.filter(widget => widget.allowed); } @@ -28,6 +26,7 @@ export default { widgetAdd(section_name, widget) { this.$refs.widgetpicker.getWidget().then(widget_id => { widget.widget = widget_id; + widget.id = 'loading_' + String((new Date()).valueOf()); delete widget.custom; widget.preset = 1; let loading = {...widget}; @@ -36,130 +35,153 @@ export default { if (section.name == section_name) section.widgets.push(loading); }); - - axios.post(this.apiurl + '/Config/addWidgetsToPreset', { - db: this.dashboard, + + const params = { + dashboard: 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; - widget.custom = 1; - 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(() => {}); + widget + }; + + return this.$api + .call(ApiDashboardPreset.addWidget(params)) + .then(result => { + let newId = result.data; + widget.id = newId; + widget.custom = 1; + this.sections.forEach(section => { + if (section.name == section_name) { + section.widgets.splice(section.widgets.indexOf(loading),1); + section.widgets.push(widget); + } + }); + this.funktionen.forEach(funktion => { + if(funktion.funktion_kurzbz === section_name && funktion.has_preset < 1) { + funktion.has_preset = 1; + } + }); + }) + .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}) - if (payload[k][prop]) - delete payload[k][prop]; - break; - } - } + const section = this.sections.find(section => section.name == section_name); + for (var wid in section.widgets) { + if (section.widgets[wid].id == k) { + payload[k] = ObjectUtils.mergeDeep(section.widgets[wid], payload[k]); + // NOTE(chris): remove internal props + for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id']) + if (payload[k][prop]) + delete payload[k][prop]; break; } } payload[k].widgetid = k; delete payload[k].custom; } - axios.post(this.apiurl + '/Config/addWidgetsToPreset', { - 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]; - section.widgets[i].custom = 1; - } - }); - } - }); - }).catch(error => { - // TODO(chris): revert placement on failure - console.error('ERROR: ', error); - alert('ERROR: ' + error.response.data.retval); - }); + this.$api + .call(Object.entries(payload).map(([key, widget]) => [ + key, + ApiDashboardPreset.addWidget({ + dashboard: this.dashboard, + funktion_kurzbz: section_name, + widget + }) + ])) + .then(result => { + 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]; + section.widgets[i].custom = 1; + } + }); + } + }); + }) + .catch(this.$fhcAlert.handleSystemError); }, widgetRemove(section_name, id) { - axios.post(this.apiurl + '/Config/removeWidgetFromPreset', { + const params = { 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); - }); + }; + return this.$api + .call(ApiDashboardPreset.removeWidget(params)) + .then(result => { + this.sections.forEach(section => { + if (section.name == section_name) + section.widgets = section.widgets.filter(widget => widget.id != id); + }); + }) + .catch(this.$fhcAlert.handleSystemError); }, loadSections(evt) { let funktionen = Array.from(evt.target.querySelectorAll("option:checked"),e=>e.value); this.sections = []; this.tmpLoading = funktionen.join('###'); - axios.get(this.apiurl + '/Config/presetBatch', {params: { + + const params = { db: this.dashboard, funktionen - }}).then(res => { - if (this.tmpLoading !== funktionen.join('###')) - return; // NOTE(chris): prevent race condition - for (var section in res.data.retval) { - let widgets = []; - for (var wid in res.data.retval[section]) { - res.data.retval[section][wid].id = wid; - res.data.retval[section][wid].custom = 1; - widgets.push(res.data.retval[section][wid]); + }; + + return this.$api + .call(ApiDashboardPreset.getBatch(params)) + .then(result => { + if (this.tmpLoading !== funktionen.join('###')) + return; // NOTE(chris): prevent race condition + for (var section in result.data) { + let widgets = []; + for (var wid in result.data[section]) { + result.data[section][wid].id = wid; + result.data[section][wid].custom = 1; + widgets.push(result.data[section][wid]); + } + this.sections.push({ + name: section, + widgets + }); } - this.sections.push({ - name: section, - widgets - }); - } - }).catch(err => console.error('ERROR:', err)); + }) + .catch(this.$fhcAlert.handleSystemError); + + }, + loadFunktionen() { + this.$api + .call(ApiDashboardPreset.list(this.dashboard)) + .then(result => { + this.funktionen = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); } }, created() { - axios.get(this.apiurl + '/Config/funktionen').then(res => { - this.funktionen = {general: 'GENERAL'}; - res.data.retval.forEach(funktion => { - this.funktionen[funktion.funktion_kurzbz] = funktion.beschreibung; - }); - }).catch(err => console.error('ERROR:', err)); + this.loadFunktionen(); }, watch: { dashboard() { // TODO(chris): this should be done without a watcher this.loadSections({target:this.$refs.funktionenList}); + this.loadFunktionen(); } }, template: `
diff --git a/public/js/components/Dashboard/Admin/Widgets.js b/public/js/components/Dashboard/Admin/Widgets.js index 645d3f8cc..a0c7b2139 100644 --- a/public/js/components/Dashboard/Admin/Widgets.js +++ b/public/js/components/Dashboard/Admin/Widgets.js @@ -1,3 +1,5 @@ +import ApiDashboardWidget from "../../../api/factory/dashboard/widget.js"; + export default { emits: [ "change", @@ -7,34 +9,25 @@ export default { dashboard_id: Number, widgets: Array }, - computed: { - apiurl() { - return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard'; - } - }, methods: { sendChange(widget_id) { let allow = !this.widgets.find(el => el.widget_id == widget_id).allowed; - axios.post(this.apiurl + '/Widget/setAllowed', { - dashboard_id: this.dashboard_id, - widget_id, - action: allow ? 'add' : 'delete' - }).catch(err => console.error('ERROR: ' + err)); + + this.$api + .call(ApiDashboardWidget.setAllowed(this.dashboard_id, widget_id, allow)) + .catch(this.$fhcAlert.handleSystemError); } }, created() { - axios.get(this.apiurl + '/Widget/getAll', { - params:{ - dashboard_id: this.dashboard_id - } - }).then( - result => { - this.$emit('assignWidgets', result.data.retval.map(el => ({ + this.$api + .call(ApiDashboardWidget.list(this.dashboard_id)) + .then(result => { + this.$emit('assignWidgets', result.data.map(el => ({ ...el, - ...{setup:JSON.parse(el.setup),arguments:JSON.parse(el.arguments),allowed:!!el.allowed} + allowed: !!el.allowed }))); - } - ).catch(err => console.error('ERROR:', err)); + }) + .catch(this.$fhcAlert.handleSystemError); }, template: `
diff --git a/public/js/components/Dashboard/Dashboard.js b/public/js/components/Dashboard/Dashboard.js index 652a2778e..e92e34f29 100644 --- a/public/js/components/Dashboard/Dashboard.js +++ b/public/js/components/Dashboard/Dashboard.js @@ -2,7 +2,8 @@ import DashboardSection from "./Section.js"; import DashboardWidgetPicker from "./Widget/Picker.js"; import ObjectUtils from "../../helpers/ObjectUtils.js"; -import ApiDashboard from '../../api/factory/cis/dashboard.js'; +import ApiDashboardWidget from '../../api/factory/dashboard/widget.js'; +import ApiDashboardUser from '../../api/factory/dashboard/user.js'; export default { name: 'Dashboard', @@ -20,181 +21,147 @@ export default { type: Object, required: true, validator(value) { - return value && value.name && value.uid && value.timezone + return value && value.name && value.timezone } } }, data() { return { - sections: [], - widgets: null, - editMode: false, - viewDataInternal: this.viewData + widgets: [], + originalWidgets: {}, + widgetsSetup: null, + editMode: false } }, provide() { return { editMode: Vue.computed(()=>this.editMode), - widgetsSetup: Vue.computed(() => this.widgets), + widgetsSetup: Vue.computed(() => this.widgetsSetup), 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) { - if (this.widgets === null) { - axios.get(this.apiurl + '/Widget/getWidgetsForDashboard', {params:{ - db: this.dashboard - }}).then(res => { - 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)); - } - 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() { this.$p.loadCategory('dashboard'); - axios.get(this.apiurl + '/Widget/getWidgetsForDashboard', { - params: { - db: this.dashboard - } - }).then(res => { - this.widgets = res.data.retval; - }).catch(err => console.error('ERROR:', err)); - 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(ApiDashboardWidget.listAllowed(this.dashboard)) + .then(res => { + this.widgetsSetup = res.data; + }) + .catch(this.$fhcAlert.handleSystemError); + + 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)); - }, - async beforeMount() { - if (!this.viewData.name || !this.viewData.uid) { - const res = await this.$api.call(ApiDashboard.getViewData()); - this.viewDataInternal = res.data - } + + remove.forEach(wid => this.widgetRemove('general', wid)); + + this.widgets = widgets; + }) + .catch(this.$fhcAlert.handleSystemError); }, template: `
-

- {{ $p.t('global/personalGreeting', [ viewDataInternal?.name ]) }} +

+ {{ $p.t('global/personalGreeting', [ viewData?.name ]) }}

- - + +
` } diff --git a/public/js/components/Dashboard/Item.js b/public/js/components/Dashboard/Item.js index 06a03ae3a..79e61293d 100644 --- a/public/js/components/Dashboard/Item.js +++ b/public/js/components/Dashboard/Item.js @@ -1,5 +1,5 @@ import BsModal from "../Bootstrap/Modal.js"; -import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js"; +import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js"; import HeightTransition from "../Tranistion/HeightTransition.js"; export default { @@ -70,6 +70,14 @@ export default { ready() { return this.component && this.arguments !== null; }, + visible: { + get() { + return !this.hidden; + }, + set(value) { + this.$emit('remove', this.hidden); + } + } }, methods: { unpin(){ @@ -142,8 +150,14 @@ export default { this.isLoading = false; }, }, + setup() { + const { actions } = useCachedWidgetLoader(); + return { + loadWidget: actions.load + }; + }, async created() { - this.widget = await CachedWidgetLoader.loadWidget(this.id); + this.widget = await this.loadWidget(this.id); let component = (await import(this.widget.setup.file)).default; this.$options.components["widget" + this.widget.widget_id] = component; this.component = "widget" + this.widget.widget_id; @@ -185,7 +199,7 @@ export default {
- +
diff --git a/public/js/components/Dashboard/Section.js b/public/js/components/Dashboard/Section.js index a238de289..817cc52a3 100644 --- a/public/js/components/Dashboard/Section.js +++ b/public/js/components/Dashboard/Section.js @@ -1,7 +1,7 @@ import BsConfirm from "../Bootstrap/Confirm.js"; import DropGrid from '../Drop/Grid.js' import DashboardItem from "./Item.js"; -import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js"; +import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js"; import WidgetIcon from "./Widget/WidgetIcon.js" export default { @@ -125,23 +125,23 @@ export default { }, checkResizeLimit(item, w, h) { // NOTE(chris): widgets needs to be loaded for this to work - let widget = CachedWidgetLoader.getWidget(item.widget); + let widget = this.widgetState[item.widget]; if (widget) { - let minmaxW = widget.setup.width; + let minmaxW = { ...widget.setup.width }; if (minmaxW.max) minmaxW.min = minmaxW.min || 1; else - minmaxW = {min:minmaxW,max:minmaxW}; + minmaxW = { min: minmaxW, max: minmaxW }; if (w < minmaxW.min) w = minmaxW.min; if (w > minmaxW.max) w = minmaxW.max; - let minmaxH = widget.setup.height; + let minmaxH = { ...widget.setup.height }; if (minmaxH.max) minmaxH.min = minmaxH.min || 1; else - minmaxH = {min:minmaxH,max:minmaxH}; + minmaxH = { min: minmaxH, max: minmaxH }; if (h < minmaxH.min) h = minmaxH.min; if (h > minmaxH.max) @@ -151,7 +151,7 @@ export default { }, removeWidget(item, revert) { if (item.custom) { - BsConfirm.popup('Are you sure you want to delete this widget?').then(() => this.$emit('widgetRemove', this.name, item.id)); + BsConfirm.popup(this.$p.t('dashboard', 'alert_deleteWidget')).then(() => this.$emit('widgetRemove', this.name, item.id)); } else { let update = {}; update[item.id] = { hidden: !revert }; @@ -199,6 +199,13 @@ export default { this.$emit('widgetUpdate', this.name, payload); } }, + setup() { + const { state: widgetState } = useCachedWidgetLoader(); + + return { + widgetState + }; + }, mounted() { let self = this; let cont = self.$refs.container; diff --git a/public/js/components/Form/Input.js b/public/js/components/Form/Input.js index 3c3fa45d5..4ad555ab7 100644 --- a/public/js/components/Form/Input.js +++ b/public/js/components/Form/Input.js @@ -170,6 +170,7 @@ export default { return this.$attrs.modelValue; }, set(v) { + this.clearValidationForThisName() if (!this.$attrs.hasOwnProperty('modelValue')) this.modelValueDummy = v; this.$emit('update:modelValue', v); @@ -242,9 +243,9 @@ export default { template: ` - - - + + diff --git a/public/js/components/filter/Filter.js b/public/js/components/filter/Filter.js index 7bfc1aebb..3f0edb672 100644 --- a/public/js/components/filter/Filter.js +++ b/public/js/components/filter/Filter.js @@ -280,7 +280,7 @@ export const CoreFilterCmpt = { }); } - if (tabulatorOptions.selectable || (tabulatorOptions.columns && tabulatorOptions.columns.filter(el => el.formatter == 'rowSelection').length)) + if (tabulatorOptions.selectable || tabulatorOptions.selectableRows || (tabulatorOptions.columns && tabulatorOptions.columns.filter(el => el.formatter == 'rowSelection').length)) this.tabulatorHasSelector = true; if (this.idField) { @@ -358,7 +358,7 @@ export const CoreFilterCmpt = { } }, _updateTabulator() { - this.tabulatorHasSelector = this.tabulatorOptions.selectable || this.filteredColumns.filter(el => el.formatter == 'rowSelection').length; + this.tabulatorHasSelector = this.tabulatorOptions.selectable || this.tabulatorOptions.selectableRows || this.filteredColumns.filter(el => el.formatter == 'rowSelection').length; this.tabulator.setColumns(this.filteredColumns); this.tabulator.setData(this.filteredData); this._setHeaderFilter() diff --git a/public/js/composables/Dashboard/CachedWidgetLoader.js b/public/js/composables/Dashboard/CachedWidgetLoader.js index a92e3e557..4bc2d4992 100644 --- a/public/js/composables/Dashboard/CachedWidgetLoader.js +++ b/public/js/composables/Dashboard/CachedWidgetLoader.js @@ -1,29 +1,36 @@ -let __widgets = {}; -let __widgetsStarted = {}; -let __path = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard/Widget'; +import ApiWidget from "../../api/factory/dashboard/widget.js"; -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!'); +const promises = Vue.ref([]); +const stateRef = Vue.ref([]); +const state = Vue.readonly(stateRef); - __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; +export function useCachedWidgetLoader() { + const $api = Vue.inject('$api'); + const $fhcAlert = Vue.inject('$fhcAlert'); + + function load(id) { + if (state.value[id]) + return Promise.resolve(state.value[id]); + + if (!promises.value[id]) + promises.value[id] = new Promise((resolve, reject) => { + $api + .call(ApiWidget.get(id)) + .then(res => { + stateRef.value[id] = res.data; + promises.value[id] = undefined; + resolve(state.value[id]); + }) + .catch($fhcAlert.handleSystemError); + }); + + return promises.value[id]; } + + return { + state, + actions: { + load + } + }; } \ No newline at end of file diff --git a/public/js/helpers/ObjectUtils.js b/public/js/helpers/ObjectUtils.js index 348d18843..c68e92e1e 100644 --- a/public/js/helpers/ObjectUtils.js +++ b/public/js/helpers/ObjectUtils.js @@ -1,31 +1,72 @@ -export default { - /** - * Performs a deep merge of objects and returns new object. Does not modify - * objects (immutable) and merges arrays via concatenation. - * - * @param {...object} objects - Objects to merge - * @returns {object} New object with merged key/values - */ - mergeDeep(...objects) { - const isObject = obj => obj && typeof obj === 'object'; - - return objects.reduce((prev, obj) => { - Object.keys(obj).forEach(key => { - const pVal = prev[key]; - const oVal = obj[key]; - - if (Array.isArray(pVal) && Array.isArray(oVal)) { - prev[key] = pVal.concat(...oVal); - } - else if (isObject(pVal) && isObject(oVal)) { - prev[key] = this.mergeDeep(pVal, oVal); - } - else { - prev[key] = oVal; - } - }); +/** + * Performs a deep merge of objects and returns new object. Does not modify + * objects (immutable) and merges arrays via concatenation. + * + * @param {...object} objects - Objects to merge + * @returns {object} New object with merged key/values + */ +function mergeDeep(...objects) { + const isObject = obj => obj && typeof obj === 'object'; + + return objects.reduce((prev, obj) => { + Object.keys(obj).forEach(key => { + const pVal = prev[key]; + const oVal = obj[key]; - return prev; - }, {}); - } + if (Array.isArray(pVal) && Array.isArray(oVal)) { + prev[key] = pVal.concat(...oVal); + } + else if (isObject(pVal) && isObject(oVal)) { + prev[key] = this.mergeDeep(pVal, oVal); + } + else { + prev[key] = oVal; + } + }); + + return prev; + }, {}); +} + +/** + * Extends VUEs toRaw() function to nested Proxies + * @see https://www.reddit.com/r/javascript/comments/10gzynk/deep_cloning_objects_in_javascript_the_modern_way/ + * + * @param object sourceObj - Object to transform + * @returns object + */ +function deepToRaw(sourceObj) { + const objectIterator = input => { + if (Array.isArray(input)) + return input.map(objectIterator); + if (Vue.isRef(input) || Vue.isReactive(input) || Vue.isProxy(input)) + return objectIterator(Vue.toRaw(input)); + if (input && typeof input === 'object') { + /** use custom handling of 'Date' objects to avoid data loss if treating it like any other object. + * reminder: + * typeof (new Date()) ==> 'object' + * Object.keys(new Date()) ==> [] + */ + if (input instanceof Date) + return input; + + return Object.keys(input).reduce((acc, key) => { + acc[key] = objectIterator(input[key]); + return acc; + }, {}); + } + + return input; + }; + + return objectIterator(sourceObj); +} + +export { + mergeDeep, + deepToRaw +} +export default { + mergeDeep, + deepToRaw } \ No newline at end of file diff --git a/public/js/plugins/Api.js b/public/js/plugins/Api.js index 889d694eb..115dd28fa 100644 --- a/public/js/plugins/Api.js +++ b/public/js/plugins/Api.js @@ -430,16 +430,16 @@ export default { fhcApiAxios.interceptors.response.use( response => { - if (response.config?.errorHandling == 'off' - || response.config?.errorHandling === false - || response.config?.errorHandling == 'fail') + const errorConfig = get_error_handler(response.config); + + if (!errorConfig.success) return clean_return_value(response); - // NOTE(chris): loop through errors - if (response.data.errors) - response.data.errors = response.data.errors.filter( - err => (response.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$api._defaultErrorHandlers[err.type])(err, response.config) - ); + const errors = popHandleableErrors(errorConfig, response.data.errors); + + for (var type in errors) { + errorConfig.handler[type](errors[type]); + } return clean_return_value(response); }, diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index 4ddb38203..ac8695de2 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -92,6 +92,7 @@ require_once('dbupdate_3.4/68744_StV_settings.php'); require_once('dbupdate_3.4/62889_reihungstest_ueberwachung_mit_constructor.php'); require_once('dbupdate_3.4/71399_dashboard_update_widget_paths.php'); require_once('dbupdate_3.4/71645_studvw_messagetab_ladezeit.php'); +require_once('dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

Pruefe Tabellen und Attribute!

'; diff --git a/system/dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php b/system/dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php new file mode 100644 index 000000000..3574a8424 --- /dev/null +++ b/system/dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php @@ -0,0 +1,15 @@ +db_query("SELECT 1 FROM public.tbl_organisationseinheittyp WHERE organisationseinheittyp_kurzbz= 'Programm';")) +{ + if($db->db_num_rows($result) == 0) + { + $qry = "INSERT INTO public.tbl_organisationseinheittyp(organisationseinheittyp_kurzbz, beschreibung, bezeichnung) VALUES ('Programm', 'Programm', 'Programm');"; + + if(!$db->db_query($qry)) + echo 'public.tbl_organisationseinheittyp: '.$db->db_last_error().'
'; + else + echo '
public.tbl_organisationseinheittyp: Zeile Programm hinzugefuegt!
'; + } +} diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index add5ce79d..f7628f8ef 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -3903,6 +3903,172 @@ $phrases = array( ) ) ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'geplZeitraum', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'geplanter Zeitraum', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'planned Period', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'bitteAuswaehlen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bitte auswählen...', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Please select...', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'hinweisLehrende', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Hinweis für Lehrende', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Note for Lecturers', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'lehreinheiten', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehreinheiten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Teaching Units', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'lead', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Leitung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Lead', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'teamlead', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Team / Leitung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Team / Lead', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'ausblick_lvplanung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ausblick auf Ihre mögliche LV-Planung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Preview of Your Potential Course Planning', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'detailselfoverview', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Achtung: die vorliegenden Informationen stellen eine Vorabplanung dar und sind als Anfrage an Sie gedacht.

+ Die Beauftragung der tatsächlichen Lehrveranstaltungen erfolgt durch Ihre Kompetenzfeldleitung.

+ Ihre aktuell gültigen Lehraufträge und den LV Plan des aktuellen Semesters (Termine) finden Sie wie gewohnt unter „mein CIS“ -> „LV-Plan Hauptmenü“ bzw. „Lehrauftragsverwaltung“.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Please note: The information provided represents a preliminary planning and is intended as an inquiry to you.

+ The official assignment of the actual courses will be carried out by your Competence Field Manager.

+ Your currently valid teaching assignments and the course schedule for the current semester (dates) can be found as usual under “My CIS” → “Schedule Main Menu” or “Teaching Assignment Administration”', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( 'app' => 'pep', 'category' => 'ui', @@ -44726,9 +44892,9 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => "Für den gesamten Studiengang verbindlicher Termin. - - Liegt ein Termin in der Vergangenheit, kann nichts mehr hochgeladen werden. Ist es dennoch erforderlich, + 'text' => "Für den gesamten Studiengang verbindlicher Termin. + + Liegt ein Termin in der Vergangenheit, kann nichts mehr hochgeladen werden. Ist es dennoch erforderlich, haben Studierende bei der Studiengangsassistenz um eine Korrektur dieses Termins anzusuchen.", 'description' => '', 'insertvon' => 'system' @@ -44919,7 +45085,7 @@ array( array( 'sprache' => 'German', 'text' => "Verspätete Projektabgabe ist bei Terminen, welche von der Studiengangsassistenz für den gesamten Studiengang fixiert wurden nicht erlaubt! - + Um einen Endupload durchführen zu können, müssen Sie ein positiv benotetes Quality Gate 1 & Quality Gate 2 in der relevanten Projektarbeit absolviert haben.", 'description' => '', 'insertvon' => 'system' @@ -45234,6 +45400,46 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4downloadLatestAbgabe', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Zuletzt getätigte Abgabe herunterladen", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Download latest uploaded File', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4termineTimeLine', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zeitstrahl Termine', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Timeline Deadlines', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'abgabetool', @@ -46695,6 +46901,26 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4noZuordnungBetreuerStudent', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Keine Zuordnung oder Berechtigung für die Projektarbeit gefunden!', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No assignment or authorization found for the project!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // ABGABETOOL PHRASEN END array( 'app' => 'core', @@ -56948,6 +57174,109 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), // ### Refactor Messages END + // + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'error_noLehrverbandAssigned', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'StudentIn ist in diesem Semester keinem Lehrverband zugeteilt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Student has no assignment to any teaching association', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + + // ### Phrases Dashboard Admin START + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'deleteInfo', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Mit dieser Aktion werden auch alle Voreinstellungen der verbundenen Widgets gelöscht.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'This action will also delete all presets of the connected widgets.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'dashboard', + 'phrase' => 'success_savePreset', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Voreinstellung erfolgreich aktualisiert', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Preset successfully updated', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( + 'app' => 'core', + 'category' => 'dashboard', + 'phrase' => 'alert_deleteWidget', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Sind Sie sicher, dass Sie dieses Widget löschen möchten?', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Are you sure you want to delete this widget?', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'confirm_delete', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Möchten Sie wirklich löschen?', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Do you really want to delete?', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + // ### Phrases Dashboard Admin END ); diff --git a/vilesci/bis/personalmeldung.php b/vilesci/bis/personalmeldung.php index cd929c24b..1def6774d 100644 --- a/vilesci/bis/personalmeldung.php +++ b/vilesci/bis/personalmeldung.php @@ -875,7 +875,6 @@ function _getLehrecontainer($sws_proStg_arr) $sws_proStg_arr = array_filter($sws_proStg_arr, function ($obj) { return !in_array($obj->studiengang_kz, BIS_EXCLUDE_STG) && - $obj->studiengang_kz > 0 && $obj->studiengang_kz < 10000; }); } @@ -886,13 +885,17 @@ function _getLehrecontainer($sws_proStg_arr) { $is_sommersemester = substr($sws_proStg->studiensemester_kurzbz, 0, 2) == 'SS'; $is_wintersemester = substr($sws_proStg->studiensemester_kurzbz, 0, 2) == 'WS'; + $is_lehrgang = isset($sws_proStg->lgartcode); + $kennzeichen_name = $is_lehrgang ? 'LehrgangNr' : 'StgKz'; // Lehreobjekt generieren if (empty($lehre_arr) || !lehre_stg_exists($sws_proStg->studiengang_kz, $lehre_arr)) { $lehre_obj = new StdClass(); - $lehre_obj->StgKz = setLeadingZero(intval($sws_proStg->studiengang_kz), 4); + $lehre_obj->{$kennzeichen_name} = $sws_proStg->melde_studiengang_kz; + //~ $lehre_obj->StgKz = setLeadingZero(intval($sws_proStg->studiengang_kz), 4); + $lehre_obj->SommersemesterSWS = $is_sommersemester ? $sws_proStg->sws : 0.00; $lehre_obj->WintersemesterSWS = $is_wintersemester ? $sws_proStg->sws : 0.00; @@ -1020,9 +1023,14 @@ function _generateXML($person_arr) foreach ($person->lehre_arr as $lehre) { $xml .= ''; - $xml .= 'StgKz. ']]>'; - $xml .= 'SommersemesterSWS. ']]>'; - $xml .= 'WintersemesterSWS. ']]>'; + + if (isset($lehre->LehrgangNr)) + $xml .= 'LehrgangNr. ']]>'; + else + $xml .= 'StgKz. ']]>'; + + $xml .= 'SommersemesterSWS, 2, '.', ''). ']]>'; + $xml .= 'WintersemesterSWS, 2, '.', ''). ']]>'; $xml .= ''; } @@ -1211,7 +1219,7 @@ function _outputHTML($person_arr) { echo ' - '. $lehre->StgKz. ' + '. (isset($lehre->LehrgangNr) ? $lehre->LehrgangNr : $lehre->StgKz). ' '. $lehre->SommersemesterSWS. ' '. $lehre->WintersemesterSWS. ' '; @@ -1359,7 +1367,8 @@ function lehre_stg_exists($studiengang_kz, $lehre_arr) { foreach($lehre_arr as $row) { - if($row->StgKz == $studiengang_kz) + $kennzeichenName = $row->LehrgangNr ?? $row->StgKz; + if(isset($row->{$kennzeichenName}) && $row->{$kennzeichenName} == $melde_studiengang_kz) return true; } return false;