diff --git a/application/config/routes.php b/application/config/routes.php index 475168141..8024f449a 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -64,9 +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'; -//Routes for Lvplan Verband and page for dropdown Stg/Semester/Verband/Gruppe +$route['Cis/OtherLvPlan/.*'] = 'Cis/OtherLvPlan/index/$1'; +//Route for LV Plan Stg/Semester/Verband/Gruppe $route['Cis/StgOrgLvPlan/.*'] = 'Cis/StgOrgLvPlan/index/$1'; -$route['Cis/OverviewLvPlan/.*'] = 'Cis/OverviewLvPlan/index/$1'; $route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz'; $route['Abgabetool/Assistenz/(:any)'] = 'Cis/Abgabetool/Assistenz/$1'; diff --git a/application/controllers/Cis/OtherLvPlan.php b/application/controllers/Cis/OtherLvPlan.php new file mode 100644 index 000000000..c657ec3d2 --- /dev/null +++ b/application/controllers/Cis/OtherLvPlan.php @@ -0,0 +1,39 @@ + ['basis/other_lv_plan:r'] + ]); + + // Load Config + $this->load->config('calendar'); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + /** + * @return void + */ + public function index() + { + + $viewData = array( + 'timezone' => $this->config->item('timezone') + ); + + $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'OtherLvPlan']); + } +} diff --git a/application/controllers/Cis/OverviewLvPlan.php b/application/controllers/Cis/OverviewLvPlan.php deleted file mode 100644 index 5fe9d9779..000000000 --- a/application/controllers/Cis/OverviewLvPlan.php +++ /dev/null @@ -1,39 +0,0 @@ - ['basis/cis:r'] - ]); - - // Load Config - $this->load->config('calendar'); - } - - // ----------------------------------------------------------------------------------------------------------------- - // Public methods - - /** - * @return void - */ - public function index() - { - - $viewData = array( - 'uid'=>getAuthUID(), - 'timezone' => $this->config->item('timezone') - ); - - $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'OverviewLvPlan']); - } -} \ No newline at end of file diff --git a/application/controllers/Cis/Profil.php b/application/controllers/Cis/Profil.php index c287d87d0..83b8f5493 100644 --- a/application/controllers/Cis/Profil.php +++ b/application/controllers/Cis/Profil.php @@ -55,15 +55,16 @@ 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, + 'editable' => true, 'profil_data' => $profil_data, + 'calendarSyncUrls' => $this->getCalendarSyncUrlData(), ); - $this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilIndex']); + $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'profilIndex']); } /** @@ -76,20 +77,23 @@ class Profil extends Auth_Controller $this->load->library('ProfilLib'); $profil_data = $this->profillib->getView($uid); $profil_data = hasData($profil_data) ? getData($profil_data) : null; - $viewData = array ( + $viewData = array( 'uid' => $uid, - 'profil_data'=>$profil_data, + 'profil_data' => $profil_data, + 'permissions' => [ + 'basis/other_lv_plan' => $this->permissionlib->isBerechtigt(('basis/other_lv_plan')), + ] ); - if($uid == getAuthUID()){ + if ($uid == getAuthUID()) { $viewData['editable'] = true; } - $this->load->view('CisRouterView/CisRouterView.php',['viewData' => $viewData, 'route' => 'profilViewUid']); + $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, '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 +123,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 +266,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)")); } } @@ -751,5 +755,29 @@ class Profil extends Auth_Controller return $zutrittskarte_ausgegebenam; } - + /** + * 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/Cis4.php b/application/controllers/Cis4.php index b7ba2029d..11c723226 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 @@ -32,12 +33,12 @@ class Cis4 extends Auth_Controller { $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') + 'timezone' => $this->config->item('timezone'), ); $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'FhcDashboard']); diff --git a/application/controllers/api/frontend/v1/LvPlan.php b/application/controllers/api/frontend/v1/LvPlan.php index d0f4fa7ba..99dc8bf59 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,8 +34,8 @@ 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, @@ -46,11 +47,11 @@ class LvPlan extends FHCAPI_Controller 'fetchFerienEvents' => self::PERM_LOGGED, 'getStudiengaenge' => self::PERM_LOGGED, 'getLehrverband' => self::PERM_LOGGED, - + 'permissionOtherLvPlan' => 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, @@ -58,17 +59,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'); @@ -95,16 +96,22 @@ class LvPlan extends FHCAPI_Controller // 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( @@ -128,15 +135,11 @@ class LvPlan extends FHCAPI_Controller $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()) - { + if (!$this->form_validation->run()) { $this->terminateWithValidationErrors($this->form_validation->error_array()); $stgOrgEvents = []; $ferienEvents = []; - } - - else - { + } 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); @@ -170,7 +173,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()); @@ -193,40 +196,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); } @@ -257,10 +262,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 @@ -273,25 +278,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); @@ -336,7 +343,7 @@ class LvPlan extends FHCAPI_Controller public function getStudiengaenge() { - $this->load->model('organisation/Studiengang_model','StudiengangModel'); + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); $this->StudiengangModel->addOrder('typ'); $this->StudiengangModel->addOrder('kurzbz'); @@ -349,19 +356,19 @@ class LvPlan extends FHCAPI_Controller return $this->terminateWithSuccess($data); } - public function getLehrverband($studiengang_kz, $semester=null, $verband=null) + public function getLehrverband($studiengang_kz, $semester = null, $verband = null) { - $this->load->model('organisation/Lehrverband_model','LehrverbandModel'); + $this->load->model('organisation/Lehrverband_model', 'LehrverbandModel'); $where = [ 'aktiv' => true, 'studiengang_kz' => $studiengang_kz, ]; - if ($semester !== null && $semester !== 'null' && $semester !== 'undefined') { + if ($semester !== null && $semester !== 'null' && $semester !== 'undefined') { $where['semester'] = $semester; } - if ($verband !== null && $verband !== 'null' && $verband !== 'undefined') { + if ($verband !== null && $verband !== 'null' && $verband !== 'undefined') { $where['verband'] = $verband; } @@ -376,6 +383,16 @@ class LvPlan extends FHCAPI_Controller 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')); + } + /** * fetch moodle events * @@ -388,19 +405,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; }, [ @@ -420,7 +437,7 @@ 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'); @@ -432,7 +449,7 @@ class LvPlan extends FHCAPI_Controller $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); diff --git a/application/controllers/api/frontend/v1/OtherLvPlan.php b/application/controllers/api/frontend/v1/OtherLvPlan.php new file mode 100644 index 000000000..a144fb549 --- /dev/null +++ b/application/controllers/api/frontend/v1/OtherLvPlan.php @@ -0,0 +1,78 @@ +. + */ + +if (!defined('BASEPATH')) + exit('No direct script access allowed'); + +class OtherLvPlan extends FHCAPI_Controller +{ + + /** + * Object initialization + */ + public function __construct() + { + parent::__construct([ + 'getBasicUserAttributesForLvPlanDisplay' => self::PERM_LOGGED, + ]); + + $this->load->library('PermissionLib'); + $this->load->library('form_validation'); + + $this->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel'); + $this->load->model('person/Benutzer_model', 'BenutzerModel'); + + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * retrieves basic user attributes necessary for LV Plan display + * @access public + * @param $uid the userID for which basic attributes are retrieved + * @return stdClass consisting of basic user attributes + */ + public function getBasicUserAttributesForLvPlanDisplay($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; + + $result = [ + "username" => $uid, + "is_student" => $isStudent, + "is_mitarbeiter" => $isMitarbeiter, + "foto" => $person[0]->foto, + "vorname" => $person[0]->vorname, + "nachname" => $person[0]->nachname, + ]; + + $this->terminateWithSuccess($result); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Private methods + +} + 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 5e4b4e453..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)) 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/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/js/api/factory/lvPlan.js b/public/js/api/factory/lvPlan.js index 33e82cff9..41aa29299 100644 --- a/public/js/api/factory/lvPlan.js +++ b/public/js/api/factory/lvPlan.js @@ -30,11 +30,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 +57,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) { @@ -118,5 +118,10 @@ export default { url: `/api/frontend/v1/lvPlan/getLehrverband/${stg_kz}/${sem}/${verband}` } }, - + checkPermissionOtherLvPlan(){ + return { + method: 'get', + url: '/api/frontend/v1/lvPlan/permissionOtherLvPlan', + } + } }; \ 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..698531ea3 --- /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 { + getBasicUserAttributesForLvPlanDisplay(uid) { + return { + method: 'get', + url: `/api/frontend/v1/OtherLvPlan/getBasicUserAttributesForLvPlanDisplay/${uid}`, + }; + }, +}; \ No newline at end of file diff --git a/public/js/apps/Cis.js b/public/js/apps/Cis.js index c88a47a35..ce148834e 100644 --- a/public/js/apps/Cis.js +++ b/public/js/apps/Cis.js @@ -1,146 +1,201 @@ 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 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', - components: { - FhcSearchbar, - CisMenu - }, - data: function() { - return { - searchbaroptions: { + name: "CisApp", + components: { + FhcSearchbar, + CisMenu, + }, + data: function () { + return { + searchbaroptions: { origin: "cis", cssclass: "", calcheightonly: true, types: { - employee: Vue.computed(() => this.$p.t("search/type_employee")), - student: Vue.computed(() => this.$p.t("search/type_student")), + employee: Vue.computed(() => + this.$p.t("search/type_employee"), + ), + student: Vue.computed(() => + this.$p.t("search/type_student"), + ), room: Vue.computed(() => this.$p.t("search/type_room")), - organisationunit: Vue.computed(() => this.$p.t("search/type_organisationunit")), + organisationunit: Vue.computed(() => + this.$p.t("search/type_organisationunit"), + ), cms: Vue.computed(() => this.$p.t("search/type_cms")), - dms: Vue.computed(() => this.$p.t("search/type_dms")) + dms: Vue.computed(() => this.$p.t("search/type_dms")), }, - actions: { - employee: { - defaultaction: { - type: "link", - action: function(data) { - return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router+ - "/Cis/Profil/View/"+data.uid; - } + actions: { + employee: { + defaultaction: { + type: "link", + action: function (data) { + return ( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + "/Cis/Profil/View/" + + data.uid + ); + }, }, - childactions: [] + childactions: [], }, student: { defaultaction: { type: "link", action: function (data) { - return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + - "/Cis/Profil/View/" + data.uid; - - } + return ( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + "/Cis/Profil/View/" + + data.uid + ); + }, }, - childactions: [] + childactions: [], }, - room: { - defaultaction: { - type: "link", - renderif: function(data) { - if(data.content_id === null){ + room: { + defaultaction: { + type: "link", + renderif: function (data) { + if (data.content_id === null) { return false; } return true; }, - action: function(data) { - const link= FHC_JS_DATA_STORAGE_OBJECT.app_root + + action: function (data) { + const link = + FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + - '/CisVue/Cms/content/' + data.content_id; + "/CisVue/Cms/content/" + + data.content_id; return link; - } - }, - childactions: [ - { - label: "LV-Plan", - icon: "fas fa-bookmark", - type: "link", - action: function(data) { - const link = FHC_JS_DATA_STORAGE_OBJECT.app_root + + }, + }, + childactions: [ + { + label: "LV-Plan", + icon: "fas fa-bookmark", + type: "link", + action: function (data) { + const link = + FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + - '/CisVue/Cms/getRoomInformation/' + data.ort_kurzbz; + "/CisVue/Cms/getRoomInformation/" + + data.ort_kurzbz; return link; - } - }, - { - label: "Rauminformation", - icon: "fas fa-info-circle", - type: "link", - renderif: function(data) { - if(data.content_id === null){ + }, + }, + { + label: "Rauminformation", + icon: "fas fa-info-circle", + type: "link", + renderif: function (data) { + if (data.content_id === null) { return false; } return true; - }, - action: function(data) { - const link= FHC_JS_DATA_STORAGE_OBJECT.app_root + + }, + action: function (data) { + const link = + FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + - '/CisVue/Cms/content/' + data.content_id; + "/CisVue/Cms/content/" + + data.content_id; return link; - } - }, - ] - }, - organisationunit: { - defaultaction: { - type: "link", - renderif: function(data) { - if(data.mailgroup) { + }, + }, + ], + }, + organisationunit: { + defaultaction: { + type: "link", + renderif: function (data) { + if (data.mailgroup) { return true; } return false; }, - action: function(data) { - const link = 'mailto:' + data.mailgroup; + action: function (data) { + const link = "mailto:" + data.mailgroup; return link; - } - }, - childactions: [] - }, + }, + }, + childactions: [], + }, cms: { defaultaction: { type: "link", action: function (data) { - const link = FHC_JS_DATA_STORAGE_OBJECT.app_root + + const link = + FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + - '/CisVue/Cms/content/' + data.content_id; + "/CisVue/Cms/content/" + + data.content_id; return link; - } + }, }, - childactions: [] + childactions: [], }, dms: { defaultaction: { type: "link", action: function (data) { - const link = FHC_JS_DATA_STORAGE_OBJECT.app_root + - 'cms/dms.php?id=' + data.dms_id; + const link = + FHC_JS_DATA_STORAGE_OBJECT.app_root + + "cms/dms.php?id=" + + data.dms_id; return link; - } + }, }, - childactions: [] - } - } - } - }; - }, - methods: { - searchfunction: function(searchsettings) { - return this.$api.call(ApiSearchbar.searchCis(searchsettings)); - } - } + childactions: [], + }, + }, + }, + }; + }, + 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); @@ -148,9 +203,9 @@ FhcApps.makeExtendable(app); app.use(primevue.config.default, { zIndex: { overlay: 9000, - tooltip: 8000 - } -}) + tooltip: 8000, + }, +}); app.use(PluginsPhrasen); app.use(Theme); -app.mount('#cis-header'); +app.mount("#cis-header"); diff --git a/public/js/apps/Dashboard/Fhc.js b/public/js/apps/Dashboard/Fhc.js index c279fd716..b2b7752df 100644 --- a/public/js/apps/Dashboard/Fhc.js +++ b/public/js/apps/Dashboard/Fhc.js @@ -18,7 +18,7 @@ import AbgabetoolAssistenz from "../../components/Cis/Abgabetool/AbgabetoolAssis 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 OverviewLvPlan from "../../components/Cis/LvPlan/OverviewLvPlan.js"; +import OtherLvPlan from "../../components/Cis/LvPlan/OtherLvPlan.js"; import ApiRenderers from '../../api/factory/renderers.js'; import ApiRouteInfo from '../../api/factory/routeinfo.js'; @@ -210,15 +210,15 @@ const router = VueRouter.createRouter({ } }, { - path: `/Cis/OverviewLvPlan`, - name: 'OverviewLvPlan', - component: OverviewLvPlan, - props(route) { - return { + path: `/Cis/OtherLvPlan/:otherUid/:mode?/:focus_date?`, + name: "OtherLvPlan", + component: OtherLvPlan, + props(route) { + return { propsViewData: route.params - }; - } - }, + }; + } + }, { path: `/Cis4`, name: 'Cis4', @@ -305,7 +305,6 @@ const app = Vue.createApp({ } }, async created(){ - await this.$api .call(ApiRenderers.loadRenderers()) .then(res => res.data) diff --git a/public/js/components/Cis/LvPlan/OtherLvPlan.js b/public/js/components/Cis/LvPlan/OtherLvPlan.js new file mode 100644 index 000000000..f4948ca50 --- /dev/null +++ b/public/js/components/Cis/LvPlan/OtherLvPlan.js @@ -0,0 +1,248 @@ +import FormForm from "../../Form/Form.js"; +import FormInput from "../../Form/Input.js"; +import FhcCalendar from "../../Calendar/LvPlan.js"; + +import ApiLvPlan from "../.././../api/factory/lvPlan.js"; +import ApiOtherLvPlan from "../.././../api/factory/otherLvPlan.js"; +import ApiAuthinfo from "../../../api/factory/authinfo.js"; + +export const DEFAULT_MODE_LVPLAN = "Week"; + +export default { + name: "OtherLvPlan", + components: { + FormForm, + FormInput, + FhcCalendar, + }, + props: { + viewData: Object, + propsViewData: Object, + }, + data() { + return { + localProps: {}, + studiensemester_kurzbz: null, + studiensemester_start: null, + studiensemester_ende: null, + isOtherPersonMitarbeiter: false, + isOtherPersonStudent: false, + currentStgBezeichnung: null, + listVerband: [], + listGroup: [], + rangeIntervalFirst: null, + otherPersonData: { + fullName: "", + photo: "", + }, + }; + }, + computed: { + currentDay() { + if ( + !this.propsViewData?.focus_date || + isNaN(new Date(this.propsViewData?.focus_date)) + ) + return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate(); + return this.propsViewData?.focus_date; + }, + currentMode() { + if ( + !this.propsViewData?.mode || + !["day", "week", "month"].includes( + this.propsViewData?.mode.toLowerCase(), + ) + ) + return DEFAULT_MODE_LVPLAN; + return this.propsViewData?.mode; + }, + downloadLinks() { + if ( + !this.studiensemester_start || + !this.studiensemester_ende || + !this.propsViewData.otherUid + ) + return false; + + const type = this.isOtherPersonStudent + ? "student" + : this.isOtherPersonMitarbeiter + ? "lektor" + : null; + + if (!type) return; + + const opts = { zone: this.viewData.timezone }; + const start = luxon.DateTime.fromISO( + this.studiensemester_start, + opts, + ).toUnixInteger(); + const ende = luxon.DateTime.fromISO( + this.studiensemester_ende, + opts, + ).toUnixInteger(); + + const download_link = + FHC_JS_DATA_STORAGE_OBJECT.app_root + + "cis/private/lvplan/stpl_kalender.php" + + "?type=" + + type + + "&pers_uid=" + + this.propsViewData.otherUid + + "&begin=" + + start + + "&ende=" + + ende; + + return [ + { + title: "excel", + icon: "fa-solid fa-file-excel", + link: download_link + "&format=excel", + }, + { + title: "csv", + icon: "fa-solid fa-file-csv", + link: download_link + "&format=csv", + }, + { + title: "ical1", + icon: "fa-regular fa-calendar", + link: download_link + "&format=ical&version=1&target=ical", + }, + { + title: "ical2", + icon: "fa-regular fa-calendar", + link: download_link + "&format=ical&version=2&target=ical", + }, + ]; + }, + get_image_base64_src: function () { + if (!this.otherPersonData.photo?.length) { + return ""; + } + return "data:image/jpeg;base64," + this.otherPersonData.photo; + }, + }, + watch: { + "propsViewData.otherUid": { + handler() { + this.$router.go(); + }, + }, + }, + methods: { + handleChangeDate(day, newMode) { + return this.handleChangeMode(newMode, day); + }, + handleChangeMode(newMode, day) { + const mode = newMode[0].toUpperCase() + newMode.slice(1); + const focus_date = day.toISODate(); + + this.$router.push({ + name: "OtherLvPlan", + params: { + mode, + focus_date, + }, + }); + }, + updateRange(rangeInterval) { + this.$api + .call( + ApiLvPlan.studiensemesterDateInterval( + rangeInterval.end.startOf("week").toISODate(), + ), + ) + .then((res) => { + this.studiensemester_kurzbz = res.data.studiensemester_kurzbz; + this.studiensemester_start = res.data.start; + this.studiensemester_ende = res.data.ende; + }); + }, + getPromiseFunc(start, end) { + return [ + this.$api.call( + ApiLvPlan.eventsPersonal( + start.toISODate(), + end.toISODate(), + this.propsViewData.otherUid, + ), + ), + this.$api.call( + ApiLvPlan.getLvPlanReservierungen( + start.toISODate(), + end.toISODate(), + this.propsViewData.otherUid, + ), + ), + ]; + }, + }, + async created() { + const authInfoResponse = await this.$api.call(ApiAuthinfo.getAuthInfo()); + const authId = authInfoResponse.data.uid; + if (authId === this.propsViewData.otherUid) { + this.$router.push({ name: "MyLvPlan" }); + } + + const userDataResponse = await this.$api.call( + ApiOtherLvPlan.getBasicUserAttributesForLvPlanDisplay( + this.propsViewData.otherUid, + ), + ); + + const userData = userDataResponse.data; + this.isOtherPersonMitarbeiter = !!userData.is_mitarbeiter; + this.isOtherPersonStudent = !!userData.is_student; + this.otherPersonData.fullName = userData.vorname + " " + userData.nachname; + this.otherPersonData.photo = userData.foto; + }, + template: ` +
+

