diff --git a/application/config/routes.php b/application/config/routes.php index aa4ba9db8..8024f449a 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -64,6 +64,9 @@ $route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1'; $route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1'; $route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1'; $route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1'; +$route['Cis/OtherLvPlan/.*'] = 'Cis/OtherLvPlan/index/$1'; +//Route for LV Plan Stg/Semester/Verband/Gruppe +$route['Cis/StgOrgLvPlan/.*'] = 'Cis/StgOrgLvPlan/index/$1'; $route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz'; $route['Abgabetool/Assistenz/(:any)'] = 'Cis/Abgabetool/Assistenz/$1'; diff --git a/application/controllers/Cis/Abgabetool.php b/application/controllers/Cis/Abgabetool.php index 04338b1a9..28414dafa 100644 --- a/application/controllers/Cis/Abgabetool.php +++ b/application/controllers/Cis/Abgabetool.php @@ -31,12 +31,8 @@ class Abgabetool extends Auth_Controller { // TODO: routing from index based on berechtigung? - $viewData = array( - 'uid'=>getAuthUID(), - ); - if(defined('CIS4') && CIS4) { - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Abgabetool']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'Abgabetool']); } else { $this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'Abgabetool']); } @@ -44,12 +40,8 @@ class Abgabetool extends Auth_Controller public function Student($student_uid_prop = '') { - $viewData = array( - 'uid'=>getAuthUID(), - ); - if(defined('CIS4') && CIS4) { - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'AbgabetoolStudent']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'AbgabetoolStudent']); } else { $this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'AbgabetoolStudent', 'student_uid_prop' => $student_uid_prop]); } @@ -57,12 +49,8 @@ class Abgabetool extends Auth_Controller public function Mitarbeiter() { - $viewData = array( - 'uid'=>getAuthUID(), - ); - if(defined('CIS4') && CIS4) { - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'AbgabetoolMitarbeiter']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'AbgabetoolMitarbeiter']); } else { $this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'AbgabetoolMitarbeiter']); } @@ -70,13 +58,8 @@ class Abgabetool extends Auth_Controller public function Assistenz($stg_kz_prop = '') { - - $viewData = array( - 'uid'=>getAuthUID(), - ); - if(defined('CIS4') && CIS4) { - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'AbgabetoolAssistenz']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'AbgabetoolAssistenz']); } else { $this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'AbgabetoolAssistenz', 'stg_kz_prop' => $stg_kz_prop]); } @@ -84,12 +67,8 @@ class Abgabetool extends Auth_Controller public function Deadlines() { - $viewData = array( - 'uid'=>getAuthUID(), - ); - if(defined('CIS4') && CIS4) { - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'DeadlinesOverview']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'DeadlinesOverview']); } else { $this->load->view('Cis/Abgabetool.php', ['uid' => getAuthUID(), 'route' => 'DeadlinesOverview']); } diff --git a/application/controllers/Cis/Auth.php b/application/controllers/Cis/Auth.php index 67267ebf6..87ef5d3da 100644 --- a/application/controllers/Cis/Auth.php +++ b/application/controllers/Cis/Auth.php @@ -40,7 +40,7 @@ class Auth extends FHC_Controller if ($this->form_validation->run()) { - redirect($this->authlib->getLandingPage('/CisVue/Dashboard')); + redirect($this->authlib->getLandingPage('/Cis4')); } else { diff --git a/application/controllers/Cis/LvPlan.php b/application/controllers/Cis/LvPlan.php index 884c8a9a0..32e622f5a 100644 --- a/application/controllers/Cis/LvPlan.php +++ b/application/controllers/Cis/LvPlan.php @@ -28,12 +28,6 @@ class LvPlan extends Auth_Controller */ public function index() { - - $viewData = array( - 'uid'=>getAuthUID(), - 'timezone' => $this->config->item('timezone') - ); - - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'LvPlan']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'LvPlan']); } } diff --git a/application/controllers/Cis/MyLv.php b/application/controllers/Cis/MyLv.php index 819d56b05..0f24d3a80 100644 --- a/application/controllers/Cis/MyLv.php +++ b/application/controllers/Cis/MyLv.php @@ -26,11 +26,6 @@ class MyLv extends Auth_Controller */ public function index() { - - $viewData = array( - - ); - - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'MyLv']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'MyLv']); } } diff --git a/application/controllers/Cis/MyLvPlan.php b/application/controllers/Cis/MyLvPlan.php index 366ce8e65..a7f21aa20 100644 --- a/application/controllers/Cis/MyLvPlan.php +++ b/application/controllers/Cis/MyLvPlan.php @@ -27,13 +27,7 @@ class MyLvPlan extends Auth_Controller * @return void */ public function index() - { - - $viewData = array( - 'uid'=>getAuthUID(), - 'timezone' => $this->config->item('timezone') - ); - - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'MyLvPlan']); + { + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'MyLvPlan']); } } diff --git a/application/controllers/Cis/OtherLvPlan.php b/application/controllers/Cis/OtherLvPlan.php new file mode 100644 index 000000000..e62644dd1 --- /dev/null +++ b/application/controllers/Cis/OtherLvPlan.php @@ -0,0 +1,34 @@ + ['basis/other_lv_plan:r'] + ]); + + // Load Config + $this->load->config('calendar'); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + /** + * @return void + */ + public function index() + { + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'OtherLvPlan']); + } +} diff --git a/application/controllers/Cis/Profil.php b/application/controllers/Cis/Profil.php index c287d87d0..dd7963228 100644 --- a/application/controllers/Cis/Profil.php +++ b/application/controllers/Cis/Profil.php @@ -55,15 +55,7 @@ class Profil extends Auth_Controller */ public function index() { - - $this->load->library('ProfilLib'); - $profil_data = $this->profillib->getView(getAuthUID()); - $profil_data = hasData($profil_data) ? getData($profil_data) : null; - $viewData = array( - 'editable'=>true, - 'profil_data' => $profil_data, - ); - $this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilIndex']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'profilIndex']); } /** @@ -73,23 +65,13 @@ class Profil extends Auth_Controller */ public function View($uid) { - $this->load->library('ProfilLib'); - $profil_data = $this->profillib->getView($uid); - $profil_data = hasData($profil_data) ? getData($profil_data) : null; - $viewData = array ( - 'uid' => $uid, - 'profil_data'=>$profil_data, - ); - if($uid == getAuthUID()){ - $viewData['editable'] = true; - } - $this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilViewUid']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'profilViewUid']); } /** - * checks whether a specific userID is a mitarbeiter or not (foreword declaration of the function isMitarbeiter in Mitarbeiter_model.php) + * checks whether a specific userID is a student or not (foreword declaration of the function isStudent in Student_model.php) * @access public - * @param $uid the userID used to check if it is a mitarbeiter + * @param $uid the userID used to check if it is a student * @return boolean */ public function isStudent($uid) @@ -119,7 +101,7 @@ class Profil extends Auth_Controller } /** - * gets the adressen that are marked as zustell from the currenlty logged in user + * gets the adressen that are marked as zustell from the currently logged in user * @access public * @return array a list of adresse_id's */ @@ -262,23 +244,23 @@ class Profil extends Auth_Controller $this->GemeindeModel->addDistinct(); $this->GemeindeModel->addSelect(["name"]); if ($nation == "A") { - if (isset($zip) && $zip > 999 && $zip < 32000) { + if (isset($zip) && $zip > 999 && $zip < 32000) { - $gemeinde_res = $this->GemeindeModel->loadWhere(['plz' => $zip]); - if (isError($gemeinde_res)) { - show_error("error while trying to query bis.tbl_gemeinde"); - } - $gemeinde_res = hasData($gemeinde_res) ? getData($gemeinde_res) : null; - $gemeinde_res = array_map(function ($obj) { - return $obj->name; - }, $gemeinde_res); - echo json_encode($gemeinde_res); - - } else { - echo json_encode(error("ortschaftskennziffer code was not valid")); + $gemeinde_res = $this->GemeindeModel->loadWhere(['plz' => $zip]); + if (isError($gemeinde_res)) { + show_error("error while trying to query bis.tbl_gemeinde"); } + $gemeinde_res = hasData($gemeinde_res) ? getData($gemeinde_res) : null; + $gemeinde_res = array_map(function ($obj) { + return $obj->name; + }, $gemeinde_res); + echo json_encode($gemeinde_res); + + } else { + echo json_encode(error("ortschaftskennziffer code was not valid")); + } } else { - echo json_encode(error("Nation was not 'A' (Austria)")); + echo json_encode(error("Nation was not 'A' (Austria)")); } } @@ -750,6 +732,4 @@ class Profil extends Auth_Controller $zutrittskarte_ausgegebenam = str_replace("-", ".", $zutrittskarte_ausgegebenam); return $zutrittskarte_ausgegebenam; } - - } diff --git a/application/controllers/Cis/Raumsuche.php b/application/controllers/Cis/Raumsuche.php index 055038275..f48beb0f3 100644 --- a/application/controllers/Cis/Raumsuche.php +++ b/application/controllers/Cis/Raumsuche.php @@ -25,11 +25,6 @@ class Raumsuche extends Auth_Controller */ public function index() { - - $viewData = array( - 'uid'=>getAuthUID(), - ); - - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Raumsuche']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'Raumsuche']); } } diff --git a/application/controllers/Cis/StgOrgLvPlan.php b/application/controllers/Cis/StgOrgLvPlan.php new file mode 100644 index 000000000..bf329fcb8 --- /dev/null +++ b/application/controllers/Cis/StgOrgLvPlan.php @@ -0,0 +1,33 @@ + ['basis/cis:r'] + ]); + + // Load Config + $this->load->config('calendar'); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + /** + * @return void + */ + public function index() + { + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'StgOrgLvPlan']); + } +} diff --git a/application/controllers/Cis/Studium.php b/application/controllers/Cis/Studium.php index a298de648..20f323dff 100644 --- a/application/controllers/Cis/Studium.php +++ b/application/controllers/Cis/Studium.php @@ -29,10 +29,7 @@ class Studium extends Auth_Controller */ public function index() { - $viewData = array( - - ); - $this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'studium']); + $this->load->view('CisRouterView/CisRouterView.php',['route' => 'studium']); } diff --git a/application/controllers/Cis4.php b/application/controllers/Cis4.php index b7ba2029d..48fd6c240 100644 --- a/application/controllers/Cis4.php +++ b/application/controllers/Cis4.php @@ -1,6 +1,7 @@ 'basis/cis:r' - ) + array( + 'index' => 'basis/cis:r' + ) ); // Load Config @@ -30,16 +31,6 @@ class Cis4 extends Auth_Controller */ public function index() { - $this->load->model('person/Person_model', 'PersonModel'); - $personData = getData($this->PersonModel->getByUid(getAuthUID()))[0]; - - $viewData = array( - 'uid' => getAuthUID(), - 'name' => $personData->vorname, - 'person_id' => $personData->person_id, - 'timezone' => $this->config->item('timezone') - ); - - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'FhcDashboard']); + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'FhcDashboard']); } } diff --git a/application/controllers/CisVue/Dashboard.php b/application/controllers/CisVue/Dashboard.php deleted file mode 100644 index ee830cb8b..000000000 --- a/application/controllers/CisVue/Dashboard.php +++ /dev/null @@ -1,43 +0,0 @@ - 'dashboard/benutzer:r' - ) - ); - } - - // ----------------------------------------------------------------------------------------------------------------- - // Public methods - - /** - * @return void - */ - public function index() - { - - $this->load->model('person/Person_model','PersonModel'); - $personData = getData($this->PersonModel->getByUid(getAuthUID()))[0]; - - $viewData = array( - 'uid' => getAuthUID(), - 'name' => $personData->vorname, - 'person_id' => $personData->person_id - ); - - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData]); - - } -} \ No newline at end of file diff --git a/application/controllers/api/frontend/v1/Bookmark.php b/application/controllers/api/frontend/v1/Bookmark.php index 3e646bb51..aa45709d4 100644 --- a/application/controllers/api/frontend/v1/Bookmark.php +++ b/application/controllers/api/frontend/v1/Bookmark.php @@ -18,6 +18,8 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); +use \DateTime as DateTime; + class Bookmark extends FHCAPI_Controller { @@ -28,111 +30,162 @@ class Bookmark extends FHCAPI_Controller { parent::__construct([ 'getBookmarks' => self::PERM_LOGGED, - 'delete' => self::PERM_LOGGED, - 'insert' => self::PERM_LOGGED, + 'delete' => self::PERM_LOGGED, + 'insert' => self::PERM_LOGGED, 'update' => self::PERM_LOGGED, - ]); + 'changeOrder' => self::PERM_LOGGED + ]); $this->load->model('dashboard/Bookmark_model', 'BookmarkModel'); $this->uid = getAuthUID(); $this->pid = getAuthPersonID(); - } //------------------------------------------------------------------------------------------------------------------ // Public methods - /** - * gets the bookmarks associated to a user + /** + * gets the bookmarks associated to a user * @access public * @return void */ public function getBookmarks() { - $this->BookmarkModel->addOrder("bookmark_id"); + $this->BookmarkModel->addOrder("sort"); $bookmarks = $this->BookmarkModel->loadWhere(["uid"=>$this->uid]); - $bookmarks = $this->getDataOrTerminateWithError($bookmarks); + $bookmarks = $this->getDataOrTerminateWithError($bookmarks); - $this->terminateWithSuccess($bookmarks); - } - - /** - * deletes bookmark from associated user - * @access public - * @return void - */ - public function delete($bookmark_id) - { - $bookmark = $this->BookmarkModel->load($bookmark_id); - - $bookmark = current($this->getDataOrTerminateWithError($bookmark)); - - // only delete bookmark if the user is the owner of the bookmark - if($bookmark->uid == $this->uid || $this->permissionlib->isBerechtigt('admin')){ - - $delete_result = $this->BookmarkModel->delete($bookmark_id); - - $delete_result = $this->getDataOrTerminateWithError($delete_result); - - $this->terminateWithSuccess($delete_result); - }else{ - $this->_outputAuthError(['delete' => ['admin:rw']]); - } - } - - /** - * inserts new bookmark into the bookmark table - * @access public - * @return void - */ - public function insert() - { - // form validation - $this->load->library('form_validation'); - $this->form_validation->set_rules('url', 'URL', 'required|valid_url|max_length[511]'); - $this->form_validation->set_rules('title', 'Title', 'required|max_length[255]'); - if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array()); - - $url = $this->input->post('url',true); - $title = $this->input->post('title',true); - $tag = $this->input->post('tag', true); - - $insert_into_result = $this->BookmarkModel->insert(['uid'=>$this->uid, 'url'=>$url, 'title'=>$title,'tag'=>$tag, 'insertvon'=>$this->uid, 'updateamum'=>NULL, 'updatevon'=>NULL]); - - $insert_into_result = $this->getDataOrTerminateWithError($insert_into_result); - - $this->terminateWithSuccess($insert_into_result); - - } + $this->terminateWithSuccess($bookmarks); + } /** - * updates bookmark in the bookmark table + * deletes bookmark from associated user * @access public * @return void */ - public function update($bookmark_id) + public function delete($bookmark_id) { - // form validation - $this->load->library('form_validation'); - $this->form_validation->set_rules('url', 'URL', 'required|valid_url|max_length[511]'); - $this->form_validation->set_rules('title', 'Title', 'required|max_length[255]'); - if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array()); + $bookmark = $this->BookmarkModel->load($bookmark_id); - $url = $this->input->post('url',true); - $title = $this->input->post('title',true); + $bookmark = current($this->getDataOrTerminateWithError($bookmark)); + + // only delete bookmark if the user is the owner of the bookmark + if ($bookmark->uid == $this->uid || $this->permissionlib->isBerechtigt('admin')) { + $delete_result = $this->BookmarkModel->delete($bookmark_id); + + $delete_result = $this->getDataOrTerminateWithError($delete_result); + + $this->terminateWithSuccess($delete_result); + } else { + $this->_outputAuthError(['delete' => ['admin:rw']]); + } + } + + /** + * inserts new bookmark into the bookmark table + * @access public + * @return void + */ + public function insert() + { + // form validation + $this->load->library('form_validation'); + $this->form_validation->set_rules('url', 'URL', 'required|valid_url|max_length[511]'); + $this->form_validation->set_rules('title', 'Title', 'required|max_length[255]'); + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $url = $this->input->post('url', true); + $title = $this->input->post('title', true); + $tag = $this->input->post('tag', true); + if (is_array($tag)) { + $tag = json_encode($tag); // convert PHP array to JSON string + } + $sort = $this->input->post('sort', true); + + $insert_into_result = $this->BookmarkModel->insert([ + 'uid' => $this->uid, + 'url' => $url, + 'title' => $title, + 'tag' => $tag, + 'insertvon' => $this->uid, + 'updateamum' => null, + 'updatevon' => null, + 'sort' => $sort + ]); + + $insert_into_result = $this->getDataOrTerminateWithError($insert_into_result); + + $this->terminateWithSuccess($insert_into_result); + } + + /** + * updates bookmark in the bookmark table + * @access public + * @return void + */ + public function update($bookmark_id) + { + // form validation + $this->load->library('form_validation'); + $this->form_validation->set_rules('url', 'URL', 'required|valid_url|max_length[511]'); + $this->form_validation->set_rules('title', 'Title', 'required|max_length[255]'); + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $url = $this->input->post('url', true); + $title = $this->input->post('title', true); + $tag = $this->input->post('tag', true); + if (is_array($tag)) { + $tag = json_encode($tag); + } $now = new DateTime(); $now = $now->format('Y-m-d H:i:s'); - $update_result = $this->BookmarkModel->update($bookmark_id,['url'=>$url, 'title'=>$title,'updateamum'=>$now]); + $update_result = $this->BookmarkModel->update($bookmark_id, [ + 'url' => $url, + 'title' => $title, + 'tag' => $tag, + 'updateamum' => $now + ]); - $update_result = $this->getDataOrTerminateWithError($update_result); + $update_result = $this->getDataOrTerminateWithError($update_result); - $this->terminateWithSuccess($update_result); + $this->terminateWithSuccess($update_result); + } - } + /** + * changes sort of two bookmarks in the bookmark table + * @access public + * @return void + */ + public function changeOrder($bookmark_id1, $bookmark_id2) + { + $update_result = []; + + $result1 = $this->BookmarkModel->load($bookmark_id1); + $data1 = $this->getDataOrTerminateWithError($result1); + $sort1 = current($data1)->sort; + + $result2 = $this->BookmarkModel->load(["bookmark_id"=>$bookmark_id2]); + $data2 = $this->getDataOrTerminateWithError($result2); + $sort2 = current($data2)->sort; + + $update_result1 = $this->BookmarkModel->update($bookmark_id1, [ + 'sort' => $sort2 + ]); + $update_result[] = $this->getDataOrTerminateWithError($update_result1); + + $update_result2 = $this->BookmarkModel->update($bookmark_id2, [ + 'sort' => $sort1 + ]); + $update_result[] = $this->getDataOrTerminateWithError($update_result2); + + $this->terminateWithSuccess($update_result); + } } diff --git a/application/controllers/api/frontend/v1/Cis4FhcApi.php b/application/controllers/api/frontend/v1/Cis4FhcApi.php index 372e4bfaa..4d0f906f0 100644 --- a/application/controllers/api/frontend/v1/Cis4FhcApi.php +++ b/application/controllers/api/frontend/v1/Cis4FhcApi.php @@ -27,7 +27,7 @@ class Cis4FhcApi extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'getViewData' => self::PERM_LOGGED, + 'dashboardViewData' => self::PERM_LOGGED, ]); } @@ -36,17 +36,22 @@ class Cis4FhcApi extends FHCAPI_Controller // Public methods /** - * fetches ViewData - */ - public function getViewData() + * retrieves view data for dashboard view + * @access public + * @param $uid the userID for which profile is being viewed, null or missing value implies one's own profile + */ + public function dashboardViewData() { $this->load->model('person/Person_model','PersonModel'); $personData = getData($this->PersonModel->getByUid(getAuthUID()))[0]; + $this->load->config('calendar'); + $viewData = array( 'uid' => getAuthUID(), 'name' => $personData->vorname, - 'person_id' => $personData->person_id + 'person_id' => $personData->person_id, + 'timezone' => $this->config->item('timezone'), ); $this->terminateWithSuccess($viewData); diff --git a/application/controllers/api/frontend/v1/LvPlan.php b/application/controllers/api/frontend/v1/LvPlan.php index 28b48e3f1..dc87732b9 100644 --- a/application/controllers/api/frontend/v1/LvPlan.php +++ b/application/controllers/api/frontend/v1/LvPlan.php @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -if (! defined('BASEPATH')) exit('No direct script access allowed'); +if (!defined('BASEPATH')) + exit('No direct script access allowed'); use CI3_Events as Events; use \DateTime as DateTime; @@ -33,19 +34,25 @@ class LvPlan extends FHCAPI_Controller parent::__construct([ 'getRoomplan' => self::PERM_LOGGED, - 'Stunden' => self::PERM_LOGGED, - 'getReservierungen' => self::PERM_LOGGED, + 'Stunden' => self::PERM_LOGGED, + 'getReservierungen' => self::PERM_LOGGED, 'LvPlanEvents' => self::PERM_LOGGED, 'eventsPersonal' => self::PERM_LOGGED, 'eventsLv' => self::PERM_LOGGED, 'getLehreinheitStudiensemester' => self::PERM_LOGGED, 'studiensemesterDateInterval' => self::PERM_LOGGED, 'getLvPlanForStudiensemester' => self::PERM_LOGGED, - 'getLv' => self::PERM_LOGGED + 'getLv' => self::PERM_LOGGED, + 'eventsStgOrg' => self::PERM_LOGGED, + 'fetchFerienEvents' => self::PERM_LOGGED, + 'getStudiengaenge' => self::PERM_LOGGED, + 'getLehrverband' => self::PERM_LOGGED, + 'permissionOtherLvPlan' => self::PERM_LOGGED, + 'compactibleEventTypes' => self::PERM_LOGGED, ]); - $this->load->library('LogLib'); - $this->loglib->setConfigs(array( + $this->load->library('LogLib'); + $this->loglib->setConfigs(array( 'classIndex' => 5, 'functionIndex' => 5, 'lineIndex' => 4, @@ -53,17 +60,17 @@ class LvPlan extends FHCAPI_Controller 'dbExecuteUser' => 'RESTful API' )); - $this->load->library('form_validation'); + $this->load->library('form_validation'); } //------------------------------------------------------------------------------------------------------------------ // Public methods /** - * fetches LvPlan and Moodle events together - * @access public - * - */ + * fetches LvPlan and Moodle events together + * @access public + * + */ public function LvPlanEvents() { $hasLv = $this->input->post('lv_id'); @@ -83,24 +90,30 @@ class LvPlan extends FHCAPI_Controller // form validation $this->form_validation->set_rules('start_date', "start_date", "required"); $this->form_validation->set_rules('end_date', "end_date", "required"); - + if (!$this->form_validation->run()) $this->terminateWithValidationErrors($this->form_validation->error_array()); // storing the post parameter in local variables $start_date = $this->input->post('start_date', true); $end_date = $this->input->post('end_date', true); + $uid = $this->input->post('uid', true); + + // disallow accessing other user's lv plan if missing permission + if ($uid && $uid !== getAuthUID() && !$this->permissionlib->isBerechtigt('basis/other_lv_plan')) { + $this->terminateWithError("Missing permission to view other users' timetables!"); + } // fetching lvplan events - $result = $this->stundenplanlib->getEventsUser($start_date, $end_date); + $result = $this->stundenplanlib->getEventsUser($start_date, $end_date, $uid); $lvplanEvents = $this->getDataOrTerminateWithError($result); // fetching moodle events - $moodleEvents = $this->fetchMoodleEvents($start_date, $end_date); + $moodleEvents = $uid ? [] : $this->fetchMoodleEvents($start_date, $end_date); // fetching ferien events - $ferienEvents = $this->fetchFerienEvents($start_date, $end_date); - + $ferienEvents = $this->fetchFerienEvents($start_date, $end_date, $uid); + $this->terminateWithSuccess(array_merge( $lvplanEvents, @@ -109,6 +122,45 @@ class LvPlan extends FHCAPI_Controller )); } + /** + * fetches LvPlan for studiengang / semester / verband / gruppe + * + * @access public + */ + public function eventsStgOrg() + { + $this->load->library('StundenplanLib'); + + // form validation + $this->form_validation->set_rules('start_date', "start_date", "required"); + $this->form_validation->set_rules('end_date', "end_date", "required"); + //$this->form_validation->set_rules('stg_kz', "stg_kz", "required"); //no validation show empty calendar + + if (!$this->form_validation->run()) { + $this->terminateWithValidationErrors($this->form_validation->error_array()); + $stgOrgEvents = []; + $ferienEvents = []; + } else { + $start_date = $this->input->post('start_date', true); + $end_date = $this->input->post('end_date', true); + $stg_kz = $this->input->post('stg_kz', true); + $sem = $this->input->post('sem', true); + $verband = $this->input->post('verband', true); + $gruppe = $this->input->post('gruppe', true); + + $result = $this->stundenplanlib->getEventsStgOrg($start_date, $end_date, $stg_kz, $sem, $verband, $gruppe); + $stgOrgEvents = $this->getDataOrTerminateWithError($result); + + $result = $this->stundenplanlib->fetchFerienTageEvents($start_date, $end_date, $stg_kz); + $ferienEvents = $this->getDataOrTerminateWithError($result); + } + + $this->terminateWithSuccess(array_merge( + $stgOrgEvents, + $ferienEvents + )); + } + /** * fetches LvPlan and Ferien events together for the lv * @@ -122,7 +174,7 @@ class LvPlan extends FHCAPI_Controller $this->form_validation->set_rules('start_date', "start_date", "required"); $this->form_validation->set_rules('end_date', "end_date", "required"); $this->form_validation->set_rules('lv_id', "lv_id", "required|integer"); - + if (!$this->form_validation->run()) $this->terminateWithValidationErrors($this->form_validation->error_array()); @@ -137,7 +189,6 @@ class LvPlan extends FHCAPI_Controller // fetching ferien events $ferienEvents = $this->fetchFerienEvents($start_date, $end_date); - $this->terminateWithSuccess(array_merge( $lvplanEvents, @@ -146,40 +197,42 @@ class LvPlan extends FHCAPI_Controller } //TODO: delete this function if we don't use the old calendar export endpoints anymore - public function studiensemesterDateInterval($date){ - $this->load->model('organisation/Studiensemester_model','StudiensemesterModel'); - $studiensemester =$this->StudiensemesterModel->getByDate(date_format(date_create($date),'Y-m-d')); - $studiensemester =current($this->getDataOrTerminateWithError($studiensemester)); + public function studiensemesterDateInterval($date) + { + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + $studiensemester = $this->StudiensemesterModel->getByDate(date_format(date_create($date), 'Y-m-d')); + $studiensemester = current($this->getDataOrTerminateWithError($studiensemester)); $this->terminateWithSuccess($studiensemester); } - public function getLvPlanForStudiensemester($studiensemester,$lvid){ + public function getLvPlanForStudiensemester($studiensemester, $lvid) + { $this->load->library('StundenplanLib'); - $this->load->model('organisation/Studiensemester_model','StudiensemesterModel'); - - $studiensemester_result = $this->StudiensemesterModel->loadWhere(["studiensemester_kurzbz"=>$studiensemester]); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + + $studiensemester_result = $this->StudiensemesterModel->loadWhere(["studiensemester_kurzbz" => $studiensemester]); $studiensemester_result = current($this->getDataOrTerminateWithError($studiensemester_result)); $timespan_start = new DateTime($studiensemester_result->start); $timespan_ende = new DateTime($studiensemester_result->ende); - $lvplan = $this->stundenplanlib->getStundenplan(date_format($timespan_start, 'Y-m-d'),date_format($timespan_ende, 'Y-m-d'), $lvid); + $lvplan = $this->stundenplanlib->getStundenplan(date_format($timespan_start, 'Y-m-d'), date_format($timespan_ende, 'Y-m-d'), $lvid); $this->terminateWithSuccess($lvplan); - - } - - /** - * fetches Stunden layout from database - * @access public - * - */ - public function Stunden() + } + + + /** + * fetches Stunden layout from database + * @access public + * + */ + public function Stunden() { $this->load->model('ressource/Stunde_model', 'StundeModel'); $this->StundeModel->addOrder('stunde', 'ASC'); $stunden = $this->StundeModel->load(); - $stunden = $this->getDataOrTerminateWithError($stunden); + $stunden = $this->getDataOrTerminateWithError($stunden); $this->terminateWithSuccess($stunden); } @@ -210,10 +263,10 @@ class LvPlan extends FHCAPI_Controller $roomplan_data = $this->stundenplanlib->getRoomplan($ort_kurzbz, $start_date, $end_date); $roomplan_data = $this->getDataOrTerminateWithError($roomplan_data); - + $this->terminateWithSuccess($roomplan_data); } - + /** * gets the reservierungen of a room if the ort_kurzbz parameter is * supplied otherwise gets the reservierungen of the lvplan of a student @@ -226,25 +279,27 @@ class LvPlan extends FHCAPI_Controller { $this->form_validation->set_rules('start_date', "StartDate", "required"); $this->form_validation->set_rules('end_date', "EndDate", "required"); - + if (!$this->form_validation->run()) $this->terminateWithValidationErrors($this->form_validation->error_array()); // storing the post parameter in local variables $start_date = $this->input->post('start_date', true); $end_date = $this->input->post('end_date', true); + $uid = $this->input->post('uid', true); // get data $this->load->library('StundenplanLib'); - $result = $this->stundenplanlib->getReservierungen($start_date, $end_date, $ort_kurzbz); + $result = $this->stundenplanlib->getReservierungen($start_date, $end_date, $ort_kurzbz, $uid); $result = $this->getDataOrTerminateWithError($result); $this->terminateWithSuccess($result); } - public function getLehreinheitStudiensemester($lehreinheit_id){ + public function getLehreinheitStudiensemester($lehreinheit_id) + { $this->load->model('education/Lehreinheit_model', 'LehreinheitModel'); $this->LehreinheitModel->addSelect(["studiensemester_kurzbz"]); $result = $this->LehreinheitModel->load($lehreinheit_id); @@ -287,6 +342,68 @@ class LvPlan extends FHCAPI_Controller return $this->terminateWithSuccess(current($result)); } + public function getStudiengaenge() + { + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); + + $this->StudiengangModel->addOrder('typ'); + $this->StudiengangModel->addOrder('kurzbz'); + $result = $this->StudiengangModel->loadWhere([ + 'aktiv' => true + ]); + + $data = $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($data); + } + + public function getLehrverband($studiengang_kz, $semester = null, $verband = null) + { + $this->load->model('organisation/Lehrverband_model', 'LehrverbandModel'); + + $where = [ + 'aktiv' => true, + 'studiengang_kz' => $studiengang_kz, + ]; + + if ($semester !== null && $semester !== 'null' && $semester !== 'undefined') { + $where['semester'] = $semester; + } + if ($verband !== null && $verband !== 'null' && $verband !== 'undefined') { + $where['verband'] = $verband; + } + + $this->LehrverbandModel->addOrder('studiengang_kz'); + $this->LehrverbandModel->addOrder('semester'); + $this->LehrverbandModel->addOrder('verband'); + $this->LehrverbandModel->addOrder('gruppe'); + $result = $this->LehrverbandModel->loadWhere($where); + + $data = $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($data); + } + + /** + * Checks if the current user has permission to view other users' timetables + * + * @return void + */ + public function permissionOtherLvPlan() + { + $this->terminateWithSuccess($this->permissionlib->isBerechtigt('basis/other_lv_plan')); + } + + /** + * get event types which can be compacted in lv plan display + * + * @return void + */ + public function compactibleEventTypes() + { + $this->terminateWithSuccess(["lehreinheit", "reservierung"]); + } + /** * fetch moodle events * @@ -299,19 +416,19 @@ class LvPlan extends FHCAPI_Controller $this->load->config('calendar'); $tz = new DateTimeZone($this->config->item('timezone')); - + $start = new DateTime($start_date); $start->setTimezone($tz); - + $end = new DateTime($end_date); $end->setTimezone($tz); $end->modify('+1 day -1 second'); - + $moodle_events = []; - + Events::trigger( 'moodleCalendarEvents', - function & () use (&$moodle_events) { + function &() use (&$moodle_events) { return $moodle_events; }, [ @@ -331,23 +448,23 @@ class LvPlan extends FHCAPI_Controller * @param string $end_date * @return array */ - private function fetchFerienEvents($start_date, $end_date) + private function fetchFerienEvents($start_date, $end_date, $uid = null) { $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); $this->load->model('education/Studentlehrverband_model', 'StudentLehrverbandModel'); $currentStudiensemester = $this->StudiensemesterModel->getByDate($start_date); $currentStudiensemester = $this->getDataOrTerminateWithError($currentStudiensemester); - + if ($currentStudiensemester) { $studentsemester_kurzbz = current($currentStudiensemester)->studiensemester_kurzbz; $studiengang = $this->StudentLehrverbandModel->loadWhere([ - "student_uid" => getAuthUID(), + "student_uid" => $uid ?? getAuthUID(), "studiensemester_kurzbz" => $studentsemester_kurzbz ]); $studiengang = $this->getDataOrTerminateWithError($studiengang); - + if ($studiengang) $studiengang_kz = current($studiengang)->studiengang_kz; else @@ -357,7 +474,7 @@ class LvPlan extends FHCAPI_Controller } $ferienEvents = $this->stundenplanlib->fetchFerienTageEvents($start_date, $end_date, $studiengang_kz); - + return $this->getDataOrTerminateWithError($ferienEvents); } } diff --git a/application/controllers/api/frontend/v1/OtherLvPlan.php b/application/controllers/api/frontend/v1/OtherLvPlan.php new file mode 100644 index 000000000..cad654f12 --- /dev/null +++ b/application/controllers/api/frontend/v1/OtherLvPlan.php @@ -0,0 +1,76 @@ +. + */ + +if (!defined('BASEPATH')) + exit('No direct script access allowed'); + +class OtherLvPlan extends FHCAPI_Controller +{ + + /** + * Object initialization + */ + public function __construct() + { + parent::__construct([ + 'otherLvPlanViewData' => ['basis/other_lv_plan:r'], + ]); + + $this->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel'); + $this->load->model('person/Benutzer_model', 'BenutzerModel'); + + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * retrieves viewData for other lv plan view + * @access public + * @param $uid the userID for which the other lv plan is being viewed + */ + public function otherLvPlanViewData($uid) + { + $isMitarbeiterResult = $this->MitarbeiterModel->isMitarbeiter($uid); + $isMitarbeiter = getData($isMitarbeiterResult); + $isStudent = !$isMitarbeiter; + + $this->BenutzerModel->addSelect(["foto", "vorname", "nachname"]); + $this->BenutzerModel->addJoin("tbl_person", "person_id"); + $personResult = $this->BenutzerModel->load([$uid]); + $person = hasData($personResult) ? getData($personResult) : null; + + $viewData = [ + "user_data" => [ + "username" => $uid, + "is_student" => $isStudent, + "is_mitarbeiter" => $isMitarbeiter, + "foto" => $person[0]->foto, + "vorname" => $person[0]->vorname, + "nachname" => $person[0]->nachname, + ], + ]; + + $this->terminateWithSuccess($viewData); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Private methods + +} + diff --git a/application/controllers/api/frontend/v1/Profil.php b/application/controllers/api/frontend/v1/Profil.php index 3133b107a..96cdea855 100644 --- a/application/controllers/api/frontend/v1/Profil.php +++ b/application/controllers/api/frontend/v1/Profil.php @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -if (! defined('BASEPATH')) exit('No direct script access allowed'); +if (!defined('BASEPATH')) + exit('No direct script access allowed'); class Profil extends FHCAPI_Controller { @@ -27,13 +28,13 @@ class Profil extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'fotoSperre' => self::PERM_LOGGED, + 'fotoSperre' => self::PERM_LOGGED, 'getGemeinden' => self::PERM_LOGGED, 'getAllNationen' => self::PERM_LOGGED, 'isMitarbeiter' => self::PERM_LOGGED, 'profilViewData' => self::PERM_LOGGED, ]); - + $this->load->library('PermissionLib'); $this->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel'); @@ -48,28 +49,37 @@ class Profil extends FHCAPI_Controller //------------------------------------------------------------------------------------------------------------------ // Public methods - public function profilViewData($uid=null){ + + /** + * retrieves view data for profile view + * @access public + * @param $uid the userID for which profile is being viewed, null or missing value implies one's own profile + */ + public function profilViewData($uid = null) + { + $authUid = getAuthUID(); + $isProfilOfAuthUser = !$uid || $uid === $authUid; + $this->load->library('ProfilLib'); - $editable = false; - if(isset($uid) && $uid != null){ - $profil_data = $this->profillib->getView($uid); - if($uid == getAuthUID()){ - $editable = true; - } - }else{ - $editable = true; - $profil_data = $this->profillib->getView(getAuthUID()); + $profileData = $this->profillib->getView($uid ?? $authUid); + $profileData = hasData($profileData) ? getData($profileData) : null; + + $viewData = [ + 'editable' => $isProfilOfAuthUser, + 'profil_data' => $profileData, + 'permissions' => [ + 'basis/other_lv_plan' => $this->permissionlib->isBerechtigt(('basis/other_lv_plan')) + ] + ]; + + if ($isProfilOfAuthUser) { + $viewData['calendar_sync_urls'] = $this->getCalendarSyncUrlData(); } - - $profil_data = hasData($profil_data) ? getData($profil_data) : null; - $viewData = array( - 'editable'=>$editable, - 'profil_data' => $profil_data, - ); + $this->terminateWithSuccess($viewData); } - /** + /** * update column foto_sperre in public.tbl_person * @access public * @param boolean $value new value for the column @@ -77,9 +87,9 @@ class Profil extends FHCAPI_Controller */ public function fotoSperre($value) { - if(!isset($value)){ - $this->terminateWithError("Missing parameter", self::ERROR_TYPE_GENERAL); - } + if (!isset($value)) { + $this->terminateWithError("Missing parameter", self::ERROR_TYPE_GENERAL); + } $res = $this->PersonModel->update($this->pid, ["foto_sperre" => $value]); if (isError($res)) { @@ -87,10 +97,10 @@ class Profil extends FHCAPI_Controller } $this->PersonModel->addSelect("foto_sperre"); $res = $this->PersonModel->load($this->pid); - - $res = $this->getDataOrTerminateWithError($res); - - $this->terminateWithSuccess(current($res)); + + $res = $this->getDataOrTerminateWithError($res); + + $this->terminateWithSuccess(current($res)); } /** @@ -109,7 +119,7 @@ class Profil extends FHCAPI_Controller if (isError($nation_res)) { $this->terminateWithError("error while trying to query table codex.tbl_nation", self::ERROR_TYPE_GENERAL); } - + $nation_res = $this->getDataOrTerminateWithError($nation_res); $this->terminateWithSuccess($nation_res); @@ -117,30 +127,30 @@ class Profil extends FHCAPI_Controller public function getGemeinden($nation, $zip) { - if(!isset($nation) || !isset($zip)){ + if (!isset($nation) || !isset($zip)) { echo json_encode(error("Missing parameters")); return; } - + $this->load->model('codex/Gemeinde_model', "GemeindeModel"); - + $gemeinde_res = $this->GemeindeModel->getGemeindeByPlz($zip); - + if (isError($gemeinde_res)) { - $this->terminateWithError(getError($gemeinde_res),self::ERROR_TYPE_GENERAL); + $this->terminateWithError(getError($gemeinde_res), self::ERROR_TYPE_GENERAL); } $gemeinde_res = $this->getDataOrTerminateWithError($gemeinde_res); - + /* $gemeinde_res = array_map(function ($obj) { return $obj->ortschaftsname; }, $gemeinde_res); */ - $this->terminateWithSuccess($gemeinde_res); - + $this->terminateWithSuccess($gemeinde_res); + } - + /** * checks whether a specific userID is a mitarbeiter or not (foreword declaration of the function isMitarbeiter in Mitarbeiter_model.php) * @access public @@ -150,23 +160,48 @@ class Profil extends FHCAPI_Controller public function isMitarbeiter($uid) { - if(!$uid) $this->terminateWithError("No uid provided", self::ERROR_TYPE_GENERAL); - - + if (!$uid) + $this->terminateWithError("No uid provided", self::ERROR_TYPE_GENERAL); + + $result = $this->MitarbeiterModel->isMitarbeiter($uid); - + if (isError($result)) { $this->terminateWithError("error when calling Mitarbeiter_model function isMitarbeiter with uid " . $uid, self::ERROR_TYPE_GENERAL); } $result = $this->getDataOrTerminateWithError($result); - + $this->terminateWithSuccess($result); } // ----------------------------------------------------------------------------------------------------------------- // Private methods - + /** + * gets the identifier, phrase, and url for each calendar sync option + * @access private + * @return array array of arrays, where each child array is a sync option + */ + private function getCalendarSyncUrlData() + { + return [ + [ + "identifier" => "cal_dav", + "labelPhrase" => "profil/calendar_sync_cal_dav", + "url" => APP_ROOT . "webdav/lvplan.php/calendars/" . $this->uid . "/LVPlan-" . $this->uid, + ], + [ + "identifier" => "cal_dav_principal", + "labelPhrase" => "profil/calendar_sync_cal_dav_principal", + "url" => APP_ROOT . "webdav/lvplan.php/principals/" . $this->uid, + ], + [ + "identifier" => "i_cal", + "labelPhrase" => "profil/calendar_sync_i_cal", + "url" => APP_ROOT . "webdav/google.php?cal=" . encryptData($this->uid, LVPLAN_CYPHER_KEY) . "&" . microtime(true), + ], + ]; + } } diff --git a/application/controllers/api/frontend/v1/StgOrgLvPlan.php b/application/controllers/api/frontend/v1/StgOrgLvPlan.php new file mode 100644 index 000000000..ffbf85e57 --- /dev/null +++ b/application/controllers/api/frontend/v1/StgOrgLvPlan.php @@ -0,0 +1,64 @@ +. + */ + +if (!defined('BASEPATH')) + exit('No direct script access allowed'); + +class StgOrgLvPlan extends FHCAPI_Controller +{ + + /** + * Object initialization + */ + public function __construct() + { + parent::__construct([ + 'stgOrgLvPlanViewData' => self::PERM_LOGGED, + ]); + + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * fetches view data for stg org lv plan + * @access public + */ + public function stgOrgLvPlanViewData() + { + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); + $this->StudiengangModel->addOrder('typ'); + $this->StudiengangModel->addOrder('kurzbz'); + $result = $this->StudiengangModel->loadWhere([ + 'aktiv' => true + ]); + $studiengaenge = $this->getDataOrTerminateWithError($result); + + $viewData = array( + 'studiengaenge' => $studiengaenge, + ); + + $this->terminateWithSuccess($viewData); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Private methods + +} + diff --git a/application/controllers/api/frontend/v1/Studium.php b/application/controllers/api/frontend/v1/Studium.php index d17f0c1a1..f53a4105c 100644 --- a/application/controllers/api/frontend/v1/Studium.php +++ b/application/controllers/api/frontend/v1/Studium.php @@ -62,21 +62,36 @@ class Studium extends FHCAPI_Controller if($this->getDataOrTerminateWithError($this->StudentModel->isStudent(getAuthUID()))){ $studentLehrverband =$this->StudentlehrverbandModel->loadWhere(["student_uid" => getAuthUID(), "studiensemester_kurzbz" => $aktuelles_studiensemester->studiensemester_kurzbz]); - $studentLehrverband = current($this->getDataOrTerminateWithError($studentLehrverband)); - - $student_studiensemester = $studentLehrverband->studiensemester_kurzbz; - $student_studiengang = $studentLehrverband->studiengang_kz; - $student_semester = $studentLehrverband->semester; + + //TODO(Manu) check if use Fallback or just comment out all paramschecks? + //add Fallback: if no LehrverbandData of actual semester, get Data of previous one + if(!hasData($studentLehrverband)) + { + $result= $this->StudiensemesterModel->getPreviousFrom($aktuelles_studiensemester->studiensemester_kurzbz); + + $data = $this->getDataOrTerminateWithError($result); + $vorheriges_studiensemester = current($data)->studiensemester_kurzbz; + $studentLehrverband =$this->StudentlehrverbandModel->loadWhere(["student_uid" => getAuthUID(), "studiensemester_kurzbz" => $vorheriges_studiensemester]); + } + $studentLehrverband = current(getData($studentLehrverband)); + $student_studienplan = $this->getStudienPlanFromPrestudentStatus(getAuthPersonId())->studienplan_id; - if(!isset($parameter_studiensemester)) - $parameter_studiensemester = $student_studiensemester; - if(!isset($parameter_studiengang)) - $parameter_studiengang = $student_studiengang; - if(!isset($parameter_semester)) - $parameter_semester = $student_semester; + if(!isset($parameter_studiensemester)) { + $student_studiensemester = $studentLehrverband->studiensemester_kurzbz; + $parameter_studiensemester = $student_studiensemester; + } + if(!isset($parameter_studiengang)) { + $student_studiengang = $studentLehrverband->studiengang_kz; + $parameter_studiengang = $student_studiengang; + } + if(!isset($parameter_semester)) { + $student_semester = $studentLehrverband->semester; + $parameter_semester = $student_semester; + } if(!isset($parameter_studienplan)) - $parameter_studienplan = $student_studienplan; + $parameter_studienplan = $student_studienplan; + } if(isset($parameter_studiensemester)){ @@ -96,8 +111,7 @@ class Studium extends FHCAPI_Controller // fetch studiensemester $allStudienSemester = $this->getDataOrTerminateWithError($this->StudiensemesterModel->load()); - - + if(isset($parameter_studiensemester) && !empty(array_filter($allStudienSemester, function($studiensemester) use($parameter_studiensemester){ return $studiensemester->studiensemester_kurzbz == $parameter_studiensemester->studiensemester_kurzbz; }))){ @@ -216,6 +230,8 @@ class Studium extends FHCAPI_Controller $studienplaene = array_map(function($studienplan){ $orgform = current($this->getDataOrTerminateWithError($this->OrgformModel->loadWhere(["orgform_kurzbz" => $studienplan->orgform_kurzbz]))); $studienplan->orgform_bezeichnung = $orgform->bezeichnung; + // bezeichnung_mehrsprachig + $studienplan->orgform_bezeichnung_english = $orgform->bezeichnung_mehrsprachig[1]; return $studienplan; },$studienplaene); return $studienplaene; diff --git a/application/controllers/api/frontend/v1/dashboard/Board.php b/application/controllers/api/frontend/v1/dashboard/Board.php index c50fec128..fdded61e3 100644 --- a/application/controllers/api/frontend/v1/dashboard/Board.php +++ b/application/controllers/api/frontend/v1/dashboard/Board.php @@ -40,11 +40,32 @@ class Board extends FHCAPI_Controller public function list() { + $this->DashboardModel->addSelect('dashboard_id'); + $this->DashboardModel->addSelect('dashboard_kurzbz'); + $this->DashboardModel->addSelect('tbl_dashboard.beschreibung'); + $this->DashboardModel->addSelect("( + SELECT json_agg(w.*) + FROM dashboard.tbl_widget w + JOIN dashboard.tbl_dashboard_widget dw + USING(widget_id) + WHERE dw.dashboard_id=tbl_dashboard.dashboard_id + ) AS \"widgetSetup\""); + $result = $this->DashboardModel->load(); $data = $this->getDataOrTerminateWithError($result); - $this->terminateWithSuccess($result); + $data = array_map(function ($dashboard) { + $tmpSetups = json_decode($dashboard->widgetSetup); + $tmpSetups = array_map(function ($widget) { + $widget->setup->file = absoluteJsImportUrl($widget->setup->file); + return $widget; + }, $tmpSetups); + $dashboard->widgetSetup = $tmpSetups; + return $dashboard; + }, $data); + + $this->terminateWithSuccess($data); } public function create() @@ -82,7 +103,7 @@ class Board extends FHCAPI_Controller $data = $this->getDataOrTerminateWithError($result); - $this->terminateWithSuccess($result); + $this->terminateWithSuccess($data); } public function delete() @@ -116,6 +137,6 @@ class Board extends FHCAPI_Controller $data = $this->getDataOrTerminateWithError($result); - $this->terminateWithSuccess($result); + $this->terminateWithSuccess($data); } } diff --git a/application/controllers/api/frontend/v1/dashboard/Preset.php b/application/controllers/api/frontend/v1/dashboard/Preset.php index 5983d9660..d9be307cf 100644 --- a/application/controllers/api/frontend/v1/dashboard/Preset.php +++ b/application/controllers/api/frontend/v1/dashboard/Preset.php @@ -120,10 +120,7 @@ class Preset extends FHCAPI_Controller $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']; + $result[$funktion] = $preset; } else { $result[$funktion] = []; } @@ -154,7 +151,7 @@ class Preset extends FHCAPI_Controller $preset_decoded = json_decode($preset->preset, true); - $this->dashboardlib->addWidgetsToWidgets($preset_decoded, $dashboard_kurzbz, $funktion_kurzbz, [$widget]); + $preset_decoded[$widget['widgetid']] = $widget; $preset->preset = json_encode($preset_decoded); @@ -186,8 +183,10 @@ class Preset extends FHCAPI_Controller $preset_decoded = json_decode($preset->preset, true); - if (!$this->dashboardlib->removeWidgetFromWidgets($preset_decoded, $funktion_kurzbz, $widgetid)) + if (!isset($preset_decoded[$widgetid])) show_404(); + + unset($preset_decoded[$widgetid]); $preset->preset = json_encode($preset_decoded); diff --git a/application/controllers/api/frontend/v1/dashboard/User.php b/application/controllers/api/frontend/v1/dashboard/User.php index 9d020649e..e603573ed 100644 --- a/application/controllers/api/frontend/v1/dashboard/User.php +++ b/application/controllers/api/frontend/v1/dashboard/User.php @@ -48,25 +48,9 @@ class User extends FHCAPI_Controller $uid = $this->authlib->getAuthObj()->username; - /*$mergedconfig = $this->dashboardlib->getMergedConfig($dashboard->dashboard_id, $uid); + $mergedconfig = $this->dashboardlib->getMergedUserConfig($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 - ]); + $this->terminateWithSuccess($mergedconfig); } public function addWidget() @@ -86,26 +70,15 @@ class User extends FHCAPI_Controller if (!isset($widget['widgetid'])) $widget['widgetid'] = $this->dashboardlib->generateWidgetId($dashboard_kurzbz); + if (isset($widget['source'])) + unset($widget['source']); + $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'] = []; + $override_decoded[$widget['widgetid']] = $widget; - 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); @@ -135,18 +108,10 @@ class User extends FHCAPI_Controller $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]); - } - } + if (!isset($override_decoded[$widget_id])) + show_404(); + + unset($override_decoded[$widget_id]); $override->override = json_encode($override_decoded); diff --git a/application/development/config.php b/application/development/config.php new file mode 100644 index 000000000..d254722bb --- /dev/null +++ b/application/development/config.php @@ -0,0 +1,517 @@ +]+$/i +| +| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!! +| +*/ +$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-'; + +/* +|-------------------------------------------------------------------------- +| Enable Query Strings +|-------------------------------------------------------------------------- +| +| By default CodeIgniter uses search-engine friendly segment based URLs: +| example.com/who/what/where/ +| +| By default CodeIgniter enables access to the $_GET array. If for some +| reason you would like to disable it, set 'allow_get_array' to FALSE. +| +| You can optionally enable standard query string based URLs: +| example.com?who=me&what=something&where=here +| +| Options are: TRUE or FALSE (boolean) +| +| The other items let you set the query string 'words' that will +| invoke your controllers and its functions: +| example.com/index.php?c=controller&m=function +| +| Please note that some of the helpers won't work as expected when +| this feature is enabled, since CodeIgniter is designed primarily to +| use segment based URLs. +| +*/ +$config['allow_get_array'] = TRUE; +$config['enable_query_strings'] = FALSE; +$config['controller_trigger'] = 'c'; +$config['function_trigger'] = 'm'; +$config['directory_trigger'] = 'd'; + +/* +|-------------------------------------------------------------------------- +| Error Logging Threshold +|-------------------------------------------------------------------------- +| +| You can enable error logging by setting a threshold over zero. The +| threshold determines what gets logged. Threshold options are: +| +| 0 = Disables logging, Error logging TURNED OFF +| 1 = Error Messages (including PHP errors) +| 2 = Debug Messages +| 3 = Informational Messages +| 4 = All Messages +| +| You can also pass an array with threshold levels to show individual error types +| +| array(2) = Debug Messages, without Error Messages +| +| For a live site you'll usually only enable Errors (1) to be logged otherwise +| your log files will fill up very fast. +| +*/ +$config['log_threshold'] = 1; + +/* +|-------------------------------------------------------------------------- +| Error Logging Directory Path +|-------------------------------------------------------------------------- +| +| Leave this BLANK unless you would like to set something other than the default +| application/logs/ directory. Use a full server path with trailing slash. +| +*/ +$config['log_path'] = ''; + +/* +|-------------------------------------------------------------------------- +| Log File Extension +|-------------------------------------------------------------------------- +| +| The default filename extension for log files. The default 'php' allows for +| protecting the log files via basic scripting, when they are to be stored +| under a publicly accessible directory. +| +| Note: Leaving it blank will default to 'php'. +| +*/ +$config['log_file_extension'] = 'log'; + +/* +|-------------------------------------------------------------------------- +| Log File Permissions +|-------------------------------------------------------------------------- +| +| The file system permissions to be applied on newly created log files. +| +| IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal +| integer notation (i.e. 0700, 0644, etc.) +*/ +$config['log_file_permissions'] = 0644; + +/* +|-------------------------------------------------------------------------- +| Date Format for Logs +|-------------------------------------------------------------------------- +| +| Each item that is logged has an associated date. You can use PHP date +| codes to set your own date formatting +| +*/ +$config['log_date_format'] = 'Y-m-d H:i:s'; + +/* +|-------------------------------------------------------------------------- +| Error Views Directory Path +|-------------------------------------------------------------------------- +| +| Leave this BLANK unless you would like to set something other than the default +| application/views/errors/ directory. Use a full server path with trailing slash. +| +*/ +$config['error_views_path'] = ''; + +/* +|-------------------------------------------------------------------------- +| Cache Directory Path +|-------------------------------------------------------------------------- +| +| Leave this BLANK unless you would like to set something other than the default +| application/cache/ directory. Use a full server path with trailing slash. +| +*/ +$config['cache_path'] = ''; + +/* +|-------------------------------------------------------------------------- +| Cache Include Query String +|-------------------------------------------------------------------------- +| +| Whether to take the URL query string into consideration when generating +| output cache files. Valid options are: +| +| FALSE = Disabled +| TRUE = Enabled, take all query parameters into account. +| Please be aware that this may result in numerous cache +| files generated for the same page over and over again. +| array('q') = Enabled, but only take into account the specified list +| of query parameters. +| +*/ +$config['cache_query_string'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Encryption Key +|-------------------------------------------------------------------------- +| +| If you use the Encryption class, you must set an encryption key. +| See the user guide for more info. +| +| http://codeigniter.com/user_guide/libraries/encryption.html +| +*/ +$config['encryption_key'] = ''; + +/* +|-------------------------------------------------------------------------- +| Session Variables +|-------------------------------------------------------------------------- +| +| 'sess_driver' +| +| The storage driver to use: files, database, redis, memcached +| +| 'sess_cookie_name' +| +| The session cookie name, must contain only [0-9a-z_-] characters +| +| 'sess_expiration' +| +| The number of SECONDS you want the session to last. +| Setting to 0 (zero) means expire when the browser is closed. +| +| 'sess_save_path' +| +| The location to save sessions to, driver dependent. +| +| For the 'files' driver, it's a path to a writable directory. +| WARNING: Only absolute paths are supported! +| +| For the 'database' driver, it's a table name. +| Please read up the manual for the format with other session drivers. +| +| IMPORTANT: You are REQUIRED to set a valid save path! +| +| 'sess_match_ip' +| +| Whether to match the user's IP address when reading the session data. +| +| 'sess_time_to_update' +| +| How many seconds between CI regenerating the session ID. +| NOTE: Keep it as it is to prevent security issues (https://en.wikipedia.org/wiki/Session_fixation) +| +| 'sess_regenerate_destroy' +| +| Whether to destroy session data associated with the old session ID +| when auto-regenerating the session ID. When set to FALSE, the data +| will be later deleted by the garbage collector. +| +| Other session cookie settings are shared with the rest of the application, +| except for 'cookie_prefix' and 'cookie_httponly', which are ignored here. +| +*/ +$config['sess_driver'] = 'files'; +$config['sess_cookie_name'] = 'sess_ci_session'; +$config['sess_expiration'] = 1800; // Session expires every 30 minutes +$config['sess_save_path'] = NULL; +$config['sess_match_ip'] = FALSE; +$config['sess_time_to_update'] = 300; +$config['sess_regenerate_destroy'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Cookie Related Variables +|-------------------------------------------------------------------------- +| +| 'cookie_prefix' = Set a cookie name prefix if you need to avoid collisions +| 'cookie_domain' = Set to .your-domain.com for site-wide cookies +| 'cookie_path' = Typically will be a forward slash +| 'cookie_secure' = Cookie will only be set if a secure HTTPS connection exists. +| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript) +| +| Note: These settings (with the exception of 'cookie_prefix' and +| 'cookie_httponly') will also affect sessions. +| +*/ +$config['cookie_prefix'] = ''; +$config['cookie_domain'] = ''; +$config['cookie_path'] = '/'; +$config['cookie_secure'] = FALSE; +$config['cookie_httponly'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Standardize newlines +|-------------------------------------------------------------------------- +| +| Determines whether to standardize newline characters in input data, +| meaning to replace \r\n, \r, \n occurrences with the PHP_EOL value. +| +| This is particularly useful for portability between UNIX-based OSes, +| (usually \n) and Windows (\r\n). +| +*/ +$config['standardize_newlines'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Global XSS Filtering +|-------------------------------------------------------------------------- +| +| Determines whether the XSS filter is always active when GET, POST or +| COOKIE data is encountered +| +| WARNING: This feature is DEPRECATED and currently available only +| for backwards compatibility purposes! +| +*/ +$config['global_xss_filtering'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Cross Site Request Forgery +|-------------------------------------------------------------------------- +| Enables a CSRF cookie token to be set. When set to TRUE, token will be +| checked on a submitted form. If you are accepting user data, it is strongly +| recommended CSRF protection be enabled. +| +| 'csrf_token_name' = The token name +| 'csrf_cookie_name' = The cookie name +| 'csrf_expire' = The number in seconds the token should expire. +| 'csrf_regenerate' = Regenerate token on every submission +| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks +*/ +$config['csrf_protection'] = FALSE; +$config['csrf_token_name'] = 'csrf_test_name'; +$config['csrf_cookie_name'] = 'csrf_cookie_name'; +$config['csrf_expire'] = 7200; +$config['csrf_regenerate'] = TRUE; +$config['csrf_exclude_uris'] = array(); + +/* +|-------------------------------------------------------------------------- +| Output Compression +|-------------------------------------------------------------------------- +| +| Enables Gzip output compression for faster page loads. When enabled, +| the output class will test whether your server supports Gzip. +| Even if it does, however, not all browsers support compression +| so enable only if you are reasonably sure your visitors can handle it. +| +| Only used if zlib.output_compression is turned off in your php.ini. +| Please do not use it together with httpd-level output compression. +| +| VERY IMPORTANT: If you are getting a blank page when compression is enabled it +| means you are prematurely outputting something to your browser. It could +| even be a line of whitespace at the end of one of your scripts. For +| compression to work, nothing can be sent before the output buffer is called +| by the output class. Do not 'echo' any values with compression enabled. +| +*/ +$config['compress_output'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Master Time Reference +|-------------------------------------------------------------------------- +| +| Options are 'local' or any PHP supported timezone. This preference tells +| the system whether to use your server's local time as the master 'now' +| reference, or convert it to the configured one timezone. See the 'date +| helper' page of the user guide for information regarding date handling. +| +*/ +$config['time_reference'] = 'local'; + +/* +|-------------------------------------------------------------------------- +| Rewrite PHP Short Tags +|-------------------------------------------------------------------------- +| +| If your PHP installation does not have short tag support enabled CI +| can rewrite the tags on-the-fly, enabling you to utilize that syntax +| in your view files. Options are TRUE or FALSE (boolean) +| +| Note: You need to have eval() enabled for this to work. +| +*/ +$config['rewrite_short_tags'] = FALSE; + +/* +|-------------------------------------------------------------------------- +| Reverse Proxy IPs +|-------------------------------------------------------------------------- +| +| If your server is behind a reverse proxy, you must whitelist the proxy +| IP addresses from which CodeIgniter should trust headers such as +| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify +| the visitor's IP address. +| +| You can use both an array or a comma-separated list of proxy addresses, +| as well as specifying whole subnets. Here are a few examples: +| +| Comma-separated: '10.0.1.200,192.168.5.0/24' +| Array: array('10.0.1.200', '192.168.5.0/24') +*/ +$config['proxy_ips'] = ''; + +/* +|-------------------------------------------------------------------------- +| FHComplete Build Version +|-------------------------------------------------------------------------- +| +| Version Number of the Current Build +| This is used to invalidate Cache for JS and CSS Files +| +| Example: 2019102901 +*/ +$config['fhcomplete_build_version'] = '2019102903'; diff --git a/application/development/database.php b/application/development/database.php new file mode 100644 index 000000000..fde6d6e68 --- /dev/null +++ b/application/development/database.php @@ -0,0 +1,122 @@ +db->last_query() and profiling of DB queries. +| When you run a query, with this setting set to TRUE (default), +| CodeIgniter will store the SQL statement for debugging purposes. +| However, this may cause high memory usage, especially if you run +| a lot of SQL queries ... disable this to avoid that problem. +| +| The $active_group variable lets you choose which connection group to +| make active. By default there is only one group (the 'default' group). +| +| The $query_builder variables lets you determine whether or not to load +| the query builder class. +*/ +$active_group = 'default'; +$query_builder = TRUE; + +$db['default'] = array( + 'dsn' => '', + 'hostname' => DB_HOST, + 'username' => DB_USER, + 'password' => DB_PASSWORD, + 'port' => DB_PORT, + 'database' => DB_NAME, + 'dbdriver' => 'postgre', + 'dbprefix' => '', + 'pconnect' => DB_CONNECT_PERSISTENT, + 'db_debug' => (ENVIRONMENT !== 'production'), + 'cache_on' => FALSE, + 'cachedir' => '', + 'char_set' => 'utf8', + 'dbcollat' => 'utf8_general_ci', + 'swap_pre' => '', + 'encrypt' => FALSE, + 'compress' => FALSE, + 'stricton' => FALSE, + 'failover' => array(), + 'save_queries' => TRUE +); + +$db['system'] = array( + 'dsn' => '', + 'hostname' => DB_HOST, + 'username' => 'fhcomplete', + 'password' => 'Fhcomplet3Onc4p1', + 'database' => DB_NAME, + 'port' => DB_PORT, + 'dbschema' => 'public', + 'dbdriver' => 'postgre', + 'dbprefix' => '', + 'pconnect' => DB_CONNECT_PERSISTENT, + 'db_debug' => (ENVIRONMENT !== 'production'), + 'cache_on' => FALSE, + 'cachedir' => '', + 'char_set' => 'utf8', + 'dbcollat' => 'utf8_general_ci', + 'swap_pre' => '', + 'encrypt' => FALSE, + 'compress' => FALSE, + 'stricton' => FALSE, + 'failover' => array(), + 'save_queries' => TRUE +); diff --git a/application/libraries/StundenplanLib.php b/application/libraries/StundenplanLib.php index 7ed64da2c..0ee80fa86 100644 --- a/application/libraries/StundenplanLib.php +++ b/application/libraries/StundenplanLib.php @@ -40,13 +40,16 @@ class StundenplanLib * @return stdClass * @access public */ - public function getEventsUser($start, $end) + public function getEventsUser($start, $end, $uid = null) { $this->_ci =& get_instance(); $this->_ci->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel'); - $uid = getAuthUID(); + if (!$uid) { + $uid = getAuthUID(); + } + if (is_null($uid)) return error("No UID"); @@ -217,7 +220,7 @@ class StundenplanLib * @param string $ort_kurzbz * @return stdClass */ - public function getReservierungen($start_date, $end_date, $ort_kurzbz = '') + public function getReservierungen($start_date, $end_date, $ort_kurzbz = '', $uid = null) { $this->_ci =& get_instance(); @@ -228,14 +231,14 @@ class StundenplanLib $this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel'); $this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel'); - $is_mitarbeiter = getData($this->_ci->MitarbeiterModel->isMitarbeiter(getAuthUID())); + $is_mitarbeiter = getData($this->_ci->MitarbeiterModel->isMitarbeiter($uid ?? getAuthUID())); if ($is_mitarbeiter && empty($ort_kurzbz)) { // request for personal lvplan show only reservations of logged in user - $reservierungen = $this->_ci->ReservierungModel->getReservierungenMitarbeiter($start_date, $end_date); + $reservierungen = $this->_ci->ReservierungModel->getReservierungenMitarbeiter($start_date, $end_date, $uid); } else { // querying the reservierungen - $reservierungen = $this->_ci->ReservierungModel->getReservierungen($start_date, $end_date, $ort_kurzbz); + $reservierungen = $this->_ci->ReservierungModel->getReservierungen($start_date, $end_date, $ort_kurzbz, $uid); } if (isError($reservierungen)) @@ -445,6 +448,24 @@ class StundenplanLib return success($ferienEventsFlattened); } + public function getEventsStgOrg( $start, $end, $stg_kz, $sem, $verband, $gruppe) + { + $this->_ci =& get_instance(); + + $this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel'); + + $stundenplan_data = $this->_ci->StundenplanModel->getStundenplanStudiengang($start, $end, $stg_kz, $sem, $verband, $gruppe); + if (isError($stundenplan_data)) + return $stundenplan_data; + $stundenplan_data = getData($stundenplan_data) ?? []; + + $function_error = $this->expandObjectInformation($stundenplan_data); + if ($function_error) + return $function_error; + + return success($stundenplan_data); + } + // start of the private functions ######################################################################################################## // function used to sort an array of studiensemester strings diff --git a/application/libraries/dashboard/DashboardLib.php b/application/libraries/dashboard/DashboardLib.php index 1c3983108..c9838f0e7 100644 --- a/application/libraries/dashboard/DashboardLib.php +++ b/application/libraries/dashboard/DashboardLib.php @@ -37,7 +37,9 @@ class DashboardLib public function getDashboardByKurzbz($dashboard_kurzbz) { - $result = $this->_ci->DashboardModel->getDashboardByKurzbz($dashboard_kurzbz); + $result = $this->_ci->DashboardModel->loadWhere([ + 'dashboard_kurzbz' => $dashboard_kurzbz + ]); if (hasData($result)) { @@ -47,17 +49,21 @@ class DashboardLib return null; } - public function getMergedConfig($dashboard_id, $uid) + public function getMergedUserConfig($dashboard_id, $uid) { - $defaultconfig = $this->getDefaultConfig($dashboard_id); - $userconfig = $this->getUserConfig($dashboard_id, $uid); + $defaultconfig = $this->getUserBaseConfig($dashboard_id); + $userconfig = $this->getUserOverrideConfig($dashboard_id, $uid); - $mergedconfig = array_replace_recursive($defaultconfig, $userconfig); + $sourceconfig = array_map(function ($value) { + return ['source' => $value['source']]; + }, $defaultconfig); + + $mergedconfig = array_replace_recursive($defaultconfig, $userconfig, $sourceconfig); return $mergedconfig; } - public function getDefaultConfig($dashboard_id) + protected function getUserBaseConfig($dashboard_id) { $funktion_kurzbzs = []; $rights = $this->_ci->permissionlib->getAccessRights(); @@ -87,7 +93,11 @@ class DashboardLib $preset = json_decode($presetobj->preset, true); if (null !== $preset) { - $defaultconfig = array_replace_recursive($defaultconfig, $preset); + $preset = array_map(function ($value) use ($presetobj) { + $value['source'] = $presetobj->funktion_kurzbz ?: self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; + return $value; + }, $preset); + $defaultconfig = array_merge_recursive($defaultconfig, $preset); } } } @@ -95,7 +105,7 @@ class DashboardLib return $defaultconfig; } - public function getUserConfig($dashboard_id, $uid) + protected function getUserOverrideConfig($dashboard_id, $uid) { $res_userconfig = $this->_ci->DashboardOverrideModel->getOverride($dashboard_id, $uid); @@ -124,7 +134,7 @@ class DashboardLib $emptyoverride = new stdClass(); $emptyoverride->dashboard_id = $dashboard->dashboard_id; $emptyoverride->uid = $uid; - $emptyoverride->override = '{"' . self::USEROVERRIDE_SECTION . '": {"widgets":{}}, "custom": { "widgets" : {}}}'; + $emptyoverride->override = '[]'; return $emptyoverride; } @@ -143,8 +153,7 @@ class DashboardLib $emptypreset = new stdClass(); $emptypreset->dashboard_id = $dashboard->dashboard_id; $emptypreset->funktion_kurzbz = $funktion_kurzbz; - $section = ($funktion_kurzbz !== null) ? $funktion_kurzbz : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; - $emptypreset->preset = '{"' . $section . '": { "widgets" : {}},"custom": { "widgets" : {}}}'; + $emptypreset->preset = '[]'; return $emptypreset; } @@ -209,44 +218,4 @@ class DashboardLib return $result; } - - public function addWidgetsToWidgets(&$widgets, $dashboard_kurzbz, $section, $addwigets) - { - foreach ($addwigets as $widget) - { - if(!isset($widget['widgetid'])) - { - $widget['widgetid'] = $this->generateWidgetId($dashboard_kurzbz); - } - $this->addWidgetToWidgets($widgets, $section, $widget, $widget['widgetid']); - } - } - - public function addWidgetToWidgets(&$widgets, $section, $widget, $widgetid) - { - $section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; - if (!isset($widgets[$section]) || !isset($widgets[$section]["widgets"]) || !is_array($widgets[$section])) - { - $widgets[$section] = array(); - $widgets[$section]["widgets"] = array(); - } - - $widgets[$section]["widgets"][$widgetid] = $widget; - } - - public function removeWidgetFromWidgets(&$widgets, $section, $widgetid) - { - $section = ($section !== null) ? $section : self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL; - if (isset($widgets[$section]) && isset($widgets[$section]["widgets"][$widgetid])) - { - unset($widgets[$section]["widgets"][$widgetid]); - if(empty($widgets[$section]["widgets"]) && $section !== self::USEROVERRIDE_SECTION) { - unset($widgets[$section]); - } - return true; - } - else { - return false; - } - } } diff --git a/application/models/content/Content_model.php b/application/models/content/Content_model.php index 278022b59..48a155cb2 100644 --- a/application/models/content/Content_model.php +++ b/application/models/content/Content_model.php @@ -234,9 +234,9 @@ class Content_model extends DB_Model FROM campus.tbl_content c1 LEFT JOIN - campus.tbl_contentsprache s1 ON c1.content_id=s1.content_id AND s1.sprache=? + campus.tbl_contentsprache s1 ON c1.content_id=s1.content_id AND s1.sprache=? AND sichtbar=true WHERE - sichtbar=true + c1.aktiv = true ) s2 LEFT JOIN campus.tbl_contentsprache s3 USING(content_id, sprache) @@ -277,7 +277,7 @@ class Content_model extends DB_Model JOIN campus.tbl_contentsprache s USING(contentsprache_id) LEFT JOIN - campus.tbl_contentchild k ON(m.content_id=k.content_id) + campus.tbl_contentchild k ON(m.content_id=k.content_id) and c.aktiv = true WHERE EXISTS ( SELECT 1 FROM campus.tbl_contentgruppe diff --git a/application/models/dashboard/Bookmark_model.php b/application/models/dashboard/Bookmark_model.php index 5efacc26b..d9756294e 100644 --- a/application/models/dashboard/Bookmark_model.php +++ b/application/models/dashboard/Bookmark_model.php @@ -11,8 +11,4 @@ class Bookmark_model extends DB_Model $this->dbTable = 'dashboard.tbl_bookmark'; $this->pk = 'bookmark_id'; } - - - - } diff --git a/application/models/dashboard/Dashboard_model.php b/application/models/dashboard/Dashboard_model.php index 88946ed83..78f6b1100 100644 --- a/application/models/dashboard/Dashboard_model.php +++ b/application/models/dashboard/Dashboard_model.php @@ -11,15 +11,4 @@ class Dashboard_model extends DB_Model $this->dbTable = 'dashboard.tbl_dashboard'; $this->pk = 'dashboard_id'; } - - - /** - * Get Dashboard by kurzbz. - * @param string dashboard_kurzbz - * @return array - */ - public function getDashboardByKurzbz($dashboard_kurzbz) - { - return $this->loadWhere(array('dashboard_kurzbz' => $dashboard_kurzbz)); - } } diff --git a/application/models/ressource/Reservierung_model.php b/application/models/ressource/Reservierung_model.php index 0c391ea20..cf5f1e4f9 100644 --- a/application/models/ressource/Reservierung_model.php +++ b/application/models/ressource/Reservierung_model.php @@ -18,10 +18,10 @@ class Reservierung_model extends DB_Model * * @return stdClass */ - public function getReservierungen($start_date, $end_date, $ort_kurzbz = null) + public function getReservierungen($start_date, $end_date, $ort_kurzbz = null, $uid = null) { - - $lvplan_reservierungen_query="SELECT r.* , stund.beginn, stund.ende, + + $lvplan_reservierungen_query = "SELECT r.* , stund.beginn, stund.ende, CASE WHEN r.gruppe_kurzbz IS NOT NULL THEN r.gruppe_kurzbz ELSE CONCAT(UPPER(studg.typ),UPPER(studg.kurzbz),'-',COALESCE(CAST(r.semester AS varchar),'/'),COALESCE(CAST(r.verband AS varchar),'/')) @@ -35,7 +35,7 @@ class Reservierung_model extends DB_Model LEFT JOIN public.tbl_studiensemester ss2 ON slv.studiensemester_kurzbz = ss2.studiensemester_kurzbz AND ss2.start <=r.datum AND ss2.ende >= r.datum WHERE datum >= ? AND datum <= ? AND (ss1.studiensemester_kurzbz IS NOT NULL OR ss2.studiensemester_kurzbz IS NOT NULL)"; - + $raum_reservierungen_query = "SELECT res.*, beginn, ende, CASE WHEN res.gruppe_kurzbz IS NOT NULL THEN res.gruppe_kurzbz @@ -46,9 +46,9 @@ class Reservierung_model extends DB_Model JOIN lehre.tbl_stunde ON lehre.tbl_stunde.stunde = res.stunde WHERE res.ort_kurzbz = ? AND datum >= ? AND datum <= ?"; - $subquery = is_null($ort_kurzbz)? $lvplan_reservierungen_query:$raum_reservierungen_query; - - $query_result= $this->execReadOnlyQuery(" + $subquery = is_null($ort_kurzbz) ? $lvplan_reservierungen_query : $raum_reservierungen_query; + + $query_result = $this->execReadOnlyQuery(" SELECT 'reservierung' as type, beginn, ende, datum, COALESCE(titel, beschreibung) as topic, @@ -59,15 +59,15 @@ class Reservierung_model extends DB_Model FROM ( - ". $subquery ." + " . $subquery . " ) AS subquery GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung ORDER BY datum, beginn - ", is_null($ort_kurzbz) ?[getAuthUID(), getAuthUID(),$start_date,$end_date]: [$ort_kurzbz, $start_date, $end_date]); + ", is_null($ort_kurzbz) ? [$uid ?? getAuthUID(), $uid ?? getAuthUID(), $start_date, $end_date] : [$ort_kurzbz, $start_date, $end_date]); + - return $query_result; } @@ -76,7 +76,7 @@ class Reservierung_model extends DB_Model * * @return stdClass */ - public function getReservierungenMitarbeiter($start_date, $end_date) + public function getReservierungenMitarbeiter($start_date, $end_date, $uid = null) { $raum_reservierungen_query = "SELECT res.*, beginn, ende, @@ -91,8 +91,8 @@ class Reservierung_model extends DB_Model $subquery = $raum_reservierungen_query; - - $query_result= $this->execReadOnlyQuery(" + + $query_result = $this->execReadOnlyQuery(" SELECT 'reservierung' as type, beginn, ende, datum, COALESCE(titel, beschreibung) as topic, @@ -103,13 +103,13 @@ class Reservierung_model extends DB_Model FROM ( - ". $subquery ." + " . $subquery . " ) AS subquery GROUP BY datum, beginn, ende, ort_kurzbz, titel, beschreibung ORDER BY datum, beginn - ", [getAuthUID(), $start_date, $end_date]); + ", [$uid ?? getAuthUID(), $start_date, $end_date]); return $query_result; @@ -129,9 +129,9 @@ class Reservierung_model extends DB_Model $this->addJoin('public.tbl_studiensemester ss2', 'slv.studiensemester_kurzbz=ss2.studiensemester_kurzbz AND ss2.start<=r.datum AND ss2.ende>=r.datum', 'LEFT'); $this->db->or_where('ss1.studiensemester_kurzbz IS NOT NULL', null, false); $this->db->or_where('ss2.studiensemester_kurzbz IS NOT NULL', null, false); - + $query = $this->db->get_compiled_select('campus.vw_reservierung r'); - + return $this->execQuery($query, [$uid, $uid]); } diff --git a/application/models/ressource/Stundenplan_model.php b/application/models/ressource/Stundenplan_model.php index d0a97ed9d..997451243 100644 --- a/application/models/ressource/Stundenplan_model.php +++ b/application/models/ressource/Stundenplan_model.php @@ -388,6 +388,84 @@ class Stundenplan_model extends DB_Model ORDER BY datum, beginn", [$start_date, $end_date, $ma_uid]); } + + /** + * queries Stundenplan and filters by studiengang, semester, verband gruppe + * + * @return void + */ + public function getStundenplanStudiengang($start_date, $end_date, $stg_kz, $sem, $verband, $gruppe) { + + $qry_params = [$start_date, $end_date, $stg_kz]; + + $qry = " + SELECT + 'lehreinheit' as type, beginn, ende, datum, + CONCAT(lehrfach,'-',lehrform) as topic, + array_agg(DISTINCT lektor) as lektor, + array_agg(DISTINCT (gruppe,verband,semester,studiengang_kz,gruppen_kuerzel)) as gruppe, + string_agg(DISTINCT ort_kurzbz, '/') as ort_kurzbz, + array_agg(DISTINCT lehreinheit_id) as lehreinheit_id, + titel, lehrfach, lehrform, lehrfach_bez, organisationseinheit, farbe, lehrveranstaltung_id + FROM + ( + SELECT unr,datum,beginn, ende, + CASE + WHEN sp.mitarbeiter_kurzbz IS NOT NULL THEN sp.mitarbeiter_kurzbz + ELSE sp.lektor + END as lektor, + CASE + WHEN gruppe_kurzbz IS NOT NULL THEN gruppe_kurzbz + ELSE CONCAT(UPPER(sp.stg_typ),UPPER(sp.stg_kurzbz),'-',COALESCE(CAST(sp.semester AS varchar),'/'),COALESCE(CAST(sp.verband AS varchar),'/')) + END as gruppen_kuerzel, + (SELECT bezeichnung + FROM public.tbl_organisationseinheit + WHERE oe_kurzbz IN( + SELECT oe_kurzbz + FROM lehre.tbl_lehrveranstaltung + WHERE lehrveranstaltung_id = sp.lehrveranstaltung_id + )) as organisationseinheit, + sp.ort_kurzbz, sp.studiengang_kz, sp.titel,sp.lehreinheit_id,sp.lehrfach_id,sp.anmerkung,fix,lehrveranstaltung_id,stg_kurzbzlang,stg_bezeichnung,stg_typ,fachbereich_kurzbz,lehrfach,lehrfach_bez,farbe,lehrform,anmerkung_lehreinheit,gruppe, verband, semester,stg_kurzbz + FROM ( + SELECT sp.* + FROM lehre.vw_stundenplan sp + WHERE + sp.datum >= ? + AND sp.datum <= ? + ) sp + JOIN lehre.tbl_stunde ON lehre.tbl_stunde.stunde = sp.stunde + WHERE studiengang_kz = ? "; + + if($sem != NULL) + { + $qry_params[] = $sem; + $qry .= " AND (semester = ? OR semester IS NULL)"; + } + if($verband != NULL) + { + $qry_params[] = $verband; + $qry .= " AND (verband = ? OR verband IS NULL OR verband = '0' OR verband = '')"; + } + if($gruppe != NULL) + { + $qry_params[] = $gruppe; + $qry .= " AND (gruppe = ? OR gruppe IS NULL OR gruppe = '0' OR gruppe = '') "; + } + $qry.= " AND ( + gruppe_kurzbz is null OR EXISTS( + SELECT 1 + FROM + public.tbl_gruppe WHERE gruppe_kurzbz = sp.gruppe_kurzbz AND direktinskription = false + ) + )"; + + $qry.= " ) as subquery + + GROUP BY unr, datum, beginn, ende, titel, lehrform, lehrfach, lehrfach_bez, organisationseinheit, farbe, lehrveranstaltung_id + ORDER BY datum, beginn; "; + + return $this->execReadOnlyQuery($qry, $qry_params); + } /** * NO STANDALONE FUNCTION - Generates a SQL query string to fetch 'stundenplan' events for a specific student within the current semester. diff --git a/application/views/CisRouterView/CisRouterView.php b/application/views/CisRouterView/CisRouterView.php index 6ff428362..c51ca40c3 100644 --- a/application/views/CisRouterView/CisRouterView.php +++ b/application/views/CisRouterView/CisRouterView.php @@ -23,12 +23,14 @@ $includesArray = array( 'public/css/components/FormUnderline.css', 'public/css/components/abgabetool/abgabe.css', 'public/css/Cis4/Cms.css', - 'public/css/Cis4/Studium.css', + 'public/css/Cis4/Studium.css' ), 'customJSs' => array( 'vendor/npm-asset/primevue/accordion/accordion.min.js', 'vendor/npm-asset/primevue/accordiontab/accordiontab.min.js', 'vendor/npm-asset/primevue/checkbox/checkbox.min.js', + 'vendor/npm-asset/primevue/chips/chips.min.js', + 'vendor/npm-asset/primevue/multiselect/multiselect.min.js', 'vendor/npm-asset/primevue/inputnumber/inputnumber.min.js', 'vendor/npm-asset/primevue/speeddial/speeddial.min.js', 'vendor/npm-asset/primevue/textarea/textarea.min.js', @@ -39,7 +41,7 @@ $includesArray = array( 'vendor/moment/luxonjs/luxon.min.js' ), 'customJSModules' => array( - 'public/js/apps/Dashboard/Fhc.js', + 'public/js/apps/Cis/Cis.js', ), ); @@ -47,8 +49,6 @@ $includesArray = array( $this->load->view('templates/CISVUE-Header', $includesArray); ?>
> - +
load->view('templates/CISVUE-Footer', $includesArray); ?> diff --git a/application/views/templates/CISVUE-Footer.php b/application/views/templates/CISVUE-Footer.php index d7c1de24c..eae2a94ff 100644 --- a/application/views/templates/CISVUE-Footer.php +++ b/application/views/templates/CISVUE-Footer.php @@ -6,7 +6,7 @@ $includesArray = array( 'fontawesome6' => true, 'axios027' => true, 'customJSModules' => array_merge([ - 'public/js/apps/Cis.js' + 'public/js/apps/Cis/Menu.js' ], $customJSModules ?? []), 'customCSSs' => array_merge([ 'public/css/Cis4/Cis.css' diff --git a/application/views/templates/CISVUE-Header.php b/application/views/templates/CISVUE-Header.php index 804a43821..d98cbc9cd 100644 --- a/application/views/templates/CISVUE-Header.php +++ b/application/views/templates/CISVUE-Header.php @@ -8,7 +8,7 @@ $includesArray = array( 'axios027' => true, 'primevue3' => true, 'customJSModules' => array_merge([ - 'public/js/apps/Cis.js' + 'public/js/apps/Cis/Menu.js' ], $customJSModules ?? []), 'customCSSs' => array_merge([ 'public/css/Cis4/Cis.css', diff --git a/composer.json b/composer.json index c1f4506c6..ce6fc71e1 100644 --- a/composer.json +++ b/composer.json @@ -70,6 +70,18 @@ } } }, + { + "type": "package", + "package": { + "name": "drag-drop-touch-js/dragdroptouch", + "version": "2.0.3", + "source": { + "url": "https://github.com/drag-drop-touch-js/dragdroptouch.git", + "type": "git", + "reference": "master" + } + } + }, { "type": "package", "package": { @@ -452,6 +464,8 @@ "easyrdf/easyrdf": "0.9.*", + "drag-drop-touch-js/dragdroptouch": "*", + "fgelinas/timepicker": "0.3.3", "fortawesome/font-awesome4": "4.7.*", "fortawesome/font-awesome6": "6.1.*", diff --git a/composer.lock b/composer.lock index 7525b7f5b..212eea3be 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f4f0af4586f46f97d8b6092c1ac0fb3a", + "content-hash": "869cbc35bd1ba90ab90934fcb41b0f51", "packages": [ { "name": "afarkas/html5shiv", @@ -804,6 +804,16 @@ "abandoned": true, "time": "2018-03-09T06:07:41+00:00" }, + { + "name": "drag-drop-touch-js/dragdroptouch", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/drag-drop-touch-js/dragdroptouch.git", + "reference": "master" + }, + "type": "library" + }, { "name": "easyrdf/easyrdf", "version": "0.9.1", diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..bbfebad87 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "FHC-Core", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/public/css/Cis4/Cis.css b/public/css/Cis4/Cis.css index 3056d36b6..bb9655cb9 100644 --- a/public/css/Cis4/Cis.css +++ b/public/css/Cis4/Cis.css @@ -147,6 +147,8 @@ html { --fhc-cis-menu-lvl-5-color-hover: var(--fhc-text); --fhc-cis-grade-positive: var(--fhc-success); --fhc-cis-grade-negative: var(--fhc-danger); + + --fhc-offcanvas-zindex: 1045; } #themeSwitch i{ @@ -375,7 +377,7 @@ html { /* searchbar */ #nav-search { background-color: var(--fhc-primary); - z-index: 1; + z-index: calc(var(--fhc-offcanvas-zindex) + 1) !important; } #nav-search.me-3 { margin: 0 !important; @@ -413,10 +415,18 @@ html { color: var(--fhc-link) !important; } +#nav-main { + z-index: var(--fhc-offcanvas-zindex); +} + #nav-main-sticky { max-height: calc(100vh - var(--fhc-cis-header-height)); } +#nav-user-menu { + z-index: calc(var(--fhc-offcanvas-zindex) + 1); +} + #nav-user-menu img { object-fit: cover; height: calc( 3 * var(--fhc-cis-header-py)); diff --git a/public/css/components/dashboard.css b/public/css/components/dashboard.css index 88d136b55..eb9f0a4b1 100644 --- a/public/css/components/dashboard.css +++ b/public/css/components/dashboard.css @@ -2,7 +2,7 @@ @import './dashboard/news.css'; @import './dashboard/LvPlan.css'; -:root{ +:root { --fhc-dashboard-danger: var(--fhc-danger, #842029); --fhc-dashboard-grid-size: 4; --fhc-dashboard-link: var(--fhc-link, #0a57ca); @@ -17,22 +17,16 @@ --fhc-dashboard-section-info-color-hover: var(--fhc-primary-highlight, #005585); } -@media(max-width: 577px) { - :root { - --fhc-dashboard-grid-size: 1; - } -} - -.core-dashboard a{ +.core-dashboard a { color: var(--fhc-dashboard-link); } -@media (max-width: 576px){ +@media (max-width: 576px) { .widget-icon { max-height: 250px; object-fit: cover; } - .widget-icon-container{ + .widget-icon-container { max-width: 250px; margin-left: auto; margin-right: auto; @@ -46,27 +40,36 @@ background-repeat: no-repeat; background-position: center; background-size: cover; - cursor:pointer; + cursor: pointer; } -.dashboard-section > .newGridRow{ - position:absolute; - width:20px; - height:20px; - padding:0; - bottom:0; - left:50%; - transform:translate(-50%, 50%); +.dashboard-section.edit-active { + /** + * replaces margin for extra row + * 10% equals 0.1 of 100% + * 1rem equals the padding of pb-3 that is overwritten here + */ + padding-bottom: calc(10% / var(--fhc-dashboard-grid-size) + 1rem) !important; +} + +.dashboard-section > .newGridRow { + position: absolute; + width: 20px; + height: 20px; + padding: 0; + bottom: 0; + left: 50%; + transform: translate(-50%, 50%); background-color: var(--fhc-dashboard-gridrow-background); } .newGridRow:hover { - color:white; + color: white; background-color: var(--fhc-dashboard-gridrow-background-highlight); } .empty-tile-hover:hover { - background-image: url('data:image/svg+xml;utf8,'); + background-image: url('data:image/svg+xml;utf8,'); } .alert-danger .form-check-input:checked { @@ -74,16 +77,6 @@ background-color: var(--fhc-dashboard-danger); } -:root { - --fhc-dashboard-grid-size: 4; -} - -@media(max-width: 1400px) { - :root { - --fhc-dashboard-grid-size: 4; - } -} - @media(max-width: 1200px) { :root { --fhc-dashboard-grid-size: 3; @@ -105,6 +98,7 @@ @media(max-width: 577px) { :root { --fhc-dashboard-grid-size: 1; + --fhc-dg-item-py: .75rem; } } @@ -132,50 +126,64 @@ cursor: move !important; } -.draggedItem { +.drop-grid-item-resize > .dashboard-item, +.drop-grid-item-move > .dashboard-item { height: 100%; width: 100%; background-color: var(--fhc-dashboard-draggeditem-background); position: relative; } -.dashboard-item-overlay{ +.drop-grid-item-resize > .dashboard-item > *, +.drop-grid-item-move > .dashboard-item > * { + display: none; +} + +.drop-grid-item-sizechanged > .dashboard-item, +.drop-grid-item-move > .dashboard-item { background-color: var(--fhc-dashboard-item-overlay-background); } -.dashboard-item-overlay::before{ - position:absolute; - content:""; - top:0.25rem; - left:0.25rem; - right:0.25rem; - bottom:0.25rem; - border:4px dashed var(--fhc-dashboard-item-overly-border-color); - opacity: 0.5; +.drop-grid-item-sizechanged > .dashboard-item::before, +.drop-grid-item-move > .dashboard-item::before { + position: absolute; + content: ""; + top: .25rem; + left: .25rem; + right: .25rem; + bottom: .25rem; + border: 4px dashed var(--fhc-dashboard-item-overly-border-color); + opacity: .5; } -#deleteBookmark i{ +.drop-grid-item-oversized > .dashboard-item { + /* Bootstrap: border-danger */ + --bs-border-opacity: 1; + border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; +} + +#deleteBookmark i { color: var(--fhc-dashboard-danger); } -.pin:hover{ +.pin:hover { cursor: pointer; } -.pin[pinned]:hover{ +.pin[pinned]:hover { color: var(--fhc-dashboard-pin-pinned-hover-color); } -.section-info{ +.section-info { color: var(--fhc-dashboard-section-info-color); - cursor:pointer; + cursor: pointer; } .section-info:hover { color: var(--fhc-dashboard-section-info-color-hover); } -.denied-dragging-animation { +.drop-grid-item-blocker [pinned='true'] { animation: wiggle 0.5s linear; color: var(--fhc-dashboard-denied-dragging-animation-color) !important; } @@ -204,13 +212,13 @@ } -.hiddenWidget{ +.hidden-widget { background: var(--fhc-disabled-background); opacity: 40%; } -.hiddenWidget .card, -.hiddenWidget .card-body, -.hiddenWidget .card-body *{ +.hidden-widget .card, +.hidden-widget .card-body, +.hidden-widget .card-body * { background: inherit !important; } diff --git a/public/js/api/factory/cis/dashboard.js b/public/js/api/factory/cis/dashboard.js index cb1ec16bb..694340b97 100644 --- a/public/js/api/factory/cis/dashboard.js +++ b/public/js/api/factory/cis/dashboard.js @@ -19,7 +19,7 @@ export default { getViewData() { return { method: 'get', - url: '/api/frontend/v1/Cis4FhcApi/getViewData' + url: '/api/frontend/v1/Cis4FhcApi/dashboardViewData' }; } }; \ No newline at end of file diff --git a/public/js/api/factory/cis/studium.js b/public/js/api/factory/cis/studium.js new file mode 100644 index 000000000..eed78a9ba --- /dev/null +++ b/public/js/api/factory/cis/studium.js @@ -0,0 +1,26 @@ +/** + * 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 { + getAllStudienSemester(studiensemester, studiengang, semester, studienplan) { + return { + method: 'get', + url: 'api/frontend/v1/Studium/getStudienAllSemester/', + params: {studiensemester, studiengang, semester, studienplan} + }; + }, +} \ No newline at end of file diff --git a/public/js/api/factory/lvPlan.js b/public/js/api/factory/lvPlan.js index 0b179c9ba..ac369c0e8 100644 --- a/public/js/api/factory/lvPlan.js +++ b/public/js/api/factory/lvPlan.js @@ -16,6 +16,12 @@ */ export default { + getMyLvPlanViewData() { + return { + method: 'get', + url: `/api/frontend/v1/LvPlan/myLvPlanViewData`, + }; + }, getRoomInfo(ort_kurzbz, start_date, end_date) { return { method: 'post', @@ -30,11 +36,11 @@ export default { params: { start_date, end_date, lv_id } }; }, - eventsPersonal(start_date, end_date) { + eventsPersonal(start_date, end_date, uid = null) { return { method: 'post', url: '/api/frontend/v1/lvPlan/eventsPersonal', - params: { start_date, end_date } + params: { start_date, end_date, uid } }; }, eventsLv(lv_id, start_date, end_date) { @@ -57,11 +63,11 @@ export default { params: { start_date, end_date } }; }, - getLvPlanReservierungen(start_date, end_date) { + getLvPlanReservierungen(start_date, end_date, uid = null) { return { method: 'post', url: '/api/frontend/v1/LvPlan/getReservierungen', - params: { start_date, end_date } + params: { start_date, end_date, uid } }; }, getLehreinheitStudiensemester(lehreinheit_id) { @@ -92,5 +98,42 @@ export default { method: 'get', url: '/api/frontend/v1/LvPlan/getLv/' + lehrveranstaltung_id }; - } + }, + eventsStgOrg(start_date, end_date, stg_kz, sem, verband, gruppe) { + return { + method: 'post', + url: '/api/frontend/v1/lvPlan/eventsStgOrg', + params: { start_date, end_date, stg_kz, sem, verband, gruppe } + }; + }, + getStudiengaenge(){ + return { + method: 'get', + url: '/api/frontend/v1/lvPlan/getStudiengaenge' + } + }, + getLehrverband(stg_kz, sem){ + return { + method: 'get', + url: `/api/frontend/v1/lvPlan/getLehrverband/${stg_kz}/${sem}` + } + }, + getGruppe(stg_kz, sem, verband){ + return { + method: 'get', + url: `/api/frontend/v1/lvPlan/getLehrverband/${stg_kz}/${sem}/${verband}` + } + }, + checkPermissionOtherLvPlan(){ + return { + method: 'get', + url: '/api/frontend/v1/lvPlan/permissionOtherLvPlan', + } + }, + getCompactibleEventTypes(){ + return { + method: 'get', + url: '/api/frontend/v1/lvPlan/compactibleEventTypes', + } + }, }; \ No newline at end of file diff --git a/public/js/api/factory/otherLvPlan.js b/public/js/api/factory/otherLvPlan.js new file mode 100644 index 000000000..21a115496 --- /dev/null +++ b/public/js/api/factory/otherLvPlan.js @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2025 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 { + getOtherLvPlanViewData(uid) { + return { + method: 'get', + url: `/api/frontend/v1/OtherLvPlan/otherLvPlanViewData/${uid}`, + }; + }, +}; \ No newline at end of file diff --git a/public/js/api/factory/profil.js b/public/js/api/factory/profil.js index 1d884c714..6959b4345 100644 --- a/public/js/api/factory/profil.js +++ b/public/js/api/factory/profil.js @@ -17,7 +17,7 @@ export default { - profilViewData(uid) { + getProfilViewData(uid = null) { let url = "/api/frontend/v1/Profil/profilViewData"; if(uid){ url += `/${uid}`; diff --git a/public/js/api/factory/stgOrgLvPlan.js b/public/js/api/factory/stgOrgLvPlan.js new file mode 100644 index 000000000..bac753f6b --- /dev/null +++ b/public/js/api/factory/stgOrgLvPlan.js @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2025 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 { + getStgOrgLvPlanViewData(uid) { + return { + method: 'get', + url: `/api/frontend/v1/StgOrgLvPlan/stgOrgLvPlanViewData`, + }; + }, +}; \ No newline at end of file diff --git a/public/js/api/factory/widget/bookmark.js b/public/js/api/factory/widget/bookmark.js index 9768e25ac..2035a9f8f 100644 --- a/public/js/api/factory/widget/bookmark.js +++ b/public/js/api/factory/widget/bookmark.js @@ -17,6 +17,7 @@ export default { getBookmarks() { + return { method: 'get', url: '/api/frontend/v1/Bookmark/getBookmarks' @@ -28,18 +29,24 @@ export default { url: `/api/frontend/v1/Bookmark/delete/${bookmark_id}` }; }, - update({ bookmark_id, url, title, tag=null }) { + update({ bookmark_id, url, title, tag }) { return { method: 'post', url: `/api/frontend/v1/Bookmark/update/${bookmark_id}`, - params: { url, title } + params: { url, title, tag } }; }, - insert({ url, title, tag }) { + insert({ url, title, tag, sort }) { return { method: 'post', url: `/api/frontend/v1/Bookmark/insert`, - params: { url, title, tag } + params: { url, title, tag, sort } }; + }, + changeOrder(bookmark_id1, bookmark_id2) { + return { + method: 'post', + url: `/api/frontend/v1/Bookmark/changeOrder/${bookmark_id1}/${bookmark_id2}`, + }; } }; \ No newline at end of file diff --git a/public/js/apps/Dashboard/Fhc.js b/public/js/apps/Cis/Cis.js similarity index 82% rename from public/js/apps/Dashboard/Fhc.js rename to public/js/apps/Cis/Cis.js index 140c76402..77c34a88b 100644 --- a/public/js/apps/Dashboard/Fhc.js +++ b/public/js/apps/Cis/Cis.js @@ -4,25 +4,27 @@ import Theme from '../../plugins/Theme.js'; import contrast from '../../directives/contrast.js'; import {setScrollbarWidth} from "../../helpers/CssVarCalcHelpers.js"; import LvPlan from "../../components/Cis/LvPlan/Lehrveranstaltung.js"; -import MyLvPlan from "../../components/Cis/LvPlan/Personal.js"; +import MyLvPlan from "../../components/Cis/LvPlan/MyLvPlan.js"; import MylvStudent from "../../components/Cis/Mylv/Student.js"; import Profil from "../../components/Cis/Profil/Profil.js"; import Raumsuche from "../../components/Cis/Raumsuche/Raumsuche.js"; import CmsNews from "../../components/Cis/Cms/News.js"; import CmsContent from "../../components/Cis/Cms/Content.js"; import Info from "../../components/Cis/Mylv/Semester/Studiengang/Lv/Info.js"; -import RoomInformation, {DEFAULT_MODE_RAUMINFO} from "../../components/Cis/Mylv/RoomInformation.js"; +import RoomInformation, {DEFAULT_MODE_RAUMINFO_DESKTOP, DEFAULT_MODE_RAUMINFO_MOBILE} from "../../components/Cis/Mylv/RoomInformation.js"; import AbgabetoolStudent from "../../components/Cis/Abgabetool/AbgabetoolStudent.js"; import AbgabetoolMitarbeiter from "../../components/Cis/Abgabetool/AbgabetoolMitarbeiter.js"; import AbgabetoolAssistenz from "../../components/Cis/Abgabetool/AbgabetoolAssistenz.js"; import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.js"; import Studium from "../../components/Cis/Studium/Studium.js"; +import StgOrgLvPlan from "../../components/Cis/LvPlan/StgOrg.js"; +import OtherLvPlan from "../../components/Cis/LvPlan/OtherLvPlan.js"; -import ApiRenderers from '../../api/factory/renderers.js'; import ApiRouteInfo from '../../api/factory/routeinfo.js'; import {capitalize} from "../../helpers/StringHelpers.js"; const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router; +const isMobile = window.matchMedia("(max-width: 767px)").matches; const router = VueRouter.createRouter({ history: VueRouter.createWebHistory(`/${ciPath}`), @@ -85,7 +87,7 @@ const router = VueRouter.createRouter({ name: "RoomInformation", params: { // in this case always populate other params since they are not optional ort_kurzbz: to.params.ort_kurzbz, - mode: DEFAULT_MODE_RAUMINFO, + mode: isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP, focus_date: new Date().toISOString().split("T")[0] }, }; @@ -102,7 +104,7 @@ const router = VueRouter.createRouter({ const mode = route.params.mode && validModes.includes(route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase()) ? route.params.mode.charAt(0).toUpperCase() + route.params.mode.slice(1).toLowerCase() - : DEFAULT_MODE_RAUMINFO; + : (isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP); // default to today date if not provided const d = new Date(route.params.focus_date) @@ -197,6 +199,26 @@ const router = VueRouter.createRouter({ }; } }, + { + path: `/Cis/StgOrgLvPlan/:mode?/:focus_date?/:stgkz?/:sem?/:verband?/:gruppe?`, + name: 'StgOrgLvPlan', + component: StgOrgLvPlan, + props(route) { + return { + propsViewData: route.params + }; + } + }, + { + path: `/Cis/OtherLvPlan/:otherUid/:mode?/:focus_date?`, + name: "OtherLvPlan", + component: OtherLvPlan, + props(route) { + return { + propsViewData: route.params + }; + } + }, { path: `/Cis4`, name: 'Cis4', @@ -227,7 +249,7 @@ const router = VueRouter.createRouter({ }) const app = Vue.createApp({ - name: 'FhcApp', + name: 'CisApp', data: () => ({ appSideMenuEntries: {}, renderers: null, @@ -238,13 +260,12 @@ const app = Vue.createApp({ const smallScreen = window.matchMedia("(max-width: 767px)").matches; const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0; return smallScreen;// && touchCapable; - } + }, }, provide() { return { // provide injectable & watchable language property language: Vue.computed(() => this.$p.user_language), - renderers: Vue.computed(() => this.renderers), - isMobile: this.isMobile + isMobile: this.isMobile, } }, methods: { @@ -282,46 +303,6 @@ const app = Vue.createApp({ } } }, - async created(){ - - await this.$api - .call(ApiRenderers.loadRenderers()) - .then(res => res.data) - .then(data => { - for (let rendertype of Object.keys(data)) { - let modalTitle = null; - let modalContent = null; - let calendarEvent = null; - if (data[rendertype].modalTitle) - modalTitle = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].modalTitle))); - if (data[rendertype].modalContent) - modalContent = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].modalContent))); - if (data[rendertype].calendarEvent) - calendarEvent = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].calendarEvent))); - - if (data[rendertype].calendarEventStyles){ - var head = document.head; - if(!head.querySelector(`link[href="${data[rendertype].calendarEventStyles}"]`)){ - var link = document.createElement("link"); - link.type = "text/css"; - link.rel = "stylesheet"; - link.href = 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; - } - }); - }, mounted() { document.addEventListener('click', this.handleClick); @@ -353,4 +334,4 @@ app.mount('#fhccontent'); router.afterEach((to, from, failure) => { app.config.globalProperties.$api.call(ApiRouteInfo.info('cis4', to.fullPath)); -}); \ No newline at end of file +}); diff --git a/public/js/apps/Cis.js b/public/js/apps/Cis/Menu.js similarity index 75% rename from public/js/apps/Cis.js rename to public/js/apps/Cis/Menu.js index c88a47a35..b05e68ee5 100644 --- a/public/js/apps/Cis.js +++ b/public/js/apps/Cis/Menu.js @@ -1,11 +1,12 @@ -import FhcSearchbar from "../components/searchbar/searchbar.js"; -import CisMenu from "../components/Cis/Menu.js"; -import PluginsPhrasen from '../plugins/Phrasen.js'; -import ApiSearchbar from '../api/factory/searchbar.js'; -import Theme from "../plugins/Theme.js"; +import FhcSearchbar from "../../components/searchbar/searchbar.js"; +import CisMenu from "../../components/Cis/Menu.js"; +import PluginsPhrasen from '../../plugins/Phrasen.js'; +import Theme from "../../plugins/Theme.js"; +import ApiSearchbar from '../../api/factory/searchbar.js'; +import ApiLvPlan from "../../api/factory/lvPlan.js"; const app = Vue.createApp({ - name: 'CisApp', + name: 'CisMenuApp', components: { FhcSearchbar, CisMenu @@ -136,11 +137,53 @@ const app = Vue.createApp({ } }; }, + computed: { + isMobile() { + const smallScreen = window.matchMedia("(max-width: 767px)").matches; + const touchCapable = ("ontouchstart" in window) || navigator.maxTouchPoints > 0; + return smallScreen;// && touchCapable; + }, + }, + provide() { + return { + isMobile: this.isMobile + } + }, methods: { searchfunction: function(searchsettings) { return this.$api.call(ApiSearchbar.searchCis(searchsettings)); } - } + }, + async mounted() { + const openOtherLvPlanAction = { + label: Vue.computed(() => this.$p.t("lehre/stundenplan")), + icon: "fas fa-calendar-days", + type: "link", + action: function (data) { + const uid = JSON.parse(data.data).uid; + const link = + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + "/Cis/OtherLvPlan/" + + uid; + return link; + }, + }; + let checkPermissionOtherLvPlanResult = await this.$api.call( + ApiLvPlan.checkPermissionOtherLvPlan(), + ); + if ( + checkPermissionOtherLvPlanResult.meta.status === "success" && + checkPermissionOtherLvPlanResult.data + ) { + this.searchbaroptions.actions.employee.childactions.push( + openOtherLvPlanAction, + ); + this.searchbaroptions.actions.student.childactions.push( + openOtherLvPlanAction, + ); + } + }, }); FhcApps.makeExtendable(app); diff --git a/public/js/apps/Dashboard/Admin.js b/public/js/apps/Dashboard/Admin.js index 32909a50a..c361c2fb4 100644 --- a/public/js/apps/Dashboard/Admin.js +++ b/public/js/apps/Dashboard/Admin.js @@ -3,13 +3,10 @@ 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: 'DashboardAdminApp', data: () => ({ - appSideMenuEntries: {}, - renderers: null + appSideMenuEntries: {} }), components: { CoreNavigationCmpt, @@ -17,51 +14,16 @@ const app = Vue.createApp({ }, 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(primevue.config.default, { + zIndex: { + overlay: 9000, + tooltip: 8000 + } +}) app.use(PluginsPhrasen); app.directive('tooltip', primevue.tooltip); app.mount('#main'); \ No newline at end of file diff --git a/public/js/apps/DashboardAdmin_DEPR.js b/public/js/apps/DashboardAdmin_DEPR.js deleted file mode 100644 index 05b438798..000000000 --- a/public/js/apps/DashboardAdmin_DEPR.js +++ /dev/null @@ -1,16 +0,0 @@ -import {CoreNavigationCmpt} from '../components/navigation/Navigation.js'; -import DashboardAdmin from '../components/Dashboard/Admin.js'; -import Phrases from "../plugin/Phrasen.js" - -Vue.createApp({ - name: 'DashboardAdminApp', - data: () => ({ - appSideMenuEntries: {} - }), - components: { - CoreNavigationCmpt, - DashboardAdmin - }, - mounted() { - } -}).use(Phrases).mount('#main'); \ No newline at end of file diff --git a/public/js/components/Calendar/Base/DragAndDrop.js b/public/js/components/Calendar/Base/DragAndDrop.js index 631a792a8..fb6cb1848 100644 --- a/public/js/components/Calendar/Base/DragAndDrop.js +++ b/public/js/components/Calendar/Base/DragAndDrop.js @@ -20,7 +20,8 @@ export default { }, inject: { mode: "mode", - dropableEvents: "dropableEvents" + dropableEvents: "dropableEvents", + timezone: "timezone" }, props: { events: Array, diff --git a/public/js/components/Calendar/Base/Grid/Line.js b/public/js/components/Calendar/Base/Grid/Line.js index 9a6c2f579..64a71c5f0 100644 --- a/public/js/components/Calendar/Base/Grid/Line.js +++ b/public/js/components/Calendar/Base/Grid/Line.js @@ -1,5 +1,5 @@ -import LineEvent from './Line/Event.js'; -import LineBackground from './Line/Background.js'; +import LineEvent from "./Line/Event.js"; +import LineBackground from "./Line/Background.js"; /** * TODO(chris): @@ -10,54 +10,117 @@ export default { name: "GridLine", components: { LineEvent, - LineBackground - }, - inject: { - axisRow: "axisRow" + LineBackground, }, + inject: ["axisRow", "shouldCompactEvents", "compactibleEventTypes"], props: { date: { type: luxon.DateTime, - required: true + required: true, }, start: { type: luxon.DateTime, - required: true + required: true, }, end: { type: luxon.DateTime, - required: true + required: true, }, events: { type: Array, - default: [] + default: [], }, backgrounds: { type: Array, - default: [] - } + default: [], + }, }, computed: { - eventsWithRowInfo() { - const events = []; - this.events.forEach(event => { - const rows = [1, -1]; + formattedEvents() { + let formattedEvents = this.events.map((event) => { + event.rows = [1, -1]; if (event.startsHere) { - rows[0] = 't_' + event.start.diff(this.date).toMillis(); + event.rows[0] = + "t_" + event.start.diff(this.date).toMillis(); } if (event.endsHere) { - rows[1] = 't_' + event.end.diff(this.date).toMillis(); + event.rows[1] = "t_" + event.end.diff(this.date).toMillis(); } - events.push({ - ...event, - rows - }); + return event; }); - return events; - } + + if (this.shouldCompactEvents && this.compactibleEventTypes?.length) { + formattedEvents = + this.compactEvents(formattedEvents, this.compactibleEventTypes); + } + + return formattedEvents; + }, }, - template: /* html */` + methods: { + compactEvents(events, compactibleEventTypes) { + let formattedEvents = events + .filter( + (event) => + !compactibleEventTypes.includes(event.type), + ) + .map((event) => { + event.display = "default"; + return event; + }); + let eventsToBeCompacted = events.filter((event) => + compactibleEventTypes.includes(event.type), + ); + let compactedEvents = []; + + eventsToBeCompacted.forEach((event) => { + let existingCompactedEvent = compactedEvents.find( + (compactedEvent) => + event.rows[0] === compactedEvent.rows[0] && + event.rows[1] === compactedEvent.rows[1], + ); + + if (!existingCompactedEvent) { + compactedEvents.push({ + events: [ + { + farbe: event.orig.farbe, + }, + ], + rows: event.rows, + }); + } else { + existingCompactedEvent.events.push({ + farbe: event.orig.farbe, + }); + } + }); + + compactedEvents.forEach((compactedEvent) => { + if (compactedEvent.events.length < 4) { + formattedEvents.push({ + display: "compacted", + ...compactedEvent, + }); + } else { + formattedEvents.push({ + display: "compacted", + events: compactedEvent.events.slice(0, 3), + rows: compactedEvent.rows, + }); + formattedEvents.push({ + display: "compactedExtra", + events: compactedEvent.events.slice(3), + rows: compactedEvent.rows, + }); + } + }); + + return formattedEvents; + }, + }, + template: /* html */ `
- - - +
- ` -} + `, +}; diff --git a/public/js/components/Calendar/LvPlan.js b/public/js/components/Calendar/LvPlan.js index e0e918f01..99c7ba8ef 100644 --- a/public/js/components/Calendar/LvPlan.js +++ b/public/js/components/Calendar/LvPlan.js @@ -3,24 +3,20 @@ import FhcCalendar from "./Base.js"; import ApiLvPlan from '../../api/factory/lvPlan.js'; import { useEventLoader } from '../../composables/EventLoader.js'; +import { useRenderers } from '../../composables/Renderers.js'; import ModeDay from './Mode/Day.js'; import ModeWeek from './Mode/Week.js'; import ModeMonth from './Mode/Month.js'; +import ModeList from './Mode/List.js'; export default { name: "CalendarLvPlan", components: { FhcCalendar }, - inject: [ - "renderers" - ], + inject: ["isMobile"], props: { - timezone: { - type: String, - required: true - }, date: { type: [Date, String, Number, luxon.DateTime], default: luxon.DateTime.local() @@ -34,6 +30,16 @@ export default { required: true } }, + provide() { + return { + shouldCompactEvents: Vue.computed( + () => this.$props.mode === "Month" && this.isMobile, + ), + compactibleEventTypes: Vue.computed( + () => this.compactibleEventTypes, + ), + }; + }, emits: [ "update:date", "update:mode", @@ -41,11 +47,7 @@ export default { ], data() { return { - modes: { - day: Vue.markRaw(ModeDay), - week: Vue.markRaw(ModeWeek), - month: Vue.markRaw(ModeMonth) - }, + timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone, modeOptions: { day: { emptyMessage: Vue.computed(() => this.$p.t('lehre/noLvFound')), @@ -53,9 +55,13 @@ export default { }, week: { collapseEmptyDays: false - } + }, + list: { + length: 7, + }, }, - teachingunits: null + teachingunits: null, + compactibleEventTypes: [], }; }, computed: { @@ -77,7 +83,20 @@ export default { label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false }) } ]; - } + }, + modes() { + let modes = { + day: Vue.markRaw(ModeDay), + month: Vue.markRaw(ModeMonth), + }; + if (this.isMobile) { + modes.list = Vue.markRaw(ModeList); + } else { + modes.week = Vue.markRaw(ModeWeek); + } + + return modes; + }, }, methods: { eventStyle(event) { @@ -88,33 +107,47 @@ export default { updateRange(rangeInterval) { this.rangeInterval = rangeInterval; this.$emit('update:range', rangeInterval); - } + }, + resetEventLoader() { + this.reset(); + }, + async getStunden() { + let stundenResponse = await this.$api.call(ApiLvPlan.getStunden()); + this.teachingunits = stundenResponse.data.map((el) => ({ + id: el.stunde, + start: el.beginn, + end: el.ende, + })); + }, + async getCompactibleEventTypes() { + let compactibleEventTypesResponse = await this.$api.call( + ApiLvPlan.getCompactibleEventTypes(), + ); + this.compactibleEventTypes = compactibleEventTypesResponse.data; + }, }, setup(props, context) { const rangeInterval = Vue.ref(null); - const { events, lv } = useEventLoader(rangeInterval, props.getPromiseFunc); + const { events, lv, reset } = useEventLoader(rangeInterval, props.getPromiseFunc); Vue.watch(lv, newValue => { context.emit('update:lv', newValue); }); + const { renderers } = useRenderers(); + return { rangeInterval, events, - lv + lv, + reset, + renderers }; }, - created() { - this.$api - .call(ApiLvPlan.getStunden()) - .then(res => { - return this.teachingunits = res.data.map(el => ({ - id: el.stunde, - start: el.beginn, - end: el.ende - })); - }); + async created() { + await this.getStunden(); + await this.getCompactibleEventTypes(); }, template: /* html */` diff --git a/public/js/components/Cis/Mylv/RoomInformation.js b/public/js/components/Cis/Mylv/RoomInformation.js index 2dd3d518d..3a07e0a76 100644 --- a/public/js/components/Cis/Mylv/RoomInformation.js +++ b/public/js/components/Cis/Mylv/RoomInformation.js @@ -2,7 +2,8 @@ import FhcCalendar from "../../Calendar/LvPlan.js"; import ApiLvPlan from '../../../api/factory/lvPlan.js'; -export const DEFAULT_MODE_RAUMINFO = 'Week' +export const DEFAULT_MODE_RAUMINFO_MOBILE = 'List'; +export const DEFAULT_MODE_RAUMINFO_DESKTOP = 'Week'; export default { name: "RoomInformation", @@ -13,12 +14,14 @@ export default { viewData: Object, // NOTE(chris): this is inherited from router-view propsViewData: Object }, + inject: ["isMobile"], computed: { currentDay() { - return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(this.viewData.timezone).toISODate(); + return this.propsViewData?.focus_date || luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate(); }, currentMode() { - return this.propsViewData?.mode || DEFAULT_MODE_RAUMINFO; + const defaultMode = this.isMobile ? DEFAULT_MODE_RAUMINFO_MOBILE : DEFAULT_MODE_RAUMINFO_DESKTOP; + return this.propsViewData?.mode || defaultMode; } }, methods:{ @@ -51,7 +54,6 @@ export default {
+

{{$p.user_language.value === 'English' ? sg_bezeichnung_eng : bezeichnung}} - {{kuerzel}} {{semester}}.{{$p.t('lehre/semester')}} @@ -41,6 +44,7 @@ export default {

+ ` }; \ No newline at end of file diff --git a/public/js/components/Cis/Mylv/Semester/Studiengang/AverageGrade.js b/public/js/components/Cis/Mylv/Semester/Studiengang/AverageGrade.js new file mode 100644 index 000000000..5fcc6262a --- /dev/null +++ b/public/js/components/Cis/Mylv/Semester/Studiengang/AverageGrade.js @@ -0,0 +1,94 @@ +import Phrasen from "../../../../../mixins/Phrasen.js"; + +export default { + mixins: [ + Phrasen + ], + props: { + lvs: Array, + }, + data: ( ) =>{ + return { + gradeAverage: null, + gradeWeightedAverage: null, + existingGrades: false + } + }, + methods: { + calculateAverages(){ + let sum = 0; + let count = 0; + let sumWeighted = 0; + let sumEcts = 0; + + this.lvs.forEach((lv) => { + if ((lv.znote >= 1 && lv.znote <= 5) && lv.znote!= null) { + this.existingGrades = true; + sum+= lv.znote; + count++; + sumWeighted += lv.znote * Number(lv.ects); + sumEcts += Number(lv.ects); + } + }); + this.gradeAverage = (sum/count).toFixed(2); + this.gradeWeightedAverage = (sumWeighted/sumEcts).toFixed(2); + } + }, + watch: { + lvs: { + handler() { + this.calculateAverages(); + }, + deep: true, + immediate: true + } + }, + mounted(){ + this.calculateAverages(); + }, + template: /*html*/` +
+ +
+
{{$p.t('lehre/notenstatistik')}}
+
+ +
+ + + + + + + + + + + +
+ {{$p.t('lehre/headerAverage')}} + + {{ gradeAverage }} +
+ {{$p.t('lehre/headerWeightedAverage')}} + + {{ gradeWeightedAverage }} +
+
+
+

{{$p.t('lehre/info_noGradesYet')}}

+
+ + + +
+ ` +} diff --git a/public/js/components/Cis/Mylv/Semester/Studiengang/Lv.js b/public/js/components/Cis/Mylv/Semester/Studiengang/Lv.js index 3fd5e6900..a8cb2bc97 100644 --- a/public/js/components/Cis/Mylv/Semester/Studiengang/Lv.js +++ b/public/js/components/Cis/Mylv/Semester/Studiengang/Lv.js @@ -73,11 +73,11 @@ export default { }, grade() { const languageIndex = this.$p.user_language.value === 'English' ? 1 : 0 + // no more showing of grade LV, if grade Zeugnis is not existing yet if(this.benotung && this.znotebez?.length) { return this.znotebez[languageIndex] - } else if(this.benotung && this.lvnotebez?.length) { - return this.lvnotebez[languageIndex] - } else return null + } + else return null }, LvHasPruefungenInformation(){ return this.pruefungenData && this.pruefungenData.length > 0; diff --git a/public/js/components/Cis/Profil/MitarbeiterProfil.js b/public/js/components/Cis/Profil/MitarbeiterProfil.js index 559006a1b..fd3c1b072 100644 --- a/public/js/components/Cis/Profil/MitarbeiterProfil.js +++ b/public/js/components/Cis/Profil/MitarbeiterProfil.js @@ -9,6 +9,7 @@ import QuickLinks from "./ProfilComponents/QuickLinks.js"; import ProfilEmails from "./ProfilComponents/ProfilEmails.js"; import RoleInformation from "./ProfilComponents/RoleInformation.js"; import ProfilInformation from "./ProfilComponents/ProfilInformation.js"; +import CalendarSync from "./ProfilComponents/CalendarSync.js"; import ApiProfilUpdate from '../../../api/factory/profilUpdate.js'; import { dateFilter } from '../../../tabulator/filters/Dates.js'; @@ -26,6 +27,7 @@ export default { ProfilEmails, RoleInformation, ProfilInformation, + CalendarSync, }, inject: ["sortProfilUpdates", "collapseFunction", "language","isEditable"], @@ -103,7 +105,6 @@ export default { }, ], }, - betriebsmittel_table_options: { persistenceID: "filterTableMaProfilBetriebsmittel", persistence: { @@ -160,6 +161,7 @@ export default { props: { data: Object, editData: Object, + calendarSyncUrls: Array, }, methods: { @@ -296,6 +298,11 @@ export default { } }; }, + quickLinks() { + let quickLinks = []; + // + return quickLinks; + }, }, created() { @@ -313,7 +320,6 @@ export default { }); //? sorts the profil Updates: pending -> accepted -> rejected this.data.profilUpdates?.sort(this.sortProfilUpdates); - }, watch: { 'data.funktionen'(newVal) { @@ -331,12 +337,6 @@ export default {
-
@@ -465,17 +465,11 @@ export default {
- +
+
+ +
+
@@ -501,12 +495,17 @@ export default {
-
+
+
+
+ +
+
diff --git a/public/js/components/Cis/Profil/MitarbeiterViewProfil.js b/public/js/components/Cis/Profil/MitarbeiterViewProfil.js index 481714590..459ad6d29 100644 --- a/public/js/components/Cis/Profil/MitarbeiterViewProfil.js +++ b/public/js/components/Cis/Profil/MitarbeiterViewProfil.js @@ -1,30 +1,30 @@ -import {CoreFilterCmpt} from "../../../components/filter/Filter.js"; +import { CoreFilterCmpt } from "../../../components/filter/Filter.js"; import Mailverteiler from "./ProfilComponents/Mailverteiler.js"; -import QuickLinks from "./ProfilComponents/QuickLinks.js"; import RoleInformation from "./ProfilComponents/RoleInformation.js"; import ProfilEmails from "./ProfilComponents/ProfilEmails.js"; import ProfilInformation from "./ProfilComponents/ProfilInformation.js"; +import QuickLinks from "./ProfilComponents/QuickLinks.js"; -import { dateFilter } from '../../../tabulator/filters/Dates.js'; +import { dateFilter } from "../../../tabulator/filters/Dates.js"; export default { components: { CoreFilterCmpt, Mailverteiler, - QuickLinks, RoleInformation, ProfilEmails, ProfilInformation, + QuickLinks, }, inject: ["collapseFunction", "language"], data() { return { collapseIconFunktionen: true, - preloadedPhrasen:{}, + preloadedPhrasen: {}, funktionen_table_options: { persistenceID: "filterTableMaViewProfilFunktionen", persistence: { - columns: false + columns: false, }, minHeight: 300, layout: "fitColumns", @@ -35,58 +35,65 @@ export default { //? option when wanting to hide the collapsed list { - title: - "", + title: "", field: "collapse", headerSort: false, headerFilter: false, formatter: "responsiveCollapse", maxWidth: 40, headerClick: this.collapseFunction, - visible: true + visible: true, }, { - title: Vue.computed(() => this.$p.t('ui/bezeichnung')), + title: Vue.computed(() => this.$p.t("ui/bezeichnung")), field: "Bezeichnung", headerFilter: true, minWidth: 200, - visible: true + visible: true, }, { - title: Vue.computed(() => this.$p.t('lehre/organisationseinheit')), + title: Vue.computed(() => + this.$p.t("lehre/organisationseinheit"), + ), field: "Organisationseinheit", headerFilter: true, minWidth: 200, - visible: true + visible: true, }, { - title: Vue.computed(() => this.$p.t('global/gueltigVon')), + title: Vue.computed(() => + this.$p.t("global/gueltigVon"), + ), field: "Gültig_von", - headerFilterFunc: 'dates', + headerFilterFunc: "dates", headerFilter: dateFilter, resizable: true, minWidth: 200, visible: true, - formatter:"datetime", - formatterParams: this.datetimeFormatterParams() + formatter: "datetime", + formatterParams: this.datetimeFormatterParams(), }, { - title: Vue.computed(() => this.$p.t('global/gueltigBis')), + title: Vue.computed(() => + this.$p.t("global/gueltigBis"), + ), field: "Gültig_bis", - headerFilterFunc: 'dates', + headerFilterFunc: "dates", headerFilter: dateFilter, resizable: true, minWidth: 200, visible: true, - formatter:"datetime", - formatterParams: this.datetimeFormatterParams() + formatter: "datetime", + formatterParams: this.datetimeFormatterParams(), }, { - title: Vue.computed(() => this.$p.t('profil/wochenstunden')), + title: Vue.computed(() => + this.$p.t("profil/wochenstunden"), + ), field: "Wochenstunden", headerFilter: true, minWidth: 200, - visible: true + visible: true, }, ], }, @@ -94,47 +101,56 @@ export default { }, //? this is the prop passed to the dynamic component with the custom data of the view - props: ["data"], + props: ["data", "permissions"], methods: { funktionenTableBuilt: function () { this.$refs.funktionenTable.tabulator.setData(this.data.funktionen); }, - datetimeFormatterParams: function() { + datetimeFormatterParams: function () { const params = { - inputFormat:"yyyy-MM-dd", - outputFormat:"dd.MM.yyyy", - invalidPlaceholder:"(invalid date)", - timezone:FHC_JS_DATA_STORAGE_OBJECT.timezone + inputFormat: "yyyy-MM-dd", + outputFormat: "dd.MM.yyyy", + invalidPlaceholder: "(invalid date)", + timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone, }; return params; - } + }, }, watch: { - 'data.funktionen'(newVal) { - if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setData(newVal); + "data.funktionen"(newVal) { + if (this.$refs.funktionenTable) + this.$refs.funktionenTable.tabulator.setData(newVal); + }, + "language.value"(newVal) { + // reevaluates computed phrasen + if (this.$refs.funktionenTable) + this.$refs.funktionenTable.tabulator.setColumns( + this.funktionen_table_options.columns, + ); }, - 'language.value'(newVal) { // reevaluates computed phrasen - if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setColumns(this.funktionen_table_options.columns) - } }, computed: { getTelefonValue() { - if(this.data.standort_telefon?.kontakt) { - return this.data.standort_telefon.kontakt + " " + this.data.telefonklappe - } else if(this.data.standort_telefon) { - return this.data.standort_telefon + " " + this.data.telefonklappe + if (this.data.standort_telefon?.kontakt) { + return ( + this.data.standort_telefon.kontakt + + " " + + this.data.telefonklappe + ); + } else if (this.data.standort_telefon) { + return ( + this.data.standort_telefon + " " + this.data.telefonklappe + ); } else { - return this.data.telefonklappe + return this.data.telefonklappe; } }, fotoStatus() { return this.data?.fotoStatus ?? null; }, - personEmails() { return this.data?.emails ? this.data.emails : []; }, - profilInformation() { if (!this.data) { return {}; @@ -151,42 +167,67 @@ export default { foto: this.data.foto, }; }, - roleInformation() { if (!this.data) { return {}; } return { geburtsdatum: { - label: `${this.$p.t('profil','Geburtsdatum')}`, - value: this.data.gebdatum + label: `${this.$p.t("profil", "Geburtsdatum")}`, + value: this.data.gebdatum, }, geburtsort: { - label: `${this.$p.t('profil','Geburtsort')}`, - value: this.data.gebort + label: `${this.$p.t("profil", "Geburtsort")}`, + value: this.data.gebort, }, personenkennzeichen: { - label: `${this.$p.t('profil','Kurzzeichen')}`, - value: this.data.kurzbz + label: `${this.$p.t("profil", "Kurzzeichen")}`, + value: this.data.kurzbz, }, telefon: { - label: `${this.$p.t('profil','Telefon')}`, - value: this.getTelefonValue + label: `${this.$p.t("profil", "Telefon")}`, + value: this.getTelefonValue, }, office: { - label: `${this.$p.t('profil','Büro')}`, - value: this.data.ort_kurzbz - } + label: `${this.$p.t("profil", "Büro")}`, + value: this.data.ort_kurzbz, + }, }; }, + quickLinks() { + let quickLinks = []; + if ( + this.$props.permissions && + this.$props.permissions["basis/other_lv_plan"] + ) { + quickLinks.push({ + icon: "fa-calendar-days", + phrase: "lehre/stundenplan", + action: () => { + this.$router.push({ + name: "OtherLvPlan", + params: { otherUid: this.$props.data.username }, + }); + }, + }); + } + return quickLinks; + }, }, - created(){ + created() { this.$p.loadCategory(["ui", "lehre", "global", "profil"]).then(() => { - this.preloadedPhrasen.bezeichnungPhrase = this.$p.t('ui/bezeichnung'); - this.preloadedPhrasen.organisationseinheitPhrase = this.$p.t('lehre/organisationseinheit'); - this.preloadedPhrasen.gueltigVonPhrase = this.$p.t('global/gueltigVon'); - this.preloadedPhrasen.gueltigBisPhrase = this.$p.t('global/gueltigBis'); - this.preloadedPhrasen.wochenstundenPhrase = this.$p.t('profil/wochenstunden'); + this.preloadedPhrasen.bezeichnungPhrase = + this.$p.t("ui/bezeichnung"); + this.preloadedPhrasen.organisationseinheitPhrase = this.$p.t( + "lehre/organisationseinheit", + ); + this.preloadedPhrasen.gueltigVonPhrase = + this.$p.t("global/gueltigVon"); + this.preloadedPhrasen.gueltigBisPhrase = + this.$p.t("global/gueltigBis"); + this.preloadedPhrasen.wochenstundenPhrase = this.$p.t( + "profil/wochenstunden", + ); this.preloadedPhrasen.loaded = true; }); }, @@ -242,7 +283,7 @@ export default {
-
+
@@ -256,26 +297,22 @@ export default {
- -
-
- - -
-
- -
+
+
+ +
+
+
+
+ + +
+
+ +
- - + + `, }; diff --git a/public/js/components/Cis/Profil/Profil.js b/public/js/components/Cis/Profil/Profil.js index da205816d..cba34951c 100644 --- a/public/js/components/Cis/Profil/Profil.js +++ b/public/js/components/Cis/Profil/Profil.js @@ -4,8 +4,8 @@ import ViewStudentProfil from "./StudentViewProfil.js"; import ViewMitarbeiterProfil from "./MitarbeiterViewProfil.js"; import Loading from "../../Loader.js"; -import ApiProfil from '../../../api/factory/profil.js'; -import ApiProfilUpdate from '../../../api/factory/profilUpdate.js'; +import ApiProfil from "../../../api/factory/profil.js"; +import ApiProfilUpdate from "../../../api/factory/profilUpdate.js"; Vue.$collapseFormatter = function (data) { //data - an array of objects containing the column title and value for each cell @@ -35,7 +35,7 @@ Vue.$collapseFormatter = function (data) { }; export const Profil = { - name: 'Profil', + name: "Profil", components: { StudentProfil, MitarbeiterProfil, @@ -46,11 +46,8 @@ export const Profil = { props: { uid: { type: String, - required:false, + required: false, }, - viewData: { - type: Object, - } }, data() { return { @@ -62,17 +59,19 @@ export const Profil = { data: null, // notfound is null by default, but contains an UID if no user exists with that UID notFoundUID: null, - isEditable: this.viewData.editable ?? false, + isEditable: false, + authPermissions: null, + calendarSyncUrls: [], }; }, provide() { return { - isEditable: Vue.computed(()=>this.isEditable), + isEditable: Vue.computed(() => this.isEditable), profilUpdateStates: Vue.computed(() => - this.profilUpdateStates ? this.profilUpdateStates : false + this.profilUpdateStates ? this.profilUpdateStates : false, ), profilUpdateTopic: Vue.computed(() => - this.profilUpdateTopic ? this.profilUpdateTopic : false + this.profilUpdateTopic ? this.profilUpdateTopic : false, ), setLoading: (newValue) => { this.loading = newValue; @@ -130,8 +129,12 @@ export const Profil = { //? if they have the same status the insert date is used for ordering if (ele1.status === ele2.status) { result = - new Date(ele2.insertamum.split(".").reverse().join("-")) - - new Date(ele1.insertamum.split(".").reverse().join("-")); + new Date( + ele2.insertamum.split(".").reverse().join("-"), + ) - + new Date( + ele1.insertamum.split(".").reverse().join("-"), + ); } return result; }, @@ -139,6 +142,8 @@ export const Profil = { }, methods: { async load() { + await this.fetchViewData(); + // fetch profilUpdateStates to provide them to children components await this.$api .call(ApiProfilUpdate.getStatus()) @@ -157,20 +162,20 @@ export const Profil = { .catch((error) => { console.error(error); }); - - - this.$api - .call(ApiProfil.profilViewData(this.$route.params.uid??null)) - .then((response) => response.data).then(data=>{ - this.view = data?.profil_data.view; - this.data = data?.profil_data.data; - this.isEditable = data?.editable ?? false; - }) - .catch((error) => { - console.error(error); - }); - - + }, + async fetchViewData() { + let viewDataResult = await this.$api.call( + ApiProfil.getProfilViewData(this.$route.params.uid ?? null), + ); + + const data = viewDataResult.data; + if (!data) return; + + this.view = data.profil_data.view; + this.isEditable = data.profil_data.editable; + this.data = data.profil_data.data; + this.calendarSyncUrls = data.calendar_sync_urls ?? []; + this.authPermissions = data.permissions; }, zustellAdressenCount() { if (!this.data || !this.data.adressen) { @@ -186,7 +191,7 @@ export const Profil = { }) .map((adresse) => { return adresse.requested_change.adresse_id; - }) + }), ); } @@ -197,8 +202,9 @@ export const Profil = { .every((adresse) => this.data.profilUpdates.some( (update) => - update.requested_change.adresse_id == adresse.adresse_id - ) + update.requested_change.adresse_id == + adresse.adresse_id, + ), ) ) { adressenArray = adressenArray.concat( @@ -208,12 +214,11 @@ export const Profil = { }) .map((adr) => { return adr.adresse_id; - }) + }), ); } return [...new Set(adressenArray)]; - }, zustellKontakteCount() { if (!this.data || !this.data.kontakte) { @@ -226,14 +231,17 @@ export const Profil = { kontakteArray = kontakteArray.concat( this.data.profilUpdates .filter((update) => { - return update.status === 'Pending' && update.requested_change.zustellung; + return ( + update.status === "Pending" && + update.requested_change.zustellung + ); }) .map((kontant) => { return { - kontakt_id: kontant.requested_change.kontakt_id, - kontakttyp: kontant.requested_change.kontakttyp - }; - }) + kontakt_id: kontant.requested_change.kontakt_id, + kontakttyp: kontant.requested_change.kontakttyp, + }; + }), ); } @@ -244,8 +252,10 @@ export const Profil = { .every((kontakt) => this.data.profilUpdates.some( (update) => - update.status === 'Pending' && update.requested_change.kontakt_id == kontakt.kontakt_id - ) + update.status === "Pending" && + update.requested_change.kontakt_id == + kontakt.kontakt_id, + ), ) ) { kontakteArray = kontakteArray.concat( @@ -255,10 +265,10 @@ export const Profil = { }) .map((kon) => { return { - kontakt_id: kon.kontakt_id, - kontakttyp: kon.kontakttyp - }; - }) + kontakt_id: kon.kontakt_id, + kontakttyp: kon.kontakttyp, + }; + }), ); } @@ -266,7 +276,6 @@ export const Profil = { }, }, computed: { - filteredEditData() { if (!this.data) { return; @@ -330,8 +339,12 @@ export const Profil = { // excludes all contacts that are already used in pending profil update requests return !this.data.profilUpdates?.some( (update) => - update.status === this.profilUpdateStates["Pending"] && - update.requested_change?.kontakt_id === item.kontakt_id + update.status === + this.profilUpdateStates[ + "Pending" + ] && + update.requested_change?.kontakt_id === + item.kontakt_id, ); }) .map((kontakt) => { @@ -347,12 +360,18 @@ export const Profil = { topic: this.profilUpdateTopic?.["Private Adressen"], data: this.data.adressen ?.filter((item) => { - return !this.data.profilUpdates?.some((update) => { - return ( - update.status === this.profilUpdateStates["Pending"] && - update.requested_change?.adresse_id == item.adresse_id - ); - }); + return !this.data.profilUpdates?.some( + (update) => { + return ( + update.status === + this.profilUpdateStates[ + "Pending" + ] && + update.requested_change + ?.adresse_id == item.adresse_id + ); + }, + ); }) .map((adresse) => { return { @@ -374,23 +393,28 @@ export const Profil = { this.$refs.loadingModalRef.hide(); } }, - uid (newVal, oldVal) { - this.load() - } + uid(newVal, oldVal) { + this.load(); + }, }, created() { - this.load() + this.load(); }, template: ` -
+

Es wurde keine Person mit der UID {{this.notFoundUID}} gefunden

- +
`, -} +}; -export default Profil \ No newline at end of file +export default Profil; diff --git a/public/js/components/Cis/Profil/ProfilComponents/CalendarSync.js b/public/js/components/Cis/Profil/ProfilComponents/CalendarSync.js new file mode 100644 index 000000000..f3aa4b2f1 --- /dev/null +++ b/public/js/components/Cis/Profil/ProfilComponents/CalendarSync.js @@ -0,0 +1,54 @@ +export default { + name: "CalendarSync", + props: { uid: String, calendarSyncUrls: Array }, + data() { + return { + syncInstructionsUrlWithoutParam: + FHC_JS_DATA_STORAGE_OBJECT.app_root + + "cms/content.php?content_id=", + }; + }, + methods: { + copyUrlToClipboard(url) { + navigator.clipboard.writeText(url); + this.$fhcAlert.alertSuccess( + this.$p.t("profil/calendar_sync_clipboard_copy_confirmation"), + ); + }, + }, + template: ` +`, +}; diff --git a/public/js/components/Cis/Profil/ProfilComponents/QuickLinks.js b/public/js/components/Cis/Profil/ProfilComponents/QuickLinks.js index 3f8b7d77c..9efb21eab 100644 --- a/public/js/components/Cis/Profil/ProfilComponents/QuickLinks.js +++ b/public/js/components/Cis/Profil/ProfilComponents/QuickLinks.js @@ -1,53 +1,31 @@ export default { - //TODO: To be implemented - props: { - data: { - type: String, - }, - title: { - type: String, - required: true, - }, - mobile: { - type: Boolean, - default: false, - }, - }, - methods: { - hideCollapse: function () { - this.collapseOpen = false; - }, - showCollapse: function () { - this.collapseOpen = true; - }, - }, - data() { - return { - collapseOpen: false, - }; - }, - template: /*html*/ ` + name: "QuickLinks", + data() { + return {}; + }, + props: { + title: { + type: String, + required: true, + }, + links: { + type: Array, + required: true, + }, + }, + template: `
- - +
`, }; diff --git a/public/js/components/Cis/Profil/StudentProfil.js b/public/js/components/Cis/Profil/StudentProfil.js index 3f8e8380d..57f933305 100644 --- a/public/js/components/Cis/Profil/StudentProfil.js +++ b/public/js/components/Cis/Profil/StudentProfil.js @@ -1,7 +1,6 @@ import {CoreFilterCmpt} from "../../../components/filter/Filter.js"; import Mailverteiler from "./ProfilComponents/Mailverteiler.js"; import AusweisStatus from "./ProfilComponents/FhAusweisStatus.js"; -import QuickLinks from "./ProfilComponents/QuickLinks.js"; import Adresse from "./ProfilComponents/Adresse.js"; import Kontakt from "./ProfilComponents/Kontakt.js"; import ProfilEmails from "./ProfilComponents/ProfilEmails.js"; @@ -9,6 +8,8 @@ import RoleInformation from "./ProfilComponents/RoleInformation.js"; import ProfilInformation from "./ProfilComponents/ProfilInformation.js"; import FetchProfilUpdates from "./ProfilComponents/FetchProfilUpdates.js"; import EditProfil from "./ProfilModal/EditProfil.js"; +import QuickLinks from "./ProfilComponents/QuickLinks.js"; +import CalendarSync from "./ProfilComponents/CalendarSync.js"; import ApiProfilUpdate from '../../../api/factory/profilUpdate.js'; import { dateFilter } from '../../../tabulator/filters/Dates.js'; @@ -18,7 +19,6 @@ export default { CoreFilterCmpt, Mailverteiler, AusweisStatus, - QuickLinks, Adresse, Kontakt, ProfilEmails, @@ -26,6 +26,8 @@ export default { ProfilInformation, FetchProfilUpdates, EditProfil, + QuickLinks, + CalendarSync, }, inject: ["sortProfilUpdates", "collapseFunction", "language","isEditable"], data() { @@ -101,6 +103,7 @@ export default { props: { data: Object, editData: Object, + calendarSyncUrls: Array, }, provide() { return { @@ -240,6 +243,11 @@ export default { } }; }, + quickLinks() { + let quickLinks = []; + // + return quickLinks; + }, }, created() { // preload phrasen @@ -265,15 +273,7 @@ export default { :value="JSON.parse(JSON.stringify(filteredEditData))" :titel="$p.t('profil','profilBearbeiten')">
-
- -
@@ -403,12 +403,11 @@ export default {
- +
@@ -434,13 +433,18 @@ export default {
-
+
+
+
+ +
+
diff --git a/public/js/components/Cis/Profil/StudentViewProfil.js b/public/js/components/Cis/Profil/StudentViewProfil.js index 2ce649dae..0f7a196f4 100644 --- a/public/js/components/Cis/Profil/StudentViewProfil.js +++ b/public/js/components/Cis/Profil/StudentViewProfil.js @@ -1,30 +1,28 @@ -import QuickLinks from "./ProfilComponents/QuickLinks.js"; import Mailverteiler from "./ProfilComponents/Mailverteiler.js"; import ProfilEmails from "./ProfilComponents/ProfilEmails.js"; import RoleInformation from "./ProfilComponents/RoleInformation.js"; import ProfilInformation from "./ProfilComponents/ProfilInformation.js"; +import QuickLinks from "./ProfilComponents/QuickLinks.js"; export default { - data() { - return {}; - }, components: { - QuickLinks, Mailverteiler, ProfilEmails, RoleInformation, ProfilInformation, + QuickLinks, + }, + props: ["data", "permissions"], + data() { + return {}; }, - - props: ["data"], provide() { return { - studiengang_kz: Vue.computed({ get: () => this.data.studiengang_kz }), - } + studiengang_kz: Vue.computed({ + get: () => this.data.studiengang_kz, + }), + }; }, - - methods: {}, - computed: { fotoStatus() { return this.data?.fotoStatus ?? null; @@ -45,66 +43,67 @@ export default { foto: this.data.foto, }; }, - personEmails() { return this.data?.emails ? this.data.emails : []; }, - roleInformation() { if (!this.data) { return {}; } - return { geburtsdatum: { - label: `${this.$p.t('profil','Geburtsdatum')}`, - value: this.data.gebdatum + label: `${this.$p.t("profil", "Geburtsdatum")}`, + value: this.data.gebdatum, }, geburtsort: { - label: `${this.$p.t('profil','Geburtsort')}`, - value: this.data.gebort + label: `${this.$p.t("profil", "Geburtsort")}`, + value: this.data.gebort, }, personenkennzeichen: { - label: `${this.$p.t('person','personenkennzeichen')}`, - value: this.data.personenkennzeichen + label: `${this.$p.t("person", "personenkennzeichen")}`, + value: this.data.personenkennzeichen, }, studiengang: { - label: `${this.$p.t('lehre','studiengang')}`, - value: this.data.studiengang + label: `${this.$p.t("lehre", "studiengang")}`, + value: this.data.studiengang, }, semester: { - label: `${this.$p.t('lehre','semester')}`, - value: this.data.semester + label: `${this.$p.t("lehre", "semester")}`, + value: this.data.semester, }, verband: { - label: `${this.$p.t('lehre','lehrverband')}`, - value: this.data.verband + label: `${this.$p.t("lehre", "lehrverband")}`, + value: this.data.verband, }, gruppe: { - label: `${this.$p.t('lehre','gruppe')}`, - value: this.data.gruppe.trim() - } + label: `${this.$p.t("lehre", "gruppe")}`, + value: this.data.gruppe.trim(), + }, }; }, + quickLinks() { + let quickLinks = []; + if (this.$props.permissions && this.$props.permissions["basis/other_lv_plan"]) { + quickLinks.push({ + icon: "fa-calendar-days", + phrase: "lehre/stundenplan", + action: () => { + this.$router.push({ + name: "OtherLvPlan", + params: { otherUid: this.$props.data.username }, + }); + }, + }); + } + return quickLinks; + }, }, - - mounted() { - }, - template: /*html*/ `
- - -
@@ -112,12 +111,18 @@ export default {
-
+
+ +
+
+ +
+
@@ -145,17 +150,12 @@ export default {
- - - - + +
+
+ +
+
diff --git a/public/js/components/Cis/Raumsuche/Raumsuche.js b/public/js/components/Cis/Raumsuche/Raumsuche.js index 365da4911..3b0ebf538 100644 --- a/public/js/components/Cis/Raumsuche/Raumsuche.js +++ b/public/js/components/Cis/Raumsuche/Raumsuche.js @@ -3,14 +3,12 @@ import VueDatePicker from '../../vueDatepicker.js.php'; import ApiOrt from '../../../api/factory/ort.js' export const Raumsuche = { name: "Raumsuche", - props: { - - }, components: { VueDatePicker, CoreFilterCmpt, InputNumber: primevue.inputnumber, }, + inject: ["isMobile"], data() { return { phrasenPromise: null, @@ -194,8 +192,9 @@ export const Raumsuche = {

{{$p.t('rauminfo/roomSearch')}}


-
+
-
+
-
+
-
+