{{ translation.title || $capitalize($p.t('ui', 'untitledNews')) }}
+ {{ $capitalize($p.t('ui', 'notPublishedYet')) }} +{{ $capitalize($p.t('ui', 'newsContentPreviewHint')) }}
+diff --git a/application/config/Events.php b/application/config/Events.php index 80a8f03b3..84c610c63 100644 --- a/application/config/Events.php +++ b/application/config/Events.php @@ -23,6 +23,15 @@ Events::on('loadRenderers', function ($renderers) { ); }); +Events::on('loadRenderers', function ($renderers) { + $fhc_core_renderers =& $renderers(); + $fhc_core_renderers["slot_room"] = array( + 'modalTitle' => APP_ROOT.'public/js/components/Cis/Renderer/Slot/roomModalTitle.js', + 'modalContent' => APP_ROOT.'public/js/components/Cis/Renderer/Slot/roomModalContent.js', + 'calendarEventStyles' => APP_ROOT.'public/css/Cis4/CoreCalendarEvents.css' + ); +}); + Events::on('loadRenderers', function ($renderers) { $fhc_core_renderers =& $renderers(); $fhc_core_renderers["ferien"] = array( diff --git a/application/config/infocenter.php b/application/config/infocenter.php index 555c30996..15889ef78 100644 --- a/application/config/infocenter.php +++ b/application/config/infocenter.php @@ -3,3 +3,4 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); $config['infocenter_studiengang_kz'] = '10021, 10027, 10002'; +$config['infocenter_qualifikationskurs_kz'] = '10002'; diff --git a/application/config/noten.php b/application/config/noten.php new file mode 100644 index 000000000..81f467a13 --- /dev/null +++ b/application/config/noten.php @@ -0,0 +1,64 @@ + 1]; // über alle Antritte hinweg nur ein entschuldigt + +// tbl_note pk of the 'entschuldigt' note. An entschuldigt Termin is preserved as its own dated +// entry when a new pruefung of the same type is created (instead of being overwritten). +$config['NOTE_ENTSCHULDIGT'] = 17; + +// The maximum number of attempts that count, the first attempt and the kommissionelle attempt +// included. null derives the number from the old flags: +// 1 (the original assessment) + TERMIN2 + TERMIN3 + KOMMPRUEF +// The examination rules permit three attempts. With TERMIN2 and KOMMPRUEF the formula gives 3. +// An installation with TERMIN3 gets 4, which is one more attempt in the same chain. +$config['CIS_GESAMTNOTE_MAX_ANTRITTE'] = null; + +// The exam types that take place before a commission. +$config['PRUEFUNG_KOMMISSIONELL_TYPEN'] = ['kommPruef', 'zusKommPruef']; + +// The type that the tool writes for the last attempt. The last attempt is always kommissionell. +$config['PRUEFUNG_TYP_KOMMISSIONELL'] = 'kommPruef'; + +// Exam types that never use an attempt. A zusKommPruef repeats a kommissionelle Prüfung that had +// a procedural fault. It stands outside the attempt chain, and the student administration enters +// it. The examination rules do not describe it. +$config['PRUEFUNG_TYPEN_OHNE_ANTRITT'] = ['zusKommPruef']; + +// Spaltenaufteilung der Prüfungen in der Notentabelle: +// 'antritt' - eine Spalte je Antrittsnummer, Datum steht in der Zelle (robust bei Einzelterminen) +// 'datum' - eine Spalte je Prüfungsdatum (kompakt, wenn ganze Jahrgänge am selben Tag antreten) +// Nur die Vorgabe; im Tool umschaltbar und pro Benutzer gespeichert. +$config['CIS_GESAMTNOTE_PRUEFUNGSSPALTEN'] = 'antritt'; + +// Bei der Notenübernahme durch die Assistenz den ersten Antritt als eigene Prüfung anlegen. +$config['CIS_GESAMTNOTE_ERSTANTRITT_BEI_UEBERNAHME'] = true; + +// availability of the two Benotungstool import flows. When both are true they are shown as +// separate buttons/dialogs. +$config['CIS_GESAMTNOTE_PRUEFUNGSIMPORT'] = true; // dated import that creates a pruefung per row +$config['CIS_GESAMTNOTE_NOTENIMPORT'] = false; // classic note-only import (uid + note, no date) + +// Noteneintragungsfrist (Prüfungsordnung §1): grade/pruefung entry is only permitted up to this +// deadline. The month/day below is applied to the studiensemester's year: +// Sommersemester (SSyyyy) -> deadline in the SAME calendar year (default 15th November) +// Wintersemester (WSyyyy) -> deadline in the FOLLOWING calendar year (default 15th May) +$config['CIS_GESAMTNOTE_NOTENEINTRAGUNGSFRIST'] = false; // switch to use the window enforcement +$config['NOTENEINTRAGUNGSFRIST_SS'] = ['month' => 11, 'day' => 15]; // Sommersemester deadline (same year) +$config['NOTENEINTRAGUNGSFRIST_WS'] = ['month' => 5, 'day' => 15]; // Wintersemester deadline (following year) \ No newline at end of file diff --git a/application/config/routes.php b/application/config/routes.php index bb0b0f65e..4dd2acee3 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -61,12 +61,29 @@ $route['api/v1/organisation/[O|o]rganisationseinheit/(:any)'] = 'api/v1/organisa $route['api/v1/ressource/[B|b]etriebsmittelperson/(:any)'] = 'api/v1/ressource/betriebsmittelperson2/$1'; $route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1'; +// Legacy CMS news routes +$route['api/frontend/v1/Cms/getNewsItem/(:num)'] = 'api/frontend/v1/NewsAdministrationAPI/getNewsItem/$1'; +$route['api/frontend/v1/Cms/storeNewsItem'] = 'api/frontend/v1/NewsAdministrationAPI/storeNewsItem'; +$route['api/frontend/v1/Cms/updateNewsItem/(:num)'] = 'api/frontend/v1/NewsAdministrationAPI/updateNewsItem/$1'; +$route['api/frontend/v1/Cms/deleteNewsItem/(:num)'] = 'api/frontend/v1/NewsAdministrationAPI/deleteNewsItem/$1'; + +// Keep legacy uppercase URLs refreshable while using the canonical controller method casing +$route['CisVue/Cms/NewsAdministration'] = 'CisVue/Cms/newsAdministration'; + $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['Cis/Benotungstool/.*'] = 'Cis/Benotungstool/index/$1'; + +$route['Cis/MyReservation'] = 'Cis/MyReservation/index/$1'; + +//Route for Zeitsperrenma +$route['Cis/Zeitsperrenma/.*'] = 'Cis/Zeitsperrenma/index/$1'; +//Route für Zeitsperren +$route['Cis/Zeitsperren/.*'] = 'Cis/Zeitsperren/index/$1'; $route['Abgabetool/Assistenz'] = 'Cis/Abgabetool/Assistenz'; $route['Abgabetool/Assistenz/(:any)'] = 'Cis/Abgabetool/Assistenz/$1'; @@ -129,13 +146,19 @@ $route['api/frontend/v1/treemenudata/stv/.*'] = 'api/frontend/v1/stv/students/in $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByStg/$1/$2'; $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/semester/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByStg/$1/$2/$3'; $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/semester/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByStg/$1/$2/$3'; -$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByStg/$1/$2/$3'; -$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByStg/$1/$2/$3'; + +$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)/semester/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByStgOrgform/$1/$2/$3/$4'; +$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)/semester/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByStgOrgform/$1/$2/$3/$4'; +$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByStgOrgform/$1/$2/$3'; +$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/stg/(:any)/orgform/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByStgOrgform/$1/$2/$3'; $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByEmp/$1/$2'; $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)/stg/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByEmp/$1/$2/$3'; $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)/stg/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByEmp/$1/$2/$3'; +$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/le/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByLe/$1/$2'; +$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/lv/(:any)'] = 'api/frontend/v1/Lehrveranstaltung/getByLv/$1/$2'; + // load routes from extensions, also look for environment-specific configs $subdirs = ['application/config/extensions', 'application/config/' . ENVIRONMENT . '/extensions']; diff --git a/application/config/treemenu/custom/lvvw.php b/application/config/treemenu/custom/lvvw.php index 977c0459e..09a10ed2b 100644 --- a/application/config/treemenu/custom/lvvw.php +++ b/application/config/treemenu/custom/lvvw.php @@ -26,7 +26,8 @@ $config['root'] = [ 'verband' => [ 'group' ] - ], - 'orgform' + ] ] ]; + +$config['root']['stg']['orgform'] = $config['root']['stg']; diff --git a/application/controllers/Cis/Benotungstool.php b/application/controllers/Cis/Benotungstool.php new file mode 100644 index 000000000..fe01f3595 --- /dev/null +++ b/application/controllers/Cis/Benotungstool.php @@ -0,0 +1,37 @@ + array('lehre/benotungstool:rw', 'lehre/benotungstool_assistenz:rw') + ]); + + $this->_ci =& get_instance(); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + /** + * @return void + */ + public function index() + { + $viewData = array( + 'uid'=>getAuthUID(), + ); + + $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'Benotungstool']); + } + +} \ No newline at end of file diff --git a/application/controllers/Cis/Compat.php b/application/controllers/Cis/Compat.php new file mode 100644 index 000000000..5c99d8bfc --- /dev/null +++ b/application/controllers/Cis/Compat.php @@ -0,0 +1,44 @@ + ['basis/cis:r'], + 'legacy' => ['basis/cis:r'], + + ]); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + + /** + * @access public + * @return void + */ + public function ci() + { + $this->load->view('CisRouterView/CisRouterView.php',['route' => 'Compat']); + } + + /** + * @access public + * @return void + */ + public function legacy() + { + $this->load->view('CisRouterView/CisRouterView.php',['route' => 'Compat']); + } + +} diff --git a/application/controllers/Cis/MyReservation.php b/application/controllers/Cis/MyReservation.php new file mode 100644 index 000000000..e4045cf90 --- /dev/null +++ b/application/controllers/Cis/MyReservation.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' => 'MyReservation']); + } +} diff --git a/application/controllers/Cis/ProjektabgabeUebersicht.php b/application/controllers/Cis/ProjektabgabeUebersicht.php new file mode 100644 index 000000000..e7ce692f0 --- /dev/null +++ b/application/controllers/Cis/ProjektabgabeUebersicht.php @@ -0,0 +1,30 @@ + ['basis/cis:r'] + ]); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + /** + * @return void + */ + public function index() + { + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'ProjektabgabeUebersicht']); + } +} diff --git a/application/controllers/Cis/Zeitsperren.php b/application/controllers/Cis/Zeitsperren.php new file mode 100644 index 000000000..caee13c48 --- /dev/null +++ b/application/controllers/Cis/Zeitsperren.php @@ -0,0 +1,30 @@ + ['cis/eigene_zeitsperren:r'], + ]); + + // Load Libraries + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + } + + /** + * index loads the view Zeitsperren + * @access public + * @return void + */ + public function index() + { + $viewData = array( + 'uid'=>getAuthUID(), + ); + + $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'zeitsperren']); + } +} diff --git a/application/controllers/Cis/Zeitsperrenma.php b/application/controllers/Cis/Zeitsperrenma.php new file mode 100644 index 000000000..851291622 --- /dev/null +++ b/application/controllers/Cis/Zeitsperrenma.php @@ -0,0 +1,31 @@ + ['cis/andere_zeitsperren:r'] + ]); + + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + /** + * @return void + */ + public function index() + { + $this->load->view('CisRouterView/CisRouterView.php', ['route' => 'Zeitsperrenma']); + } +} diff --git a/application/controllers/CisVue/Cms.php b/application/controllers/CisVue/Cms.php index aa07b919f..8f70e91c8 100644 --- a/application/controllers/CisVue/Cms.php +++ b/application/controllers/CisVue/Cms.php @@ -17,14 +17,15 @@ class Cms extends Auth_Controller array( 'content' => 'basis/cis:r', 'getNews' => 'basis/cis:r', - 'getNewsRowCount' => 'basis/cis:r', 'getRoomInformation' => 'basis/cis:r', - 'news' => 'basis/cis:r' + 'news' => 'basis/cis:r', + 'newsAdministration' => ['basis/news:r'], ) ); // Loads Libraries $this->load->library('CmsLib'); + $this->load->library('PermissionLib'); // Loads phrases system $this->loadPhrases([ @@ -83,10 +84,18 @@ class Cms extends Auth_Controller */ public function news($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true) { - $viewData = array(); - $this->load->view('CisRouterView/CisRouterView.php', ['viewData'=>$viewData, 'route' => 'News']); + $this->load->view('CisRouterView/CisRouterView.php', [ + 'route' => 'News', + ]); } + public function newsAdministration() + { + $this->load->view('CisRouterView/CisRouterView.php', [ + 'route' => 'NewsAdministration', + ]); + } + public function getRoomInformation($ort_kurzbz) { // Load Config diff --git a/application/controllers/api/frontend/v1/Abgabe.php b/application/controllers/api/frontend/v1/Abgabe.php index 759c32d58..62d29f029 100644 --- a/application/controllers/api/frontend/v1/Abgabe.php +++ b/application/controllers/api/frontend/v1/Abgabe.php @@ -1916,7 +1916,7 @@ class Abgabe extends FHCAPI_Controller if (!$mailres) { - $this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailBegutachterv2'), 'general'); + $this->terminateWithError($this->p->t('abgabetool', 'c4fehlerMailZweitBegutachterv2'), 'general'); } } diff --git a/application/controllers/api/frontend/v1/Benutzer.php b/application/controllers/api/frontend/v1/Benutzer.php new file mode 100644 index 000000000..f5dea1c7f --- /dev/null +++ b/application/controllers/api/frontend/v1/Benutzer.php @@ -0,0 +1,48 @@ +. + */ + +if (!defined('BASEPATH')) exit('No direct script access allowed'); + +class Benutzer extends FHCAPI_Controller +{ + + /** + * Object initialization + */ + public function __construct() + { + parent::__construct([ + 'getUserData' => self::PERM_LOGGED, + ]); + + $this->load->model('person/Benutzer_model', 'BenutzerModel'); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + public function getUserData() + { + $uid = $this->input->get("uid") ?? getAuthUID(); + $this->BenutzerModel->addSelect("uid, person_id, insertamum"); + $userResult = $this->BenutzerModel->load(["uid" => $uid]); + $userData = $this->getDataOrTerminateWithError($userResult); + $this->terminateWithSuccess($userData); + } +} + diff --git a/application/controllers/api/frontend/v1/CisMenu.php b/application/controllers/api/frontend/v1/CisMenu.php index 163c337ae..debab48a4 100644 --- a/application/controllers/api/frontend/v1/CisMenu.php +++ b/application/controllers/api/frontend/v1/CisMenu.php @@ -67,8 +67,11 @@ class CisMenu extends FHCAPI_Controller private function generateUrlForMenuItem($menuItem) { - $menuItem->url = $this->menuItemUrlHelper($menuItem); - unset($menuItem->content); + if(!isset($menuItem->url)) + { + $menuItem->url = $this->menuItemUrlHelper($menuItem); + unset($menuItem->content); + } if ($menuItem->childs && count($menuItem->childs)) { $menuItem->childs = $this->generateUrlsForMenuItems($menuItem->childs); @@ -83,7 +86,7 @@ class CisMenu extends FHCAPI_Controller return site_url("/CisVue/Cms/content/" . $menuItem->content_id); } - if (!$menuItem->content || !mb_strlen($menuItem->content)) { + if (!isset($menuItem->content) || !$menuItem->content || !mb_strlen($menuItem->content)) { return ''; } @@ -91,6 +94,8 @@ class CisMenu extends FHCAPI_Controller $doc->loadXML($menuItem->content); $urlElem = $doc->getElementsByTagName('url')->item(0); + $this->menuItemUrlTargetHelper($menuItem, $doc); + if (!$urlElem) { return ''; } @@ -122,5 +127,18 @@ class CisMenu extends FHCAPI_Controller return $url; } + private function menuItemUrlTargetHelper($menuItem, $xmldoc) + { + if($menuItem->template_kurzbz === 'redirect') + { + $targetElem = $xmldoc->getElementsByTagName('target')->item(0); + $target = ($targetElem) ? $targetElem->textContent : ''; + if(!in_array($target, array('_self', '_top', '_blank'))) + { + $target = '_self'; + } + $menuItem->target = $target; + } + } } diff --git a/application/controllers/api/frontend/v1/Cms.php b/application/controllers/api/frontend/v1/Cms.php index b8937a1f4..06b88c454 100644 --- a/application/controllers/api/frontend/v1/Cms.php +++ b/application/controllers/api/frontend/v1/Cms.php @@ -36,14 +36,15 @@ class Cms extends FHCAPI_Controller 'ContentID' => self::PERM_LOGGED, 'getOrtKurzbzContent' => self::PERM_LOGGED, 'content' => self::PERM_LOGGED, - 'news' => self::PERM_LOGGED, - 'getNewsRowCount' => self::PERM_LOGGED, + 'newsForWidget' => self::PERM_LOGGED, 'getNews' => self::PERM_LOGGED, ]); $this->load->model('content/News_model', 'NewsModel'); - + $this->load->model('crm/Student_model', 'StudentModel'); + $this->load->model('crm/Prestudent_model', 'PrestudentModel'); + // setting up the papgination_size $this->page_size = 10; @@ -59,7 +60,6 @@ class Cms extends FHCAPI_Controller //------------------------------------------------------------------------------------------------------------------ // Private methods - //------------------------------------------------------------------------------------------------------------------ // Public methods @@ -106,7 +106,7 @@ class Cms extends FHCAPI_Controller $this->terminateWithSuccess($content_id); } - public function news() + public function newsForWidget() { // form validation @@ -118,7 +118,7 @@ class Cms extends FHCAPI_Controller $this->load->model('content/news_model', 'NewsModel'); $limit = $this->input->get('limit',TRUE); - + //query the news $news = $this->NewsModel->getAll($limit); @@ -157,23 +157,7 @@ class Cms extends FHCAPI_Controller } - public function getNewsRowCount($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $fachbereich_kurzbz = null, $maxalter = 0, $edit = false, $sichtbar = true, $page = 1, $page_size = 10) - { - list($studiengang_kz, $semester) = $this->cmslib->getStgAndSem($studiengang_kz, $semester); - $all = $edit; - - $this->load->model('content/News_model','NewsModel'); - - $num_rows = $this->NewsModel->countNewsWithContent(getSprache(), $studiengang_kz, $semester, $fachbereich_kurzbz, $sichtbar, $maxalter, $page, $page_size, $all, $mischen); - - $num_rows = $this->getDataOrTerminateWithError($num_rows); - - $this->terminateWithSuccess($num_rows); - - } - - - public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = true, $titel = '', $edit = false, $sichtbar = true) + public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $mischen = false, $titel = '', $edit = false, $sichtbar = true) { //form validation $this->load->library('form_validation'); @@ -193,15 +177,45 @@ class Cms extends FHCAPI_Controller // default value for the page_size is 10 $page_size = $page_size ?? 10; + + $passedSichtbar = $this->input->get('published', true); + if($passedSichtbar !== null) + { + $sichtbar = $passedSichtbar; + } - $news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, $mischen, $titel, $edit, $sichtbar, $page, $page_size, $sprache); + $filterForDegreePrograms = false; + $entitledDegreePrograms = []; + $student = $this->StudentModel->loadWhere(['student_uid' => getAuthUID()]); + if(isError($student)) + { + $this->terminateWithError(getError($student)); + } + if(hasData($student)) + { + $student = current(getData($student)); + + $degreeProgramSemesterDataResult = $this->PrestudentModel->getStudiengaengAndAusbildungssemesterByStudentUid(getAuthUID()); + $degreeProgramSemesterData = $this->getDataOrTerminateWithError($degreeProgramSemesterDataResult)[0]; + if ($degreeProgramSemesterData) { + $studiengang_kz = isset($degreeProgramSemesterData->studiengang_kz) ? $degreeProgramSemesterData->studiengang_kz : null; + $semester = isset($degreeProgramSemesterData->ausbildungssemester) ? $degreeProgramSemesterData->ausbildungssemester : null; + } + + $mischen = true; + } else { + $filterForDegreePrograms = true; + $entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news"); + array_push($entitledDegreePrograms, 0); + } + + $news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, $mischen, $titel, $edit, $sichtbar, $page, $page_size, $sprache, $filterForDegreePrograms, $entitledDegreePrograms, true); $news = $this->getDataOrTerminateWithError($news); - $this->addMeta('phrases', json_decode($this->p->getJson())); - $this->terminateWithSuccess($news); + $this->addMeta('row_count', $news["row_count"] ?? 0); + $this->terminateWithSuccess($news["content"]); } } - diff --git a/application/controllers/api/frontend/v1/Lehre.php b/application/controllers/api/frontend/v1/Lehre.php index c2ebeb10b..fae2f2f3b 100644 --- a/application/controllers/api/frontend/v1/Lehre.php +++ b/application/controllers/api/frontend/v1/Lehre.php @@ -18,18 +18,9 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); -//require_once('../../../include/studiengang.class.php'); -//require_once('../../../include/student.class.php'); -//require_once('../../../include/datum.class.php'); -//require_once('../../../include/mail.class.php'); -//require_once('../../../include/benutzerberechtigung.class.php'); -//require_once('../../../include/phrasen.class.php'); -//require_once('../../../include/projektarbeit.class.php'); -//require_once('../../../include/projektbetreuer.class.php'); - class Lehre extends FHCAPI_Controller { - + /** * Object initialization */ @@ -40,38 +31,56 @@ class Lehre extends FHCAPI_Controller 'LV' => self::PERM_LOGGED, 'Pruefungen' => self::PERM_LOGGED, 'semesterAverageGrade' => self::PERM_LOGGED, + 'getZugewieseneLv' => self::PERM_LOGGED, + 'getLeForLv' => self::PERM_LOGGED ]); - + + $this->load->library('PhrasesLib'); + + $this->loadPhrases( + array( + 'global', + 'ui', + 'abgabetool' + ) + ); + + $this->load->helper('hlp_sancho_helper'); + + require_once(FHCPATH . 'include/studiengang.class.php'); + require_once(FHCPATH . 'include/student.class.php'); + require_once(FHCPATH . 'include/projektarbeit.class.php'); + require_once(FHCPATH . 'include/projektbetreuer.class.php'); } //------------------------------------------------------------------------------------------------------------------ // Public methods - /** + /** * constructs the emails of the groups from a lehrveranstaltung */ - public function lvStudentenMail() + public function lvStudentenMail() { - $lehreinheit_id = $this->input->get("lehreinheit_id",TRUE); - - // return early if the required parameter is missing - if(!isset($lehreinheit_id)) - { - $this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL); - } + $lehreinheit_id = $this->input->get("lehreinheit_id",TRUE); - $this->load->model('education/Lehreinheit_model', 'LehreinheitModel'); - - $studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id); + // return early if the required parameter is missing + if(!isset($lehreinheit_id)) + { + $this->terminateWithError('Missing required parameter', self::ERROR_TYPE_GENERAL); + } - $studentenMails = $this->getDataOrTerminateWithError($studentenMails); + $this->load->model('education/Lehreinheit_model', 'LehreinheitModel'); + + $studentenMails = $this->LehreinheitModel->getStudentenMail($lehreinheit_id); + + $studentenMails = $this->getDataOrTerminateWithError($studentenMails); //convert array of objects into array of strings $studentenMails = array_map(function($element){ return $element->mail; }, $studentenMails); - $this->terminateWithSuccess($studentenMails); + $this->terminateWithSuccess($studentenMails); } public function LV($studiensemester_kurzbz, $lehrveranstaltung_id) @@ -81,13 +90,13 @@ class Lehre extends FHCAPI_Controller $result = $this->LehrveranstaltungModel->getLvsByStudentWithGrades(getAuthUID(), $studiensemester_kurzbz, getUserLanguage(), $lehrveranstaltung_id); $result = current($this->getDataOrTerminateWithError($result)); - + $this->terminateWithSuccess($result); } /** * fetches all Pruefungen of a student for a specific lehrveranstaltung - * if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database + * if the student passed the Pruefung on the first attempt, no information about the Pruefungen is stored in the database * @param mixed $lehrveranstaltung_id * @return void */ @@ -145,5 +154,46 @@ class Lehre extends FHCAPI_Controller $this->terminateWithSuccess(['average_grade' => $averageGrade, 'weighted_average_grade' => $weightedAverageGrade]); } -} + /** + * fetches all assigned lehrveranstaltungen of a mitarbeiter for a given semester + * @param mixed $uid + * @param mixed $sem_kurzbz + * @return void + */ + public function getZugewieseneLv() { + $uid = $this->input->get("uid",TRUE); + $sem_kurzbz = $this->input->get("sem_kurzbz",TRUE); + + // TODO: error messages + + if(!isset($sem_kurzbz) || isEmptyString($sem_kurzbz)) + $this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general'); + + if (!isset($uid) || isEmptyString($uid)) + $uid = getAuthUID(); + + // querying other ma_uids data requires admin permission + if($uid !== getAuthUID()) { + $this->load->library('PermissionLib'); + $isAdmin = $this->permissionlib->isBerechtigt('admin'); + if(!$isAdmin) $this->terminateWithError($this->p->t('ui', 'keineBerechtigung'), 'general'); + } + + $this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + $result = $this->LehrveranstaltungModel->getLvForLektorInSemester($sem_kurzbz, $uid); + $data = $this->getDataOrTerminateWithError($result); + $this->terminateWithSuccess($data); + } + + public function getLeForLv() { + $lv_id = $this->input->get("lv_id",TRUE); + $sem_kurzbz = $this->input->get("sem_kurzbz",TRUE); + + $this->load->model('education/Lehreinheit_model', 'LehreinheitModel'); + +// $this->terminateWithSuccess($this->LehreinheitModel->getLesForLv($lv_id, $sem_kurzbz)); + $this->terminateWithSuccess($this->LehreinheitModel->getAllLehreinheitenForLvaAndMaUid($lv_id, getAuthUID(), $sem_kurzbz)); + } + +} diff --git a/application/controllers/api/frontend/v1/Lehrveranstaltung.php b/application/controllers/api/frontend/v1/Lehrveranstaltung.php index 66bda9ebf..e67a8a53f 100644 --- a/application/controllers/api/frontend/v1/Lehrveranstaltung.php +++ b/application/controllers/api/frontend/v1/Lehrveranstaltung.php @@ -28,7 +28,10 @@ class Lehrveranstaltung extends FHCAPI_Controller { parent::__construct([ 'getByEmp' => ['admin:r', 'assistenz:r'], + 'getByLe' => ['admin:r', 'assistenz:r'], + 'getByLv' => ['admin:r', 'assistenz:r'], 'getByStg' => ['admin:r', 'assistenz:r'], + 'getByStgOrgform' => ['admin:r', 'assistenz:r'], 'loadByLV' => ['admin:r', 'assistenz:r'], ]); @@ -77,41 +80,117 @@ class Lehrveranstaltung extends FHCAPI_Controller $this->terminateWithSuccess($tree); } - public function getByStg($studiensemester_kurzbz = null, $stg_kz = null, $semester = null) - { - $studiengang_kz = $this->getStudiengangKz($stg_kz); - if (is_null($studiengang_kz) || !preg_match("/^-?[1-9][0-9]*$/", (string)$studiengang_kz)) + public function getByLe($studiensemester_kurzbz = null, $lehreinheit_id = null) + { + if (is_null($lehreinheit_id)) $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); - $verband = null; - if (!is_null($semester) && !is_numeric($semester)) - { - $verband = $this->getOrgformKurzbz($semester); - $semester = null; - } + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); - $this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel'); + + $lehreinheiten = $this->_ci->LehreinheitModel->getByLeStudiensemester($lehreinheit_id, $studiensemester_kurzbz); + $lehreinheiten_data = $this->getDataOrTerminateWithError($lehreinheiten); + + $this->terminateWithSuccess($lehreinheiten_data); + } + + public function getByLv($studiensemester_kurzbz = null, $lehrveranstaltung_id = null) + { + if (is_null($lehrveranstaltung_id)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); - $studienplan_data = $this->_ci->StudienplanModel->getStudienplaeneBySemester($studiengang_kz, $studiensemester_kurzbz, $semester, $verband); - $studienplan_ids = array(); - $only_ids = array(); - $placeholders = array(); + $tree = []; - if (hasData($studienplan_data)) + $this->_ci->LehrveranstaltungModel->addSelect('studiengang_kz'); + $lv = $this->_ci->LehrveranstaltungModel->load($lehrveranstaltung_id); + $lv = $this->getDataOrTerminateWithError($lv); + + if (empty($lv)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $lehrveranstaltungen_data = array(); + foreach ($this->_loadLvRows(current($lv)->studiengang_kz, $studiensemester_kurzbz) as $row) { - foreach (getData($studienplan_data) as $studienplan) { - $placeholders[] = "(?, ?)"; - $studienplan_ids[] = $studienplan->studienplan_id; - $studienplan_ids[] = $studienplan->semester; - $only_ids[] = $studienplan->studienplan_id; + if ((string)$row->lehrveranstaltung_id === (string)$lehrveranstaltung_id) + { + $lehrveranstaltungen_data[] = $row; + break; } } - $lehrveranstaltungen_data = $this->_ci->LehrveranstaltungModel->getLvsByStudiengang($studienplan_ids, $placeholders, $only_ids, $studiengang_kz, $studiensemester_kurzbz, $semester, $verband); - $lehrveranstaltungen_data = hasData($lehrveranstaltungen_data) ? getData($lehrveranstaltungen_data) : array(); + if (empty($lehrveranstaltungen_data)) + { + + $this->_ci->LehrveranstaltungModel->addSelect(' + tbl_lehrveranstaltung.*, + tbl_lehrveranstaltung.kurzbz as lv_kurzbz, + lehrveranstaltung_id, + lehrtyp_kurzbz, + studiengang_kz as lv_studiengang_kz, + UPPER(CONCAT(tbl_studiengang.typ, tbl_studiengang.kurzbz)) as studiengang, + semester as semester, + tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung, + ects as lv_ects, + lehreverzeichnis as lv_lehreverzeichnis, + planfaktor as lv_planfaktor, + planlektoren as lv_planlektoren, + planpersonalkosten as lv_planpersonalkosten, + plankostenprolektor as lv_plankostenprolektor, + tbl_lehrveranstaltung.orgform_kurzbz as orgform_kurzbz, + tbl_lehrveranstaltung.sprache as sprache, + lehrform_kurzbz as lv_lehrform_kurzbz, + tbl_lehrveranstaltung.bezeichnung_english as lv_bezeichnung_english, + semesterstunden as lv_semesterstunden, + '); + $this->_ci->LehrveranstaltungModel->addJoin('public.tbl_studiengang', 'studiengang_kz'); + $lehrveranstaltungen = $this->_ci->LehrveranstaltungModel->load($lehrveranstaltung_id); + $lehrveranstaltungen_data = $this->getDataOrTerminateWithError($lehrveranstaltungen); + } + + foreach ($lehrveranstaltungen_data as $lehrveranstaltung) + { + $lehreinheiten = $this->_ci->LehreinheitModel->getByLvidStudiensemester($lehrveranstaltung->lehrveranstaltung_id, $studiensemester_kurzbz); + $lehreinheiten_data = $this->getDataOrTerminateWithError($lehreinheiten); + + if (!isset($lehrveranstaltung->_children)) + { + $lehrveranstaltung->_children = $lehreinheiten_data; + } + $tree[] = $lehrveranstaltung; + } + + $counter = 0; + $this->assignUniqueIndex($tree, $counter); + $this->terminateWithSuccess($tree); + } + + public function getByStg($studiensemester_kurzbz = null, $stg_kz = null, $semester = null) + { + $this->_getLvTree($studiensemester_kurzbz, $stg_kz, $semester, null); + } + + public function getByStgOrgform($studiensemester_kurzbz = null, $stg_kz = null, $orgform = null, $semester = null) + { + $orgform_kurzbz = $this->getOrgformKurzbz($orgform); + $this->_getLvTree($studiensemester_kurzbz, $stg_kz, $semester, $orgform_kurzbz); + } + + private function _getLvTree($studiensemester_kurzbz, $stg_kz, $semester, $orgform_kurzbz) + { + $studiengang_kz = $this->getStudiengangKz($stg_kz); + + if (is_null($studiengang_kz) || !preg_match("/^(-?[1-9][0-9]*|0)$/", (string)$studiengang_kz)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + if (!is_null($semester) && !ctype_digit((string)$semester)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); + + $lehrveranstaltungen_data = $this->_loadLvRows($studiengang_kz, $studiensemester_kurzbz, $semester, $orgform_kurzbz); $tree = []; foreach ($lehrveranstaltungen_data as $row) @@ -244,6 +323,31 @@ class Lehrveranstaltung extends FHCAPI_Controller $this->LehreinheitModel->getLvsByFachbereich($fachbereich, $studiensemester_kurzbz, $mitarbeiter_uid); }*/ + + private function _loadLvRows($studiengang_kz, $studiensemester_kurzbz, $semester = null, $orgform_kurzbz = null) + { + $this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel'); + + $studienplan_data = $this->_ci->StudienplanModel->getStudienplaeneBySemester($studiengang_kz, $studiensemester_kurzbz, $semester, $orgform_kurzbz); + + $studienplan_ids = array(); + $only_ids = array(); + $placeholders = array(); + + if (hasData($studienplan_data)) + { + foreach (getData($studienplan_data) as $studienplan) { + $placeholders[] = "(?, ?)"; + $studienplan_ids[] = $studienplan->studienplan_id; + $studienplan_ids[] = $studienplan->semester; + $only_ids[] = $studienplan->studienplan_id; + } + } + + $lehrveranstaltungen_data = $this->_ci->LehrveranstaltungModel->getLvsByStudiengang($studienplan_ids, $placeholders, $only_ids, $studiengang_kz, $studiensemester_kurzbz, $semester, $orgform_kurzbz); + return hasData($lehrveranstaltungen_data) ? getData($lehrveranstaltungen_data) : array(); + } + private function _setAuthUID() { $this->_uid = getAuthUID(); @@ -309,8 +413,8 @@ class Lehrveranstaltung extends FHCAPI_Controller $data = $this->getDataOrTerminateWithError($result); if (!$data) - return $orgform_kurzbz; + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); return current($data)->orgform_kurzbz; } -} +} \ No newline at end of file diff --git a/application/controllers/api/frontend/v1/LvMenu.php b/application/controllers/api/frontend/v1/LvMenu.php index 45936d9f5..311edb1d9 100644 --- a/application/controllers/api/frontend/v1/LvMenu.php +++ b/application/controllers/api/frontend/v1/LvMenu.php @@ -36,7 +36,8 @@ class LvMenu extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'getLvMenu' => self::PERM_LOGGED + 'getLvMenu' => self::PERM_LOGGED, + 'getMultipleLvMenu' => self::PERM_LOGGED ]); $this->load->model("ressource/Mitarbeiter_model"); @@ -61,24 +62,23 @@ class LvMenu extends FHCAPI_Controller /** * alternative function to get multiple lvMenus with a single http request + * not yet working as intended as the menu_lv.inc.php scripts called by the + * lvMenuBuild event have logic coupled to require_once import which results in + * a wrong logic after the first invocation -> faulty results for lvinfo, moodle + * and several others */ - public function getMultipleLvMenu($lvMenuOptionList){ + public function getMultipleLvMenu(){ + $lvMenuOptionList = $this->input->post('lvMenuOptionList', true); $result =[]; foreach($lvMenuOptionList as $lvMenuOptions){ - $lvMenu = $this->getLvMenu($lvMenuOptions['lvid'],$lvMenuOptions['studiensemester_kurzbz']); - if(isError($lvMenu)){ - // TODO: some lvMenu threw an error, handle error here - } + $lvMenu = $this->getLvMenuInternal($lvMenuOptions['lvid'],$lvMenuOptions['studiensemester_kurzbz']); + $result[$lvMenuOptions['lvid']]=$lvMenu; } $this->terminateWithSuccess($result); } - - /** - * - */ - public function getLvMenu($lvid, $studiensemester_kurzbz) - { + + private function getLvMenuInternal($lvid, $studiensemester_kurzbz) { // return early if parameters are missing if(!isset($lvid) || !isset($studiensemester_kurzbz)) @@ -89,14 +89,14 @@ class LvMenu extends FHCAPI_Controller // get the user if (!$user=getAuthUID()) - $this->terminateWithError($this->p->t('global', 'nichtAngemeldet')); + $this->terminateWithError($this->p->t('global', 'nichtAngemeldet')); // check if is_lector $is_lector = false; $mares = $this->Mitarbeiter_model->isMitarbeiter($user); if(hasData($mares)) { - $is_lector = getData($mares); + $is_lector = getData($mares); } // definition of user_is_allowed_to_upload @@ -105,7 +105,7 @@ class LvMenu extends FHCAPI_Controller // load lehrveranstaltung $lvres = $this->Lehrveranstaltung_model->load($lvid); - if(!hasData($lvres)) + if(!hasData($lvres)) { $this->terminateWithError('LV ' . $lvid . ' not found.'); } @@ -124,7 +124,7 @@ class LvMenu extends FHCAPI_Controller $stgres = $this->Studiengang_model->load(strval($studiengang_kz)); if(!hasData($stgres)) { - $this->terminateWithError('Stg ' . $lv->studiengang_kz . ' not found.'); + $this->terminateWithError('Stg ' . $lv->studiengang_kz . ' not found.'); } $stg = (getData($stgres))[0]; $kurzbz = strtoupper($stg->typ . $stg->kurzbz); @@ -139,7 +139,7 @@ class LvMenu extends FHCAPI_Controller $angemeldet = false; $lesres = $this->Lehreinheit_model->getLehreinheitenForStudentAndStudienSemester( - $lvid, $user, $angezeigtes_stsem + $lvid, $user, $angezeigtes_stsem ); if(hasData($lesres) && count(getData($lesres)) > 0) @@ -148,7 +148,7 @@ class LvMenu extends FHCAPI_Controller // lehrfach $lehrfach_id=''; - + if(defined('CIS_LEHRVERANSTALTUNG_LEHRFACH_ANZEIGEN') && CIS_LEHRVERANSTALTUNG_LEHRFACH_ANZEIGEN) { // Wenn der eingeloggte User zu einer der Lehreinheiten zugeteilt ist @@ -211,8 +211,8 @@ class LvMenu extends FHCAPI_Controller foreach($fbs as $row) { $lehrfach_oe_kurzbz_arr[] = $row->oe_kurzbz; - if($this->PermissionLib->isBerechtigt('lehre', null, $row->oe_kurzbz) - || $this->PermissionLib->isBerechtigt('assistenz', null, $stg->oe_kurzbz)) + if($this->PermissionLib->isBerechtigt('lehre', null, $row->oe_kurzbz) + || $this->PermissionLib->isBerechtigt('assistenz', null, $stg->oe_kurzbz)) { $user_is_allowed_to_upload=true; } @@ -224,21 +224,21 @@ class LvMenu extends FHCAPI_Controller $menu = array(); $this->fhc_menu_lvinfo($menu, $lvid, $studiengang_kz, $lektor_der_lv, $is_lector, $lehrfach_oe_kurzbz_arr); - + $this->fhc_menu_feedback($menu, $angemeldet, $lvid); - + $this->fhc_menu_gesamtnote($menu, $angemeldet, $lvid, $lv, $is_lector, $angezeigtes_stsem); - + $this->fhc_menu_emailStudierende($menu, $user, $angemeldet, $lvid, $angezeigtes_stsem); - + $this->fhc_menu_abmeldung($menu, $user, $is_lector, $lvid, $angezeigtes_stsem); - + $this->fhc_menu_lehretools($menu, $lvid, $angezeigtes_stsem, $sprache); - + $this->fhc_menu_anrechnungStudent($menu, $lvid, $angezeigtes_stsem); - + $this->fhc_menu_anrechnungLector($menu, $angezeigtes_stsem); - + // Addons Menu Logic // ########################################################################################## @@ -272,18 +272,18 @@ class LvMenu extends FHCAPI_Controller 'permissionLib' => &$this->PermissionLib, 'phrasesLib' => &$this->PhrasesLib ]; - - Events::trigger('lvMenuBuild', - // passing $menu per reference - function & () use (&$menu) { - return $menu; - }, - $params + + Events::trigger('lvMenuBuild', + // passing $menu per reference + function & () use (&$menu) { + return $menu; + }, + $params ); // Menu sortieren // ########################################################################################## - + foreach ($menu as $key => $row){ // removes menu points that are not needed in the c4 lvUebersicht @@ -291,7 +291,7 @@ class LvMenu extends FHCAPI_Controller unset($menu[$key]); continue; } - + // fills pos array to sort the menu $pos[$key] = $row['position']; @@ -299,11 +299,18 @@ class LvMenu extends FHCAPI_Controller array_multisort($pos, SORT_ASC, SORT_NUMERIC, $menu); - // HTTP response - // ########################################################################################## + + return $menu; + } + + /** + * + */ + public function getLvMenu($lvid, $studiensemester_kurzbz) + { + $menu = $this->getLvMenuInternal($lvid, $studiensemester_kurzbz); $this->terminateWithSuccess($menu); - } private function fhc_menu_lvinfo(&$menu, $lvid, $studiengang_kz, $lektor_der_lv, $is_lector, $lehrfach_oe_kurzbz_arr){ @@ -372,7 +379,9 @@ class LvMenu extends FHCAPI_Controller 'name'=>$this->p->t('lehre', 'gesamtnote'), 'phrase' => 'lehre/gesamtnote', 'c4_icon'=> base_url('skin/images/button_endnote.png'), - 'c4_link'=> base_url('cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem)) + 'c4_link'=> base_url('cis.php/Cis/Compat/legacy/cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem)) + //'c4_link'=> site_url('Cis/Benotungstool/'.urlencode($lvid).'/'.urlencode($angezeigtes_stsem)) + //'c4_link'=> base_url('cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem)) //'c4_link'=> base_url('benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem)) ); } @@ -541,4 +550,4 @@ class LvMenu extends FHCAPI_Controller ); } } -} \ No newline at end of file +} diff --git a/application/controllers/api/frontend/v1/LvPlan.php b/application/controllers/api/frontend/v1/LvPlan.php index 36054003c..f9d2cc3cf 100644 --- a/application/controllers/api/frontend/v1/LvPlan.php +++ b/application/controllers/api/frontend/v1/LvPlan.php @@ -114,6 +114,7 @@ class LvPlan extends FHCAPI_Controller // fetching lvplan events $result = $this->stundenplanlib->getEventsUser($start_date, $end_date, $uid); + $lvplanEvents = $this->getDataOrTerminateWithError($result); // fetching moodle events diff --git a/application/controllers/api/frontend/v1/MaZeitsperren.php b/application/controllers/api/frontend/v1/MaZeitsperren.php new file mode 100644 index 000000000..0045b15e8 --- /dev/null +++ b/application/controllers/api/frontend/v1/MaZeitsperren.php @@ -0,0 +1,167 @@ + ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'], + 'getAllZeitsperrenFixeMa' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'], + 'getAllZeitsperrenLector' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'], + 'getAllZeitsperrenOes' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'], + 'getZeitsperrenAss' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'], + 'getStgLectors' => self::PERM_LOGGED, + 'loadZeitsperrenLectorStg' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'], + 'loadZeitsperrenMa' => ['cis/andere_zeitsperren:r', 'admin:r', 'assistenz:r'], + 'getDetailsMa' => self::PERM_LOGGED + ]); + + // Load Libraries + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + $this->load->library('form_validation'); + + // Load language phrases + $this->loadPhrases([ + 'ui', + 'person', + 'zeitsperren' + ]); + + // Load models + $this->load->model('ressource/Zeitsperre_model', 'ZeitsperreModel'); + } + + //only user with zeitsperren + public function getAllActiveZeitsperren($days) + { + $result = $this->ZeitsperreModel->getZeitsperrenForNextDays($days); + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + //TODO: check query: more results after join with hr-table + //all fixe Ma + public function getAllZeitsperrenFixeMa($days) + { + $von = date('Y-m-d'); + $bis = date('Y-m-d', strtotime("+{$days} days")); + $result = $this->ZeitsperreModel->getMitarbeiterWithZeitsperren($von, $bis, true, false, null, false); + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function getAllZeitsperrenLector($days) + { + $von = date('Y-m-d'); + $bis = date('Y-m-d', strtotime("+{$days} days")); + $result = $this->ZeitsperreModel->getMitarbeiterWithZeitsperren($von, $bis, false, true, null, false); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function getZeitsperrenAss($days) + { + $von = date('Y-m-d'); + $bis = date('Y-m-d', strtotime("+{$days} days")); + $result = $this->ZeitsperreModel->getMitarbeiterWithZeitsperren($von, $bis, false, false, null, true); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function getAllZeitsperrenOes($days, $oe) + { + if(!$oe) + { + return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'OE Kurzbezeichnung']), self::ERROR_TYPE_GENERAL); + } + $von = date('Y-m-d'); + $bis = date('Y-m-d', strtotime("+{$days} days")); + $result = $this->ZeitsperreModel->getMitarbeiterWithZeitsperren($von, $bis, false, false, $oe, false); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function loadZeitsperrenLectorStg($days, $stg) + { + if(!$stg) + { + return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Studiengangskennzahl']), self::ERROR_TYPE_GENERAL); + } + if(!is_numeric($stg)) + { + return $this->terminateWithError($this->p->t('ui', 'error_paramNoNumber', ['param'=> 'Studiengangskennzahl']), self::ERROR_TYPE_GENERAL); + } + + $von = date('Y-m-d'); + $bis = date('Y-m-d', strtotime("+{$days} days")); + $result = $this->ZeitsperreModel->getMitarbeiterWithZeitsperren($von, $bis, false, true, false, false, $stg); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function getStgLectors() + { + $sql = " + select + studiengang_kz, + kurzbz, + kurzbzlang, + typ, + bezeichnung, + kurzbzlang || ' (' || bezeichnung || ')' AS label, + aktiv + from + public.tbl_studiengang + where + typ in ('b','m') + order by typ asc, kurzbz Asc + "; + + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); + $result = $this->StudiengangModel->execReadOnlyQuery($sql); + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function loadZeitsperrenMa($days, $uid) + { + if ($uid === null || $uid === '') + { + return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Mitarbeiter UID']), self::ERROR_TYPE_GENERAL); + } + $result = $this->ZeitsperreModel->getZeitsperrenForNextDays($days, $uid); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function getDetailsMa($uid) + { + if ($uid === null || $uid === '') + { + $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Mitarbeiter UID']), self::ERROR_TYPE_GENERAL); + } + $this->load->model('person/Person_model', 'PersonModel'); + $result = $this->PersonModel->getFullName($uid); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + +} + diff --git a/application/controllers/api/frontend/v1/NewsAdministrationAPI.php b/application/controllers/api/frontend/v1/NewsAdministrationAPI.php new file mode 100644 index 000000000..e15e403cd --- /dev/null +++ b/application/controllers/api/frontend/v1/NewsAdministrationAPI.php @@ -0,0 +1,948 @@ +. + */ + +if (!defined('BASEPATH')) exit('No direct script access allowed'); + +class NewsAdministrationAPI extends FHCAPI_Controller +{ + /** + * News API constructor. + */ + public function __construct() + { + parent::__construct([ + 'getNews' => 'basis/news:r', + 'getNewsItem' => 'basis/news:r', + 'storeNewsItem' => 'basis/news:w', + 'updateNewsItem' => 'basis/news:w', + 'deleteNewsItem' => 'basis/news:w' + ]); + + $this->load->model('content/Content_model', 'ContentModel'); + $this->load->model('content/Contentsprache_model', 'ContentspracheModel'); + $this->load->model('content/News_model', 'NewsModel'); + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); + $this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel'); + $this->load->model('system/Sprache_model', 'SpracheModel'); + $this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel'); + + $this->loadPhrases([ + 'global' + ]); + + $this->load->library('CmsLib'); + + $this->load->helper('hlp_sancho_helper'); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + public function getNews($infoscreen = false, $studiengang_kz = null, $semester = null, $titel = '', $active = true, $sichtbar = true) + { + $this->load->library('form_validation'); + $this->form_validation->set_data($_GET); + $this->form_validation->set_rules('page','Page','required|is_natural'); + $this->form_validation->set_rules('page_size', 'PageSize', 'is_natural'); + if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $page = intval($this->input->get('page', true)); + $page_size = intval($this->input->get('page_size', true)); + $sprache = $this->input->get('sprache', true); + if(!$sprache) + { + $sprache = getUserLanguage(); + } + + // default value for the page_size is 10 + $page_size = $page_size ?? 10; + + $passedSichtbar = $this->input->get('published', true); + if($passedSichtbar !== null) + { + $sichtbar = $passedSichtbar; + } + + $passedIsActive = $this->input->get('isActive', true); + if ($passedIsActive !== null) + { + $isActive = filter_var( + $passedIsActive, + FILTER_VALIDATE_BOOLEAN, + FILTER_NULL_ON_FAILURE + ); + if ($isActive !== null) + { + $active = $isActive; + } + } + + $degreeProgramShortCode = $this->input->get('degreeProgramShortCode', true); + if ($degreeProgramShortCode !== null && $degreeProgramShortCode !== '') + { + if (!ctype_digit((string)$degreeProgramShortCode)) + { + $this->terminateWithValidationErrors([ + 'degreeProgramShortCode' => 'A valid degree program is required' + ]); + } + $studiengang_kz = $degreeProgramShortCode; + } + + $passedSemester = $this->input->get('semester', true); + if ($passedSemester !== null && $passedSemester !== '') + { + if ( + !ctype_digit((string)$passedSemester) + || (int)$passedSemester < 1 + || (int)$passedSemester > 8 + ) + { + $this->terminateWithValidationErrors([ + 'semester' => 'A valid semester is required' + ]); + } + $semester = filter_var($passedSemester, FILTER_VALIDATE_INT); + } + + $entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news"); + + $news = $this->cmslib->getNews($infoscreen, $studiengang_kz, $semester, false, $titel, true, $sichtbar, $page, $page_size, $sprache, true, $entitledDegreePrograms, $active); + $news = $this->getDataOrTerminateWithError($news); + + $this->addMeta('row_count', $news["row_count"] ?? 0); + $this->terminateWithSuccess($news["content"]); + } + + /** + * Returns one news entry with all available language versions for editing. + * + * @return void + */ + public function getNewsItem($newsId) + { + if (!is_numeric($newsId) || (int)$newsId < 1) + { + $this->terminateWithValidationErrors([ + 'newsId' => 'A valid news ID is required' + ]); + } + + $newsId = (int)$newsId; + $newsResult = $this->NewsModel->load($newsId); + + if (isError($newsResult)) + { + $this->terminateWithError(getError($newsResult), self::ERROR_TYPE_DB); + } + + if (!hasData($newsResult)) + { + $this->terminateWithValidationErrors([ + 'newsId' => 'The requested news entry does not exist' + ]); + } + + $news = current(getData($newsResult)); + $entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news"); + + if (!in_array($news->studiengang_kz, $entitledDegreePrograms)) { + $this->terminateWithError('You are not entitled to access this news entry', self::ERROR_TYPE_PERMISSION); + } + $translationsResult = $this->ContentspracheModel->loadWhere([ + 'content_id' => $news->content_id + ]); + + if (isError($translationsResult)) + { + $this->terminateWithError(getError($translationsResult), self::ERROR_TYPE_DB); + } + + $languages = []; + + foreach ((array)getData($translationsResult) as $translation) + { + $languages[$translation->sprache] = true; + } + + $translations = []; + + foreach (array_keys($languages) as $language) + { + $contentResult = $this->ContentModel->getContent( + $news->content_id, + $language, + null, + null, + false + ); + + if (isError($contentResult)) + { + $this->terminateWithError(getError($contentResult), self::ERROR_TYPE_DB); + } + + $content = getData($contentResult); + $document = new DOMDocument('1.0', 'UTF-8'); + + if ($content->content !== '' && $document->loadXML($content->content) === false) + { + $this->terminateWithError( + 'The stored news content is invalid', + self::ERROR_TYPE_GENERAL + ); + } + + $author = $document->getElementsByTagName('verfasser')->item(0); + $title = $document->getElementsByTagName('betreff')->item(0); + $text = $document->getElementsByTagName('text')->item(0); + + $translations[] = [ + 'language' => $language, + 'author' => $author ? $author->nodeValue : '', + 'title' => $title ? $title->nodeValue : '', + 'text' => $text ? str_replace('dms.php', APP_ROOT . 'cms/dms.php', $text->nodeValue) : '', + 'isPublished' => (bool)$content->sichtbar + ]; + } + + $this->terminateWithSuccess([ + 'newsId' => (int)$news->news_id, + 'visibleFrom' => $news->datum, + 'visibleTo' => $news->datum_bis, + 'degreeProgramShortCode' => $news->studiengang_kz, + 'semester' => $news->semester, + 'translations' => $translations + ]); + } + + /** + * Creates a news entry and all supplied translations. + * + * @return void + */ + public function storeNewsItem() + { + $this->load->library('form_validation'); + $postData = $this->input->post(null, true); + $this->form_validation->set_data(is_array($postData) ? $postData : []); + $this->form_validation->set_rules('visibleFrom', 'Visible from', 'required|is_valid_date'); + $this->form_validation->set_rules('visibleTo', 'Visible to', 'is_valid_date'); + $this->form_validation->set_rules( + 'degreeProgramShortCode', + 'Degree program', + 'required|is_natural' + ); + $this->form_validation->set_rules( + 'semester', + 'Semester', + 'is_natural_no_zero|less_than_equal_to[8]' + ); + + $errors = $this->form_validation->run() === false + ? $this->form_validation->error_array() + : []; + $visibleFrom = $this->input->post('visibleFrom', true); + $visibleTo = $this->input->post('visibleTo', true); + $translations = $this->input->post('translations', true); + + if ( + is_string($visibleFrom) + && is_string($visibleTo) + && $visibleTo !== '' + && is_valid_date($visibleFrom) + && is_valid_date($visibleTo) + && new DateTime($visibleTo) < new DateTime($visibleFrom) + ) + { + $errors['visibleTo'] = 'Visible to must not be before Visible from'; + } + + if (!is_array($translations) || count($translations) === 0) + { + $errors['translations'] = 'At least one translation is required'; + } + else + { + foreach ($translations as $index => $translation) + { + if (!is_array($translation)) + { + $errors['translations.' . $index] = 'Each translation must be an object'; + } + } + } + + if (count($errors) > 0) + { + $this->terminateWithValidationErrors($errors); + } + + + $this->_ensureUserIsEntitledForDegreeProgram($this->input->post('degreeProgramShortCode', true)); + + $degreeProgramShortCode = $this->input->post('degreeProgramShortCode', true); + $semester = $this->input->post('semester', true); + $semester = $semester === '' ? null : $semester; + $visibleTo = $visibleTo === '' ? null : $visibleTo; + + $studiengang = $this->_getStudiengang($degreeProgramShortCode); + + $data = [ + 'studiengang_kz' => $degreeProgramShortCode, + 'semester' => $semester, + 'datum' => $visibleFrom, + 'datum_bis' => $visibleTo, + 'translations' => [] + ]; + + foreach ($translations as $index => $translation) + { + $data['translations'][$index] = [ + 'sprache' => $translation['language'] ?? '', + 'verfasser' => $translation['author'] ?? '', + 'betreff' => $translation['title'] ?? '', + 'text' => $translation['text'] ?? '', + 'sichtbar' => isset($translation['isPublished']) ? (bool)$translation['isPublished'] : false + ]; + } + + $this->_validateLanguages($data['translations']); + + foreach ($data['translations'] as $index => $translation) + { + $data['translations'][$index]['content'] = $this->_buildNewsXml($translation); + } + + $uid = getAuthUID(); + $now = date('Y-m-d H:i:s'); + + if ($this->db->trans_begin() === false) + { + $this->terminateWithError($this->db->error(), self::ERROR_TYPE_DB); + } + + $contentResult = $this->ContentModel->insert([ + 'template_kurzbz' => 'news', + 'oe_kurzbz' => $studiengang->oe_kurzbz, + 'aktiv' => true, + 'menu_open' => false, + 'insertamum' => $now, + 'insertvon' => $uid + ]); + $contentId = (int)$this->_getInsertedIdOrRollback($contentResult); + + $newsResult = $this->NewsModel->insert([ + 'uid' => $uid, + 'studiengang_kz' => $data['studiengang_kz'], + 'semester' => $data['semester'], + 'datum' => $data['datum'], + 'datum_bis' => $data['datum_bis'], + 'content_id' => $contentId, + 'insertamum' => $now, + 'insertvon' => $uid, + 'updateamum' => $now, + 'updatevon' => $uid + ]); + $newsId = (int)$this->_getInsertedIdOrRollback($newsResult); + + foreach ($data['translations'] as $translation) + { + $translation['content'] = $this->formatContentURLsToRelative($translation['content']); + + $translationResult = $this->ContentspracheModel->insert([ + 'sprache' => $translation['sprache'], + 'content_id' => $contentId, + 'version' => 1, + 'sichtbar' => $translation['sichtbar'], + 'content' => $translation['content'], + 'titel' => $translation['betreff'], + 'insertamum' => $now, + 'insertvon' => $uid, + 'updateamum' => $now, + 'updatevon' => $uid + ]); + + $this->_getInsertedIdOrRollback($translationResult); + } + + $newsItemResult = $this->NewsModel->load($newsId); + $newsItem = $this->getDataOrTerminateWithError($newsItemResult); + $newsItem = $newsItem[0]; + + $this->_sendNewsItemTranslationRequiredMail( + $newsItem + ); + + if ($this->db->trans_status() === false) + { + $error = $this->db->error(); + $this->db->trans_rollback(); + $this->terminateWithError($error, self::ERROR_TYPE_DB); + } + + if ($this->db->trans_commit() === false) + { + $error = $this->db->error(); + $this->db->trans_rollback(); + $this->terminateWithError($error, self::ERROR_TYPE_DB); + } + + $this->terminateWithSuccess($newsId); + } + + /** + * Updates a news entry and its supplied translations. + * + * @return void + */ + public function updateNewsItem($newsId) + { + if (!is_numeric($newsId) || (int)$newsId < 1) + { + $this->terminateWithValidationErrors([ + 'newsId' => 'A valid news ID is required' + ]); + } + + $newsId = (int)$newsId; + $existingNewsResult = $this->NewsModel->load($newsId); + + if (isError($existingNewsResult)) + { + $this->terminateWithError(getError($existingNewsResult), self::ERROR_TYPE_DB); + } + + if (!hasData($existingNewsResult)) + { + $this->terminateWithValidationErrors([ + 'newsId' => 'The requested news entry does not exist' + ]); + } + + $existingNews = current(getData($existingNewsResult)); + + $this->_ensureUserIsEntitledForDegreeProgram($existingNews->studiengang_kz); + + $this->load->library('form_validation'); + $postData = $this->input->post(null, true); + $this->form_validation->set_data(is_array($postData) ? $postData : []); + $this->form_validation->set_rules('visibleFrom', 'Visible from', 'required|is_valid_date'); + $this->form_validation->set_rules('visibleTo', 'Visible to', 'is_valid_date'); + $this->form_validation->set_rules( + 'degreeProgramShortCode', + 'Degree program', + 'required|is_natural' + ); + $this->form_validation->set_rules( + 'semester', + 'Semester', + 'is_natural_no_zero|less_than_equal_to[8]' + ); + + $errors = $this->form_validation->run() === false + ? $this->form_validation->error_array() + : []; + $visibleFrom = $this->input->post('visibleFrom', true); + $visibleTo = $this->input->post('visibleTo', true); + $translations = $this->input->post('translations', true); + + if ( + is_string($visibleFrom) + && is_string($visibleTo) + && $visibleTo !== '' + && is_valid_date($visibleFrom) + && is_valid_date($visibleTo) + && new DateTime($visibleTo) < new DateTime($visibleFrom) + ) + { + $errors['visibleTo'] = 'Visible to must not be before Visible from'; + } + + if (!is_array($translations) || count($translations) === 0) + { + $errors['translations'] = 'At least one translation is required'; + } + else + { + foreach ($translations as $index => $translation) + { + if (!is_array($translation)) + { + $errors['translations.' . $index] = 'Each translation must be an object'; + } + } + } + + if (count($errors) > 0) + { + $this->terminateWithValidationErrors($errors); + } + + $degreeProgramShortCode = $this->input->post('degreeProgramShortCode', true); + $semester = $this->input->post('semester', true); + $semester = $semester === '' ? null : $semester; + $visibleTo = $visibleTo === '' ? null : $visibleTo; + + $studiengang = $this->_getStudiengang($degreeProgramShortCode); + + $data = [ + 'studiengang_kz' => $degreeProgramShortCode, + 'semester' => $semester, + 'datum' => $visibleFrom, + 'datum_bis' => $visibleTo, + 'translations' => [] + ]; + + foreach ($translations as $index => $translation) + { + $data['translations'][$index] = [ + 'sprache' => $translation['language'] ?? '', + 'verfasser' => $translation['author'] ?? '', + 'betreff' => $translation['title'] ?? '', + 'text' => $translation['text'] ?? '', + 'sichtbar' => isset($translation['isPublished']) ? (bool)$translation['isPublished'] : false + ]; + } + + $this->_validateLanguages($data['translations']); + + foreach ($data['translations'] as $index => $translation) + { + $data['translations'][$index]['content'] = $this->_buildNewsXml($translation); + } + + $uid = getAuthUID(); + $now = date('Y-m-d H:i:s'); + + if ($this->db->trans_begin() === false) + { + $this->terminateWithError($this->db->error(), self::ERROR_TYPE_DB); + } + + $contentResult = $this->ContentModel->update($existingNews->content_id, [ + 'oe_kurzbz' => $studiengang->oe_kurzbz, + 'updateamum' => $now, + 'updatevon' => $uid + ]); + + if (isError($contentResult)) + { + $this->db->trans_rollback(); + $this->terminateWithError(getError($contentResult), self::ERROR_TYPE_DB); + } + + $newsResult = $this->NewsModel->update($newsId, [ + 'studiengang_kz' => $data['studiengang_kz'], + 'semester' => $data['semester'], + 'datum' => $data['datum'], + 'datum_bis' => $data['datum_bis'], + 'updateamum' => $now, + 'updatevon' => $uid + ]); + + if (isError($newsResult)) + { + $this->db->trans_rollback(); + $this->terminateWithError(getError($newsResult), self::ERROR_TYPE_DB); + } + + $existingTranslationsResult = $this->ContentspracheModel->loadWhere([ + 'content_id' => $existingNews->content_id + ]); + + if (isError($existingTranslationsResult)) + { + $this->db->trans_rollback(); + $this->terminateWithError(getError($existingTranslationsResult), self::ERROR_TYPE_DB); + } + + $existingTranslations = []; + + foreach ((array)getData($existingTranslationsResult) as $existingTranslation) + { + $language = $existingTranslation->sprache; + + if ( + !isset($existingTranslations[$language]) + || (int)$existingTranslation->version > (int)$existingTranslations[$language]->version + ) + { + $existingTranslations[$language] = $existingTranslation; + } + } + + $submittedLanguages = array_column($data['translations'], 'sprache'); + $this->db->where('content_id', $existingNews->content_id); + $this->db->where_not_in('sprache', $submittedLanguages); + + if ($this->db->delete('campus.tbl_contentsprache') === false) + { + $error = $this->db->error(); + $this->db->trans_rollback(); + $this->terminateWithError($error, self::ERROR_TYPE_DB); + } + + foreach ($data['translations'] as $translation) + { + $translation['content'] = $this->formatContentURLsToRelative($translation['content']); + $translationData = [ + 'sichtbar' => $translation['sichtbar'], + 'content' => $translation['content'], + 'titel' => $translation['betreff'], + 'updateamum' => $now, + 'updatevon' => $uid + ]; + + $existingTranslation = $existingTranslations[$translation['sprache']] ?? null; + + if ($existingTranslation) + { + $translationResult = $this->ContentspracheModel->update( + $existingTranslation->contentsprache_id, + $translationData + ); + } + else + { + $translationResult = $this->ContentspracheModel->insert([ + 'sprache' => $translation['sprache'], + 'content_id' => $existingNews->content_id, + 'version' => 1, + 'insertamum' => $now, + 'insertvon' => $uid + ] + $translationData); + } + + $this->_getInsertedIdOrRollback($translationResult); + } + + if ($this->db->trans_status() === false) + { + $error = $this->db->error(); + $this->db->trans_rollback(); + $this->terminateWithError($error, self::ERROR_TYPE_DB); + } + + if ($this->db->trans_commit() === false) + { + $error = $this->db->error(); + $this->db->trans_rollback(); + $this->terminateWithError($error, self::ERROR_TYPE_DB); + } + + $this->terminateWithSuccess($newsId); + } + + /** + * Deletes one news entry and its linked content. + * + * @return void + */ + public function deleteNewsItem($newsId) + { + if (!is_numeric($newsId) || (int)$newsId < 1) + { + $this->terminateWithValidationErrors([ + 'newsId' => 'A valid news ID is required' + ]); + } + + $newsId = (int)$newsId; + $newsResult = $this->NewsModel->load($newsId); + + if (isError($newsResult)) + { + $this->terminateWithError(getError($newsResult), self::ERROR_TYPE_DB); + } + + if (!hasData($newsResult)) + { + $this->terminateWithValidationErrors([ + 'newsId' => 'The requested news entry does not exist' + ]); + } + + $news = current(getData($newsResult)); + + + $this->_ensureUserIsEntitledForDegreeProgram($news->studiengang_kz); + + if ($this->db->trans_begin() === false) + { + $this->terminateWithError($this->db->error(), self::ERROR_TYPE_DB); + } + + $newsDeleteResult = $this->NewsModel->delete($newsId); + + if (isError($newsDeleteResult)) + { + $this->db->trans_rollback(); + $this->terminateWithError(getError($newsDeleteResult), self::ERROR_TYPE_DB); + } + + $this->db->where('content_id', $news->content_id); + + if ($this->db->delete('campus.tbl_contentsprache') === false) + { + $error = $this->db->error(); + $this->db->trans_rollback(); + $this->terminateWithError($error, self::ERROR_TYPE_DB); + } + + $contentDeleteResult = $this->ContentModel->delete($news->content_id); + + if (isError($contentDeleteResult)) + { + $this->db->trans_rollback(); + $this->terminateWithError(getError($contentDeleteResult), self::ERROR_TYPE_DB); + } + + if ($this->db->trans_status() === false) + { + $error = $this->db->error(); + $this->db->trans_rollback(); + $this->terminateWithError($error, self::ERROR_TYPE_DB); + } + + if ($this->db->trans_commit() === false) + { + $error = $this->db->error(); + $this->db->trans_rollback(); + $this->terminateWithError($error, self::ERROR_TYPE_DB); + } + + $this->terminateWithSuccess($newsId); + } + + //------------------------------------------------------------------------------------------------------------------ + // Private methods + + /** + * Loads the requested Studiengang or reports a validation error. + * + * @param integer $studiengangKz + * @return stdClass + */ + private function _getStudiengang($studiengangKz) + { + $result = $this->StudiengangModel->load($studiengangKz); + + if (isError($result)) + { + $this->terminateWithError(getError($result), self::ERROR_TYPE_DB); + } + + if (!hasData($result)) + { + $this->terminateWithValidationErrors([ + 'studiengang_kz' => 'The requested Studiengang does not exist' + ]); + } + + $studiengaenge = getData($result); + + return current($studiengaenge); + } + + /** + * Ensures that all supplied languages exist in tbl_sprache. + * + * @param array $translations + * @return void + */ + private function _validateLanguages($translations) + { + $languages = []; + + foreach ($translations as $translation) + { + $languages[] = $translation['sprache']; + } + + $result = $this->SpracheModel->loadMultiple($languages); + + if (isError($result)) + { + $this->terminateWithError(getError($result), self::ERROR_TYPE_DB); + } + + $existingLanguages = []; + + foreach ((array)getData($result) as $language) + { + $existingLanguages[$language->sprache] = true; + } + + $errors = []; + + foreach ($translations as $index => $translation) + { + if (!isset($existingLanguages[$translation['sprache']])) + { + $errors['translations.' . $index . '.sprache'] = 'The requested language does not exist'; + } + } + + if (count($errors) > 0) + { + $this->terminateWithValidationErrors($errors); + } + } + + private function _ensureUserIsEntitledForDegreeProgram($degreeProgramShortCode) + { + $entitledDegreePrograms = $this->permissionlib->getSTG_isEntitledFor("basis/news:w"); + if (!is_array($entitledDegreePrograms)) + { + $this->terminateWithError('Failed to retrieve entitled degree programs for the user', self::ERROR_TYPE_GENERAL); + } + + if (!in_array($degreeProgramShortCode, $entitledDegreePrograms)) { + $this->terminateWithError("User is not entitled for the specified degree program"); + } + } + /** + * Returns an inserted identifier or rolls the transaction back on error. + * + * @param stdClass $result + * @return mixed + */ + private function _getInsertedIdOrRollback($result) + { + if (isError($result)) + { + $this->db->trans_rollback(); + $this->terminateWithError(getError($result), self::ERROR_TYPE_DB); + } + + return getData($result); + } + + /** + * Builds the XML format consumed by existing news readers. + * + * @param array $translation + * @return string + */ + private function _buildNewsXml($translation) + { + $document = new DOMDocument('1.0', 'UTF-8'); + $news = $document->createElement('news'); + $document->appendChild($news); + + $this->_appendCdataElement($document, $news, 'verfasser', $translation['verfasser']); + $this->_appendCdataElement($document, $news, 'betreff', $translation['betreff']); + $this->_appendCdataElement($document, $news, 'text', $translation['text']); + + return $document->saveXML($news); + } + + /** + * Appends a CDATA element and safely preserves CDATA terminators. + * + * @param DOMDocument $document + * @param DOMElement $parent + * @param string $name + * @param string $value + * @return void + */ + private function _appendCdataElement($document, $parent, $name, $value) + { + $element = $document->createElement($name); + $parts = explode(']]>', $value); + $lastIndex = count($parts) - 1; + + foreach ($parts as $index => $part) + { + if ($index > 0) + { + $part = '>' . $part; + } + + if ($index < $lastIndex) + { + $part .= ']]'; + } + + $element->appendChild($document->createCDATASection($part)); + } + + $parent->appendChild($element); + } + + private function _sendNewsItemTranslationRequiredMail($newsItem) + { + $translatorsResult = $this->BenutzerfunktionModel->getBenutzerFunktionen("translate", $newsItem->studiengang_kz); + $translators = $this->getDataOrTerminateWithError($translatorsResult); + + $newsItemContentResult = $this->ContentspracheModel->loadWhere([ + 'content_id' => $newsItem->content_id, + 'sprache' => 'German' + ]); + $newsItemContentEntry = getData($newsItemContentResult)[0] ?? null; + if ($newsItemContentEntry) { + $newsItem->sichtbar = $newsItemContentEntry->sichtbar ?? false; + $newsItem->content = $newsItemContentEntry->content; + } + $content = getData( + $this->cmslib->createNewsContent( + $this->cmslib->createNewsWrapper($newsItem, true), + false, + "" + ) + ); + + foreach ($translators as $translator) { + sendSanchoMail( + "Sancho_Mail_News_Translation_Req", + [ + "newsItemUrl" => APP_ROOT . "cis.php/CisVue/Cms/newsAdministration?newsId=" . $newsItem->news_id, + "newsItemAuthor" => $newsItem->verfasser, + "newsItemSubject" => $newsItem->betreff, + "newsItemContent" => $content + ], + $translator->uid . "@" . DOMAIN, + "Übersetzung erforderlich / Translation required" + ); + } + } + + private function formatContentURLsToRelative($content) + { + $content = preg_replace( + '#https?://[^"\'>\s]*/(dms\.php(?:\?[^"\'>\s]*)?)#i', + '$1', + $content + ); + + $content = preg_replace( + '#(?:\.\./)+[^"\'>\s]*/(dms\.php(?:\?[^"\'>\s]*)?)#i', + '$1', + $content + ); + + $content = preg_replace( + '#/[^"\'>\s]*/(dms\.php(?:\?[^"\'>\s]*)?)#i', + '$1', + $content + ); + + return $content; + } +} diff --git a/application/controllers/api/frontend/v1/Noten.php b/application/controllers/api/frontend/v1/Noten.php new file mode 100644 index 000000000..6a7d0f640 --- /dev/null +++ b/application/controllers/api/frontend/v1/Noten.php @@ -0,0 +1,1782 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +use CI3_Events as Events; + +class Noten extends FHCAPI_Controller +{ + /** tbl_note by PK, read once per request. @see aktiveNoten() */ + private $aktiveNotenCache = null; + + /** + * Object initialization + */ + public function __construct() + { + $permissions = array('lehre/benotungstool:rw', 'lehre/benotungstool_assistenz:rw'); + parent::__construct([ + 'getStudentenNoten' => $permissions, + 'getNoten' => $permissions, + 'saveStudentenNoten' => $permissions, + 'getNotenvorschlagStudent' => $permissions, + 'saveNotenvorschlag' => $permissions, + 'saveStudentPruefung' => $permissions, + 'createPruefungen' => $permissions, + 'saveNotenvorschlagBulk' => $permissions, + 'savePruefungenBulk' => $permissions, + 'getCisConfig' => $permissions, + 'getNoteByPunkte' => $permissions, + 'getBenotungstoolContext' => $permissions, + 'getLehrendeFuerLehreinheit' => $permissions, + 'getLvForStudiengang' => $permissions + ]); + + $this->load->library('AuthLib', null, 'AuthLib'); + $this->load->library('PhrasesLib'); + $this->load->library('PruefungsverlaufLib', null, 'VerlaufLib'); + + // Loads LogLib with different debug trace levels to get data of the job that extends this class + // It also specify parameters to set database fields + $this->load->library('LogLib', array( + 'classIndex' => 5, + 'functionIndex' => 5, + 'lineIndex' => 4, + 'dbLogType' => 'API', // required + 'dbExecuteUser' => 'RESTful API', + 'requestId' => 'API', + 'requestDataFormatter' => function ($data) { + return json_encode($data); + } + ), 'logLib'); + + // Loads phrases system + $this->loadPhrases([ + 'global', + 'person', + 'benotungstool', + 'lehre', + 'ui', + 'password' + ]); + + $this->load->model('education/LePruefung_model', 'LePruefungModel'); + $this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel'); + $this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + $this->load->model('education/Notenschluesselaufteilung_model', 'NotenschluesselaufteilungModel'); + $this->load->model('education/Note_model', 'NoteModel'); + $this->load->model('person/Person_model', 'PersonModel'); + $this->load->model('organisation/Studienplan_model', 'StudienplanModel'); + $this->load->model('crm/Student_model', 'StudentModel'); + $this->load->model('codex/Mobilitaet_model', 'MobilitaetModel'); + $this->load->model('organisation/Erhalter_model', 'ErhalterModel'); + + $this->load->config('noten'); + $this->load->helper('hlp_sancho_helper'); + + } + + public function getCisConfig() { + // resolved from tbl_note (Bezeichnung) with config fallback -> single source of truth + $special = $this->VerlaufLib->getSpecialNotes(); + $NOTEN_OHNE_ANTRITT = $special['ohneAntritt']; + $NOTEN_OCCURANCE_LIMIT_MAP = $special['limitMap']; + $NOTE_ENTSCHULDIGT = $special['entschuldigt']; + + $this->terminateWithSuccess( + array( + // show the points during the grade entry + 'CIS_GESAMTNOTE_PUNKTE' => CIS_GESAMTNOTE_PUNKTE, + + // basically on/of toggle for the points/grade col and the arrow button + 'CIS_GESAMTNOTE_UEBERSCHREIBEN' => CIS_GESAMTNOTE_UEBERSCHREIBEN, + + // only relevant in punkte calculation in backend + // 'CIS_GESAMTNOTE_GEWICHTUNG' => CIS_GESAMTNOTE_GEWICHTUNG, + + // The maximum number of attempts that count in this tool. The server derives it from the + // configuration or from the old TERMIN2/TERMIN3 flags. The client does not calculate it. + 'CIS_GESAMTNOTE_MAX_ANTRITTE' => $this->VerlaufLib->getMaxAntritte(), + + // the default column layout ('antritt' or 'datum'); the user can change it in the tool + 'CIS_GESAMTNOTE_PRUEFUNGSSPALTEN' => $this->config->item('CIS_GESAMTNOTE_PRUEFUNGSSPALTEN'), + + // used to toggle availability of kommPruef type pruefungen + 'CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF' => CIS_GESAMTNOTE_PRUEFUNG_KOMMPRUEF, + + //technically exists but is never used, could be LE pendant to next flag + // 'CIS_GESAMTNOTE_PRUEFUNG_MOODLE_NOTE' => CIS_GESAMTNOTE_PRUEFUNG_MOODLE_NOTE, + + // basically a toggle for "use teilnoten" and the source is always moodle + // setting this to false breaks legacy tool and if that was fixed it wouldnt render any table at all + // anyway so not sure why this even is a config at all. placebo at best + + // toggles availability of the teilnoten column... existas but do we really need this? + 'CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE' => CIS_GESAMTNOTE_PRUEFUNG_MOODLE_LE_NOTE, + + // availability of the two import flows (application/config/noten.php); when both are + // true they are shown separately + 'CIS_GESAMTNOTE_PRUEFUNGSIMPORT' => $this->config->item('CIS_GESAMTNOTE_PRUEFUNGSIMPORT'), + 'CIS_GESAMTNOTE_NOTENIMPORT' => $this->config->item('CIS_GESAMTNOTE_NOTENIMPORT'), + + // send a mail when approving grades + 'CIS_GESAMTNOTE_FREIGABEMAIL_NOTE' => CIS_GESAMTNOTE_FREIGABEMAIL_NOTE, + + 'NOTEN_OHNE_ANTRITT' => $NOTEN_OHNE_ANTRITT, + + 'NOTEN_OCCURANCE_LIMIT_MAP' => $NOTEN_OCCURANCE_LIMIT_MAP, + + // pk of the 'entschuldigt' note; used to preserve excused Termine on new pruefung creation + 'NOTE_ENTSCHULDIGT' => $NOTE_ENTSCHULDIGT, + + // Noteneintragungsfrist window (enforced server-side; also surfaced so the UI can hint at it) + 'CIS_GESAMTNOTE_NOTENEINTRAGUNGSFRIST' => $this->config->item('CIS_GESAMTNOTE_NOTENEINTRAGUNGSFRIST') + ) + ); + } + + /** + * GET METHOD + * expects (optional) 'sem_kurzbz' + * Single role-determining entry point for the tool: the server decides which flow the current user + * gets and returns exactly the initial dropdown data for it. + * A teacher (holds lehre/benotungstool) gets their assigned + * Lehrveranstaltungen directly. Assistenz (only lehre/benotungstool_assistenz) gets the + * degree programmes they are entitled for, and picks one before a course is loaded. + */ + public function getBenotungstoolContext() { + $sem_kurzbz = $this->input->get("sem_kurzbz", TRUE); + $lv_id = $this->input->get("lv_id", TRUE); // optional: deep-link target, used to preselect + + $this->load->library('PermissionLib'); + + // teachers keep the classic assigned-LV flow; the Studiengang flow is only for Assistenz. + // Role determination mirrors assertLvAccess, which scopes each role's actual data + // access. A teacher sees the own courses, an assistant sees the entitled degree programmes. + $isLektor = $this->permissionlib->isBerechtigt('lehre/benotungstool'); + $entitledStgs = $this->permissionlib->getSTG_isEntitledFor('lehre/benotungstool_assistenz'); + $isAssistenz = !$isLektor && is_array($entitledStgs) && count($entitledStgs) > 0; + + $studiengaenge = array(); + $lehrveranstaltungen = array(); + $preselectStudiengang_kz = null; + + if (isset($sem_kurzbz) && !isEmptyString($sem_kurzbz)) { + if ($isAssistenz) { + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); + $result = $this->StudiengangModel->getByStgs($entitledStgs, $sem_kurzbz); + if (!isError($result)) $studiengaenge = getData($result) ?? array(); + + // deep-link: resolve the Studiengang of the requested LV so the frontend can preselect + // the Studiengang dropdown (and then its LV) - only if the Assistenz is entitled for it + if (isset($lv_id) && !isEmptyString($lv_id)) { + $res = $this->LehrveranstaltungModel->load($lv_id); + if (!isError($res) && hasData($res)) { + $stg = getData($res)[0]->studiengang_kz; + if (in_array($stg, $entitledStgs)) $preselectStudiengang_kz = $stg; + } + } + } else { + $result = $this->LehrveranstaltungModel->getLvForLektorInSemester($sem_kurzbz, getAuthUID()); + if (!isError($result)) $lehrveranstaltungen = getData($result) ?? array(); + } + } + + $this->terminateWithSuccess(array( + 'isAssistenz' => $isAssistenz, + 'studiengaenge' => $studiengaenge, + 'lehrveranstaltungen' => $lehrveranstaltungen, + 'preselectStudiengang_kz' => $preselectStudiengang_kz + )); + } + + /** + * GET METHOD + * expects 'studiengang_kz', 'sem_kurzbz' + * Returns the Lehrveranstaltungen of a Studiengang in a Studiensemester for the Assistenz flow. + * The caller may load only the degree programmes of lehre/benotungstool_assistenz. + */ + public function getLvForStudiengang() { + $studiengang_kz = $this->input->get("studiengang_kz", TRUE); + $sem_kurzbz = $this->input->get("sem_kurzbz", TRUE); + + if (!isset($studiengang_kz) || isEmptyString($studiengang_kz) + || !isset($sem_kurzbz) || isEmptyString($sem_kurzbz)) { + $this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general'); + } + + $this->load->library('PermissionLib'); + $entitledStgs = $this->permissionlib->getSTG_isEntitledFor('lehre/benotungstool_assistenz'); + $isAdmin = $this->permissionlib->isBerechtigt('admin'); + + if (!$isAdmin && (!is_array($entitledStgs) || !in_array($studiengang_kz, $entitledStgs))) { + $this->terminateWithError($this->p->t('ui', 'keineBerechtigung'), 'general'); + } + + $result = $this->LehrveranstaltungModel->getLvForStudiengangInSemester($sem_kurzbz, $studiengang_kz); + $data = $this->getDataOrTerminateWithError($result); + $this->terminateWithSuccess($data); + } + + /** + * Scopes data access to the Lehrveranstaltungen a user may act on, so a shared/guessed URL cannot be + * used to read or edit foreign grades. A teacher (lehre/benotungstool) may + * only touch LVs they are assigned to teach in the given Studiensemester. Assistenz may + * touch LVs of a Studiengang they are entitled for. + */ + private function assertLvAccess($lv_id, $sem_kurzbz = null) + { + $this->load->library('PermissionLib'); + + // admins keep full access + if ($this->permissionlib->isBerechtigt('admin')) return; + + // teachers: only their own LVs (assigned as lehreinheitmitarbeiter in this semester) + if ($this->permissionlib->isBerechtigt('lehre/benotungstool')) { + $res = $this->LehrveranstaltungModel->getLektorIsTeachingLva($lv_id, getAuthUID(), $sem_kurzbz); + $rows = getData($res); + if (!isError($res) && !empty($rows) && $rows[0]->teaches > 0) return; + // not a teacher of this LV -> fall through (a both-role user may still be entitled as Assistenz) + } + + // (pure or additional) Assistenz: only LVs of an entitled Studiengang + $entitledStgs = $this->permissionlib->getSTG_isEntitledFor('lehre/benotungstool_assistenz'); + $lv = null; + if (is_array($entitledStgs) && count($entitledStgs) > 0) { + $res = $this->LehrveranstaltungModel->load($lv_id); + if (!isError($res) && hasData($res)) { + $lv = getData($res)[0]; + if (in_array($lv->studiengang_kz, $entitledStgs)) return; + } + } + + if ($lv === null) { + $res = $this->LehrveranstaltungModel->load($lv_id); + if (!isError($res) && hasData($res)) $lv = getData($res)[0]; + } + $bezeichnung = $lv !== null ? $lv->bezeichnung : $lv_id; + + $this->terminateWithError( + $this->p->t('benotungstool', 'keineBerechtigungNoten', [$bezeichnung, $sem_kurzbz]), + 'general' + ); + } + + /** + * GET METHOD + * expects 'lv_id', 'sem_kurzbz' + * returns List of all Students of given lehrveranstaltung and semester and fetches their grades. + * Loads LvGesamtnote aswell as Teilnoten from externalSources via getExternalGrades Event. + * Calculates the Notenvorschlag for every student based on averaging their Teilnoten. + * At the end it also fetches all exams of every student for that course and semester. + */ + public function getStudentenNoten() { + $lv_id = $this->input->get("lv_id",TRUE); + $sem_kurzbz = $this->input->get("sem_kurzbz",TRUE); + + if (!isset($lv_id) || isEmptyString($lv_id) + || !isset($sem_kurzbz) || isEmptyString($sem_kurzbz)) + $this->terminateWithError($this->p->t('global', 'wrongParameters'), 'general'); + + $this->assertLvAccess($lv_id, $sem_kurzbz); + + // get studenten for lva & sem with zeugnisnote if available + $studenten = $this->LehrveranstaltungModel->getStudentsByLv($sem_kurzbz, $lv_id); + $studentenData = $this->getDataOrTerminateWithError($studenten); + + if(count($studentenData) == 0) { + $this->terminateWithError($this->p->t('benotungstool', 'c4keineStudentenGefunden')); + } + + $func = function ($value) { + return $value->uid; + }; + + $grades = array(); + $student_uids = array_map($func, $studentenData); + + $funcpre = function ($value) { + return $value->prestudent_id; + }; + + $prestudent_ids = array_map($funcpre, $studentenData); + + if(count($student_uids) > 0) { + $mobres = $this->MobilitaetModel->getMobilityZusatzForUids($student_uids); + $mobData = $this->getDataOrTerminateWithError($mobres); + + $result = $this->ErhalterModel->load(); + $erhalter = getData($result)[0]; + + $erhalter_kz = '9' . sprintf("%03s", $erhalter->erhalter_kz); + foreach($mobData as $mob) { + $grades[$mob->uid]['mobility_zusatz'] = $this->MobilitaetModel->formatZusatz($mob, $erhalter_kz); + } + } + + foreach($student_uids as $uid) { + $grades[$uid]['grades'] = []; + + // Read without the filter. getLvGesamtNoten() gives released grades only. With that getter + // the course grade and the release state are empty after a reload. + $lvgesamtnote = $this->getLvGesamtnoteRow($lv_id, $uid, $sem_kurzbz); + if($lvgesamtnote !== null) { + $grades[$uid]['note_lv'] = $lvgesamtnote->note; + $grades[$uid]['freigabedatum'] = $lvgesamtnote->freigabedatum; + $grades[$uid]['benotungsdatum'] = $lvgesamtnote->benotungsdatum; + $grades[$uid]['punkte_lv'] = $lvgesamtnote->punkte; + } else { + $grades[$uid]['note_lv'] = null; + $grades[$uid]['freigabedatum'] = null; + $grades[$uid]['benotungsdatum'] = null; + $grades[$uid]['punkte_lv'] = null; + } + } + + // send $grades reference to moodle addon + try { + Events::trigger( + 'getExternalGrades', + function & () use (&$grades) + { + return $grades; + }, + [ + 'lvid' => $lv_id, + 'stsem' => $sem_kurzbz + ] + ); + } catch (Throwable $t) { +// $this->addMeta('throwable', $t->getTrace()); + $this->addMeta('getExternalGradesError', $t->getMessage()); + } + + // assign the anw% to the students in the studentData loop + $anwresult = $this->getAnwesenheiten($prestudent_ids, $lv_id, $sem_kurzbz); + + // calculate the grade proposals from the partial grades + foreach($studentenData as $student) { + + // null when the Anwesenheiten addon is absent - the column stays empty in the UI + $student->anwquote = $anwresult[$student->prestudent_id] ?? null; + + $g = $grades[$student->uid]['grades']; + $note_lv = $grades[$student->uid]['note_lv']; + + // overwrite any calculation with lv note once available + if(!is_null($note_lv)) { + $student->note_vorschlag = $note_lv; + } else if(count($g) > 0) { + + $notensumme = 0; + $notensumme_gewichtet = 0; + $gewichtsumme = 0; + $punktesumme = 0; + $punktesumme_gewichtet = 0; + $anzahlnoten = 0; + foreach($g as $teilnote) { + if (is_numeric($teilnote['grade']) || (is_null($teilnote['grade']) && is_numeric($teilnote['points']))) + { + $notensumme += $teilnote['grade']; + $punktesumme += $teilnote['points']; + $notensumme_gewichtet += $teilnote['grade'] * $teilnote['weight']; + $punktesumme_gewichtet += $teilnote['points'] * $teilnote['weight']; + $gewichtsumme += $teilnote['weight']; + $anzahlnoten += 1; + } + } + + // Without a partial grade that counts there is no average. Without this guard the + // division uses zero. PHP 7 gives INF, and INF gives the best grade. PHP 8 stops with + // a fatal error and the full grade table stays empty. + $gewichtet = defined('CIS_GESAMTNOTE_GEWICHTUNG') && CIS_GESAMTNOTE_GEWICHTUNG; + $divisor = $gewichtet ? $gewichtsumme : $anzahlnoten; + + if ($divisor > 0) { + if (CIS_GESAMTNOTE_PUNKTE) { + $punkte_vorschlag = round(($gewichtet ? $punktesumme_gewichtet : $punktesumme) / $divisor, 2); + $note_vorschlag_result = $this->NotenschluesselaufteilungModel->getNote($punkte_vorschlag, $lv_id, $sem_kurzbz); + $note_vorschlag = $this->getDataOrTerminateWithError($note_vorschlag_result); + } else { + $note_vorschlag = round(($gewichtet ? $notensumme_gewichtet : $notensumme) / $divisor); + } + + $student->note_vorschlag = $note_vorschlag; + } + } + } + + // get all exams with grades of that semester and that course + $pruefungen = $this->LePruefungModel->getPruefungenByLvStudiensemester($lv_id, $sem_kurzbz); + $pruefungenData = getData($pruefungen); + + // the server derives the history for each student; the client only reads it + $proStudent = []; + foreach($pruefungenData ?: [] as $p) { + $proStudent[$p->student_uid][] = $p; + } + + // the transcript grades come with the student list (tbl_zeugnisnote.note) + $zeugnisnoten = []; + foreach($studentenData as $s) $zeugnisnoten[$s->uid] = $s->note; + + $pruefungenAbgeleitet = []; + foreach(array_unique(array_merge($student_uids, array_keys($proStudent))) as $uid) { + $lvNote = isset($grades[$uid]) ? ($grades[$uid]['note_lv'] ?? null) : null; + $verlauf = $this->VerlaufLib->buildVerlauf($proStudent[$uid] ?? [], $lvNote, $zeugnisnoten[$uid] ?? null); + + foreach($verlauf->pruefungen as $p) $pruefungenAbgeleitet[] = $p; + + if(isset($grades[$uid])) { + $grades[$uid]['verlauf'] = $this->verlaufSummary( + $verlauf, false, $this->getLvGesamtnoteRow($lv_id, $uid, $sem_kurzbz) !== null + ); + } + } + + $this->terminateWithSuccess(array($studentenData, $pruefungenAbgeleitet, DOMAIN, $grades, $anwresult)); + } + + /** + * GET METHOD + * returns List of all available & active NotenOptions + */ + public function getNoten() { + $this->load->model('education/Note_model', 'NoteModel'); + + $result = $this->NoteModel->getAllActive(); + $noten = $this->getDataOrTerminateWithError($result); + $this->terminateWithSuccess($noten); + } + + /** + * POST METHOD + * expects 'lv_id', 'sem_kurzbz', 'password', 'noten' + * Notenfreigabe method which checks the users password as a security measure. + * Tries to load Lehrveranstaltung, Studiengang and Person via Model in order to validate the coherency of input parameters + * lv_id & sem_kurzbz in relation to the noten array delivered. + * Updates the LvGesamtnote note, aswell as freigabedatum, which is key in the logic of the freigegeben/offen/changed notenStatus + * Along this process builds a html table to be placed in a confirmation email (uid only and full variant depending on config) + * which is being sent to the Lektor, aswell as the assigned Assistenz. + */ + public function saveStudentenNoten() { + $result = $this->getPostJSON(); + + if(!property_exists($result, 'sem_kurzbz') || !property_exists($result, 'lv_id') || + !property_exists($result, 'password') || !property_exists($result, 'noten')) { + $this->terminateWithError($this->p->t('global', 'missingParameters'), 'general'); + } + + if(!$this->AuthLib->checkUserAuthByUsernamePassword(getAuthUID(), $result->password)->retval) { + $this->terminateWithError($this->p->t('password', 'wrongPassword'), 'general'); + } + + $lv_id = $result->lv_id; + $sem_kurzbz = $result->sem_kurzbz; + + $this->assertLvAccess($lv_id, $sem_kurzbz); + + $ret = []; + + $res = $this->LehrveranstaltungModel->load($lv_id); + if(isError($res) || !hasData($res)) { + $this->terminateWithError($this->p->t('benotungstool', 'noValidLvFoundForId', [$lv_id])); + } + + $lv = getData($res)[0]; + + $studiengang_kz = $lv->studiengang_kz; + $res = $this->StudiengangModel->load($studiengang_kz); + if(isError($res) || !hasData($res)) { + $this->terminateWithError($this->p->t('benotungstool', 'noValidStudiengangFoundForId', [$studiengang_kz])); + } + $sg = getData($res)[0]; + $lvaFullName = $sg->kurzbzlang . ' ' . $lv->semester . '.Semester + ' . $lv->bezeichnung . " - " .$lv->lehrform_kurzbz. " " . $lv->orgform_kurzbz . " - " . $sem_kurzbz; + + $emails = explode(', ', $sg->email); + + + $res = $this->PersonModel->load(getAuthPersonId()); + if(isError($res) || !hasData($res)) { + $this->terminateWithError($this->p->t('benotungstool', 'noValidPersonFoundForId', [getAuthPersonId()])); + } + $pers = getData($res)[0]; + $lektorFullName = $pers->anrede.' '.$pers->vorname.' '.$pers->nachname; //.' ('.$pers->kurzbz.')'; + + + $res = $this->StudienplanModel->getStudienplanByLvaSemKurzbz($lv_id, $sem_kurzbz); + $data = getData($res); + $studienplan_bezeichnung = ''; + foreach ($data as $row) { + $studienplan_bezeichnung .= $row->bezeichnung . ' '; + } + $betreff = $this->p->t('benotungstool','notenfreigabe').' ' . $lv->bezeichnung . ' ' . $lv->orgform_kurzbz . ' - ' . $studienplan_bezeichnung; + + $studlist = "
| " . $this->p->t('person','personenkennzeichen') . " | \n +" . $this->p->t('lehre','studiengang') . " | \n +" . $this->p->t('benotungstool','c4nachname') . " | \n +" . $this->p->t('benotungstool','c4vorname') . " | \n"; + if(defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE) { + $studlist .= "" . $this->p->t('benotungstool','c4punkte') . " | \n"; + } + $studlist .= "" . $this->p->t('benotungstool','c4grade') . " | \n"; + $studlist .= "" . $this->p->t('ui','bearbeitetVon') . " | " . $this->p->t('person','uid') . " | \n"; + } + + foreach($result->noten as $note) { + + $resultLVGes = $this->LvgesamtnoteModel->getLvGesamtNoteVorschlag($lv_id, $note->uid, $sem_kurzbz); + if (!isError($resultLVGes) && hasData($resultLVGes)) + { + $lvgesamtnote = getData($resultLVGes)[0]; + + if ($lvgesamtnote->benotungsdatum > $lvgesamtnote->freigabedatum) + { + + $id = $this->LvgesamtnoteModel->update( + [$lvgesamtnote->student_uid, $lvgesamtnote->studiensemester_kurzbz, $lvgesamtnote->lehrveranstaltung_id], + array( + 'note' => $lvgesamtnote->note, + 'freigabevon_uid' => getAuthUID(), + 'freigabedatum' => date("Y-m-d H:i:s"), + 'updateamum' => date("Y-m-d H:i:s"), + 'updatevon' => getAuthUID() + ) + ); + + if($id) { + $res = $this->LvgesamtnoteModel->load($id->retval); + if(hasData($res)) { + $lvgesamtnote = getData($res)[0]; + + // The release makes the grade binding, therefore the first exam starts + // here. A new exam never creates a second exam in addition. + $this->upsertErstantritt( + $lv_id, $lvgesamtnote->student_uid, $sem_kurzbz, + $lvgesamtnote->note, $lvgesamtnote->punkte, $lvgesamtnote->benotungsdatum + ); + + // The verlauf goes back with the answer, so the table shows the new exam + // at once. Without it the row updates only after a reload. + $ret[] = array( + 'uid' => $note->uid, + 'freigabedatum' => $lvgesamtnote->freigabedatum, + 'benotungsdatum' => $lvgesamtnote->benotungsdatum, + 'verlauf' => $this->buildVerlaufSummary($note->uid, $lv_id, $sem_kurzbz) + ); + } + } + + if (defined('CIS_GESAMTNOTE_FREIGABEMAIL_NOTE') && CIS_GESAMTNOTE_FREIGABEMAIL_NOTE) + { + $studlist .= "
| " . trim($note->matrikelnr) . " | "; + $studlist .= "" . trim($note->kuerzel) . " | "; + $studlist .= "" . trim($note->nachname) . " | "; + $studlist .= "" . trim($note->vorname) . " | "; + + if(defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE) { + $studlist .= "" . trim($lvgesamtnote->punkte) . " | "; + } + $studlist .= "" .$note->noteBezeichnung. " | "; + + $studlist .= "" . $lvgesamtnote->mitarbeiter_uid; + if ($lvgesamtnote->updatevon != '') + $studlist .= " (" . $lvgesamtnote->updatevon . ")"; + $studlist .= " |
| " . trim($note->uid) . " |
freigabedatum); ?> - +
| '. ($row->semester == '1' ? $p->t('testtool/basisgebiete') : $p->t('testtool/quereinstiegsgebiete')).' |
| ' . $p->t('testtool/basisgebiete') . ' |
| ' . $p->t('testtool/quereinstiegsgebiete') . ' |
| {{ $capitalize($p.t('benotungstool/c4student')) }} | +{{ $capitalize($p.t('benotungstool/c4freigabeSummaryCurrent')) }} | ++ | {{ $capitalize($p.t('benotungstool/c4freigabeSummaryReleased')) }} | +
|---|---|---|---|
| {{ row.name }} ({{ row.uid }}) | +{{ row.currentNote }} | ++ | {{ row.releasedNote }} | +
{{ $capitalize($p.t('ui', 'allLanguagesAdded')) }}
++ {{ getCopyingTranslationText() }} +
+{{ $capitalize($p.t('ui', 'existingContentWillBeLost')) }}
+{{ getChooseLanguageText() }}
+{{ $capitalize($p.t('ui', 'addLanguageBeforeCopying')) }}
+ +{{ $capitalize($p.t('ui', 'newsContentPreviewHint')) }}
+{{ $capitalize($p.t('ui', 'selectLanguageTabToPreview')) }}
++
diff --git a/public/js/components/Cis/Compat.js b/public/js/components/Cis/Compat.js new file mode 100644 index 000000000..40f14ddb9 --- /dev/null +++ b/public/js/components/Cis/Compat.js @@ -0,0 +1,99 @@ +export default { + name: "Compat", + props: { + mode: { type: String, required: true }, + path: { type: String, required: true }, + query_string: { type: String, required: true, default: ''} + }, + data: function() { + return { + lastLoadediFrameURL: '', + srcUrl: '' + }; + }, + computed: { + propsWatchHelper: function() { + return this.mode + '#' + this.path + '#' + this.query_string; + } + }, + beforeMount: function() { + document.body.classList.add('compat'); + }, + mounted: function() { + this.srcUrl = this.buildSrcUrl(); + }, + beforeUnmount: function() { + document.body.classList.remove('compat'); + }, + watch: { + propsWatchHelper: function() { + if(this.lastLoadediFrameURL === '') { + return; + } + + let currentiFrameURL = this.$refs.compatiframe ? this.$refs.compatiframe.src : ''; + + let url = this.buildSrcUrl(); + if(this.lastLoadediFrameURL !== url) { + this.srcUrl = url; + if(this.srcUrl === url) { + this.$refs.compatiframe.contentWindow.location.href = url; + } + } + } + }, + methods: { + buildSrcUrl: function() { + let url = false; + switch(this.mode) { + case 'ci': + url = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/' + this.path; + break; + case 'legacy': + url = FHC_JS_DATA_STORAGE_OBJECT.app_root + this.path; + break; + default: + url = false; + } + if(this.query_string !== '' && url) { + url += '?' + this.query_string; + } + + return url; + }, + loadHandler: function() { + let iframe_href = this.$refs.compatiframe.contentWindow.location.href; + let ci_urlstart = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/'; + let legacy_urlstart = FHC_JS_DATA_STORAGE_OBJECT.app_root; + let routerpath = null; + + this.lastLoadediFrameURL = iframe_href; + + if(iframe_href.startsWith(ci_urlstart)) { + routerpath = iframe_href.replace( + ci_urlstart, '/Cis/Compat/ci/'); + } else if(iframe_href.startsWith(legacy_urlstart)) { + routerpath = iframe_href.replace( + legacy_urlstart, '/Cis/Compat/legacy/'); + } else { + return; + } + + if(this.$route.fullPath !== routerpath) { + this.$router.push(routerpath); + } + } + }, + template: ` +
{{lv.lehrform_kurzbz}}
-{{lv.lehrform_kurzbz}}
+{{lehrveranstaltung.lehrform_kurzbz}}
+| {{$p.t("global/name")}} | +
+ {{ day.weekday }}
+ {{ day.day }}
+ |
+
|---|---|
| {{m.sperren[0].nachname}} {{m.sperren[0].vorname}} | +
+
+ {{$p.t('zeitsperren/abwesend')}}
+
+ V:
+ {{ sperre.kurzbz }}
+
+
+ E: {{ sperre.erreichbarkeit_kurzbz }}
+
+ |
+
{{$p.t('ui/keineEintraegeGefunden')}}
+