+
+ + {{ $p.t('lehre/stundenplan') + (studiensemester_kurzbz ? " " + studiensemester_kurzbz : "") }} + +
+ + {{ otherPersonData.fullName }} + + profile picture +
+
+

+
+ + + +
+ `, +}; diff --git a/public/js/components/Cis/LvPlan/OverviewLvPlan.js b/public/js/components/Cis/LvPlan/OverviewLvPlan.js deleted file mode 100644 index e594e140f..000000000 --- a/public/js/components/Cis/LvPlan/OverviewLvPlan.js +++ /dev/null @@ -1,200 +0,0 @@ -import FormForm from '../../Form/Form.js'; -import FormInput from '../../Form/Input.js'; - -import ApiLvPlan from '../../../api/factory/lvPlan.js'; -export const DEFAULT_MODE_LVPLAN = 'Week'; - -export default { - name: "OverviewLvPlan", - components: { - FormForm, - FormInput, - }, - props: { - viewData: Object, - propsViewData: Object - }, - data() { - return { - formData: { - stgkz: null, - sem: null, - verband: null, - gruppe: null, - }, - listStg: [], - listSem: [1,2,3,4,5,6,7,8,9,10], - listVerband: [], - listGroup: [], - }; - }, - methods: { - loadLvPlan() { - if (!this.formData.stgkz) { - this.$fhcAlert.alertError(this.$p.t('LvPlan', 'chooseStg')); - return; - } - if (!this.formData.sem && (this.formData.verband || this.formData.gruppe)) { - this.$fhcAlert.alertError(this.$p.t('LvPlan', 'error_SemMissing')); - return; - } - - if (!this.formData.verband && this.formData.gruppe) { - this.$fhcAlert.alertError(this.$p.t('LvPlan', 'error_VerbandMissing')); - return; - } - - const params = { - mode: this.currentMode, - focus_date: this.currentDay, - stgkz: this.formData.stgkz, - sem: this.formData.sem, - verband: this.formData.verband, - gruppe: this.formData.gruppe, - }; - - //ensure logic: no value after a null value in route - if (params.sem == null) { - params.verband = null; - params.gruppe = null; - } - if (params.verband == null) { - params.gruppe = null; - } - - //delete all null values to avoid null in router - Object.keys(params).forEach( - key => params[key] == null && delete params[key] - ); - - this.$router.push({ - name: "StgOrgLvPlan", - params, - }); - }, - loadListSem(){ - this.listSem = [...Array(this.maxSemester).keys()].map(i => i + 1); - }, - loadListVerband(){ - this.$api - .call(ApiLvPlan.getLehrverband(this.formData.stgkz, this.formData.sem, this.formData.verband)) - .then(result => { - const data = result.data; - const mappedData = data.map(item => item.verband); - this.listVerband = [...new Set(mappedData.filter(v => - v !== null && - v !== undefined && - String(v).trim() !== "" - ))] - .sort(); - }) - .catch(this.$fhcAlert.handleSystemError); - }, - loadListGroup(){ - this.$api - .call(ApiLvPlan.getGruppe(this.formData.stgkz, this.formData.sem, this.formData.verband)) - .then(result => { - const data = result.data; - const mappedData = data.map(item => item.gruppe); - this.listGroup = [...new Set(mappedData.filter(v => - v !== null && - v !== undefined && - String(v).trim() !== ""))] - .sort(); - }) - .catch(this.$fhcAlert.handleSystemError); - } - }, - computed: { - maxSemester(){ - const currentStg = this.listStg.find( - item => item.studiengang_kz === this.formData.stgkz - ); - return currentStg.max_semester; - }, - currentDay() { - if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date))) - return luxon.DateTime.now().setZone(this.viewData.timezone).toISODate(); - return this.propsViewData?.focus_date; - }, - currentMode() { - if (!this.propsViewData?.mode || !['day', 'week', 'month'].includes(this.propsViewData?.mode.toLowerCase())) - return DEFAULT_MODE_LVPLAN; - return this.propsViewData?.mode; - }, - }, - created(){ - this.$api - .call(ApiLvPlan.getStudiengaenge()) - .then(result => { - this. listStg = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - }, - template: ` -
-
- - - - - - - - - - - - - - - - - - - - -
-
- `, -}; diff --git a/public/js/components/Cis/LvPlan/StgOrg.js b/public/js/components/Cis/LvPlan/StgOrg.js index 4554e7ec7..e63286b29 100644 --- a/public/js/components/Cis/LvPlan/StgOrg.js +++ b/public/js/components/Cis/LvPlan/StgOrg.js @@ -46,7 +46,7 @@ export default { const currentStg = this.listStg.find( item => item.studiengang_kz === this.formData.stgkz ); - return currentStg.max_semester; + return currentStg?.max_semester; }, currentDay() { if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date))) diff --git a/public/js/components/Cis/Profil/MitarbeiterProfil.js b/public/js/components/Cis/Profil/MitarbeiterProfil.js index 559006a1b..44e0ca6eb 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,7 @@ export default { }, ], }, - + quickLinks: [], betriebsmittel_table_options: { persistenceID: "filterTableMaProfilBetriebsmittel", persistence: { @@ -160,6 +162,7 @@ export default { props: { data: Object, editData: Object, + calendarSyncUrls: Array, }, methods: { @@ -313,7 +316,6 @@ export default { }); //? sorts the profil Updates: pending -> accepted -> rejected this.data.profilUpdates?.sort(this.sortProfilUpdates); - }, watch: { 'data.funktionen'(newVal) { @@ -331,12 +333,6 @@ export default {
-
@@ -465,17 +461,11 @@ export default {
- +
+
+ +
+
@@ -501,12 +491,17 @@ export default {
-
+
+
+
+ +
+
diff --git a/public/js/components/Cis/Profil/MitarbeiterViewProfil.js b/public/js/components/Cis/Profil/MitarbeiterViewProfil.js index 481714590..42a2700f3 100644 --- a/public/js/components/Cis/Profil/MitarbeiterViewProfil.js +++ b/public/js/components/Cis/Profil/MitarbeiterViewProfil.js @@ -1,9 +1,9 @@ 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'; @@ -11,10 +11,10 @@ export default { components: { CoreFilterCmpt, Mailverteiler, - QuickLinks, RoleInformation, ProfilEmails, ProfilInformation, + QuickLinks, }, inject: ["collapseFunction", "language"], data() { @@ -90,11 +90,12 @@ export default { }, ], }, + quickLinks: [], }; }, //? 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); @@ -189,6 +190,21 @@ export default { this.preloadedPhrasen.wochenstundenPhrase = this.$p.t('profil/wochenstunden'); this.preloadedPhrasen.loaded = true; }); + + if (this.$props.permissions["basis/other_lv_plan"]) { + this.quickLinks.push( + { + icon: "fa-calendar-days", + phrase: "lehre/stundenplan", + action: () => { + this.$router.push({ + name: "OtherLvPlan", + params: { otherUid: this.$props.data.username }, + }) + }, + } + ); + } }, template: /*html*/ ` @@ -242,7 +258,7 @@ export default {
-
+
@@ -256,26 +272,22 @@ export default {
- -
-
- - -
-
- -
+
+
+ +
+
+
+
+ + +
+
+ +
- - + + `, }; diff --git a/public/js/components/Cis/Profil/Profil.js b/public/js/components/Cis/Profil/Profil.js index da205816d..b5f3fd552 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,11 @@ export const Profil = { props: { uid: { type: String, - required:false, + required: false, }, viewData: { type: Object, - } + }, }, data() { return { @@ -67,12 +67,12 @@ export const Profil = { }, 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 +130,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; }, @@ -157,11 +161,11 @@ export const Profil = { .catch((error) => { console.error(error); }); - - + this.$api - .call(ApiProfil.profilViewData(this.$route.params.uid??null)) - .then((response) => response.data).then(data=>{ + .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; @@ -169,8 +173,6 @@ export const Profil = { .catch((error) => { console.error(error); }); - - }, zustellAdressenCount() { if (!this.data || !this.data.adressen) { @@ -186,7 +188,7 @@ export const Profil = { }) .map((adresse) => { return adresse.requested_change.adresse_id; - }) + }), ); } @@ -197,8 +199,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 +211,11 @@ export const Profil = { }) .map((adr) => { return adr.adresse_id; - }) + }), ); } return [...new Set(adressenArray)]; - }, zustellKontakteCount() { if (!this.data || !this.data.kontakte) { @@ -226,14 +228,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 +249,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 +262,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 +273,6 @@ export const Profil = { }, }, computed: { - filteredEditData() { if (!this.data) { return; @@ -330,8 +336,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 +357,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,12 +390,12 @@ export const Profil = { this.$refs.loadingModalRef.hide(); } }, - uid (newVal, oldVal) { - this.load() - } + uid(newVal, oldVal) { + this.load(); + }, }, created() { - this.load() + this.load(); }, template: `
@@ -388,9 +404,14 @@ export const Profil = {
- +
`, -} +}; -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..e3e90d3bf 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() { @@ -95,12 +97,14 @@ export default { }, ], }, + quickLinks: [], }; }, props: { data: Object, editData: Object, + calendarSyncUrls: Array, }, provide() { return { @@ -265,15 +269,7 @@ export default { :value="JSON.parse(JSON.stringify(filteredEditData))" :titel="$p.t('profil','profilBearbeiten')">
-
- -
@@ -403,12 +399,11 @@ export default {
- +
@@ -434,13 +429,18 @@ export default {
-
+
+
+
+ +
+
diff --git a/public/js/components/Cis/Profil/StudentViewProfil.js b/public/js/components/Cis/Profil/StudentViewProfil.js index 2ce649dae..3bc4dabd7 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 { + quickLinks: [], + }; }, - - props: ["data"], provide() { return { studiengang_kz: Vue.computed({ get: () => this.data.studiengang_kz }), } }, - - methods: {}, - computed: { fotoStatus() { return this.data?.fotoStatus ?? null; @@ -88,23 +86,28 @@ export default { }; }, }, - - mounted() { + methods: {}, + created() { + if (this.$props.permissions["basis/other_lv_plan"]) { + this.quickLinks.push( + { + icon: "fa-calendar-days", + phrase: "lehre/stundenplan", + action: () => { + this.$router.push({ + name: "OtherLvPlan", + params: { otherUid: this.$props.data.username }, + }) + }, + } + ); + } }, - template: /*html*/ `
- - -
@@ -112,12 +115,18 @@ export default {
-
+
+ +
+
+ +
+
@@ -145,17 +154,12 @@ export default {
- - - - + +
+
+ +
+
diff --git a/public/js/components/Cis/Renderer/Lehreinheit/calendarEvent.js b/public/js/components/Cis/Renderer/Lehreinheit/calendarEvent.js index d4ddeaf60..fa3ea6618 100644 --- a/public/js/components/Cis/Renderer/Lehreinheit/calendarEvent.js +++ b/public/js/components/Cis/Renderer/Lehreinheit/calendarEvent.js @@ -31,7 +31,7 @@ export default { this.event.lektor.slice(0, 3).map(lektor => lektor.kurzbz).join("\n") + "\n" + this.$p.t('lehre/weitereLektoren', [this.event.lektor.length - 3]) ].join(": ")); - } else {//console.log(this.event.lektor); + } else { tooltipArray.push([ this.$p.t('lehre/lektor'), this.event.lektor.map(lektor => lektor.kurzbz).join("\n") diff --git a/public/js/components/Cis/Renderer/Lehreinheit/modalContent.js b/public/js/components/Cis/Renderer/Lehreinheit/modalContent.js index d773a52ed..738c0c75c 100644 --- a/public/js/components/Cis/Renderer/Lehreinheit/modalContent.js +++ b/public/js/components/Cis/Renderer/Lehreinheit/modalContent.js @@ -142,7 +142,6 @@ export default { - - +
`, } \ No newline at end of file diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index 8b6af9f2d..f75c150e9 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -94,6 +94,7 @@ require_once('dbupdate_3.4/71399_dashboard_update_widget_paths.php'); require_once('dbupdate_3.4/71645_studvw_messagetab_ladezeit.php'); require_once('dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php'); require_once('dbupdate_3.4/70376_lohnguide.php'); +require_once('dbupdate_3.4/76150_perm_other_lv_plan.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

Pruefe Tabellen und Attribute!

'; diff --git a/system/dbupdate_3.4/76150_perm_other_lv_plan.php b/system/dbupdate_3.4/76150_perm_other_lv_plan.php new file mode 100644 index 000000000..9a23a4cb2 --- /dev/null +++ b/system/dbupdate_3.4/76150_perm_other_lv_plan.php @@ -0,0 +1,41 @@ +, + * + * Beschreibung: + * Permission basis/other_lv_plan + */ +if (! defined('DB_NAME')) exit('No direct script access allowed'); + +// Add permission: basis/gehaelter +if($result = @$db->db_query("SELECT 1 FROM system.tbl_berechtigung WHERE berechtigung_kurzbz = 'basis/other_lv_plan';")) +{ + if($db->db_num_rows($result) == 0) + { + $qry = "INSERT INTO system.tbl_berechtigung(berechtigung_kurzbz, beschreibung) VALUES('basis/other_lv_plan', 'Permission holder can view other users timetables (LV plans)');"; + + if(!$db->db_query($qry)) + { + echo 'system.tbl_berechtigung '.$db->db_last_error().'
'; + } + else + { + echo 'system.tbl_berechtigung: Added permission "basis/other_lv_plan"
'; + } + } +} diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 071728f0f..95a3f9be0 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -29582,6 +29582,126 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'profil', + 'phrase' => 'calendar_sync', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Persönlichen LV-Plan abonnieren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Subscribe to personal schedule', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'profil', + 'phrase' => 'calendar_sync_instructions', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anleitung LV-Plan Synchronisation', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Instructions for synchronizing your schedule', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'profil', + 'phrase' => 'calendar_sync_cal_dav', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'CalDAV URL (Android, Thunderbird, CalDAV-Synchronizer)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'CalDAV URL (Android, Thunderbird, CalDAV-Synchronizer)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'profil', + 'phrase' => 'calendar_sync_cal_dav_principal', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'CalDAV Principal URL (MacOS, iOS)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'CalDAV Principal URL (MacOS, iOS)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'profil', + 'phrase' => 'calendar_sync_i_cal', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'iCAL URL (Google)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'iCAL URL (Google)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'profil', + 'phrase' => 'calendar_sync_clipboard_copy_confirmation', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'URL in die Zwischenablage kopiert.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'URL copied to clipboard.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), //Profil Phrasen ende // LvPlan Phrasen start array( @@ -58319,6 +58439,28 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), // ### LvPlanStgOrg END ### + // DMS-Link Phrasen Start + array( + 'app' => 'core', + 'category' => 'DMS-Link', + 'phrase' => 'lvplanSyncFAQ', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '7188', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => '7188', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + // DMS-Link Phrasen End );