diff --git a/application/config/lvverwaltung.php b/application/config/lvverwaltung.php index 4fe09bff3..d315c9b38 100644 --- a/application/config/lvverwaltung.php +++ b/application/config/lvverwaltung.php @@ -8,4 +8,15 @@ $config['lvverwaltung_tags'] = [ ]; */ -$config['lvverwaltung_tags'] = []; \ No newline at end of file +$config['lvverwaltung_tags'] = []; + +# Additional columns: +/* +$config["list_columns"] = [ + 'fieldname' => [ + 'js' => 'path/to/snippet.js', // tabulator config snippet (eg: return { name: 'Name', field: 'fieldname' }; ) + // NOTE: 'default' & 'joins' like in stv.php config are currently not available here + ] +]; +*/ + diff --git a/application/config/navigation.php b/application/config/navigation.php index 4d4dcc22a..cd71b02ca 100644 --- a/application/config/navigation.php +++ b/application/config/navigation.php @@ -170,6 +170,13 @@ $config['navigation_header'] = array( 'expand' => true, 'sort' => 51, 'requiredPermissions' => 'vertrag/mitarbeiter:r' + ), + 'studierendenverwaltung' => array( + 'link' => site_url('studentenverwaltung'), + 'description' => 'Studierendenverwaltung', + 'expand' => true, + 'sort' => 52, + 'requiredPermissions' => ['admin:r', 'assistenz:r'] ) ) ), @@ -378,7 +385,7 @@ $config['navigation_menu']['apps'] = [ ], 'lav' => [ 'link' => site_url('lehre/lehrauftrag/Lehrauftrag/Dashboard'), - 'description' => 'Lehraufträge', + 'description' => 'Lehrauftragsverwaltung', #'icon' => 'person-chalkboard', 'requiredPermissions' => array('lehre/lehrauftrag_bestellen:r', 'lehre/lehrauftrag_erteilen:r') ] diff --git a/application/config/routes.php b/application/config/routes.php index 8024f449a..a58fded36 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -75,52 +75,65 @@ $route['Abgabetool/Student'] = 'Cis/Abgabetool/Student'; $route['Abgabetool/Student/(:any)'] = 'Cis/Abgabetool/Student/$1'; $route['Abgabetool/Deadlines'] = 'Cis/Abgabetool/Deadlines'; +// Studierendenverwaltung App Routes +$route['studvw'] = 'studentenverwaltung/index'; +$route['studvw/.*'] = 'studentenverwaltung/index'; +// Old routes +$route['studentenverwaltung'] = 'redirect/301/studvw'; +$route['studentenverwaltung/(:any)'] = 'redirect/301/studvw/stdsem/$1'; +$route['studentenverwaltung/(:any)/(prestudent|student|person|search)/(:any)'] = 'redirect/301/studvw/stdsem/$1/$2/$3'; +$route['studentenverwaltung/(:any)/prestudent/(:any)/tab/:any'] = 'redirect/301/studvw/stdsem/$1/prestudent/$2'; +$route['studentenverwaltung/(:any)/search/(:any)/(:any)'] = 'redirect/301/studvw/stdsem/$1/search/$2/$3'; +$route['studentenverwaltung/(:any)/(:any)'] = 'redirect/301/studvw/stdsem/$1/stg/$2'; + // Studierendenverwaltung List Routes -$route['api/frontend/v1/stv/[sS]tudents/inout'] = 'api/frontend/v1/stv/Students/index'; -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})'] = 'api/frontend/v1/stv/Students/index'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/inout/1/incoming/1'] = 'api/frontend/v1/stv/students/getIncoming/$1'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/inout/1/outgoing/1'] = 'api/frontend/v1/stv/students/getOutgoing/$1'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/inout/1/shared_studies/1'] = 'api/frontend/v1/stv/students/getGemeinsamestudien/$1'; -// (studiensemester_kurzbz)/inout[/(incoming|outgoing|gemeinsamestudien)] -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout'] = 'api/frontend/v1/stv/Students/index'; -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/incoming'] = 'api/frontend/v1/stv/Students/getIncoming/$1'; -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/outgoing'] = 'api/frontend/v1/stv/Students/getOutgoing/$1'; -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/gemeinsamestudien'] = 'api/frontend/v1/stv/Students/getGemeinsamestudien/$1'; +$route['api/frontend/v1/treemenudata/stv/stdsem/:any/stg/(:any)/prestudent/1/stdsem/(:any)/:any/1/(:any)/1'] = 'api/frontend/v1/stv/students/getPrestudents/$1/$2/$3'; +$route['api/frontend/v1/treemenudata/stv/stdsem/:any/stg/(:any)/prestudent/1/stdsem/(:any)/(:any)/1'] = 'api/frontend/v1/stv/students/getPrestudents/$1/$2/$3'; +$route['api/frontend/v1/treemenudata/stv/stdsem/:any/stg/(:any)/prestudent/1/stdsem/(:any)'] = 'api/frontend/v1/stv/students/getPrestudents/$1/$2'; +$route['api/frontend/v1/treemenudata/stv/stdsem/:any/stg/(:any)/prestudent/1'] = 'api/frontend/v1/stv/students/getPrestudents/$1'; -// (studiengang_kz)/prestudent[/(studiensemester_kurzbz)[/(filter)[/(otherfilter)]]] -$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/prestudent'] = 'api/frontend/v1/stv/Students/getPrestudents/$1'; -$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/prestudent/([WS]S[0-9]{4})'] = 'api/frontend/v1/stv/Students/getPrestudents/$1/$2'; -$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/prestudent/([WS]S[0-9]{4})/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudents/$1/$2/$3'; -$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/prestudent/([WS]S[0-9]{4})/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudents/$1/$2/$4'; +$route['api/frontend/v1/treemenudata/stv/stdsem/:any/stg/(:any)/orgform/(:any)/prestudent/1/stdsem/(:any)/:any/1/(:any)/1'] = 'api/frontend/v1/stv/students/getPrestudentsOrgform/$1/$2/$3/$4'; +$route['api/frontend/v1/treemenudata/stv/stdsem/:any/stg/(:any)/orgform/(:any)/prestudent/1/stdsem/(:any)/(:any)/1'] = 'api/frontend/v1/stv/students/getPrestudentsOrgform/$1/$2/$3/$4'; +$route['api/frontend/v1/treemenudata/stv/stdsem/:any/stg/(:any)/orgform/(:any)/prestudent/1/stdsem/(:any)'] = 'api/frontend/v1/stv/students/getPrestudentsOrgform/$1/$2/$3'; +$route['api/frontend/v1/treemenudata/stv/stdsem/:any/stg/(:any)/orgform/(:any)/prestudent/1'] = 'api/frontend/v1/stv/students/getPrestudentsOrgform/$1/$2'; -// (studiengang_kz)/(orgform)/prestudent[/(studiensemester_kurzbz)[/(filter)[/(otherfilter)]]] -$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/([A-Z]{2,3})/prestudent'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2'; -$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/([A-Z]{2,3})/prestudent/([WS]S[0-9]{4})'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2/$3'; -$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/([A-Z]{2,3})/prestudent/([WS]S[0-9]{4})/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2/$3/$4'; -$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/([A-Z]{2,3})/prestudent/([WS]S[0-9]{4})/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2/$3/$5'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/stg/(:any)/semester/(:any)/verband/(:any)/group/(:any)'] = 'api/frontend/v1/stv/students/getStudents/$1/$2/$3/$4/$5'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/stg/(:any)/semester/(:any)/verband/(:any)'] = 'api/frontend/v1/stv/students/getStudents/$1/$2/$3/$4'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/stg/(:any)/semester/(:any)'] = 'api/frontend/v1/stv/students/getStudents/$1/$2/$3'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/stg/(:any)'] = 'api/frontend/v1/stv/students/getStudents/$1/$2'; -// (studiensemester_kurzbz)/(studiengang_kz)/(semester)/grp/(gruppe) -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/(:num)/grp/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsSpezialgruppe/$1/$2/$3/$4'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/stg/(:any)/orgform/(:any)/semester/(:any)/verband/(:any)/group/(:any)'] = 'api/frontend/v1/stv/students/getStudentsOrgform/$1/$2/$3/$4/$5/$6'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/stg/(:any)/orgform/(:any)/semester/(:any)/verband/(:any)'] = 'api/frontend/v1/stv/students/getStudentsOrgform/$1/$2/$3/$4/$5'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/stg/(:any)/orgform/(:any)/semester/(:any)'] = 'api/frontend/v1/stv/students/getStudentsOrgform/$1/$2/$3/$4'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/stg/(:any)/orgform/(:any)'] = 'api/frontend/v1/stv/students/getStudentsOrgform/$1/$2/$3'; -// (studiensemester_kurzbz)/(studiengang_kz)[/(semester)[/(verband)[/(gruppe)]]] -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2'; -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/(:num)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2/$3'; -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/(:num)/(:any)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2/$3/$4'; -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/(:num)/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2/$3/$4/$5'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/stg/(:any)/semester/(:any)/group/(:any)'] = 'api/frontend/v1/stv/students/getStudentsSpezialgruppe/$1/$2/$3/$4'; -// (studiensemester_kurzbz)/(studiengang_kz)/(orgform)/(semester)/grp/(gruppe) -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/([A-Z]{2,3})/(:num)/grp/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsOrgformSpezialgruppe/$1/$2/$3/$4/$5'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/stg/(:any)/orgform/(:any)/semester/(:any)/group/(:any)'] = 'api/frontend/v1/stv/students/getStudentsOrgformSpezialgruppe/$1/$3/$2/$4/$5'; -// (studiensemester_kurzbz)/(studiengang_kz)/(orgform)[/(semester)[/(verband)[/(gruppe)]]] -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/([A-Z]{2,3})'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3'; -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/([A-Z]{2,3})/(:num)'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3/$4'; -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/([A-Z]{2,3})/(:num)/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3/$4/$5'; -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/([A-Z]{2,3})/(:num)/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3/$4/$5/$6'; +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/prestudent/(:any)'] = 'api/frontend/v1/stv/students/getPrestudent/$1/$2'; + +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/student/(:any)'] = 'api/frontend/v1/stv/students/getStudent/$1/$2'; + +$route['api/frontend/v1/treemenudata/stv/stdsem/(:any)/person/(:any)'] = 'api/frontend/v1/stv/students/getPerson/$1/$2'; + +$route['api/frontend/v1/treemenudata/stv/.*'] = 'api/frontend/v1/stv/students/index'; + +// Lvverwaltung List Routes +$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)/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'; -// // (studiensemester_kurzbz)/uid/(uid) -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/uid/(:any)'] = 'api/frontend/v1/stv/Students/getStudent/$1/$2'; -// // (studiensemester_kurzbz)/prestudent/(prestudent_id) -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/prestudent/(:num)'] = 'api/frontend/v1/stv/Students/getPrestudent/$1/$2'; -// // (studiensemester_kurzbz)/person/(person_id) -$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/person/(:num)'] = 'api/frontend/v1/stv/Students/getPerson/$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/stv.php b/application/config/stv.php index 639c5d23b..b12153eda 100644 --- a/application/config/stv.php +++ b/application/config/stv.php @@ -1,6 +1,27 @@ [ + 'js' => 'path/to/snippet.js', // tabulator config snippet (eg: return { name: 'Name', field: 'fieldname' }; ) + 'default' => "SQL SELECT statement for value", + 'joins' => [ + // additional joins needed for the SELECT statement above + [ + 'tablename', + 'join condition', // single fieldname for USING or full condition for ON + 'LEFT|RIGHT', // optional - join type + 'after_xxx|before_xxx|end' // optional - position in query + ] + // ... + ] + ] +]; +*/ + $config['tabs'] = [ 'details' => [ @@ -139,13 +160,25 @@ $config['students_tab_order'] = [ $config['stv_prestudent_tags'] = [ 'prioone' => ['readonly' => false], - 'priotwo' => ['readonly' => true], + 'priotwo' => ['readonly' => false], 'hinweis' => ['readonly' => false], - 'hinweis_assistenz' => ['readonly' => true], - 'hinweis_kf' => ['readonly' => true], + 'hinweis_assistenz' => ['readonly' => false], + 'hinweis_kf' => ['readonly' => false], 'hinweis_lehrende' => ['readonly' => false], - 'hinweis_stg_kf' => ['readonly' => true], - 'finished_stg' => ['readonly' => true], - 'finished_kf' => ['readonly' => true], - 'inwork_kf' => ['readonly' => true], + 'hinweis_stg_kf' => ['readonly' => false], + 'finished_stg' => ['readonly' => false], + 'finished_kf' => ['readonly' => false], + 'inwork_kf' => ['readonly' => false], + 'dd_auto' => ['readonly' => true], + 'wh_auto' => ['readonly' => true], + 'prewh_auto' => ['readonly' => true], + 'out_auto' => ['readonly' => true], + 'zgv_auto' => ['readonly' => true], + 'unterbrecher_auto' => ['readonly' => true], + 'stbtr_erh_auto' => ['readonly' => true], + 'jgv_auto' => ['readonly' => true], + 'in_auto' => ['readonly' => true], + 'preabbr_auto' => ['readonly' => true], ]; + +$config['userAutomatedTags'] = 'sftest'; diff --git a/public/js/api/factory/stv/verband.js b/application/config/treemenu.php similarity index 60% rename from public/js/api/factory/stv/verband.js rename to application/config/treemenu.php index 114931e4c..72a02947b 100644 --- a/public/js/api/factory/stv/verband.js +++ b/application/config/treemenu.php @@ -1,5 +1,7 @@ +. */ -export default { - get(path) { - let url = 'api/frontend/v1/stv/verband'; - if (path) - url += '/' + path; - return { - method: 'get', - url - }; - }, - favorites: { - get() { - return { - method: 'get', - url: 'api/frontend/v1/stv/favorites' - }; - }, - set(favorites) { - return { - method: 'post', - url: 'api/frontend/v1/stv/favorites/set', - params: { favorites } - }; - } - } -}; \ No newline at end of file +if (!defined('BASEPATH')) exit('No direct script access allowed'); + +$config['stv'] = [ + "library" => "treemenu/StudVwLib", + "permissions" => ["admin:r", "assistenz:r"] +]; +$config['lvv'] = [ + "library" => "treemenu/LvVwLib", + "permissions" => ["admin:r", "assistenz:r"] +]; diff --git a/application/config/treemenu/custom/lvvw.php b/application/config/treemenu/custom/lvvw.php new file mode 100644 index 000000000..977c0459e --- /dev/null +++ b/application/config/treemenu/custom/lvvw.php @@ -0,0 +1,32 @@ +. + */ + +if (!defined('BASEPATH')) exit('No direct script access allowed'); + +$config['root'] = [ + 'stg' => [ + 'semester' => [ + 'group', + 'verband' => [ + 'group' + ] + ], + 'orgform' + ] +]; diff --git a/application/config/treemenu/stg_base.php b/application/config/treemenu/stg_base.php new file mode 100644 index 000000000..fcb9748b7 --- /dev/null +++ b/application/config/treemenu/stg_base.php @@ -0,0 +1,65 @@ +. + */ + +if (!defined('BASEPATH')) exit('No direct script access allowed'); + +$config['root'] = [ + 'stg' => [ + 'prestudent' => [ + 'stdsem' => [ + 'interessenten' => [ + 'bewerbungnichtabgeschickt', + 'bewerbungabgeschickt', + 'zgv', + 'statusbestaetigt' => [ + 'statusbestaetigtrtnichtangemeldet', + 'statusbestaetigtrtangemeldet' + ], + 'reihungstestnichtangemeldet', + 'reihungstestangemeldet' + ], + 'bewerber' => [ + 'bewerberrtnichtangemeldet', + 'bewerberrtangemeldet' => [ + 'bewerberrtangemeldetteilgenommen', + 'bewerberrtangemeldetnichtteilgenommen' + ] + ], + 'aufgenommen', + 'warteliste', + 'absage', + 'incoming' + ] + ], + 'semester' => [ + 'group', + 'verband' => [ + 'group' + ] + ] + ], + 'inout' => [ + 'incoming', + 'outgoing', + 'shared_studies' + ] +]; + +// copy stg tree into stg/orgforms +$config['root']['stg']['orgform'] = $config['root']['stg']; diff --git a/application/controllers/Redirect.php b/application/controllers/Redirect.php new file mode 100644 index 000000000..f4a9f4cd7 --- /dev/null +++ b/application/controllers/Redirect.php @@ -0,0 +1,44 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * This controller redirects urls to another location. This is mainly used + * with the $routes config + */ +class Redirect extends CI_Controller +{ + public function __construct() + { + parent::__construct(); + } + + /** + * Main function to handle all redirects. + * + * @param string $method - the redirect code (30x) + * @param array $params - the url to redirect to + * + * @return void + */ + public function _remap($method, $params) + { + redirect($params, 'location', $method); + } +} diff --git a/application/controllers/api/frontend/v1/stv/Favorites.php b/application/controllers/api/frontend/v1/Favorites.php similarity index 78% rename from application/controllers/api/frontend/v1/stv/Favorites.php rename to application/controllers/api/frontend/v1/Favorites.php index 951eb01a4..787163abb 100644 --- a/application/controllers/api/frontend/v1/stv/Favorites.php +++ b/application/controllers/api/frontend/v1/Favorites.php @@ -20,8 +20,8 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); /** * This controller operates between (interface) the JS (GUI) and the back-end - * Provides data to the ajax get calls about favorite verbände - * Listens to ajax post calls to change the favorite verbände data + * Provides data to the ajax get calls about favorite treemenu entries + * Listens to ajax post calls to change the favorite treemenu entries * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON */ class Favorites extends FHCAPI_Controller @@ -29,7 +29,7 @@ class Favorites extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'index' => self::PERM_LOGGED, + 'get' => self::PERM_LOGGED, 'set' => self::PERM_LOGGED ]); @@ -37,19 +37,19 @@ class Favorites extends FHCAPI_Controller $this->load->model('system/Variable_model', 'VariableModel'); } - public function index() + public function get($app) { - $result = $this->VariableModel->getVariables(getAuthUID(), ['stv_favorites']); + $result = $this->VariableModel->getVariables(getAuthUID(), [$app . '_favorites']); $data = $this->getDataOrTerminateWithError($result); if (!$data) $this->terminateWithSuccess(null); else - $this->terminateWithSuccess($data['stv_favorites'] ?? null); + $this->terminateWithSuccess($data[$app . '_favorites'] ?? null); } - public function set() + public function set($app) { $this->load->library('form_validation'); @@ -60,7 +60,7 @@ class Favorites extends FHCAPI_Controller $favorites = $this->input->post('favorites'); - $result = $this->VariableModel->setVariable(getAuthUID(), 'stv_favorites', $favorites); + $result = $this->VariableModel->setVariable(getAuthUID(), $app . '_favorites', $favorites); $this->getDataOrTerminateWithError($result); diff --git a/application/controllers/api/frontend/v1/Lehrveranstaltung.php b/application/controllers/api/frontend/v1/Lehrveranstaltung.php index 935602391..66bda9ebf 100644 --- a/application/controllers/api/frontend/v1/Lehrveranstaltung.php +++ b/application/controllers/api/frontend/v1/Lehrveranstaltung.php @@ -56,7 +56,9 @@ class Lehrveranstaltung extends FHCAPI_Controller $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); - $lehrveranstaltungen = $this->_ci->LehreinheitModel->getLvsByEmployee($mitarbeiter_uid, $studiensemester_kurzbz, $stg_kz); + $studiengang_kz = $this->getStudiengangKz($stg_kz); + + $lehrveranstaltungen = $this->_ci->LehreinheitModel->getLvsByEmployee($mitarbeiter_uid, $studiensemester_kurzbz, $studiengang_kz); $lehrveranstaltungen_data = $this->getDataOrTerminateWithError($lehrveranstaltungen); $tree = []; @@ -75,15 +77,17 @@ class Lehrveranstaltung extends FHCAPI_Controller $this->terminateWithSuccess($tree); } - public function getByStg($studiensemester_kurzbz = null, $studiengang_kz = null, $semester = null) + 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)) $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); $verband = null; if (!is_null($semester) && !is_numeric($semester)) { - $verband = $semester; + $verband = $this->getOrgformKurzbz($semester); $semester = null; } @@ -260,18 +264,53 @@ class Lehrveranstaltung extends FHCAPI_Controller } } + protected function getStudiengangKz($studiengang_typ_kurzbz) + { + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); + $result = $this->StudiengangModel->loadWhere([ + 'LOWER(CONCAT(typ, kurzbz)) =' => strtolower($studiengang_typ_kurzbz) + ]); + + $data = $this->getDataOrTerminateWithError($result); + + if (!$data) + return null; + + return current($data)->studiengang_kz; + } + private function getStudiensemesterKurzbz($studiensemester_kurzbz = null) { if (!is_null($studiensemester_kurzbz)) { - $studiensemester_result = $this->_ci->StudiensemesterModel->load($studiensemester_kurzbz); + $studiensemester_result = $this->_ci->StudiensemesterModel->loadWhere([ + 'LOWER(studiensemester_kurzbz)' => strtolower($studiensemester_kurzbz) + ]); if (isError($studiensemester_result) || !hasData($studiensemester_result)) - $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); return getData($studiensemester_result)[0]->studiensemester_kurzbz; } $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); } + + protected function getOrgformKurzbz($orgform_kurzbz) + { + if ($orgform_kurzbz === null) + return null; + + $this->load->model('codex/Orgform_model', 'OrgformModel'); + $result = $this->OrgformModel->loadWhere([ + 'LOWER(orgform_kurzbz) =' => strtolower($orgform_kurzbz) + ]); + + $data = $this->getDataOrTerminateWithError($result); + + if (!$data) + return $orgform_kurzbz; + + return current($data)->orgform_kurzbz; + } } diff --git a/application/controllers/api/frontend/v1/LvPlan.php b/application/controllers/api/frontend/v1/LvPlan.php index 2a900e92d..36054003c 100644 --- a/application/controllers/api/frontend/v1/LvPlan.php +++ b/application/controllers/api/frontend/v1/LvPlan.php @@ -49,6 +49,8 @@ class LvPlan extends FHCAPI_Controller 'getLehrverband' => self::PERM_LOGGED, 'permissionOtherLvPlan' => self::PERM_LOGGED, 'compactibleEventTypes' => self::PERM_LOGGED, + 'getLeEvents' => self::PERM_LOGGED, + 'getLvEvents' => self::PERM_LOGGED, ]); $this->load->library('LogLib'); @@ -60,7 +62,13 @@ class LvPlan extends FHCAPI_Controller 'dbExecuteUser' => 'RESTful API' )); - $this->load->library('form_validation'); + $this->load->library('form_validation'); + $this->load->library('PhrasesLib'); + $this->loadPhrases( + array( + 'ui' + ) + ); } //------------------------------------------------------------------------------------------------------------------ @@ -196,6 +204,38 @@ class LvPlan extends FHCAPI_Controller )); } + public function getLeEvents($le_id = null, $start_date = null, $end_date = null, $stundenplan = 'stundenplandev') + { + + if (is_null($le_id) || is_null($start_date) || is_null($end_date)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + if ($stundenplan !== 'stundenplandev' && $stundenplan !== 'stundenplan') + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $this->load->library('StundenplanLib'); + + $result = $this->stundenplanlib->getEventsByLE($le_id, $start_date, $end_date, $stundenplan); + $lvplanEvents = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($lvplanEvents); + } + + public function getLvEvents($lv_id = null, $start_date = null, $end_date = null, $stundenplan = 'stundenplandev') + { + if (is_null($lv_id) || is_null($start_date) || is_null($end_date)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + if ($stundenplan !== 'stundenplandev' && $stundenplan !== 'stundenplan') + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $this->load->library('StundenplanLib'); + + $result = $this->stundenplanlib->getEventsByLV($lv_id, $start_date, $end_date, $stundenplan); + + $this->terminateWithSuccess(hasData($result) ? getData($result) : []); + } + //TODO: delete this function if we don't use the old calendar export endpoints anymore public function studiensemesterDateInterval($date) { diff --git a/application/controllers/api/frontend/v1/Treemenu.php b/application/controllers/api/frontend/v1/Treemenu.php new file mode 100644 index 000000000..e25610370 --- /dev/null +++ b/application/controllers/api/frontend/v1/Treemenu.php @@ -0,0 +1,148 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +use \ReflectionMethod as ReflectionMethod; +use \ReflectionFunction as ReflectionFunction; + +/** + * This controller operates between (interface) the JS (GUI) and the back-end + * Provides data to the ajax get calls about menues + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Treemenu extends FHCAPI_Controller +{ + public function __construct() + { + // Load Router + $router = load_class('Router'); + + // Load Config + $config = load_class('Config'); + $config->load('treemenu'); + + // Get Treemenu name & config + $name = $router->method; + $conf = $config->item($name); + + // Get Permissions from config + $permissions = $conf['permissions'] ?? self::PERM_ANONYMOUS; + + // Call constructor + parent::__construct([ + $name => $permissions + ]); + + // Load Library + $libraryName = $conf['library'] ?? null; + $this->load->library($libraryName, null, 'treemenulib'); + } + + /** + * @param string $method + * @param array $params (optional) + * + * @return void + */ + public function _remap($method, $params = []) + { + if (!$this->treemenulib) + show_404(); + + $this->addMeta('ci_method', $method); + $this->addMeta('ci_params', $params); + + $params = $this->uri->uri_to_assoc(6); // 6 = start of $params + $config = $this->treemenulib->config; + + foreach (array_keys($params) as $key) { + if (!isset($config[$key]) || !$config[$key]) + show_404(); + + $config = $config[$key]; + } + + // Prepare aliases + $argumentaliases = []; + if (property_exists($this->treemenulib, 'path_to_argument')) + $argumentaliases = array_flip($this->treemenulib->path_to_argument); + $methodaliases = []; + if (property_exists($this->treemenulib, 'redirect_method')) + $methodaliases = $this->treemenulib->redirect_method; + + $items = []; + foreach ($config as $uripart => $children) { + if (!is_array($children)) { + $uripart = $children; + $children = []; + } + + $methodName = $uripart; + if (isset($methodaliases[$methodName])) { + $methodName = $methodaliases[$methodName]; + } elseif (strpbrk($methodName, ' _-') !== false) { + $methodName = lcfirst(str_replace([' ', '_', '-'], '', ucwords($methodName, " \t\r\n\f\v_-"))); + } + + // Prepare arguments + if (is_callable($methodName, false, $name)) { + if (is_string($methodName) && is_callable([$this->treemenulib, $methodName])) + $callable = [$this->treemenulib, $methodName]; + else + $callable = $methodName; + } else { + $callable = [$this->treemenulib, $methodName]; + } + + if (is_array($callable)) + $reflection = new ReflectionMethod($callable[0], $callable[1]); + elseif (is_string($callable) && strstr($callable, '::')) + $reflection = new ReflectionMethod($callable); + elseif ($name != 'Closure::__invoke' && strstr($name, '::__invoke')) + $reflection = new ReflectionMethod($callable, '__invoke'); + else + $reflection = new ReflectionFunction($callable); + + $arguments = []; + foreach ($reflection->getParameters() as $arg) { + if ($arg->name == 'path_template') { + $path = $params; + $path[$uripart] = '%s'; + $arguments[] = $this->uri->assoc_to_uri($path); + } elseif ($arg->name == 'has_children') { + $arguments[] = $children; + } elseif ($arg->name == 'original_method') { + $arguments[] = $uripart; + } elseif (isset($argumentaliases[$arg->name]) && isset($params[$argumentaliases[$arg->name]])) { + $arguments[] = $params[$argumentaliases[$arg->name]]; + } elseif (isset($params[$arg->name])) { + $arguments[] = $params[$arg->name]; + } else { + $arguments[] = null; + } + } + + // Call function from lib + $items = array_merge($items, call_user_func_array($callable, $arguments)); + } + + $this->terminateWithSuccess($items); + } +} diff --git a/application/controllers/api/frontend/v1/detailheader/Detailheader.php b/application/controllers/api/frontend/v1/detailheader/Detailheader.php index 2aacb4cc0..89e9821eb 100644 --- a/application/controllers/api/frontend/v1/detailheader/Detailheader.php +++ b/application/controllers/api/frontend/v1/detailheader/Detailheader.php @@ -9,9 +9,10 @@ class Detailheader extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'getHeader' => ['vertrag/mitarbeiter:r', 'basis/mitarbeiter:r'], - 'getPersonAbteilung' => ['vertrag/mitarbeiter:r', 'basis/mitarbeiter:r'], - 'getLeitungOrg' => ['vertrag/mitarbeiter:r', 'basis/mitarbeiter:r'], + 'getHeader' => ['vertrag/mitarbeiter:r', 'basis/mitarbeiter:r', 'admin:r', 'assistenz:r'], + 'getPersonAbteilung' => ['vertrag/mitarbeiter:r', 'basis/mitarbeiter:r', 'admin:r', 'assistenz:r'], + 'getLeitungOrg' => ['vertrag/mitarbeiter:r', 'basis/mitarbeiter:r', 'admin:r', 'assistenz:r'], + 'getSemesterStati' => ['admin:r', 'assistenz:r'], ]); } @@ -48,6 +49,17 @@ class Detailheader extends FHCAPI_Controller $this->terminateWithSuccess(current($data)); } + public function getSemesterStati($prestudent_id) + { + $this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); + + $result = $this->PrestudentstatusModel->getAllPrestudentstatiWithStudiensemester($prestudent_id); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + } diff --git a/application/controllers/api/frontend/v1/lv/Config.php b/application/controllers/api/frontend/v1/lv/Config.php new file mode 100644 index 000000000..4f39c6b8b --- /dev/null +++ b/application/controllers/api/frontend/v1/lv/Config.php @@ -0,0 +1,86 @@ + ['admin:r', 'assistenz:r'], + 'set' => ['admin:r', 'assistenz:r'], + ]); + + $this->_ci = &get_instance(); + $this->_setAuthUID(); + + $this->loadPhrases([ + 'lehre' + ]); + + $this->_ci->load->library('VariableLib', ['uid' => $this->_uid]); + $this->_ci->load->library('PermissionLib'); + + } + + + public function get() + { + if (!($this->permissionlib->isBerechtigt('basis/tempus')) && !($this->permissionlib->isBerechtigt('lv-plan'))) + $this->terminateWithSuccess([]); + + $ignore_kollision = $this->_ci->variablelib->getVar('ignore_kollision'); + $ignore_zeitsperre = $this->_ci->variablelib->getVar('ignore_zeitsperre'); + $ignore_reservierung = $this->_ci->variablelib->getVar('ignore_reservierung'); + + $config['ignore_kollision'] = [ + "type" => "checkbox", + "label" => 'ignore_kollision', + "value" => $ignore_kollision, + ]; + + $config['ignore_zeitsperre'] = [ + "type" => "checkbox", + "label" => 'ignore_zeitsperre', + "value" => $ignore_zeitsperre, + ]; + + $config['ignore_reservierung'] = [ + "type" => "checkbox", + "label" => 'ignore_reservierung', + "value" => $ignore_reservierung, + ]; + + $this->terminateWithSuccess($config); + } + public function set() + { + if (!($this->permissionlib->isBerechtigt('basis/tempus')) && !($this->permissionlib->isBerechtigt('lv-plan'))) + $this->terminateWithSuccess([]); + + $this->load->model('system/Variable_model', 'VariableModel'); + + foreach (['ignore_kollision','ignore_zeitsperre','ignore_reservierung'] as $variable) + { + if ($this->_ci->input->post($variable) !== null) + { + $this->VariableModel->update(array('uid' => $this->_uid, 'name' => $variable), array('wert' => $this->input->post($variable))); + + } + } + + $this->terminateWithSuccess(); + } + + private function _setAuthUID() + { + $this->_uid = getAuthUID(); + + if (!$this->_uid) + show_error('User authentification failed'); + } +} diff --git a/application/controllers/api/frontend/v1/lv/Favorites.php b/application/controllers/api/frontend/v1/lv/Favorites.php deleted file mode 100644 index 080a4ec6e..000000000 --- a/application/controllers/api/frontend/v1/lv/Favorites.php +++ /dev/null @@ -1,47 +0,0 @@ - self::PERM_LOGGED, - 'set' => self::PERM_LOGGED - ]); - - // Load models - $this->load->model('system/Variable_model', 'VariableModel'); - } - - public function index() - { - $result = $this->VariableModel->getVariables(getAuthUID(), ['lv_favorites']); - - $data = $this->getDataOrTerminateWithError($result); - - if (!$data) - $this->terminateWithSuccess(null); - else - $this->terminateWithSuccess(isset($data['lv_favorites']) ? $data['lv_favorites'] : null); - } - - public function set() - { - $this->load->library('form_validation'); - - $this->form_validation->set_rules('favorites', 'Favorites', 'required'); - - if (!$this->form_validation->run()) - $this->terminateWithValidationErrors($this->form_validation->error_array()); - - $favorites = $this->input->post('favorites'); - - $result = $this->VariableModel->setVariable(getAuthUID(), 'lv_favorites', $favorites); - - $this->getDataOrTerminateWithError($result); - - $this->terminateWithSuccess(true); - } -} diff --git a/application/controllers/api/frontend/v1/lv/Gruppe.php b/application/controllers/api/frontend/v1/lv/Gruppe.php index daebe8a61..f60d3a99a 100644 --- a/application/controllers/api/frontend/v1/lv/Gruppe.php +++ b/application/controllers/api/frontend/v1/lv/Gruppe.php @@ -15,6 +15,7 @@ class Gruppe extends FHCAPI_Controller 'getBenutzerSearch' => ['admin:r', 'assistenz:r'], 'getAllSearch' => ['admin:r', 'assistenz:r'], 'getByLehreinheit' => ['admin:r', 'assistenz:r'], + 'getGruppe' => ['admin:r', 'assistenz:r'], ]); $this->_ci = &get_instance(); @@ -67,7 +68,7 @@ class Gruppe extends FHCAPI_Controller $this->checkPermission($lehreinheit_id); - $result = $this->_ci->LehreinheitgruppeModel->addGroup($lehreinheit_id, $gid, !($lehrverband === 'false')); + $result = $this->_ci->LehreinheitgruppeModel->addGroup($lehreinheit_id, $gid, $lehrverband === true || $lehrverband === 'true'); if (isError($result)) $this->terminateWithError(getError($result)); @@ -75,6 +76,71 @@ class Gruppe extends FHCAPI_Controller $this->terminateWithSuccess($result); } + public function getGruppe() + { + $lehrverband = $this->input->post('lehrverband'); + + $gruppen_result = array(); + + if ($lehrverband === false) + { + $gruppen_result = $this->_ci->GruppeModel->loadWhere(array( + 'studiengang_kz' => $this->input->post('stg_kz'), + 'gruppe_kurzbz' => $this->input->post('gruppe_kurzbz'), + 'aktiv' => true + )); + } + else if ($lehrverband === true) + { + + if (!isEmptyString($this->input->post('verband'))) + { + $this->LehrverbandModel->db->where('verband', $this->input->post('verband')); + } + else + { + $this->LehrverbandModel->db->group_start(); + $this->LehrverbandModel->db->where("trim(verband) = ''"); + $this->LehrverbandModel->db->or_where("verband IS NULL"); + $this->LehrverbandModel->db->group_end(); + } + + if (!isEmptyString($this->input->post('gruppe'))) + { + $this->LehrverbandModel->db->where('gruppe', $this->input->post('gruppe')); + } + else + { + $this->LehrverbandModel->db->group_start(); + $this->LehrverbandModel->db->where("trim(gruppe) = ''"); + $this->LehrverbandModel->db->or_where("gruppe IS NULL"); + $this->LehrverbandModel->db->group_end(); + } + + if (!isEmptyString((string)$this->input->post('semester'))) + { + $this->LehrverbandModel->db->where('semester', $this->input->post('semester')); + } + else + { + + $this->LehrverbandModel->db->group_start(); + $this->LehrverbandModel->db->where("semester = ''"); + $this->LehrverbandModel->db->or_where("semester IS NULL"); + $this->LehrverbandModel->db->group_end(); + } + + $gruppen_result = $this->LehrverbandModel->loadWhere(array('studiengang_kz' => $this->input->post('stg_kz'), 'aktiv' => true)); + } + + if (!hasData($gruppen_result)) + return $this->terminateWithError('No group found'); + + $gruppen_array = getData($gruppen_result)[0]; + + $this->terminateWithSuccess($gruppen_array->gid); + } + public function getByLehreinheit($lehreinheit_id = null) { if (is_null($lehreinheit_id) || !ctype_digit((string)$lehreinheit_id)) diff --git a/application/controllers/api/frontend/v1/lv/Lektor.php b/application/controllers/api/frontend/v1/lv/Lektor.php index cce7f6e8b..64cfd3f1c 100644 --- a/application/controllers/api/frontend/v1/lv/Lektor.php +++ b/application/controllers/api/frontend/v1/lv/Lektor.php @@ -134,9 +134,14 @@ class Lektor extends FHCAPI_Controller $this->terminateWithValidationErrors($this->form_validation->error_array()); } - if (isset($formData['semesterstunden']) && (!is_numeric($formData['semesterstunden']) || $formData['semesterstunden'] === '')) + $nullable_fields = array('semesterstunden', 'stundensatz', 'planstunden'); + + foreach ($nullable_fields as $nullable_field) { - $formData['semesterstunden'] = null; + if (isset($formData[$nullable_field]) && (!is_numeric($formData[$nullable_field]) || $formData[$nullable_field] === '')) + { + $formData[$nullable_field] = null; + } } $lehreinheit_permission = $this->checkPermission($lehreinheit_id, array('admin', 'assistenz', 'lv-plan')); @@ -144,6 +149,9 @@ class Lektor extends FHCAPI_Controller if (!$lehreinheit_permission) $this->terminateWithError($this->p->t('ui', 'error_fieldWriteAccess')); + if (!is_null($this->getLektorVertrag($lehreinheit_id, $mitarbeiter_uid)) && (array_key_exists('mitarbeiter_uid', $formData) && $mitarbeiter_uid !== $formData['mitarbeiter_uid'])) + $this->terminateWithError($this->p->t('ui', 'error_fieldWriteAccess')); + $result = $this->_ci->lektorlib->updateLektorFromLehreinheit($lehreinheit_id, $mitarbeiter_uid, $formData); if (isError($result)) $this->terminateWithError(getError($result)); @@ -154,7 +162,7 @@ class Lektor extends FHCAPI_Controller { $value = str_replace(',', '.', $value); - if (!is_numeric($value)) + if (!is_numeric($value) && $value !== "") { $this->form_validation->set_message('_check_decimal', 'Das Feld {field} muss eine Zahl sein.'); return false; diff --git a/application/controllers/api/frontend/v1/lv/Noten.php b/application/controllers/api/frontend/v1/lv/Noten.php new file mode 100644 index 000000000..614ee6609 --- /dev/null +++ b/application/controllers/api/frontend/v1/lv/Noten.php @@ -0,0 +1,84 @@ + 'student/noten:r', + 'getTeacherProposal' => 'student/noten:r', + ]); + + // Load Libraries + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + + // Load Phrases + $this->loadPhrases([ + 'stv', + 'person', + 'lehre' + ]); + } + + public function getCertificate($lv_id, $studiensemester_kurzbz = null) + { + if (is_null($lv_id) || !ctype_digit((string)$lv_id)) + $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $this->load->model('education/lehrveranstaltung_model', 'LehrveranstaltungModel'); + $this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel'); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + + $result = $this->LehrveranstaltungModel->loadWhere([ + 'lehrveranstaltung_id' => $lv_id + ]); + + $lehrveranstaltung = $this->getDataOrTerminateWithError($result); + + if (!$lehrveranstaltung) + $this->terminateWithSuccess([]); + + if ($studiensemester_kurzbz !== null && !$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz)) + { + $this->terminateWithError($studiensemester_kurzbz . ' - ' . $this->p->t('lehre', 'error_noStudiensemester')); + } + + $result = $this->ZeugnisnoteModel->getZeugnisnoten(null, $studiensemester_kurzbz, $lehrveranstaltung[0]->lehrveranstaltung_id); + + $grades = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($grades); + } + + public function getTeacherProposal($lv_id, $studiensemester_kurzbz = null) + { + if (is_null($lv_id) || !ctype_digit((string)$lv_id)) + $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $this->load->model('education/lehrveranstaltung_model', 'LehrveranstaltungModel'); + $this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel'); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + + $result = $this->LehrveranstaltungModel->loadWhere([ + 'lehrveranstaltung_id' => $lv_id + ]); + + $lehrveranstaltung = $this->getDataOrTerminateWithError($result); + + if (!$lehrveranstaltung) + $this->terminateWithSuccess([]); + + if ($studiensemester_kurzbz !== null && !$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz)) + { + $this->terminateWithError($studiensemester_kurzbz . ' - ' . $this->p->t('lehre', 'error_noStudiensemester')); + } + + $result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, null, $studiensemester_kurzbz); + + $grades = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($grades); + } + +} diff --git a/application/controllers/api/frontend/v1/lv/Setup.php b/application/controllers/api/frontend/v1/lv/Setup.php index eea4befa5..4b91b99c1 100644 --- a/application/controllers/api/frontend/v1/lv/Setup.php +++ b/application/controllers/api/frontend/v1/lv/Setup.php @@ -19,6 +19,8 @@ if (!defined('BASEPATH')) exit('No direct script access allowed'); +use CI3_Events as Events; + class Setup extends FHCAPI_Controller { private $_ci; @@ -27,7 +29,8 @@ class Setup extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'getTabs' => ['admin:r', 'assistenz:r'], + 'getLETabs' => ['admin:r', 'assistenz:r'], + 'getLVTabs' => ['admin:r', 'assistenz:r'], 'getStudiensemester' => ['admin:r', 'assistenz:r'], 'getSprache' => ['admin:r', 'assistenz:r'], 'getRaumtyp' => ['admin:r', 'assistenz:r'], @@ -41,9 +44,10 @@ class Setup extends FHCAPI_Controller $this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); $this->_ci->load->library('VariableLib', ['uid' => $this->_uid]); + $this->_ci->load->helper('hlp_document'); } - public function getTabs() + public function getLETabs() { $tabs['details'] = array ( 'title' => 'Details', @@ -60,11 +64,43 @@ class Setup extends FHCAPI_Controller 'component' => absoluteJsImportUrl('public/js/components/LVVerwaltung/Tabs/Lektor.js'), 'config' => [] ); + $tabs['termine'] = array ( + 'title' => 'Termine', + 'component' => absoluteJsImportUrl('public/js/components/LVVerwaltung/Tabs/Termine.js'), + 'config' => [] + ); $tabs['notiz'] = array ( 'title' => 'Notizen', 'component' => absoluteJsImportUrl('public/js/components/LVVerwaltung/Tabs/Notiz.js'), 'config' => [] ); + + Events::trigger('lvv_conf_tabs', function & () use (&$tabs) { + return $tabs; + }); + + $this->terminateWithSuccess($tabs); + } + + public function getLVTabs() + { + $tabs['termine'] = array ( + 'title' => 'Termine', + 'component' => absoluteJsImportUrl('public/js/components/LVVerwaltung/Tabs/LVTermine.js'), + 'config' => [] + ); + $tabs['noten'] = array ( + 'title' => 'Noten', + 'component' => absoluteJsImportUrl('public/js/components/LVVerwaltung/Tabs/Noten.js'), + 'config' => [ + 'usePoints' => defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE, + 'edit' => 'both', // Possible values: both|header|inline + 'delete' => 'inline', // Possible values: both|header|inline + 'documents' => 'inline', // Possible values: both|header|inline + 'documentslist' => gradesDocumentsList(), + 'semesterSelect' => false + ] + ); $this->terminateWithSuccess($tabs); } diff --git a/application/controllers/api/frontend/v1/lv/StgTree.php b/application/controllers/api/frontend/v1/lv/StgTree.php deleted file mode 100644 index 8272da978..000000000 --- a/application/controllers/api/frontend/v1/lv/StgTree.php +++ /dev/null @@ -1,117 +0,0 @@ -method] = ['admin:r', 'assistenz:r']; - parent::__construct($permissions); - - $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); - } - - public function _remap($method, $params = []) - { - if ($method == '' || $method == 'index') - return $this->getBase(); - - if (!$this->permissionlib->isBerechtigt('assistenz', 's', $method) - && !$this->permissionlib->isBerechtigt('admin', 's', $method) - ) { - return $this->_outputAuthError([$method => ['admin:r', 'assistenz:r']]); - } - - return $this->getStudiengang($method); - show_404(); - } - - protected function getBase() - { - $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); - - $this->StudiengangModel->addDistinct(); - $this->StudiengangModel->addSelect("v.studiengang_kz AS link"); - $this->StudiengangModel->addSelect( - "CONCAT(kurzbzlang, ' (', UPPER(CONCAT(typ, kurzbz)), ') - ', tbl_studiengang.bezeichnung) AS name", - false - ); - $this->StudiengangModel->addSelect('erhalter_kz'); - $this->StudiengangModel->addSelect('typ'); - $this->StudiengangModel->addSelect('kurzbz'); - $this->StudiengangModel->addSelect('studiengang_kz'); - $this->StudiengangModel->addSelect('studiengang_kz AS stg_kz'); - - $this->StudiengangModel->addOrder('erhalter_kz'); - $this->StudiengangModel->addOrder('typ'); - $this->StudiengangModel->addOrder('kurzbz'); - - $stgs = $this->permissionlib->getSTG_isEntitledFor('admin') ?: []; - $stgs = array_merge($stgs, $this->permissionlib->getSTG_isEntitledFor('assistenz') ?: []); - - if (!$stgs) - $this->terminateWithSuccess([]); - - $this->StudiengangModel->db->where_in('studiengang_kz', $stgs); - - $result = $this->StudiengangModel->loadWhere(['v.aktiv' => true]); - - $list = $this->getDataOrTerminateWithError($result); - - $this->terminateWithSuccess($list); - } - - protected function getStudiengang($studiengang_kz) - { - $link = $studiengang_kz . '/'; - - $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); - - $this->StudiengangModel->addDistinct(); - $this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", semester) AS link", false); - $this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester ORDER BY verband, gruppe LIMIT 1)) AS name", false); - $this->StudiengangModel->addSelect("TRUE AS leaf", false); - - $this->StudiengangModel->addSelect('semester'); - $this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); - - $this->StudiengangModel->addOrder('semester'); - - $result = $this->StudiengangModel->loadWhere([ - 'v.studiengang_kz' => $studiengang_kz, - 'v.aktiv' => true - ]); - $list = $this->getDataOrTerminateWithError($result); - - $result = $this->StudiengangModel->load($studiengang_kz); - $result = $this->getDataOrTerminateWithError($result); - if ($result) - { - if (current($result)->mischform) - { - $this->load->model('organisation/Studienordnung_model', 'StudienordnungModel'); - - $this->StudienordnungModel->addDistinct(); - $this->StudienordnungModel->addSelect("CONCAT(studiengang_kz, '/', p.orgform_kurzbz) AS link"); - $this->StudienordnungModel->addSelect("p.orgform_kurzbz AS name"); - $this->StudienordnungModel->addSelect("TRUE as leaf", false); - - $this->StudienordnungModel->addJoin('lehre.tbl_studienplan p', 'studienordnung_id'); - - $result = $this->StudienordnungModel->loadWhere([ - 'aktiv' => true, - 'studiengang_kz' => $studiengang_kz, - 'p.orgform_kurzbz !=' => 'DDP' - ]); - $result = $this->getDataOrTerminateWithError($result); - - $list = array_merge($list, $result); - } - } - - $this->terminateWithSuccess($list); - } -} diff --git a/application/controllers/api/frontend/v1/organisation/Studiensemester.php b/application/controllers/api/frontend/v1/organisation/Studiensemester.php index 3c6b72d2f..eaafc6ac5 100644 --- a/application/controllers/api/frontend/v1/organisation/Studiensemester.php +++ b/application/controllers/api/frontend/v1/organisation/Studiensemester.php @@ -26,7 +26,9 @@ class Studiensemester extends FHCAPI_Controller 'getAll' => self::PERM_LOGGED, 'getAktNext' => self::PERM_LOGGED, 'getStudienjahrByStudiensemester' => self::PERM_LOGGED, - 'getAllStudiensemesterAndAktOrNext' => self::PERM_LOGGED + 'getAllStudiensemesterAndAktOrNext' => self::PERM_LOGGED, + 'current' => self::PERM_LOGGED, + 'set' => self::PERM_LOGGED ) ); // Load model StudiensemesterModel @@ -166,4 +168,39 @@ class Studiensemester extends FHCAPI_Controller $this->terminateWithSuccess(array($studiensemester, $aktuell)); } + + public function current() + { + $result = $this->StudiensemesterModel->getNearest(); + + $data = $this->getDataOrTerminateWithError($result); + + if (!$data) + $this->terminateWithError('Kein Studiensemester aktiv'); + if (count($data) > 1) + $this->terminateWithError('Mehrere Studiensemester aktiv'); + + $this->terminateWithSuccess(current($data)->studiensemester_kurzbz); + } + + public function set() + { + $this->load->library('AuthLib'); + $this->load->library('form_validation'); + + $this->form_validation->set_rules('studiensemester_kurzbz', 'Studiensemester', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $stdsem = $this->input->post('studiensemester_kurzbz'); + + $this->load->model('system/Variable_model', 'VariableModel'); + + $result = $this->VariableModel->setVariable(getAuthUID(), 'semester_aktuell', $stdsem); + + $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess(true); + } } diff --git a/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php b/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php index 8f2b1edd6..7c1c974e2 100644 --- a/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php +++ b/application/controllers/api/frontend/v1/stv/Abschlusspruefung.php @@ -246,12 +246,12 @@ class Abschlusspruefung extends FHCAPI_Controller { $searchString = $this->input->get('searchString') ?? ''; - $this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel'); + $this->load->model('person/Person_model', 'PersonModel'); - $result = $this->MitarbeiterModel->searchMitarbeiter($searchString, 'ohneMaUid'); + $result = $this->PersonModel->searchPerson($searchString, 'mitMaUid'); if (isError($result)) { - $this->terminateWithError($result, self::ERROR_TYPE_GENERAL); + $this->terminateWithError($result, self::ERROR_TYPE_GENERAL); } $this->terminateWithSuccess($result ?: []); diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index bc1fbebfe..cc6c04430 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -301,6 +301,7 @@ class Config extends FHCAPI_Controller public function student() { + $this->load->helper('hlp_document'); $result = []; $config = $this->config->item('tabs'); @@ -376,9 +377,9 @@ class Config extends FHCAPI_Controller 'config' => [ 'usePoints' => defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE, 'edit' => 'both', // Possible values: both|header|inline - 'delete' => 'both', // Possible values: both|header|inline - 'documents' => 'both', // Possible values: both|header|inline - 'documentslist' => $this->gradesDocumentsList() + 'delete' => 'inline', // Possible values: both|header|inline + 'documents' => 'inline', // Possible values: both|header|inline + 'documentslist' => gradesDocumentsList() ] ]; @@ -622,188 +623,6 @@ class Config extends FHCAPI_Controller ] + $this->kontoColumns(); } - /** - * Helper function to generate the default documentslist config for the - * grades tab. - * - * The resulting array consists of elements which are associative arrays - * that can have the following entries: - * title (required) on the first level this can be HTML code. - * permissioncheck (optional) an URL to an FHCAPI endpoint which returns - * true or false. - * link (optional) an URL that will be called if "action" and - * "children" are not defined. - * action (optional) an associative array that describes an - * POST action that will be called if "children" is - * not defined. - * It can have the following entries: - * - url (required) an URL to an FHCAPI endpoint. - * - post (optional) an associative array with the POST data to - * be sent. - * - response (optional) a string that will be displayed on success. - * children (optional) an array of child elements - * - * All strings that start with { and end with } in the URLs and the - * actions post parameter will be replaced with the corresponding - * attribute of the current dataset (e.G: {uid} will be replaced with the - * uid of the current dataset) - * - * @return array - */ - protected function gradesDocumentsList() - { - $permissioncheck = site_url("api/frontend/v1/documents/permissionAlternativeFormat/{studiengang_kz}"); - - $title_ger = $this->p->t("global", "deutsch"); - $title_eng = $this->p->t("global", "englisch"); - $title_ff = $this->p->t("stv", "document_certificate"); - $title_lv = $this->p->t("stv", "document_coursecertificate"); - - $link_ff = "documents/export/" . - "zertifikat.rdf.php/" . - "Zertifikat" . - "?stg_kz={studiengang_kz_lv}" . - "&uid={uid}" . - "&ss={studiensemester_kurzbz}" . - "&lvid={lehrveranstaltung_id}"; - $link_lv_ger = "documents/export/" . - "lehrveranstaltungszeugnis.rdf.php/" . - "LVZeugnis" . - "?stg_kz={studiengang_kz}" . - "&uid={uid}" . - "&ss={studiensemester_kurzbz}" . - "&lvid={lehrveranstaltung_id}"; - $link_lv_eng = "documents/export/" . - "lehrveranstaltungszeugnis.rdf.php/" . - "LVZeugnisEng" . - "?stg_kz={studiengang_kz}" . - "&uid={uid}" . - "&ss={studiensemester_kurzbz}" . - "&lvid={lehrveranstaltung_id}"; - - $archive_url = "api/frontend/v1/documents/archiveSigned"; - $archive_response = $this->p->t("stv", "document_signed_and_archived"); - $archive_post_ff = [ - "xml" => "zertifikat.rdf.php", - "xsl" => "Zertifikat", - "stg_kz" => "{studiengang_kz_lv}", - "uid" => "{uid}", - "ss" => "{studiensemester_kurzbz}", - "lvid" => "{lehrveranstaltung_id}" - ]; - $archive_post_lv_ger = [ - "xml" => "lehrveranstaltungszeugnis.rdf.php", - "xsl" => "LVZeugnis", - "stg_kz" => "{studiengang_kz}", - "uid" => "{uid}", - "ss" => "{studiensemester_kurzbz}", - "lvid" => "{lehrveranstaltung_id}" - ]; - $archive_post_lv_eng = [ - "xml" => "lehrveranstaltungszeugnis.rdf.php", - "xsl" => "LVZeugnisEng", - "stg_kz" => "{studiengang_kz}", - "uid" => "{uid}", - "ss" => "{studiensemester_kurzbz}", - "lvid" => "{lehrveranstaltung_id}" - ]; - - $list = [ - [ - 'title' => '', - 'children' => [ - [ - 'title' => $title_ff, - 'link' => site_url($link_ff) - ], - [ - 'title' => $title_lv, - 'children' => [ - [ - 'title' => $title_ger, - 'link' => site_url($link_lv_ger), - 'children' => [ - [ - 'title' => 'PDF', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_ger) - ], - [ - 'title' => 'DOC', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_ger . "&output=doc") - ], - [ - 'title' => 'ODT', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_ger . "&output=odt") - ] - ] - ], - [ - 'title' => $title_eng, - 'link' => site_url($link_lv_eng), - 'children' => [ - [ - 'title' => 'PDF', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_eng) - ], - [ - 'title' => 'DOC', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_eng . "&output=doc") - ], - [ - 'title' => 'ODT', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_eng . "&output=odt") - ] - ] - ] - ] - ] - ] - ], - [ - 'title' => '', - 'children' => [ - [ - 'title' => $title_ff, - 'action' => [ - 'url' => site_url($archive_url), - 'post' => $archive_post_ff, - 'response' => $archive_response - ] - ], - [ - 'title' => $title_lv, - 'children' => [ - [ - 'title' => $title_ger, - 'action' => [ - 'url' => site_url($archive_url), - 'post' => $archive_post_lv_ger, - 'response' => $archive_response - ] - ], - [ - 'title' => $title_eng, - 'action' => [ - 'url' => site_url($archive_url), - 'post' => $archive_post_lv_eng, - 'response' => $archive_response - ] - ] - ] - ] - ] - ] - ]; - - return $list; - } - /** * Sort tab list * diff --git a/application/controllers/api/frontend/v1/stv/LvTermine.php b/application/controllers/api/frontend/v1/stv/LvTermine.php index c29f56964..b9e23e77d 100644 --- a/application/controllers/api/frontend/v1/stv/LvTermine.php +++ b/application/controllers/api/frontend/v1/stv/LvTermine.php @@ -34,7 +34,7 @@ class LvTermine extends FHCAPI_Controller //TODO Build own lib or combine with Controller Stundenplan.php //here use of logic of Stundenplan.php, extended with parameters uid, grouping, and used dbTable - public function getStundenplan($uid, $start_date = null, $end_date = null, $groupConsecutiveHours = false, $dbStundenplanTable = "stundenplan") + public function getStundenplan($uid, $start_date = null, $end_date = null, $dbStundenplanTable = "stundenplan", $groupConsecutiveHours = false) { $student_uid = $uid; $semester_range = $this->studienSemesterErmitteln($start_date, $end_date); @@ -80,13 +80,6 @@ class LvTermine extends FHCAPI_Controller $stundenplan_data = $this->getDataOrTerminateWithError($stundenplan_data) ?? []; $this->terminateWithSuccess($stundenplan_data); - - $this->expand_object_information($stundenplan_data); - - $this->returnObj['$stundenplan_query'] = $stundenplan_query; - $this->returnObj['$student_lehrverband'] = $student_lehrverband; - $this->returnObj['$benutzer_gruppen'] = $benutzer_gruppen; - $this->terminateWithSuccess($stundenplan_data); } public function getStudiensemester() diff --git a/application/controllers/api/frontend/v1/stv/Student.php b/application/controllers/api/frontend/v1/stv/Student.php index 55aaef178..943df7b91 100644 --- a/application/controllers/api/frontend/v1/stv/Student.php +++ b/application/controllers/api/frontend/v1/stv/Student.php @@ -144,6 +144,7 @@ class Student extends FHCAPI_Controller . $this->PrestudentModel->escape($studiensemester_kurzbz) . ") AS statusofsemester" ); + $this->PrestudentModel->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz'); $this->PrestudentModel->addJoin('public.tbl_student s', 'prestudent_id', 'LEFT'); $this->PrestudentModel->addJoin('public.tbl_benutzer b', 'student_uid = uid', 'LEFT'); diff --git a/application/controllers/api/frontend/v1/stv/Students.php b/application/controllers/api/frontend/v1/stv/Students.php index 047652452..39ed72072 100644 --- a/application/controllers/api/frontend/v1/stv/Students.php +++ b/application/controllers/api/frontend/v1/stv/Students.php @@ -138,7 +138,7 @@ class Students extends FHCAPI_Controller $result = $this->studentlistlib->execute($studiensemester_kurzbz); $data = $this->getDataOrTerminateWithError($result); - + $this->decodeTagsJsonInResult($data); $this->terminateWithSuccess($data); } @@ -190,14 +190,14 @@ class Students extends FHCAPI_Controller ) AS semester", false); $this->studentlistlib->addSelect("COALESCE(v.verband::text, ''::text) AS verband"); $this->studentlistlib->addSelect("COALESCE(v.gruppe::text, ''::text) AS gruppe"); - + $this->addFilter($studiensemester_kurzbz); $result = $this->studentlistlib->execute($studiensemester_kurzbz); $data = $this->getDataOrTerminateWithError($result); - + $this->decodeTagsJsonInResult($data); $this->terminateWithSuccess($data); } @@ -235,59 +235,66 @@ class Students extends FHCAPI_Controller ) AS semester", false); $this->studentlistlib->addSelect("COALESCE(v.verband::text, ''::text) AS verband"); $this->studentlistlib->addSelect("COALESCE(v.gruppe::text, ''::text) AS gruppe"); - + $this->addFilter($studiensemester_kurzbz); $result = $this->studentlistlib->execute($studiensemester_kurzbz); $data = $this->getDataOrTerminateWithError($result); - + $this->decodeTagsJsonInResult($data); $this->terminateWithSuccess($data); } public function getPrestudents( - $studiengang_kz, + $studiengang_typ_kurzbz, $studiensemester_kurzbz = null, $filter = null ) { $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', array( - 'studiengang_kz' => $studiengang_kz, + 'studiengang_typ_kurzbz' => $studiengang_typ_kurzbz, 'studiensemester_kurzbz' => $studiensemester_kurzbz, 'filter' => $filter )); - $this->fetchPrestudents($studiengang_kz, $studiensemester_kurzbz, $filter); + $this->fetchPrestudents($studiengang_typ_kurzbz, $studiensemester_kurzbz, $filter); } public function getPrestudentsOrgform( - $studiengang_kz, + $studiengang_typ_kurzbz, $orgform_kurzbz, $studiensemester_kurzbz = null, $filter = null ) { $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', array( - 'studiengang_kz' => $studiengang_kz, + 'studiengang_typ_kurzbz' => $studiengang_typ_kurzbz, 'studiensemester_kurzbz' => $studiensemester_kurzbz, 'filter' => $filter, 'orgform_kurzbz' => $orgform_kurzbz )); - $this->fetchPrestudents($studiengang_kz, $studiensemester_kurzbz, $filter, $orgform_kurzbz); + $this->fetchPrestudents($studiengang_typ_kurzbz, $studiensemester_kurzbz, $filter, $orgform_kurzbz); } /** - * @param integer $studiengang_kz + * @param string $studiengang_typ_kurzbz * @param string $studiensemester_kurzbz (optional) * @param string $filter (optional) * @param string $orgform_kurzbz (optional) * * @return void */ - protected function fetchPrestudents($studiengang_kz, $studiensemester_kurzbz = null, $filter = null, $orgform_kurzbz = null) + protected function fetchPrestudents($studiengang_typ_kurzbz, $studiensemester_kurzbz = null, $filter = null, $orgform_kurzbz = null) { + $studiengang_kz = $this->getStudiengangKz($studiengang_typ_kurzbz); + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); + $orgform_kurzbz = $this->getOrgformKurzbz($orgform_kurzbz); + + if ($studiengang_kz === null) + show_404(); + $stdsemEsc = $studiensemester_kurzbz ? $this->PrestudentModel->escape($studiensemester_kurzbz) : 'NULL'; $selectRT = " @@ -439,19 +446,21 @@ class Students extends FHCAPI_Controller $this->studentlistlib->addSelect("'' AS verband"); $this->studentlistlib->addSelect("'' AS gruppe"); + $query_studiensemester_kurzbz = $studiensemester_kurzbz ? $this->PrestudentModel->escape($studiensemester_kurzbz) : '\'NULL\''; + $this->studentlistlib->addSelect($query_studiensemester_kurzbz . ' as query_studiensemester_kurzbz'); $this->addFilter($studiensemester_kurzbz); $result = $this->studentlistlib->execute($studiensemester_kurzbz); $data = $this->getDataOrTerminateWithError($result); - + $this->decodeTagsJsonInResult($data); $this->terminateWithSuccess($data); } public function getStudents( $studiensemester_kurzbz, - $studiengang_kz, + $studiengang_typ_kurzbz, $semester = null, $verband = null, $gruppe = null @@ -459,18 +468,18 @@ class Students extends FHCAPI_Controller $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', array( 'studiensemester_kurzbz' => $studiensemester_kurzbz, - 'studiengang_kz' => $studiengang_kz, + 'studiengang_typ_kurzbz' => $studiengang_typ_kurzbz, 'semester' => $semester, 'verband' => $verband, 'gruppe' => $gruppe )); - $this->fetchStudents($studiensemester_kurzbz, $studiengang_kz, $semester, $verband, $gruppe, null, null); + $this->fetchStudents($studiensemester_kurzbz, $studiengang_typ_kurzbz, $semester, $verband, $gruppe, null, null); } public function getStudentsOrgform( $studiensemester_kurzbz, - $studiengang_kz, + $studiengang_typ_kurzbz, $orgform_kurzbz, $semester = null, $verband = null, @@ -479,37 +488,37 @@ class Students extends FHCAPI_Controller $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', array( 'studiensemester_kurzbz' => $studiensemester_kurzbz, - 'studiengang_kz' => $studiengang_kz, + 'studiengang_typ_kurzbz' => $studiengang_typ_kurzbz, 'orgform_kurzbz' => $orgform_kurzbz, 'semester' => $semester, 'verband' => $verband, 'gruppe' => $gruppe )); - $this->fetchStudents($studiensemester_kurzbz, $studiengang_kz, $semester, $verband, $gruppe, null, $orgform_kurzbz); + $this->fetchStudents($studiensemester_kurzbz, $studiengang_typ_kurzbz, $semester, $verband, $gruppe, null, $orgform_kurzbz); } public function getStudentsSpezialgruppe( $studiensemester_kurzbz, - $studiengang_kz, + $studiengang_typ_kurzbz, $semester, $gruppe_kurzbz ) { $this->addMeta('ci_method', __FUNCTION__); $this->addMeta('ci_params', array( 'studiensemester_kurzbz' => $studiensemester_kurzbz, - 'studiengang_kz' => $studiengang_kz, + 'studiengang_typ_kurzbz' => $studiengang_typ_kurzbz, 'semester' => $semester, 'gruppe_kurzbz' => $gruppe_kurzbz )); - $this->fetchStudents($studiensemester_kurzbz, $studiengang_kz, $semester, null, null, $gruppe_kurzbz, null); + $this->fetchStudents($studiensemester_kurzbz, $studiengang_typ_kurzbz, $semester, null, null, $gruppe_kurzbz, null); } public function getStudentsOrgformSpezialgruppe( $studiensemester_kurzbz, $orgform_kurzbz, - $studiengang_kz, + $studiengang_typ_kurzbz, $semester, $gruppe_kurzbz ) { @@ -517,17 +526,17 @@ class Students extends FHCAPI_Controller $this->addMeta('ci_params', array( 'studiensemester_kurzbz' => $studiensemester_kurzbz, 'orgform_kurzbz' => $orgform_kurzbz, - 'studiengang_kz' => $studiengang_kz, + 'studiengang_typ_kurzbz' => $studiengang_typ_kurzbz, 'semester' => $semester, 'gruppe_kurzbz' => $gruppe_kurzbz )); - $this->fetchStudents($studiensemester_kurzbz, $studiengang_kz, $semester, null, null, $gruppe_kurzbz, $orgform_kurzbz); + $this->fetchStudents($studiensemester_kurzbz, $studiengang_typ_kurzbz, $semester, null, null, $gruppe_kurzbz, $orgform_kurzbz); } /** - * @param integer $studiengang_kz * @param string $studiensemester_kurzbz + * @param string $studiengang_typ_kurzbz * @param integer $semester (optional) * @param string $verband (optional) * @param integer $gruppe (optional) @@ -538,13 +547,20 @@ class Students extends FHCAPI_Controller */ protected function fetchStudents( $studiensemester_kurzbz, - $studiengang_kz, + $studiengang_typ_kurzbz, $semester = null, $verband = null, $gruppe = null, $gruppe_kurzbz = null, $orgform_kurzbz = null ) { + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); + $studiengang_kz = $this->getStudiengangKz($studiengang_typ_kurzbz); + $orgform_kurzbz = $this->getOrgformKurzbz($orgform_kurzbz); + + if ($studiengang_kz === null) + show_404(); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); if (!$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz)) @@ -556,6 +572,7 @@ class Students extends FHCAPI_Controller $this->studentlistlib->addJoin("public.tbl_student s", "prestudent_id"); $this->studentlistlib->addSelect("'' AS priorisierung_relativ"); + $this->studentlistlib->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz'); if ($gruppe_kurzbz !== null) { $this->studentlistlib->addJoin('public.tbl_benutzergruppe g', 'uid', '', 'after_b'); @@ -594,10 +611,22 @@ class Students extends FHCAPI_Controller $result = $this->studentlistlib->execute($studiensemester_kurzbz); $data = $this->getDataOrTerminateWithError($result); - + $this->decodeTagsJsonInResult($data); $this->terminateWithSuccess($data); } + protected function decodeTagsJsonInResult(&$data) + { + if(defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED) { + array_walk($data, function($item, $key) { + if(isset($item->tags)) + { + $item->tags = json_decode($item->tags); + } + }); + } + } + /** * @param string $prestudent_id * @@ -611,6 +640,8 @@ class Students extends FHCAPI_Controller 'prestudent_id' => $prestudent_id, )); + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); if (!$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz)) @@ -618,7 +649,7 @@ class Students extends FHCAPI_Controller $this->terminateWithError($studiensemester_kurzbz . ' - ' . $this->p->t('lehre', 'error_noStudiensemester')); } - + $this->studentlistlib->addSelect("COALESCE( v.semester::text, CASE @@ -638,7 +669,7 @@ class Students extends FHCAPI_Controller $result = $this->studentlistlib->execute($studiensemester_kurzbz); $data = $this->getDataOrTerminateWithError($result); - + $this->decodeTagsJsonInResult($data); $this->terminateWithSuccess($data); } @@ -655,6 +686,8 @@ class Students extends FHCAPI_Controller 'student_uid' => $student_uid, )); + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); if (!$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz)) @@ -664,14 +697,14 @@ class Students extends FHCAPI_Controller $this->studentlistlib->addWhere('s.student_uid', $student_uid); - + $this->addFilter($studiensemester_kurzbz); $result = $this->studentlistlib->execute($studiensemester_kurzbz); $data = $this->getDataOrTerminateWithError($result); - + $this->decodeTagsJsonInResult($data); $this->terminateWithSuccess($data); } @@ -689,6 +722,8 @@ class Students extends FHCAPI_Controller 'person_id' => $person_id, )); + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); if (!$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz)) @@ -699,13 +734,13 @@ class Students extends FHCAPI_Controller $this->studentlistlib->addWhere('p.person_id', $person_id); - + $this->addFilter($studiensemester_kurzbz); $result = $this->studentlistlib->execute($studiensemester_kurzbz); $data = $this->getDataOrTerminateWithError($result); - + $this->decodeTagsJsonInResult($data); $this->terminateWithSuccess($data); } @@ -721,6 +756,8 @@ class Students extends FHCAPI_Controller 'studiensemester_kurzbz' => $studiensemester_kurzbz )); + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); + $this->load->library('SearchLib', [ 'config' => 'searchstv' ]); $this->load->library('form_validation'); @@ -755,6 +792,7 @@ class Students extends FHCAPI_Controller END ) AS semester", false); + $this->studentlistlib->addSelect($this->PrestudentModel->escape($studiensemester_kurzbz) . ' as query_studiensemester_kurzbz'); if ($prestudent_ids && $student_uids) { $this->studentlistlib->addWhere('tbl_prestudent.prestudent_id', $prestudent_ids); @@ -773,7 +811,7 @@ class Students extends FHCAPI_Controller $result = $this->studentlistlib->execute($studiensemester_kurzbz); $data = $this->getDataOrTerminateWithError($result); - + $this->decodeTagsJsonInResult($data); $this->terminateWithSuccess($data); } @@ -802,4 +840,55 @@ class Students extends FHCAPI_Controller } } } + + protected function getStudiengangKz($studiengang_typ_kurzbz) + { + $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); + $result = $this->StudiengangModel->loadWhere([ + 'LOWER(CONCAT(typ, kurzbz)) =' => strtolower($studiengang_typ_kurzbz) + ]); + + $data = $this->getDataOrTerminateWithError($result); + + if (!$data) + return null; + + return current($data)->studiengang_kz; + } + + protected function getStudiensemesterKurzbz($studiensemester_kurzbz) + { + if ($studiensemester_kurzbz === null) + return null; + + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + $result = $this->StudiensemesterModel->loadWhere([ + 'LOWER(studiensemester_kurzbz) =' => strtolower($studiensemester_kurzbz) + ]); + + $data = $this->getDataOrTerminateWithError($result); + + if (!$data) + return $studiensemester_kurzbz; + + return current($data)->studiensemester_kurzbz; + } + + protected function getOrgformKurzbz($orgform_kurzbz) + { + if ($orgform_kurzbz === null) + return null; + + $this->load->model('codex/Orgform_model', 'OrgformModel'); + $result = $this->OrgformModel->loadWhere([ + 'LOWER(orgform_kurzbz) =' => strtolower($orgform_kurzbz) + ]); + + $data = $this->getDataOrTerminateWithError($result); + + if (!$data) + return $orgform_kurzbz; + + return current($data)->orgform_kurzbz; + } } diff --git a/application/controllers/api/frontend/v1/stv/Tags.php b/application/controllers/api/frontend/v1/stv/Tags.php index 3004a1f3b..bd5c61953 100644 --- a/application/controllers/api/frontend/v1/stv/Tags.php +++ b/application/controllers/api/frontend/v1/stv/Tags.php @@ -15,7 +15,9 @@ class Tags extends Tag_Controller 'addTag' => self::BERECHTIGUNG_KURZBZ, 'updateTag' => self::BERECHTIGUNG_KURZBZ, 'doneTag' => self::BERECHTIGUNG_KURZBZ, - 'deleteTag' => self::BERECHTIGUNG_KURZBZ + 'deleteTag' => self::BERECHTIGUNG_KURZBZ, + 'getAllTags' => self::BERECHTIGUNG_KURZBZ, + 'rebuildTagsForTypeId' => self::BERECHTIGUNG_KURZBZ, ]); $this->config->load('stv'); @@ -45,4 +47,5 @@ class Tags extends Tag_Controller { parent::doneTag($this->config->item('stv_prestudent_tags')); } + } diff --git a/application/controllers/api/frontend/v1/stv/Verband.php b/application/controllers/api/frontend/v1/stv/Verband.php deleted file mode 100644 index 32ef30a45..000000000 --- a/application/controllers/api/frontend/v1/stv/Verband.php +++ /dev/null @@ -1,513 +0,0 @@ -. - */ - -if (! defined('BASEPATH')) exit('No direct script access allowed'); - -/** - * This controller operates between (interface) the JS (GUI) and the back-end - * Provides data to the ajax get calls about verbände - * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON - */ -class Verband extends FHCAPI_Controller -{ - public function __construct() - { - $permissions = []; - $router = load_class('Router'); - $permissions[$router->method] = ['admin:r', 'assistenz:r']; - parent::__construct($permissions); - - // Load Models - $this->load->model('organisation/Studiengang_model', 'StudiengangModel'); - } - - /** - * Remap calls: - * / - * /(studiengang_kz) => getStudiengang - * /(studiengang_kz)/(semester) => getSemester - * /(studiengang_kz)/(semester)/(verband) => getVerband - * /(studiengang_kz)/(org_form) => getStudiengang - * /(studiengang_kz)/(org_form)/(semester) => getSemester - * /(studiengang_kz)/(org_form)/(semester)/(verband) => getVerband - * - * @param string $method - * @param array $params (optional) - * - * @return void - */ - public function _remap($method, $params = []) - { - if ($method == '' || $method == 'index') - return $this->getBase(); - - // NOTE(chris): Test if access is allowed ($method is the Studiengang) - if (!$this->permissionlib->isBerechtigt('assistenz', 's', $method) - && !$this->permissionlib->isBerechtigt('admin', 's', $method) - ) { - return $this->_outputAuthError([$method => ['admin:r', 'assistenz:r']]); - } - - $count = count($params); - if (!$count) - return $this->getStudiengang($method); - - if ($count == 1) { - if (is_numeric($params[0])) - return $this->getSemester($method, $params[0]); - elseif ($params[0] == 'prestudent') - return $this->terminateWithSuccess($this->getStdSem($method . '/prestudent/', $method)); - else - return $this->getStudiengang($method, $params[0]); - } - if ($count == 2) { - if (is_numeric($params[0])) - return $this->getVerband($method, $params[0], $params[1]); - elseif ($params[1] == 'prestudent') - return $this->terminateWithSuccess($this->getStdSem($method . '/' . $params[0] . '/prestudent/', $method)); - else - return $this->getSemester($method, $params[1], $params[0]); - } - if ($count == 3 && !is_numeric($params[0]) && is_numeric($params[1]) && !is_numeric($params[2])) - return $this->getVerband($method, $params[1], $params[2], $params[0]); - - show_404(); - } - - /** - * @return void - */ - protected function getBase() - { - $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); - - $this->StudiengangModel->addDistinct(); - $this->StudiengangModel->addSelect("v.studiengang_kz AS link"); - $this->StudiengangModel->addSelect( - "CONCAT(kurzbzlang, ' (', UPPER(CONCAT(typ, kurzbz)), ') - ', tbl_studiengang.bezeichnung) AS name", - false - ); - $this->StudiengangModel->addSelect('erhalter_kz'); - $this->StudiengangModel->addSelect('typ'); - $this->StudiengangModel->addSelect('kurzbz'); - $this->StudiengangModel->addSelect('studiengang_kz'); - $this->StudiengangModel->addSelect('studiengang_kz AS stg_kz'); - - $this->StudiengangModel->addOrder('erhalter_kz'); - $this->StudiengangModel->addOrder('typ'); - $this->StudiengangModel->addOrder('kurzbz'); - - $stgs = $this->permissionlib->getSTG_isEntitledFor('admin') ?: []; - $stgs = array_merge($stgs, $this->permissionlib->getSTG_isEntitledFor('assistenz') ?: []); - - if (!$stgs) - $this->terminateWithSuccess([]); - - $this->StudiengangModel->db->where_in('studiengang_kz', $stgs); - - $result = $this->StudiengangModel->loadWhere(['v.aktiv' => true]); - - $list = $this->getDataOrTerminateWithError($result); - - if ($this->permissionlib->isBerechtigt('inout/uebersicht')) - $list[] = [ - 'name' => 'International', - 'link' => 'inout', - 'children' => [ - [ - 'name' => 'Incoming', - 'link' => 'inout/incoming', - 'leaf' => true - ], - [ - 'name' => 'Outgoing', - 'link' => 'inout/outgoing', - 'leaf' => true - ], - [ - 'name' => 'Gemeinsame Studien', - 'link' => 'inout/gemeinsamestudien', - 'leaf' => true - ] - ] - ]; - $this->terminateWithSuccess($list); - } - - /** - * @param integer $studiengang_kz - * @param string $orgform (optional) - * - * @return void - */ - protected function getStudiengang($studiengang_kz, $org_form = null) - { - $link = $studiengang_kz . '/'; - if ($org_form !== null) - $link .= $org_form . '/'; - - $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); - - $this->StudiengangModel->addDistinct(); - $this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", semester) AS link", false); - $this->StudiengangModel->addSelect("CONCAT( - UPPER(CONCAT(typ, kurzbz)), - '-', - semester, - ( - SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END - FROM public.tbl_lehrverband - WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester - ORDER BY verband, gruppe LIMIT 1 - ) - ) AS name", false); - - $this->StudiengangModel->addSelect('semester'); - $this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); - - $this->StudiengangModel->addOrder('semester'); - - if ($org_form !== null) { - $this->StudiengangModel->addSelect("v.orgform_kurzbz"); - $this->StudiengangModel->db->group_start(); - $this->StudiengangModel->db->where('v.semester', 0); - $this->StudiengangModel->db->or_where('v.orgform_kurzbz', $org_form); - $this->StudiengangModel->db->group_end(); - } - - $result = $this->StudiengangModel->loadWhere([ - 'v.studiengang_kz' => $studiengang_kz, - 'v.aktiv' => true - ]); - $list = $this->getDataOrTerminateWithError($result); - - array_unshift($list, [ - 'name' => 'PreStudent', - 'link' => $link . 'prestudent', - 'no_sem_reload' => true, - 'stg_kz' => (int)$studiengang_kz, - 'children' => $this->getStdSem($link . 'prestudent/', $studiengang_kz) - ]); - - if ($org_form === null) { - // NOTE(chris): if mischform show orgforms - $result = $this->StudiengangModel->load($studiengang_kz); - $result = $this->getDataOrTerminateWithError($result); - if ($result) { - if (current($result)->mischform) { - $this->load->model('organisation/Studienordnung_model', 'StudienordnungModel'); - - $this->StudienordnungModel->addDistinct(); - $this->StudienordnungModel->addSelect("CONCAT(studiengang_kz, '/', p.orgform_kurzbz) AS link"); - $this->StudienordnungModel->addSelect("p.orgform_kurzbz AS name"); - $this->StudienordnungModel->addSelect("studiengang_kz AS stg_kz"); - - $this->StudienordnungModel->addJoin('lehre.tbl_studienplan p', 'studienordnung_id'); - - $result = $this->StudienordnungModel->loadWhere([ - 'aktiv' => true, - 'studiengang_kz' => $studiengang_kz, - 'p.orgform_kurzbz !=' => 'DDP' - ]); - $result = $this->getDataOrTerminateWithError($result); - - $list = array_merge($list, $result); - } - } - } - $this->terminateWithSuccess($list); - } - - /** - * @param integer $studiengang_kz - * @param integer $semester - * @param string $orgform - * - * @return void - */ - protected function getSemester($studiengang_kz, $semester, $org_form = null) - { - $link = $studiengang_kz . '/'; - if ($org_form !== null) - $link .= $org_form . '/'; - $link .= $semester . '/'; - - - $this->load->model('organisation/Gruppe_model', 'GruppeModel'); - - $this->GruppeModel->addDistinct(); - $this->GruppeModel->addSelect("CONCAT(" . $this->GruppeModel->escape($link . 'grp/') . ", gruppe_kurzbz) AS link", false); - $this->GruppeModel->addSelect("CONCAT(gruppe_kurzbz, ' (', bezeichnung, ')') AS name", false); - $this->GruppeModel->addSelect("TRUE AS leaf", false); - - $this->GruppeModel->addSelect('sort'); - $this->GruppeModel->addSelect('gruppe_kurzbz'); - $this->GruppeModel->addSelect($this->GruppeModel->escape($studiengang_kz) . '::integer AS stg_kz', false); - - $this->GruppeModel->addOrder('sort'); - $this->GruppeModel->addOrder('gruppe_kurzbz'); - - $where = [ - 'studiengang_kz' => $studiengang_kz, - 'semester' => $semester, - 'lehre' => true, - 'sichtbar' => true, - 'aktiv' => true, - 'direktinskription' => false - ]; - - if ($org_form !== null) - $where['orgform_kurzbz'] = $org_form; - - $result = $this->GruppeModel->loadWhere($where); - - $list = $this->getDataOrTerminateWithError($result); - - $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); - - $this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", verband) AS link", false); - $this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband ORDER BY gruppe LIMIT 1)) AS name", false); - $this->StudiengangModel->addSelect("CASE WHEN MAX(gruppe)='' OR MAX(gruppe)=' ' THEN TRUE ELSE FALSE END AS leaf"); - - $this->StudiengangModel->addSelect($this->StudiengangModel->escape($semester) . ' AS semester'); - $this->StudiengangModel->addSelect('verband'); - $this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); - - $this->StudiengangModel->addOrder('verband'); - - $this->StudiengangModel->addGroupBy('link, name, verband'); - - $where = [ - 'v.studiengang_kz' => $studiengang_kz, - 'v.semester' => $semester, - 'v.verband !=' => '', - 'v.aktiv' => true - ]; - - if ($org_form !== null && $semester) // NOTE(chris): on semester 0 show all? - $where['v.orgform_kurzbz'] = $org_form; - - $result = $this->StudiengangModel->loadWhere($where); - $result = $this->getDataOrTerminateWithError($result); - - $list = array_merge($list, $result); - - $this->terminateWithSuccess($list); - } - - /** - * @param integer $studiengang_kz - * @param integer $semester - * @param integer $verband - * @param string $orgform - * - * @return void - */ - protected function getVerband($studiengang_kz, $semester, $verband, $org_form = null) - { - $link = $studiengang_kz . '/'; - if ($org_form !== null) - $link .= $org_form . '/'; - $link .= $semester . '/'. $verband . '/'; - - - $this->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); - - $this->StudiengangModel->addDistinct(); - $this->StudiengangModel->addSelect("CONCAT(" . $this->StudiengangModel->escape($link) . ", gruppe) AS link", false); - $this->StudiengangModel->addSelect("CONCAT(UPPER(CONCAT(typ, kurzbz)), '-', semester, verband, gruppe, (SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END FROM public.tbl_lehrverband WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester AND verband=v.verband AND gruppe=v.gruppe ORDER BY gruppe LIMIT 1)) AS name", false); - $this->StudiengangModel->addSelect("TRUE AS leaf", false); - - $this->StudiengangModel->addSelect('v.semester'); - $this->StudiengangModel->addSelect('v.verband'); - $this->StudiengangModel->addSelect('gruppe'); - $this->StudiengangModel->addSelect($this->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); - - $this->StudiengangModel->addOrder('gruppe'); - - $where = [ - 'v.studiengang_kz' => $studiengang_kz, - 'v.semester' => $semester, - 'v.verband' => $verband, - 'v.gruppe !=' => '', - 'v.aktiv' => true - ]; - - if ($org_form !== null && $semester) // NOTE(chris): on semester 0 show all? - $where['v.orgform_kurzbz'] = $org_form; - - $result = $this->StudiengangModel->loadWhere($where); - - $list = $this->getDataOrTerminateWithError($result); - - $this->terminateWithSuccess($list); - } - - /** - * @param string $link - * @param integer $studiengang_kz - * - * @return array - */ - protected function getStdSem($link, $studiengang_kz) - { - $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); - - $this->load->model('system/Variable_model', 'VariableModel'); - $result = $this->VariableModel->getVariables(getAuthUID(), ['number_displayed_past_studiensemester']); - $data = $this->getDataOrTerminateWithError($result); - - $this->load->config('stv'); - $number_displayed_past_studiensemester_default = $this->config->item('number_displayed_past_studiensemester_default'); - - $number_displayed_past_studiensemester = $data['number_displayed_past_studiensemester'] ?? $number_displayed_past_studiensemester_default; - - $this->StudiensemesterModel->addPlusMinus(null, $number_displayed_past_studiensemester); - $this->StudiensemesterModel->addOrder('ende'); - $result = $this->StudiensemesterModel->load(); - - $studiensemester = $this->getDataOrTerminateWithError($result); - $result = []; - - $studiengang_kz = (int)$studiengang_kz; - - foreach ($studiensemester as $sem) { - $semlink = $link . $sem->studiensemester_kurzbz; - $intlink = $semlink . '/interessenten'; - $result[] = [ - 'name' => $sem->studiensemester_kurzbz, - 'link' => $semlink, - 'stg_kz' => $studiengang_kz, - 'children' => [ - [ - 'name' => 'Interessenten', - 'link' => $intlink, - 'stg_kz' => $studiengang_kz, - 'children' => [ - [ - 'name' => 'Bewerbung nicht abgeschickt', - 'link' => $intlink . '/bewerbungnichtabgeschickt', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ], - [ - 'name' => 'Bewerbung abgeschickt, Status unbestätigt', - 'link' => $intlink . '/bewerbungabgeschickt', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ], - [ - 'name' => 'ZGV erfüllt', - 'link' => $intlink . '/zgv', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ], - [ - 'name' => 'Status bestätigt', - 'link' => $intlink . '/statusbestaetigt', - 'stg_kz' => $studiengang_kz, - 'children' => [ - [ - 'name' => 'Nicht zum Reihungstest angemeldet', - 'link' => $intlink . '/statusbestaetigtrtnichtangemeldet', - 'leaf' => true - ], - [ - 'name' => 'Reihungstest angemeldet', - 'link' => $intlink . '/statusbestaetigtrtangemeldet', - 'leaf' => true - ] - ] - ], - [ - 'name' => 'Nicht zum Reihungstest angemeldet', - 'link' => $intlink . '/reihungstestnichtangemeldet', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ], - [ - 'name' => 'Reihungstest angemeldet', - 'link' => $intlink . '/reihungstestangemeldet', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ] - ] - ], - [ - 'name' => 'Bewerber', - 'link' => $semlink . '/bewerber', - 'stg_kz' => $studiengang_kz, - 'children' => [ - [ - 'name' => 'Nicht zum Reihungstest angemeldet', - 'link' => $intlink . '/bewerberrtnichtangemeldet', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ], - [ - 'name' => 'Reihungstest angemeldet', - 'link' => $intlink . '/bewerberrtangemeldet', - 'stg_kz' => $studiengang_kz, - 'children' => [ - [ - 'name' => 'Teilgenommen', - 'link' => $intlink . '/bewerberrtangemeldetteilgenommen', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ], - [ - 'name' => 'Nicht teilgenommen', - 'link' => $intlink . '/bewerberrtangemeldetnichtteilgenommen', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ] - ] - ] - ] - ], - [ - 'name' => 'Aufgenommen', - 'link' => $semlink . '/aufgenommen', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ], - [ - 'name' => 'Warteliste', - 'link' => $semlink . '/warteliste', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ], - [ - 'name' => 'Absage', - 'link' => $semlink . '/absage', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ], - [ - 'name' => 'Incoming', - 'link' => $semlink . '/incoming', - 'stg_kz' => $studiengang_kz, - 'leaf' => true - ] - ] - ]; - } - - return $result; - } -} diff --git a/application/controllers/api/v1/system/Tag.php b/application/controllers/api/v1/system/Tag.php index 7ce375217..df3003b44 100644 --- a/application/controllers/api/v1/system/Tag.php +++ b/application/controllers/api/v1/system/Tag.php @@ -37,6 +37,7 @@ class Tag extends API_Controller if (isset($tag)) { + $this->TagModel->addJoin('public.tbl_notiz_typ', 'typ_kurzbz', 'typ_kurzbz'); $result = $this->TagModel->load($tag); $this->response($result, REST_Controller::HTTP_OK); diff --git a/application/controllers/components/stv/Studiensemester.php b/application/controllers/components/stv/Studiensemester.php deleted file mode 100644 index c3db99686..000000000 --- a/application/controllers/components/stv/Studiensemester.php +++ /dev/null @@ -1,78 +0,0 @@ - self::PERM_LOGGED, - 'now' => self::PERM_LOGGED, - 'set' => self::PERM_LOGGED - ]); - } - - public function index() - { - $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); - - $this->StudiensemesterModel->addOrder('start'); - - $result = $this->StudiensemesterModel->load(); - - if (isError($result)) { - $this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR); - } - $this->outputJson($result); - } - - public function now() - { - $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); - - $result = $this->StudiensemesterModel->getNearest(); - - if (isError($result)) { - $this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR); - $this->outputJson(getError($result)); - } - $result = getData($result) ?: []; - - if (count($result) != 1) { - $this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR); - $this->outputJsonError(count($result) ? 'Mehrere Studiensemester aktiv' : 'Kein Studiensemester aktiv'); - } else { - $this->outputJsonSuccess(current($result)->studiensemester_kurzbz); - } - } - - public function set() - { - $this->load->library('AuthLib'); - $this->load->library('form_validation'); - - $_POST = json_decode(utf8_encode($this->input->raw_input_stream), true); - - $this->form_validation->set_rules('studiensemester', 'Studiensemester', 'required'); - - if ($this->form_validation->run() == false) { - $this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST); - return $this->outputJsonError($this->form_validation->error_array()); - } - - $stdsem = $this->input->post('studiensemester'); - - $this->load->model('system/Variable_model', 'VariableModel'); - - $result = $this->VariableModel->setVariable(getAuthUID(), 'semester_aktuell', $stdsem); - - if (isError($result)) { - $this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR); - return $this->outputJson($result); - } - - $this->outputJsonSuccess(true); - } -} diff --git a/application/controllers/jobs/TagJob.php b/application/controllers/jobs/TagJob.php new file mode 100644 index 000000000..ae21883e9 --- /dev/null +++ b/application/controllers/jobs/TagJob.php @@ -0,0 +1,110 @@ +load->config('stv'); + + // Library + $this->load->library('TagLib'); + + // Load Models + $this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + $this->load->model('system/Notiztyp_model', 'NotiztypModel'); + + $this->loadPhrases([ + 'lehre' + ]); + } + + public function rebuildAutomatedTags() + { + $automatedTagsRes = $this->NotiztypModel->loadWhere(array('automatisiert' => true, 'taglib IS NOT NULL' => null)); + $automatedTags = hasData($automatedTagsRes) ? getData($automatedTagsRes) : []; + + + $result = $this->StudiensemesterModel->getAktOrNextSemester(); + if (isError($result)) + { + $this->logInfo('Start Job rebuild Automated Tags'); + $this->logError('Error occurred during retrieving studiensemester'); + return $this->logInfo('End Job rebuild Automated Tags'); + } + + if (empty($result->retval) || !isset($result->retval[0])) { + $this->logInfo('Start Job rebuild Automated Tags'); + $this->logError('No Studiensemester found'); + return $this->logInfo('End Job rebuild Automated Tags'); + } + $studiensemester_kurzbz = $result->retval[0]->studiensemester_kurzbz ?? null; + $params = array( + 'studiensemester_kurzbz' => $studiensemester_kurzbz + ); + + $this->logInfo('Start Job rebuild Automated Tags ' . $studiensemester_kurzbz); + foreach($automatedTags as $autoTag) + { + // getPath: must not be lost + $filePath = APPPATH . 'libraries/' . $autoTag->taglib . '.php'; // APPPATH = application/ + + if(file_exists($filePath)) { + require_once($filePath); + } else { + $this->logInfo("File not found: " . $filePath); + continue; + } + + $kurz_bz = $autoTag->typ_kurzbz; + // className without PATH (basename) + $className = basename($autoTag->taglib); + + $obj = new $className(); + + $outputArray = $obj->getZuordnungIds($params); + $typeId = $outputArray->typeId; + + $paramsTag = array( + 'studiensemester_kurzbz' => $studiensemester_kurzbz, + 'kurzbz' => $kurz_bz, + 'data' => $outputArray->data, + 'typeId' => $typeId + ); + + $result = $this->taglib->updateAutomatedTags($paramsTag); + + if (isError($result)) { + $this->logError('Error occurred during updateAutomatedTags ' . $kurz_bz); + continue; + } + + $data = is_array($result) ? $result['retval'] : $result->retval; + + //SUMMARY + $this->logInfo("Tag " . $result->retval['input']['tag'] . " | type_id " . $typeId . " --" + . " Count Recycled: " . $result->retval['summary']['recycled'] + . " Count Added: ". $result->retval['summary']['added'] + . " Count Deleted: ". $result->retval['summary']['deleted']); + + //DETAILS + if($result->retval['results']['newTags']) + $this->logInfo("Tag " . $result->retval['input']['tag'] . "New tag(s): " . implode(', ', $result->retval['results']['newTags'])); + if($result->retval['results']['deletedTagsIds']) + $this->logInfo("Tag " . $result->retval['input']['tag'] . "Deleted tags(s: " . implode(', ', $result->retval['results']['deletedTagsIds'])); + if ($result->retval['results']['retaggedIds']) + $this->logInfo("Tag " . $result->retval['input']['tag'] . "Recycled tag(s): " . implode(', ', $result->retval['results']['retaggedIds'])); + } + $this->logInfo( "End Job rebuild Automated Tags"); + } +} diff --git a/application/controllers/js/Tabulatorcolumns.php b/application/controllers/js/Tabulatorcolumns.php new file mode 100644 index 000000000..1a4621219 --- /dev/null +++ b/application/controllers/js/Tabulatorcolumns.php @@ -0,0 +1,60 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * This controller generates javascript from snippets for tabulator columns + */ +class Tabulatorcolumns extends Auth_Controller +{ + public function __construct() + { + parent::__construct([ + 'stv' => ['admin:r', 'assistenz:r'], + 'lvverwaltung' => ['admin:r', 'assistenz:r'] + ]); + } + + /** + * Render tabulator columns for config + * + * @param string $config + * + * @return void + */ + public function _remap($config) + { + $this->load->config($config); + $list_columns = $this->config->item('list_columns') ?: []; + + $snippets = [ + 'jssnippets/tabulatorcolumns/' . $config . '.js' + ]; + + foreach ($list_columns as $col) { + $snippets[] = $col['js']; + } + + $this->output->set_content_type('application/javascript'); + + $this->load->view('jssnippets/tabulatorcolumns.js', [ + 'snippets' => $snippets + ]); + } +} diff --git a/application/controllers/person/Gradelist.php b/application/controllers/person/Gradelist.php index 230c7f67f..d6df2c43a 100644 --- a/application/controllers/person/Gradelist.php +++ b/application/controllers/person/Gradelist.php @@ -198,7 +198,19 @@ class Gradelist extends Auth_Controller if (!isset($row_noten->found)) { $result_lv = $this->LehrveranstaltungModel->load($row_noten->lehrveranstaltung_id); - $result_stg = $this->StudiengangModel->load($result_lv->retval[0]->studiengang_kz); + + $studiengang_kz = null; + + if (!empty($result_lv->retval) && isset($result_lv->retval[0]) && isset($result_lv->retval[0]->studiengang_kz)) + { + $result_stg = $this->StudiengangModel->load($result_lv->retval[0]->studiengang_kz); + + if (!empty($result_stg->retval) && isset($result_stg->retval[0]) && is_object($result_stg->retval[0]) && isset($result_stg->retval[0]->kurzbzlang)) + { + $studiengang_kz = $result_stg->retval[0]->kurzbzlang; + } + } + $courses['semester'][$row_noten->studiensemester_kurzbz]['lvs_nonstpl'][] = array( 'lehrveranstaltung_id' => $row_noten->lehrveranstaltung_id, 'lehrtyp_kurzbz' => $result_lv->retval[0]->lehrtyp_kurzbz, @@ -212,8 +224,8 @@ class Gradelist extends Auth_Controller 'semester' => $result_lv->retval[0]->semester, 'note' => $row_noten->note, 'datum' => $row_noten->benotungsdatum, - 'zugeordnet' => true, - 'studiengang_kurzbz' => $result_stg->retval[0]->kurzbzlang + 'studiengang_kurzbz' => $studiengang_kz, + 'zugeordnet' => true ); if(!isset($courses['semester'][$row_noten->studiensemester_kurzbz]['data']['ectssumme_nonstpl'])) $courses['semester'][$row_noten->studiensemester_kurzbz]['data']['ectssumme_nonstpl'] = 0; diff --git a/application/core/Tag_Controller.php b/application/core/Tag_Controller.php index 5b9bac6c5..c4b6edb81 100644 --- a/application/core/Tag_Controller.php +++ b/application/core/Tag_Controller.php @@ -7,7 +7,7 @@ class Tag_Controller extends FHCAPI_Controller { private $_uid; - const BERECHTIGUNG_KURZBZ = 'admin:rw'; + const BERECHTIGUNG_KURZBZ = ['admin:rw']; public function __construct($permissions) { @@ -15,10 +15,11 @@ class Tag_Controller extends FHCAPI_Controller 'getTag' => self::BERECHTIGUNG_KURZBZ, 'getTags' => self::BERECHTIGUNG_KURZBZ, 'addTag' => self::BERECHTIGUNG_KURZBZ, - 'updateTag' => self::BERECHTIGUNG_KURZBZ, 'doneTag' => self::BERECHTIGUNG_KURZBZ, 'deleteTag' => self::BERECHTIGUNG_KURZBZ, + 'getAllTags' => self::BERECHTIGUNG_KURZBZ, + 'rebuildTagsForTypeId' => self::BERECHTIGUNG_KURZBZ, ]; $merged_permissions = array_merge($default_permissions, $permissions); @@ -26,6 +27,10 @@ class Tag_Controller extends FHCAPI_Controller parent::__construct($merged_permissions); $this->_setAuthUID(); + + // Library + $this->load->library('TagLib'); + $this->load->model('person/Notiz_model', 'NotizModel'); $this->load->model('system/Notiztyp_model', 'NotiztypModel'); $this->load->model('person/Notizzuordnung_model', 'NotizzuordnungModel'); @@ -38,6 +43,8 @@ class Tag_Controller extends FHCAPI_Controller public function getTag($readonly_tags = null) { $language = $this->_getLanguageIndex(); + $index_bezeichnung_mehrsprachig = $language - 1; + $id = $this->input->get('id'); if (is_array($readonly_tags) && !isEmptyArray($readonly_tags)) @@ -62,14 +69,18 @@ class Tag_Controller extends FHCAPI_Controller $this->NotizModel->addSelect( "tbl_notiz.titel, tbl_notiz.text, - array_to_json(bezeichnung_mehrsprachig::varchar[])->>". $language. " as bezeichnung, + array_to_json(bezeichnung_mehrsprachig::varchar[])->>". $index_bezeichnung_mehrsprachig . " as bezeichnung, tbl_notiz.notiz_id, tbl_notiz_typ.style, + tbl_notiz_typ.automatisiert, + tbl_notiz_typ.prioritaet, tbl_notiz.erledigt as done, tbl_notiz.insertamum, tbl_notiz.updateamum, (verfasserperson.vorname || ' ' || verfasserperson.nachname || ' ' || '(' || verfasserbenutzer.uid || ')') as verfasser, - (bearbeiterperson.vorname || ' ' || bearbeiterperson.nachname || ' ' || '(' || bearbeiterbenutzer.uid || ')') as bearbeiter + (bearbeiterperson.vorname || ' ' || bearbeiterperson.nachname || ' ' || '(' || bearbeiterbenutzer.uid || ')') as bearbeiter, + tbl_notiz.start, + tbl_notiz.ende " ); $this->NotizModel->addJoin('public.tbl_notiz_typ', 'public.tbl_notiz.typ = public.tbl_notiz_typ.typ_kurzbz'); @@ -82,18 +93,24 @@ class Tag_Controller extends FHCAPI_Controller $notiz = $this->NotizModel->loadWhere(array('notiz_id' => $id)); + $this->terminateWithSuccess(hasData($notiz) ? getData($notiz)[0] : array()); } public function getTags($tags = null) { + $language = $this->_getLanguageIndex(); + $index_bezeichnung_mehrsprachig = $language - 1; + $this->NotiztypModel->addSelect( - 'typ_kurzbz as tag_typ_kurzbz, - array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung, + "typ_kurzbz as tag_typ_kurzbz, + array_to_json(bezeichnung_mehrsprachig::varchar[])->>". $index_bezeichnung_mehrsprachig . " as bezeichnung, style, beschreibung, - tag - ' + tag, + automatisiert, + prioritaet + " ); $this->NotiztypModel->addOrder('prioritaet'); @@ -271,6 +288,90 @@ class Tag_Controller extends FHCAPI_Controller $this->terminateWithSuccess($deleteNotiz); } + public function getAllTags($readonly_tags = false) + { + $language = $this->_getLanguageIndex(); + $index_bezeichnung_mehrsprachig = $language - 1; + $prestudent_id = $this->input->get('prestudent_id'); + + if (is_array($readonly_tags) && !isEmptyArray($readonly_tags)) + { + $readonly_tags = $this->_filterTag($readonly_tags, true); + + foreach ($readonly_tags as $key => $tag) + { + $readonly_tags[$key] = $this->NotizModel->db->escape($tag); + } + $tags = '(' . implode(',', $readonly_tags) . ')'; + + $this->NotizModel->addSelect(" + CASE + WHEN tbl_notiz_typ.typ_kurzbz IN $tags + THEN TRUE + ELSE FALSE + END as readonly + "); + } + $this->NotizModel->addSelect( + "tbl_notiz.titel, + tbl_notiz.text, + array_to_json(bezeichnung_mehrsprachig::varchar[])->>". $index_bezeichnung_mehrsprachig . " as bezeichnung, + tbl_notiz.notiz_id, + tbl_notiz_typ.style, + tbl_notiz_typ.automatisiert, + tbl_notiz_typ.prioritaet, + tbl_notiz.erledigt as done, + tbl_notiz.insertamum, + tbl_notiz.updateamum, + (verfasserperson.vorname || ' ' || verfasserperson.nachname || ' ' || '(' || verfasserbenutzer.uid || ')') as verfasser, + (bearbeiterperson.vorname || ' ' || bearbeiterperson.nachname || ' ' || '(' || bearbeiterbenutzer.uid || ')') as bearbeiter, + tbl_notiz.start, + tbl_notiz.ende + " + ); + $this->NotizModel->addJoin('public.tbl_notiz_typ', 'public.tbl_notiz.typ = public.tbl_notiz_typ.typ_kurzbz'); + + $this->NotizModel->addJoin('public.tbl_benutzer verfasserbenutzer', 'tbl_notiz.verfasser_uid = verfasserbenutzer.uid', 'LEFT'); + $this->NotizModel->addJoin('public.tbl_person verfasserperson', 'verfasserbenutzer.person_id = verfasserperson.person_id', 'LEFT'); + + $this->NotizModel->addJoin('public.tbl_benutzer bearbeiterbenutzer', 'tbl_notiz.bearbeiter_uid = bearbeiterbenutzer.uid', 'LEFT'); + $this->NotizModel->addJoin('public.tbl_person bearbeiterperson', 'bearbeiterbenutzer.person_id = bearbeiterperson.person_id', 'LEFT'); + + $this->NotizModel->addJoin('public.tbl_notizzuordnung notizzuordnung', 'tbl_notiz.notiz_id = notizzuordnung.notiz_id'); + + $this->NotizModel->addOrder('done'); + $this->NotizModel->addOrder('tbl_notiz_typ.prioritaet'); + $notiz = $this->NotizModel->loadWhere(array('prestudent_id' => $prestudent_id)); + + + $this->terminateWithSuccess(hasData($notiz) ? getData($notiz) : array()); + } + + public function rebuildTagsForTypeId() + { + $ids = $this->input->post('ids'); + $typeId = $this->input->post('typeId'); + $semester = $this->input->post('sem'); + + $idsSuccess = []; + $idsError = []; + $outputResult = []; + + foreach ($ids as $id) + { + $result = $this->taglib->rebuildTagsForTypeId($typeId, $id, $semester); + + if (isError($result)) + $idsError[] = $id; + else + $idsSuccess[] = $id; + + $outputResult[] = $result; + } + + $this->terminateWithSuccess([$outputResult, $idsSuccess, $idsError]); + } + private function _setAuthUID() { $this->_uid = getAuthUID(); @@ -311,4 +412,4 @@ class Tag_Controller extends FHCAPI_Controller )); } -} \ No newline at end of file +} diff --git a/application/helpers/hlp_document_helper.php b/application/helpers/hlp_document_helper.php new file mode 100644 index 000000000..5b457fe82 --- /dev/null +++ b/application/helpers/hlp_document_helper.php @@ -0,0 +1,188 @@ +load->library('PhrasesLib', array('stv'), 'p'); + + $permissioncheck = site_url("api/frontend/v1/documents/permissionAlternativeFormat/{studiengang_kz}"); + + $title_ger = $ci->p->t("global", "deutsch"); + $title_eng = $ci->p->t("global", "englisch"); + $title_ff = $ci->p->t("stv", "document_certificate"); + $title_lv = $ci->p->t("stv", "document_coursecertificate"); + + $link_ff = "documents/export/" . + "zertifikat.rdf.php/" . + "Zertifikat" . + "?stg_kz={studiengang_kz_lv}" . + "&uid={uid}" . + "&ss={studiensemester_kurzbz}" . + "&lvid={lehrveranstaltung_id}"; + $link_lv_ger = "documents/export/" . + "lehrveranstaltungszeugnis.rdf.php/" . + "LVZeugnis" . + "?stg_kz={studiengang_kz}" . + "&uid={uid}" . + "&ss={studiensemester_kurzbz}" . + "&lvid={lehrveranstaltung_id}"; + $link_lv_eng = "documents/export/" . + "lehrveranstaltungszeugnis.rdf.php/" . + "LVZeugnisEng" . + "?stg_kz={studiengang_kz}" . + "&uid={uid}" . + "&ss={studiensemester_kurzbz}" . + "&lvid={lehrveranstaltung_id}"; + + $archive_url = "api/frontend/v1/documents/archiveSigned"; + $archive_response = $ci->p->t("stv", "document_signed_and_archived"); + $archive_post_ff = [ + "xml" => "zertifikat.rdf.php", + "xsl" => "Zertifikat", + "stg_kz" => "{studiengang_kz_lv}", + "uid" => "{uid}", + "ss" => "{studiensemester_kurzbz}", + "lvid" => "{lehrveranstaltung_id}" + ]; + $archive_post_lv_ger = [ + "xml" => "lehrveranstaltungszeugnis.rdf.php", + "xsl" => "LVZeugnis", + "stg_kz" => "{studiengang_kz}", + "uid" => "{uid}", + "ss" => "{studiensemester_kurzbz}", + "lvid" => "{lehrveranstaltung_id}" + ]; + $archive_post_lv_eng = [ + "xml" => "lehrveranstaltungszeugnis.rdf.php", + "xsl" => "LVZeugnisEng", + "stg_kz" => "{studiengang_kz}", + "uid" => "{uid}", + "ss" => "{studiensemester_kurzbz}", + "lvid" => "{lehrveranstaltung_id}" + ]; + + $list = [ + [ + 'title' => '', + 'children' => [ + [ + 'title' => $title_ff, + 'link' => site_url($link_ff) + ], + [ + 'title' => $title_lv, + 'children' => [ + [ + 'title' => $title_ger, + 'link' => site_url($link_lv_ger), + 'children' => [ + [ + 'title' => 'PDF', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_ger) + ], + [ + 'title' => 'DOC', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_ger . "&output=doc") + ], + [ + 'title' => 'ODT', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_ger . "&output=odt") + ] + ] + ], + [ + 'title' => $title_eng, + 'link' => site_url($link_lv_eng), + 'children' => [ + [ + 'title' => 'PDF', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_eng) + ], + [ + 'title' => 'DOC', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_eng . "&output=doc") + ], + [ + 'title' => 'ODT', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_eng . "&output=odt") + ] + ] + ] + ] + ] + ] + ], + [ + 'title' => '', + 'children' => [ + [ + 'title' => $title_ff, + 'action' => [ + 'url' => site_url($archive_url), + 'post' => $archive_post_ff, + 'response' => $archive_response + ] + ], + [ + 'title' => $title_lv, + 'children' => [ + [ + 'title' => $title_ger, + 'action' => [ + 'url' => site_url($archive_url), + 'post' => $archive_post_lv_ger, + 'response' => $archive_response + ] + ], + [ + 'title' => $title_eng, + 'action' => [ + 'url' => site_url($archive_url), + 'post' => $archive_post_lv_eng, + 'response' => $archive_response + ] + ] + ] + ] + ] + ] + ]; + + return $list; +} diff --git a/application/helpers/hlp_header_helper.php b/application/helpers/hlp_header_helper.php index 27dfba5a1..eb2cd9f85 100644 --- a/application/helpers/hlp_header_helper.php +++ b/application/helpers/hlp_header_helper.php @@ -111,7 +111,8 @@ function generateJSDataStorageObject($indexPage, $calledPath, $calledMethod) 'theme' => [ 'name'=>$ci->config->item('theme_name'), 'modes'=>$ci->config->item('theme_modes'), - ] + ], + 'domain' => DOMAIN ); $toPrint = "\n"; @@ -270,6 +271,28 @@ function absoluteJsImportUrl($relurl) return $url; } +/* + * Generate Css File Include if Extension contains file + * + * @param $relativeFilePath path relative to Extension public/css dir + */ +function generateCSSsIncludeIfExtensionCssExists($relativeFilePath) +{ + $fsiterator = new FilesystemIterator(FHCPATH . 'application/extensions'); + foreach ($fsiterator as $fsitem) + { + if(preg_match('/^FHC-Core-/', $fsitem->getBasename())) + { + $extensionfile = 'public/extensions/' . $fsitem->getBasename() + . '/css/' . $relativeFilePath; + if(is_readable(FHCPATH . $extensionfile)) + { + generateCSSsInclude($extensionfile); + } + } + } +} + /* * Manipulate CI views includes Array to load * - public/js/FhcApps.js via customJSs and diff --git a/application/libraries/LektorLib.php b/application/libraries/LektorLib.php index bbe630eaf..8a60e0aaa 100644 --- a/application/libraries/LektorLib.php +++ b/application/libraries/LektorLib.php @@ -17,7 +17,12 @@ class LektorLib $this->_ci->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel'); $this->_ci->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel'); $this->_ci->load->model('person/Benutzer_model', 'BenutzerModel'); + $this->_ci->load->model('ressource/Zeitsperre_model', 'ZeitsperreModel'); + $this->_ci->load->model('ressource/Reservierung_model', 'ReservierungModel'); + $this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel'); + $this->_ci->load->library('PhrasesLib', array('lehre')); + $this->_ci->load->library('VariableLib', ['uid' => getAuthUID()]); } public function addLektorToLehreinheit($lehreinheit_id, $mitarbeiter_uid) @@ -78,10 +83,13 @@ class LektorLib public function updateLektorFromLehreinheit($lehreinheit_id, $mitarbeiter_uid, $new_data) { + $old_uid = $mitarbeiter_uid; + $new_uid = isset($new_data['mitarbeiter_uid']) ? $new_data['mitarbeiter_uid'] : $mitarbeiter_uid; + $this->_ci->LehreinheitmitarbeiterModel->addSelect('lehre.tbl_lehreinheitmitarbeiter.*, lehre.tbl_lehreinheit.studiensemester_kurzbz, tbl_lehrveranstaltung.studiengang_kz'); $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id'); $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id'); - $lehreinheit_result = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid)); + $lehreinheit_result = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid)); if (isError($lehreinheit_result)) return $lehreinheit_result; @@ -89,27 +97,47 @@ class LektorLib $lehreinheit = getData($lehreinheit_result)[0]; + $semesterstunden_alt = $lehreinheit->semesterstunden; + $semesterstunden_neu = isset($new_data['semesterstunden']) ? $new_data['semesterstunden'] : $semesterstunden_alt; + $bismelden_neu = isset($new_data['bismelden']) ? $new_data['bismelden'] : $lehreinheit->bismelden; + $neue_stunden_eingerechnet = (bool)$bismelden_neu; + $alte_stunden_eingerechnet = (bool)$lehreinheit->bismelden; + $stundenplan_update = false; - //TODO kollision check, wird vorerst nicht implementiert -> nur über das FAS möglich - if (isset($new_data['mitarbeiter_uid']) && $new_data['mitarbeiter_uid'] !== $mitarbeiter_uid) + if ($old_uid !== $new_uid) { - $this->_ci->load->model('ressource/stundenplandev_model', 'StundenplandevModel'); - $this->_ci->StundenplandevModel->addGroupBy('stundenplandev_id'); - $this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid'); - $this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid'); - $verplant = $this->_ci->StundenplandevModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid)); - - if (hasData($verplant)) - return error($this->_ci->phraseslib->t("lehre", "lektorbereitsverplant")); - - $lehreinheit_data = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('mitarbeiter_uid' => $new_data['mitarbeiter_uid'], 'lehreinheit_id' => $lehreinheit_id)); + $lehreinheit_data = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('mitarbeiter_uid' => $new_uid, 'lehreinheit_id' => $lehreinheit_id)); if (hasData($lehreinheit_data)) return error($this->_ci->phraseslib->t("lehre", "bereitzugeteilt")); + $this->_ci->StundenplandevModel->addGroupBy('stundenplandev_id'); + $this->_ci->StundenplandevModel->addGroupBy('mitarbeiter_uid'); + $verplant = $this->_ci->StundenplandevModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid)); + + if (hasData($verplant)) + { + $kollision = $this->hasKollision(getData($verplant), $new_uid); + + $ignore_kollision = $this->_ci->variablelib->getVar('ignore_kollision'); + + if ($kollision === false || $ignore_kollision == 'true') + { + $stundenplan_update = true; + } + else if (is_array($kollision)) + { + return error( "Änderung fehlgeschlagen! Die Änderung des Lektors führt zu ".count($kollision)." Kollision(en) im LV-Plan. Deaktivieren Sie die Kollisionspruefung oder wenden Sie sich an die LV-Planung!\n zB. $kollision[0]"); + } + else + { + return error($kollision); + } + } } + $warning = ''; - if (isset($new_data['semesterstunden'])) + if (($semesterstunden_neu !== '' && $semesterstunden_alt !== '') && (($semesterstunden_neu > $semesterstunden_alt) || $neue_stunden_eingerechnet)) { $studiengang_result = $this->_ci->StudiengangModel->loadWhere(array('studiengang_kz' => $lehreinheit->studiengang_kz)); if (isError($studiengang_result)) return $studiengang_result; @@ -120,7 +148,7 @@ class LektorLib if (isError($studiensemester_result)) return $studiensemester_result; $studiensemester = getData($studiensemester_result)[0]; - $echter_dv_result = $this->_ci->DienstverhaeltnisModel->existsDienstverhaeltnis($mitarbeiter_uid, $studiensemester->start, $studiensemester->ende, 'echterdv'); + $echter_dv_result = $this->_ci->DienstverhaeltnisModel->existsDienstverhaeltnis($new_uid, $studiensemester->start, $studiensemester->ende, 'echterdv'); $echter_dv = false; @@ -129,83 +157,81 @@ class LektorLib $echter_dv = true; } - $neue_stunden_eingerechnet = isset($new_data['bismelden']) ? $new_data['bismelden'] : $lehreinheit->bismelden; - $alte_stunden_eingerechnet = $lehreinheit->bismelden; + $stundengrenze_result = $this->_ci->OrganisationseinheitModel->getStundengrenze($studiengang->oe_kurzbz, $echter_dv); + if (isError($stundengrenze_result)) return $stundengrenze_result; - if (($new_data['semesterstunden'] > $lehreinheit->semesterstunden) || $neue_stunden_eingerechnet) + $stundengrenze = getData($stundengrenze_result)[0]; + + $oe_result = $this->_ci->OrganisationseinheitModel->getChilds($stundengrenze->oe_kurzbz); + $oe_array = hasData($oe_result) ? array_column(getData($oe_result), 'oe_kurzbz') : array(); + + $semesterstunden_alt_sql = is_null($semesterstunden_alt) ? 0 : $semesterstunden_alt; + $semesterstunden_neu_sql = is_null($semesterstunden_neu) ? 0 : $semesterstunden_neu; + + if ($alte_stunden_eingerechnet && $neue_stunden_eingerechnet) + $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($semesterstunden_alt_sql) + {$this->_ci->LehreinheitmitarbeiterModel->db->escape($semesterstunden_neu_sql)}) as summe"); + else if ($alte_stunden_eingerechnet && !$neue_stunden_eingerechnet) + $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($semesterstunden_alt_sql)) as summe"); + else if (!$alte_stunden_eingerechnet && $neue_stunden_eingerechnet) + $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) + ({$this->_ci->LehreinheitmitarbeiterModel->db->escape($semesterstunden_neu_sql)})) as summe"); + else if (!$alte_stunden_eingerechnet && !$neue_stunden_eingerechnet) + $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden)) as summe"); + + $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id'); + $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id'); + $this->_ci->LehreinheitmitarbeiterModel->addJoin('public.tbl_studiengang', 'studiengang_kz'); + + $this->_ci->LehreinheitmitarbeiterModel->db->where('mitarbeiter_uid', $new_uid); + $this->_ci->LehreinheitmitarbeiterModel->db->where('studiensemester_kurzbz', $lehreinheit->studiensemester_kurzbz); + $this->_ci->LehreinheitmitarbeiterModel->db->where('bismelden', true); + $this->_ci->LehreinheitmitarbeiterModel->db->where('lower(mitarbeiter_uid) NOT LIKE', '_dummy%'); + + if (count($oe_array) > 0) { - $stundengrenze_result = $this->_ci->OrganisationseinheitModel->getStundengrenze($studiengang->oe_kurzbz, $echter_dv); - if (isError($stundengrenze_result)) return $stundengrenze_result; - - $stundengrenze = getData($stundengrenze_result)[0]; - - $oe_result = $this->_ci->OrganisationseinheitModel->getChilds($stundengrenze->oe_kurzbz); - $oe_array = hasData($oe_result) ? array_column(getData($oe_result), 'oe_kurzbz') : array(''); - - if ($alte_stunden_eingerechnet && $neue_stunden_eingerechnet) - $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($lehreinheit->semesterstunden) + {$this->_ci->LehreinheitmitarbeiterModel->db->escape($new_data['semesterstunden'])}) as summe"); - else if ($alte_stunden_eingerechnet && !$neue_stunden_eingerechnet) - $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) - ($lehreinheit->semesterstunden)) as summe"); - else if (!$alte_stunden_eingerechnet && $neue_stunden_eingerechnet) - $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden) + ({$this->_ci->LehreinheitmitarbeiterModel->db->escape($new_data['semesterstunden'])})) as summe"); - else if (!$alte_stunden_eingerechnet && !$neue_stunden_eingerechnet) - $this->_ci->LehreinheitmitarbeiterModel->addSelect("(SUM(tbl_lehreinheitmitarbeiter.semesterstunden)) as summe"); - - $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehreinheit', 'lehreinheit_id'); - $this->_ci->LehreinheitmitarbeiterModel->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id'); - $this->_ci->LehreinheitmitarbeiterModel->addJoin('public.tbl_studiengang', 'studiengang_kz'); - - $this->_ci->LehreinheitmitarbeiterModel->db->where('mitarbeiter_uid', (isset($new_data['mitarbeiter_uid']) ? $new_data['mitarbeiter_uid'] : $mitarbeiter_uid)); - $this->_ci->LehreinheitmitarbeiterModel->db->where('studiensemester_kurzbz', $lehreinheit->studiensemester_kurzbz); - $this->_ci->LehreinheitmitarbeiterModel->db->where('bismelden', true); - $this->_ci->LehreinheitmitarbeiterModel->db->where('lower(mitarbeiter_uid) NOT LIKE', '_dummy%'); - $this->_ci->LehreinheitmitarbeiterModel->db->where_in('tbl_studiengang.oe_kurzbz', $oe_array); + } + if(defined('FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE') + && is_array(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) + && count(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) > 0) + { + $this->_ci->LehreinheitmitarbeiterModel->db->where_not_in('tbl_studiengang.oe_kurzbz', FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE); + } - if(defined('FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE') - && is_array(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) - && count(FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE) > 0) + $summe_result = $this->_ci->LehreinheitmitarbeiterModel->load(); + + if (isError($summe_result)) return $summe_result; + + if (!hasData($summe_result)) return error('Fehler beim Ermitteln der Gesamtstunden'); + + $summe = getData($summe_result)[0]->summe; + + if ($summe > $stundengrenze->stunden) + { + if (!$echter_dv && (!$this->_ci->permissionlib->isBerechtigt('admin'))) { - $this->_ci->LehreinheitmitarbeiterModel->db->where_not_in('tbl_studiengang.oe_kurzbz', FAS_LV_LEKTORINNENZUTEILUNG_STUNDEN_IGNORE_OE); + if (!$this->LehrauftragAufFirma($new_uid)) + return error("ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden NICHT gespeichert!\n\n"); + } + else + { + $warning .= "ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden gespeichert!\n\n"; } - $summe_result = $this->_ci->LehreinheitmitarbeiterModel->load(); + $stunden_limit_result = $this->getStundenInstitut($new_uid, $lehreinheit->studiensemester_kurzbz, $oe_array); - if (isError($summe_result)) return $summe_result; - - if (!hasData($summe_result)) return error('Fehler beim Ermitteln der Gesamtstunden'); - - $summe = getData($summe_result)[0]->summe; - - if ($summe > $stundengrenze->stunden) + if (hasData($stunden_limit_result)) { - - if (!$echter_dv && (!$this->_ci->permissionlib->isBerechtigt('admin'))) + $stunden_limit_array = getData($stunden_limit_result); + foreach ($stunden_limit_array as $stunden_limit) { - if (!$this->LehrauftragAufFirma(isset($formData['mitarbeiter_uid']) ? $formData['mitarbeiter_uid'] : $mitarbeiter_uid)) - return error("ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden NICHT gespeichert!\n\n"); - } - else - { - $warning .= "ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden gespeichert!\n\n"; - } - - $stunden_limit_result = $this->getStundenInstitut($mitarbeiter_uid, $lehreinheit->studiensemester_kurzbz, $oe_array); - - if (hasData($stunden_limit_result)) - { - $stunden_limit_array = getData($stunden_limit_result); - foreach ($stunden_limit_array as $stunden_limit) - { - $warning .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n"; - } + $warning .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n"; } } } } - $benutzer_result = $this->_ci->BenutzerModel->load(array(isset($formData['mitarbeiter_uid']) ? $formData['mitarbeiter_uid'] : $mitarbeiter_uid)); + $benutzer_result = $this->_ci->BenutzerModel->load(array($new_uid)); if (isError($benutzer_result)) return $benutzer_result; @@ -227,12 +253,23 @@ class LektorLib 'bismelden' ); + $nullable_fields = array('stundensatz', 'semesterstunden', 'planstunden'); + $updateData = array(); foreach ($updatableFields as $field) { - $value = isset($new_data[$field]) ? $new_data[$field] : null; + if (!array_key_exists($field, $new_data)) + { + continue; + } - if ($value !== null) + $value = $new_data[$field]; + + if (in_array($field, $nullable_fields)) + { + $updateData[$field] = $value; + } + elseif ($value !== null) { $updateData[$field] = $value; } @@ -240,10 +277,24 @@ class LektorLib $updateData['updatevon'] = getAuthUID(); $updateData['updateamum'] = date('Y-m-d H:i:s'); - $result = $this->_ci->LehreinheitmitarbeiterModel->update(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid), $updateData); + $result = $this->_ci->LehreinheitmitarbeiterModel->update(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $old_uid), $updateData); if (isError($result)) return $result; + if ($stundenplan_update) + { + $update_result = $this->_ci->StundenplandevModel->update([ + 'lehreinheit_id' => $lehreinheit_id, + 'mitarbeiter_uid' => $old_uid, + ], [ + 'mitarbeiter_uid' => $new_uid, + 'updateamum' => date('Y-m-d H:i:s'), + 'updatevon' => getAuthUID() + ]); + + if (isError($update_result)) return $update_result; + } + if ($warning !== '') return success(['warning' => $warning]); return success('Erfolgreich geupdated'); @@ -347,4 +398,52 @@ class LektorLib $this->_ci->LehreinheitmitarbeiterModel->addGroupBy('tbl_studiengang.bezeichnung'); return $this->_ci->LehreinheitmitarbeiterModel->load(); } + + private function hasKollision($stunden, $mitarbeiter) + { + $kollision = array(); + + $ignore_zeitsperre = $this->_ci->variablelib->getVar('ignore_zeitsperre'); + $ignore_reservierung = $this->_ci->variablelib->getVar('ignore_reservierung'); + + foreach ($stunden as $stunde) + { + $stundenplan_result = $this->_ci->StundenplandevModel->lektorHasStundenplandevEintrag($mitarbeiter, $stunde->datum, $stunde->stunde); + + if (isError($stundenplan_result)) + return $stundenplan_result; + + if (hasData($stundenplan_result)) + { + $stundenplan_result = getData($stundenplan_result)[0]; + $kollision[] = "Kollision stundenplandev: $stundenplan_result->stundenplandev_id|$stundenplan_result->lektor|$stundenplan_result->ort_kurzbz|$stundenplan_result->stg_kurzbz-$stundenplan_result->semester$stundenplan_result->verband$stundenplan_result->gruppe$stundenplan_result->gruppe_kurzbz - $stundenplan_result->datum/$stundenplan_result->stunde"; + } + else + { + if ($ignore_zeitsperre == 'false' && (!defined('KOLLISIONSFREIE_USER') || !in_array($mitarbeiter, unserialize(KOLLISIONSFREIE_USER)))) + { + $zeitsperre_result = $this->_ci->ZeitsperreModel->checkIfZeitsperreExists($mitarbeiter, $stunde->datum, $stunde->stunde); + + if (hasData($zeitsperre_result)) + { + $zeitsperre_result = getData($zeitsperre_result)[0]; + $kollision[] = "Kollision (Zeitsperre): $zeitsperre_result->zeitsperre_id|$zeitsperre_result->mitarbeiter_uid|$zeitsperre_result->zeitsperretyp_kurzbz - $zeitsperre_result->vondatum/$zeitsperre_result->vonstunde|$zeitsperre_result->bisdatum/$zeitsperre_result->bisstunde"; + } + } + + if ($ignore_reservierung == 'false' && (!defined('KOLLISIONSFREIE_USER') || !in_array($mitarbeiter, unserialize(KOLLISIONSFREIE_USER)))) + { + $reservierung_result = $this->_ci->ReservierungModel->lektorHasReservierung($mitarbeiter, $stunde->datum, $stunde->stunde); + + if (hasData($reservierung_result)) + { + $reservierung_result = getData($reservierung_result)[0]; + $kollision[] = "Kollision (Reservierung): $reservierung_result->reservierung_id|$reservierung_result->uid|$reservierung_result->ort_kurzbz|$reservierung_result->stg_kurzbz-$reservierung_result->semester$reservierung_result->verband$reservierung_result->gruppe$reservierung_result->gruppe_kurzbz - $reservierung_result->datum/$reservierung_result->stunde"; + } + } + } + } + + return isEmptyArray($kollision) ? false : $kollision; + } } diff --git a/application/libraries/StundenplanLib.php b/application/libraries/StundenplanLib.php index 1d88ad0db..8d40c7abd 100644 --- a/application/libraries/StundenplanLib.php +++ b/application/libraries/StundenplanLib.php @@ -186,6 +186,21 @@ class StundenplanLib return success($stundenplan_data); } + public function getEventsByLE($lehreinheit_id, $start, $end, $stundenplan) + { + $this->_ci =& get_instance(); + + $this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel'); + return $this->_ci->StundenplanModel->getStundenplanLE($lehreinheit_id, $start, $end, $stundenplan); + } + + public function getEventsByLV($lehrveranstaltung_id, $start, $end, $stundenplan) + { + $this->_ci =& get_instance(); + + $this->_ci->load->model('ressource/Stundenplan_model', 'StundenplanModel'); + return $this->_ci->StundenplanModel->getStundenplanLV($lehrveranstaltung_id, $start, $end, $stundenplan); + } /** * Get stundenplan for a room * diff --git a/application/libraries/TagLib.php b/application/libraries/TagLib.php new file mode 100644 index 000000000..637431360 --- /dev/null +++ b/application/libraries/TagLib.php @@ -0,0 +1,392 @@ +_ci =& get_instance(); + + // Configs + $this->_ci->load->config('stv'); + + // Models + $this->_ci->load->model('person/Notiz_model', 'NotizModel'); + $this->_ci->load->model('system/Notiztyp_model', 'NotiztypModel'); + $this->_ci->load->model('person/Notizzuordnung_model', 'NotizzuordnungModel'); + $this->_ci->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); + $this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + + // Libraries + $this->_ci->load->library('PermissionLib'); + $this->_ci->load->library('PrestudentLib'); + + $this->batchUser = $this->_ci->config->item('userAutomatedTags'); + } + + public function updateAutomatedTags($paramsTag) + { + // --------------------------------- + // check params + // --------------------------------- + $required = ['kurzbz', 'data', 'typeId']; + + foreach ($required as $key) { + if (!isset($paramsTag[$key])) { + return error('Missing Parameter: ' . $key); + } + } + + $studiensemester_kurzbz = (isset ($paramsTag['studiensemester_kurzbz']) ? $paramsTag['studiensemester_kurzbz'] : null); + $tag = $paramsTag['kurzbz']; + $inputData = $paramsTag['data']; + $typeId = $paramsTag['typeId']; + + // --------------------------------- + // prepare input + // --------------------------------- + $zeitraum = []; + $arrayIds = []; + + foreach ($inputData as $item) { + $id = $item['id']; + $arrayIds[] = $id; + + $zeitraum[$id] = [ + 'von' => $item['von'] ?? null, + 'bis' => $item['bis'] ?? null + ]; + } + $arrayIds = array_unique($arrayIds); + + $result = $this->_ci->StudiensemesterModel->load($studiensemester_kurzbz); + if (isError($result)) { + return $result; + } + $data = $result->retval[0] ?? null; + + $von = $data->start ?? null; + $bis = $data->ende ?? null; + + // --------------------------------- + // load existing tags + // --------------------------------- + $allTags = []; + $resultAllTags = $this->_ci->NotizModel->getAllTags($tag, $von, $bis); + if (isError($resultAllTags)) { + return $resultAllTags; + } + $allTagsData = getData($resultAllTags); + + if (!empty($allTagsData)) { + foreach ($allTagsData as $item) { + $allTags[$item->$typeId] = $item->notiz_id; + } + } + + // --------------------------------- + // map the data + // --------------------------------- + $toRecycle = []; + $toAdd = []; + $toDelete = []; + + foreach ($arrayIds as $id) { + if (isset($allTags[$id])) { + $toRecycle[$id] = $allTags[$id]; + } else { + $toAdd[] = $id; + } + } + + foreach ($allTags as $id => $notizId) { + if (!in_array($id, $arrayIds)) { + $toDelete[$id] = $notizId; + + } + } + + // --------------------------------- + // recycle (update existing) + // --------------------------------- + $countRecycled = 0; + $retagged = []; + + foreach ($toRecycle as $id => $notizId) + { + $this->_updateTag($notizId, $zeitraum[$id]['von'], $zeitraum[$id]['bis']); + + $countRecycled++; + $retagged[] = $id; + } + + // --------------------------------- + // ADD + // --------------------------------- + $countAdded = 0; + $tagged = []; + + foreach ($toAdd as $id) + { + $this->_insertTag($typeId, $id, $tag, $zeitraum[$id]['von'], $zeitraum[$id]['bis']); + $countAdded++; + + $tagged[] = $id; + } + + // --------------------------------- + // delete + // --------------------------------- + $countDeleted = 0; + $deleted = []; + + foreach ($toDelete as $id => $notizId) + { + $result = $this->_deleteTag($notizId); + + if (isError($result)) { + return $result; + } + + $countDeleted++; + $deleted[] = $id; + } + + // --------------------------------- + // return + // --------------------------------- + return success([ + 'input' => [ + 'tag' => $tag, + 'arrayIds' => $arrayIds + ], + 'summary' => [ + 'recycled' => $countRecycled, + 'added' => $countAdded, + 'deleted' => $countDeleted + ], + 'details' => [ + 'existingTags' => $allTags, + 'toAdd' => $toAdd, + 'toRecycle' => $toRecycle, + 'toDelete' => $toDelete + ], + 'results' => [ + 'retaggedIds' => $retagged, + 'newTags' => $tagged, + 'deletedTagsIds' => $deleted + ] + ]); + } + + public function updateAutomatedTagsForTypeId(array $params) + { + $return = null; + $notiz_id = null; + + $von = $params['von']; + $bis = $params['bis']; + $tag = $params['kurzbz']; + $id = $params['id']; + $typeId = $params['typeId']; + + $this->_ci->NotizModel->addSelect('nz.notiz_id'); + $this->_ci->NotizModel->addSelect($typeId); + $this->_ci->NotizModel->addJoin('public.tbl_notizzuordnung nz', 'notiz_id'); + $resultAllTags = $this->_ci->NotizModel->loadWhere([ + 'typ' => $tag, + $typeId => $id + ]); + if(hasData($resultAllTags)) + { + $notiz_id = $resultAllTags->retval[0]->notiz_id; + } + + //RECYCLE + if ($notiz_id !== null) + { + $resultUpdateNotiz = $this->_updateTag($notiz_id, $von, $bis); + $return = ['recycled' => $resultUpdateNotiz]; + } + else + //ADD + { + $resultInsertNotiz = $this->_insertTag($typeId, $id, $tag, $von, $bis); + $return = ['added' => $resultInsertNotiz]; + } + return success($return); + } + + /* + * main function for rebuild Tags for typeId + * */ + public function rebuildTagsForTypeId($typeId, $id, $studiensemester_kurzbz) + { + $automatedTagsRes = $this->_ci->NotiztypModel->loadWhere(array('automatisiert' => true, 'taglib IS NOT NULL' => null)); + $automatedTags = hasData($automatedTagsRes) ? getData($automatedTagsRes) : []; + + $result = $this->_ci->StudiensemesterModel->load($studiensemester_kurzbz); + if (isError($result)) + return error('Error occurred during retrieving studiensemester'); + if (empty($result->retval) || !isset($result->retval[0])) { + return error('No studiensemester found'); + } + + $startSem = $result->retval[0]->start ?? null; + $endeSem = $result->retval[0]->ende ?? null; + $return = []; + + foreach ($automatedTags as $autoTag) + { + // getPath: must not be lost + $filePath = APPPATH . 'libraries/' . $autoTag->taglib . '.php'; // APPPATH = application/ + + if (file_exists($filePath)) { + require_once($filePath); + } else { + echo "File not found: " . $filePath; + continue; + } + + $className = basename($autoTag->taglib); + $kurz_bz = $autoTag->typ_kurzbz; + + $obj = new $className(); + + $criteriaIsSet = $obj->isCriteriaSetFor([ + 'typeId' => $typeId, + 'id' => $id, + 'studiensemester_kurzbz' => $studiensemester_kurzbz + ]); + + if (hasData($criteriaIsSet)) + { + $von = isset($criteriaIsSet->retval[0]->von) ? $criteriaIsSet->retval[0]->von : ''; + $bis = isset($criteriaIsSet->retval[0]->bis) ? $criteriaIsSet->retval[0]->bis : ''; + + $params = [ + 'von' => $von, + 'bis' => $bis, + 'kurzbz' => $autoTag->typ_kurzbz, + 'typeId' => $typeId, + 'id' => $id, + ]; + + $result = $this->updateAutomatedTagsForTypeId($params); + if (isError($result)) + return error('Error occurred during updateAutomatedTags' . $kurz_bz); + + $return[$kurz_bz] = $result; + } + else + { + //CHECK FOR DELETE + $params = [ + 'von' => $startSem, + 'bis' => $endeSem, + 'kurzbz' => $autoTag->typ_kurzbz, + 'typeId' => $typeId, + 'id' => $id, + ]; + $result = $this->_ci->NotizModel->checkIfExistingTag($kurz_bz, $typeId, $id, $startSem, $endeSem); + if (hasData($result)) + { + $notizId = $result->retval[0]->notiz_id; + $this->_deleteTag($notizId); + $return[$kurz_bz] = ['deleted' => $notizId]; + } + } + } + return success($return); + } + + private function _insertTag($typeId, $id, $tag, $von, $bis) + { + $resultInsert = $this->_ci->NotizModel->insert([ + 'titel' => 'TAG', + 'text' => 'AUTOMATED TAG', + 'verfasser_uid' => $this->batchUser, + 'erledigt' => false, + 'insertamum' => date('Y-m-d H:i:s'), + 'insertvon' => 'BatchJobTagAdd', + 'typ' => $tag, + 'start' => $von, + 'ende' => $bis + ]); + + if (isError($resultInsert)) { + return error('Error inserting tag for ' . $typeId . ': ' . $id); + } + + $notizId = $resultInsert->retval; + + $resultZuordnung = $this->_ci->NotizzuordnungModel->insert([ + 'notiz_id' => $notizId, + $typeId => $id + ]); + + if (isError($resultZuordnung)) { + return error('Error inserting relation for ' . $typeId . ': ' . $id); + } + + return $notizId; + } + + private function _updateTag($notiz_id, $von, $bis) + { + $resultUpdateNotiz = $this->_ci->NotizModel->update( + [ + 'notiz_id' => $notiz_id + ], + array( + 'updateamum' => date('Y-m-d H:i:s'), + 'updatevon' => 'BatchJobTagUpdate', + 'start' => $von, + 'ende' => $bis + )); + + + if (isError($resultUpdateNotiz)) + return error ('Error occurred during Update ' . $notiz_id); + + return $notiz_id; + } + + private function _deleteTag($notiz_id) + { + $result = $this->_ci->NotizzuordnungModel->delete([ + 'notiz_id' => $notiz_id + ]); + if (isError($result)) { + return error('Error occurred during delete Notizzuordnung ' . $notiz_id); + } + + $result = $this->_ci->NotizModel->delete([ + 'notiz_id' => $notiz_id + ]); + if (isError($result)) { + return error('Error occurred during delete Notiz ' . $notiz_id); + } + + return success([ + 'deleted' => $notiz_id + ]); + } + +} diff --git a/application/libraries/stv/StudentListLib.php b/application/libraries/stv/StudentListLib.php index bb44c8bb0..293d74932 100644 --- a/application/libraries/stv/StudentListLib.php +++ b/application/libraries/stv/StudentListLib.php @@ -28,6 +28,7 @@ class StudentListLib private $_allowedStgs = []; private $_selects = []; private $_joins = []; + private $_wheres = []; /** * Gets the CI instance, loads model and prepares default values @@ -40,6 +41,8 @@ class StudentListLib { $this->_ci =& get_instance(); // get code igniter instance + $this->_ci->load->config('stv'); + $this->_ci->load->model('crm/Prestudent_model', 'PrestudentModel'); if (isset($params['allowedStgs'])) @@ -47,8 +50,6 @@ class StudentListLib // Add default SELECTs $this->addSelect("b.uid"); - if (defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED) - $this->addSelect('tag_data_agg.tags'); $this->addSelect('titelpre'); $this->addSelect('nachname'); $this->addSelect('vorname'); @@ -72,6 +73,13 @@ class StudentListLib $this->addSelect('pls.status_kurzbz AS status'); $this->addSelect('pls.datum AS status_datum'); $this->addSelect('pls.bestaetigtam AS status_bestaetigung'); + $this->addSelect(" + CASE + WHEN pls.status_kurzbz IN ('Interessent','Aufgenommener','Bewerber','Wartender') + THEN pls.ausbildungssemester + ELSE s.semester + END AS semester_berechnet + "); $this->addSelect( "(SELECT kontakt FROM public.tbl_kontakt WHERE kontakttyp='email' AND person_id=p.person_id AND zustellung LIMIT 1) AS mail_privat", false @@ -111,7 +119,7 @@ class StudentListLib ) prest WHERE laststatus NOT IN ('Abbrecher', 'Abgewiesener', 'Absolvent') AND priorisierung <= tbl_prestudent.priorisierung - ) || ' (' || COALESCE(tbl_prestudent.priorisierung::text, ' '::text) || ')' AS priorisierung_relativ", false); // TODO(chris): overwrite in fetchStudents + ) || ' (' || COALESCE(tbl_prestudent.priorisierung::text, ' '::text) || ')' AS priorisierung_relativ", false); $this->addSelect('mentor'); $this->addSelect('b.aktiv AS bnaktiv'); $this->addSelect('unruly'); @@ -119,7 +127,7 @@ class StudentListLib // Add default JOINs $this->addJoin('public.tbl_studiengang stg', 'studiengang_kz', 'LEFT'); $this->addJoin('public.tbl_person p', 'person_id'); - $this->addJoin('public.tbl_student s', 'prestudent_id', 'LEFT'); // TODO(chris): overwrite in fetchStudents + $this->addJoin('public.tbl_student s', 'prestudent_id', 'LEFT'); $this->addJoin('public.tbl_prestudentstatus pls', ' pls.status_kurzbz=public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) AND pls.prestudent_id=tbl_prestudent.prestudent_id @@ -129,40 +137,14 @@ class StudentListLib $this->addJoin('public.tbl_benutzer b', 's.student_uid=b.uid', 'LEFT'); $this->addJoin("v", "", ""); // Will be replaced later $this->addJoin("ps", "", ""); // Will be replaced later - if (defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED) { - $this->_ci->load->config('stv'); - $tags = $this->_ci->config->item('stv_prestudent_tags'); - $whereTags = ''; - if (is_array($tags) && !isEmptyArray($tags)) { - $tags = array_keys($tags); - - foreach ($tags as $key => $tag) { - $tags[$key] = $this->_ci->PrestudentModel->escape($tag); - } - $whereTags = " AND nt.typ_kurzbz IN (" . implode(",", $tags) . ")"; - } - $subQueryTag = "( - SELECT - tag.prestudent_id, - COALESCE(json_agg(tag ORDER BY tag.done), '[]'::json) AS tags - FROM ( - SELECT DISTINCT ON (n.notiz_id) - n.notiz_id AS id, - nt.typ_kurzbz, - array_to_json(nt.bezeichnung_mehrsprachig)->>0 AS beschreibung, - n.text AS notiz, - nt.style, - n.erledigt AS done, - nz.prestudent_id - FROM public.tbl_notizzuordnung AS nz - JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id - JOIN public.tbl_notiz_typ AS nt ON n.typ = nt.typ_kurzbz " . $whereTags . " - ) AS tag - GROUP BY tag.prestudent_id - ) AS tag_data_agg"; - - $this->addJoin($subQueryTag, 'tag_data_agg.prestudent_id = tbl_prestudent.prestudent_id', 'LEFT'); + // Add SELECTs and JOINs from config + $config = $this->_ci->config->item('list_columns') ?: []; + foreach ($config as $conf) { + $this->addSelect($conf['default']); + if (isset($conf['joins'])) + foreach ($conf['joins'] as $join) + call_user_func_array([$this, 'addJoin'], $join); } } @@ -249,11 +231,7 @@ class StudentListLib */ public function addWhere($key, $value = null, $escape = true) { - if (!is_array($key) && is_array($value)) { - $this->_ci->PrestudentModel->db->where_in($key, $value, $escape); - } else { - $this->_ci->PrestudentModel->db->where($key, $value, $escape); - } + $this->_wheres[] = [$key, $value, $escape, false]; } /** @@ -267,11 +245,7 @@ class StudentListLib */ public function addOrWhere($key, $value = null, $escape = true) { - if (!is_array($key) && is_array($value)) { - $this->_ci->PrestudentModel->db->or_where_in($key, $value, $escape); - } else { - $this->_ci->PrestudentModel->db->or_where($key, $value, $escape); - } + $this->_wheres[] = [$key, $value, $escape, true]; } /** @@ -283,8 +257,9 @@ class StudentListLib */ public function execute($studiensemester_kurzbz) { - $stdsemEsc = $studiensemester_kurzbz ? $this->_ci->PrestudentModel->escape($studiensemester_kurzbz) : 'NULL'; + $this->addSelectAndJoinForTagsIfConfigured($studiensemester_kurzbz); + $stdsemEsc = $studiensemester_kurzbz ? $this->_ci->PrestudentModel->escape($studiensemester_kurzbz) : 'NULL'; $this->addSelect( "public.get_rolle_prestudent( @@ -315,6 +290,20 @@ class StudentListLib foreach ($this->_selects as $select) $this->_ci->PrestudentModel->addSelect($select[0], $select[1]); + foreach ($this->_wheres as $where) { + if (!is_array($where[0]) && is_array($where[1])) { + if ($where[3]) + $this->_ci->PrestudentModel->db->or_where_in($where[0], $where[1], $where[2]); + else + $this->_ci->PrestudentModel->db->where_in($where[0], $where[1], $where[2]); + } else { + if ($where[3]) + $this->_ci->PrestudentModel->db->or_where($where[0], $where[1], $where[2]); + else + $this->_ci->PrestudentModel->db->where($where[0], $where[1], $where[2]); + } + } + $this->_ci->PrestudentModel->addOrder('nachname'); $this->_ci->PrestudentModel->addOrder('vorname'); @@ -325,6 +314,81 @@ class StudentListLib //------------------------------------------------------------------------------------------------------------------ // Protected methods + /** + * Add Select and Join for Tags if configured + * + * @param string studiensemester_kurzbz + * + */ + protected function addSelectAndJoinForTagsIfConfigured($studiensemester_kurzbz) + { + if (defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED) { + + $this->_ci->load->model('system/Sprache_model', 'SpracheModel'); + $this->_ci->SpracheModel->addSelect('index'); + $result = $this->_ci->SpracheModel->loadWhere(array('sprache' => getUserLanguage())); + $language = hasData($result) ? getData($result)[0]->index : 1; + $index_bezeichnung_mehrsprachig = $language - 1; + + $this->_ci->load->config('stv'); + $tags = $this->_ci->config->item('stv_prestudent_tags'); + + $whereTags = ''; + if (is_array($tags) && !isEmptyArray($tags)) { + $tags = array_keys($tags); + + foreach ($tags as $key => $tag) { + $tags[$key] = $this->_ci->PrestudentModel->escape($tag); + } + $whereTags = " AND nt.typ_kurzbz IN (" . implode(",", $tags) . ")"; + } + $studiensemester_kurzbz_escaped = $this->_ci->PrestudentModel->escape($studiensemester_kurzbz); + $subQueryTag = "( + SELECT + tag.prestudent_id, + COALESCE(json_agg(tag ORDER BY tag.done, tag.prioritaet), '[]'::json) AS tags + FROM ( + SELECT DISTINCT ON (n.notiz_id) + n.notiz_id AS id, + nt.typ_kurzbz, + array_to_json(nt.bezeichnung_mehrsprachig)->>". $index_bezeichnung_mehrsprachig . " AS beschreibung, + n.text AS notiz, + nt.style, + n.erledigt AS done, + nt.prioritaet AS prioritaet, + nt.automatisiert AS automatisiert, + nz.prestudent_id, + n.start, + n.ende + FROM public.tbl_notizzuordnung AS nz + JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id AND nz.prestudent_id IS NOT NULL + JOIN public.tbl_notiz_typ AS nt ON n.typ = nt.typ_kurzbz " . $whereTags . " + WHERE + COALESCE(n.start, '1970-01-01') <= ( + SELECT + ende + FROM + public.tbl_studiensemester + WHERE + studiensemester_kurzbz = " . $studiensemester_kurzbz_escaped . " + ) + AND COALESCE(n.ende, '2170-12-31') >= ( + SELECT + start + FROM + public.tbl_studiensemester + WHERE + studiensemester_kurzbz = " . $studiensemester_kurzbz_escaped . " + ) + ) AS tag + GROUP BY tag.prestudent_id + ) AS tag_data_agg"; + + $this->addSelect('tag_data_agg.tags'); + $this->addJoin($subQueryTag, 'tag_data_agg.prestudent_id = tbl_prestudent.prestudent_id', 'LEFT'); + } + } + /** * Get alias of a table or select statement * diff --git a/application/libraries/tags/CoreDoubleDegreeTagLib.php b/application/libraries/tags/CoreDoubleDegreeTagLib.php new file mode 100644 index 000000000..13b113a21 --- /dev/null +++ b/application/libraries/tags/CoreDoubleDegreeTagLib.php @@ -0,0 +1,81 @@ +ci = get_instance(); + $this->ci->load->model('codex/Mobilitaet_model', 'MobilitaetModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $this->ci->MobilitaetModel->addJoin('bis.tbl_gsprogramm', 'gsprogramm_id'); + $this->ci->MobilitaetModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + + $result = $this->ci->MobilitaetModel-> loadWhere(array( + 'gsprogrammtyp_kurzbz' => 'Double', + 'studiensemester_kurzbz' => $semester + )); + $data = $result->retval; + + $doubledegree_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->start, + 'bis' => $item->ende + ]; + }, $data); + + return (object) array( + 'data' => $doubledegree_data, + 'typeId' => 'prestudent_id' + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $this->ci->MobilitaetModel->addSelect('prestudent_id'); + $this->ci->MobilitaetModel->addSelect('start as von'); + $this->ci->MobilitaetModel->addSelect('ende as bis'); + + $this->ci->MobilitaetModel->addJoin('bis.tbl_gsprogramm', 'gsprogramm_id'); + $this->ci->MobilitaetModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + + $result = $this->ci->MobilitaetModel->loadWhere(array( + 'gsprogrammtyp_kurzbz' => 'Double', + 'studiensemester_kurzbz' => $semester, + 'prestudent_id' => $prestudent_id + )); + + if(hasData($result)) + { + //array mit prestudent_id, von und bis + return $result; + } + else + return null; + } + +} diff --git a/application/libraries/tags/CoreFiftyFiveTagLib.php b/application/libraries/tags/CoreFiftyFiveTagLib.php new file mode 100644 index 000000000..4e2738e8b --- /dev/null +++ b/application/libraries/tags/CoreFiftyFiveTagLib.php @@ -0,0 +1,81 @@ +ci = get_instance(); + $this->ci->load->model('person/Person_model', 'PersonModel'); + $this->ci-> load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'person_id' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $result = $this->ci->StudiensemesterModel->loadWhere(array( + 'studiensemester_kurzbz' => $semester + )); + $data = $result->retval[0]; + + $semVon = $data->start; + $semBis = $data->ende; + $result = $this->ci->PersonModel->getFiftyFivers($semVon, $semBis); + + $data = $result->retval; + $fiftyFiveData = array_map(function($item) { + return [ + 'id' => $item->person_id + ]; + }, $data); + + return (object) array( + 'data' => $fiftyFiveData, + 'typeId' => 'person_id' + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'person_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $person_id = $params['id']; + $typeId = $params['typeId']; + + + $result = $this->ci->StudiensemesterModel->loadWhere(array( + 'studiensemester_kurzbz' => $semester + )); + $data = $result->retval[0]; + + $semVon = $data->start; + $semBis = $data->ende; + $result = $this->ci->PersonModel->isFiftyFive($semVon, $semBis, $person_id); + + + if(hasData($result)) + { + return $result; + } + else + return null; + } + +} + diff --git a/application/libraries/tags/CoreIncomingTagLib.php b/application/libraries/tags/CoreIncomingTagLib.php new file mode 100644 index 000000000..26d7dad0b --- /dev/null +++ b/application/libraries/tags/CoreIncomingTagLib.php @@ -0,0 +1,63 @@ +ci = get_instance(); + $this->ci->load->model('codex/Bisio_model', 'BisioModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $result = $this->ci->BisioModel->getIncomingsOfSemester($semester); + + $data = $result->retval; + + $outgoing_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->von, + 'bis' => $item->bis + ]; + }, $data); + + return (object) array( + 'data' => $outgoing_data, + 'typeId' => 'prestudent_id', + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $result = $this->ci->BisioModel->isPrestudentIncoming($semester, $prestudent_id); + + if (hasData($result)) { + return $result; + } + else + return null; + } + +} diff --git a/application/libraries/tags/CoreJgvTagLib.php b/application/libraries/tags/CoreJgvTagLib.php new file mode 100644 index 000000000..15e77dc98 --- /dev/null +++ b/application/libraries/tags/CoreJgvTagLib.php @@ -0,0 +1,64 @@ +ci = get_instance(); + $this->ci->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $result = $this->ci->BenutzerfunktionModel->getPrestudentsOfJgv($semester); + + $data = $result->retval; + + $jgv_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->datum_von, + 'bis' => $item->datum_bis + ]; + }, $data); + + return (object) array( + 'data' => $jgv_data, + 'typeId' => 'prestudent_id' + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $result = $this->ci->BenutzerfunktionModel->isJgv($semester, $prestudent_id); + + if(hasData($result)) + { + return $result; + } + else + return null; + } + +} diff --git a/application/libraries/tags/CoreMissingZgvTagLib.php b/application/libraries/tags/CoreMissingZgvTagLib.php new file mode 100644 index 000000000..e25aed4cc --- /dev/null +++ b/application/libraries/tags/CoreMissingZgvTagLib.php @@ -0,0 +1,81 @@ +ci = get_instance(); + $this->ci->load->model('crm/Prestudent_model', 'PrestudentModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $this->ci->PrestudentModel->addJoin('public.tbl_prestudentstatus', 'prestudent_id'); + $this->ci->PrestudentModel->addJoin('public.tbl_benutzer bn', 'person_id'); + $this->ci->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz'); + $result = $this->ci->PrestudentModel-> loadWhere(array( + 'bn.aktiv' => true, //check if necessary + 'zgvdatum' => null, + 'typ' => 'b', + 'studiensemester_kurzbz' => $semester + )); + + $data = $result->retval; + + $zgvmissing_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => null, + 'bis' => null + ]; + }, $data); + + return (object) array( + 'typeId' => 'prestudent_id', + 'data' => $zgvmissing_data + ); + + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $this->ci->PrestudentModel->addJoin('public.tbl_prestudentstatus', 'prestudent_id'); + $this->ci->PrestudentModel->addJoin('public.tbl_benutzer bn', 'person_id'); + $this->ci->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz'); + $result = $this->ci->PrestudentModel->loadWhere(array( + 'bn.aktiv' => true, //check if necessary + 'zgvdatum' => null, + 'typ' => 'b', + 'studiensemester_kurzbz' => $semester, + 'prestudent_id' => $prestudent_id + )); + if(hasData($result)) + { + return $result; + } + else + return null; + } + +} diff --git a/application/libraries/tags/CoreOutgoingTagLib.php b/application/libraries/tags/CoreOutgoingTagLib.php new file mode 100644 index 000000000..904839dee --- /dev/null +++ b/application/libraries/tags/CoreOutgoingTagLib.php @@ -0,0 +1,63 @@ +ci = get_instance(); + $this->ci->load->model('codex/Bisio_model', 'BisioModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $result = $this->ci->BisioModel->getOutgoingsOfSemester($semester); + + $data = $result->retval; + + $outgoing_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->von, + 'bis' => $item->bis + ]; + }, $data); + + return (object) array( + 'data' => $outgoing_data, + 'typeId' => 'prestudent_id', + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $result = $this->ci->BisioModel->isPrestudentOutgoing($semester, $prestudent_id); + + if (hasData($result)) { + return $result; + } + else + return null; + } + +} diff --git a/application/libraries/tags/CorePreabbrecherTagLib.php b/application/libraries/tags/CorePreabbrecherTagLib.php new file mode 100644 index 000000000..41712ac3b --- /dev/null +++ b/application/libraries/tags/CorePreabbrecherTagLib.php @@ -0,0 +1,80 @@ +ci = get_instance(); + $this->ci->load->model('education/Studierendenantrag_model', 'StudierendenantragModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $this->ci->StudierendenantragModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + $this->ci->StudierendenantragModel->db->where_in('typ', self::TYP_ABMELDUNG); + $this->ci->StudierendenantragModel->db->where('studiensemester_kurzbz', $semester); + $result = $this->ci->StudierendenantragModel->getWithLastStatusWhere([ + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_CREATED + ]); + + $data = $result->retval; + + $prewiederholer_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->start, + 'bis' => $item->ende + ]; + }, $data); + + return (object) array( + 'data' => $prewiederholer_data, + 'typeId' => 'prestudent_id', + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $this->ci->StudierendenantragModel->addSelect('prestudent_id'); + $this->ci->StudierendenantragModel->addSelect('start as von'); + $this->ci->StudierendenantragModel->addSelect('ende as bis'); + + $this->ci->StudierendenantragModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + $this->ci->StudierendenantragModel->db->where_in('typ', self::TYP_ABMELDUNG); + $this->ci->StudierendenantragModel->db->where('studiensemester_kurzbz', $semester); + $this->ci->StudierendenantragModel->db->where('prestudent_id', $prestudent_id); + $result = $this->ci->StudierendenantragModel->getWithLastStatusWhere([ + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_CREATED + ]); + + if(hasData($result)) + { + return $result; + } + else + return null; + } + +} diff --git a/application/libraries/tags/CorePrewiederholerTagLib.php b/application/libraries/tags/CorePrewiederholerTagLib.php new file mode 100644 index 000000000..8b7037b07 --- /dev/null +++ b/application/libraries/tags/CorePrewiederholerTagLib.php @@ -0,0 +1,76 @@ +ci = get_instance(); + $this->ci->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + $result = $this->ci->PrestudentstatusModel-> loadWhere(array( + 'statusgrund_id' => 15, + 'studiensemester_kurzbz' => $semester + )); + $data = $result->retval; + + $prewiederholer_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->start, + 'bis' => $item->ende + ]; + }, $data); + + return (object) array( + 'data' => $prewiederholer_data, + 'typeId' => 'prestudent_id', + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $this->ci->PrestudentstatusModel->addSelect('prestudent_id'); + $this->ci->PrestudentstatusModel->addSelect('start as von'); + $this->ci->PrestudentstatusModel->addSelect('ende as bis'); + + $this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + + $result = $this->ci->PrestudentstatusModel->loadWhere(array( + 'statusgrund_id' => 15, + 'studiensemester_kurzbz' => $semester, + 'prestudent_id' => $prestudent_id + )); + if(hasData($result)) + { + return $result; + } + else + return null; + } + +} diff --git a/application/libraries/tags/CoreStbErhoehtTagLib.php b/application/libraries/tags/CoreStbErhoehtTagLib.php new file mode 100644 index 000000000..886aff468 --- /dev/null +++ b/application/libraries/tags/CoreStbErhoehtTagLib.php @@ -0,0 +1,79 @@ +ci = get_instance(); + $this->ci->load->model('crm/Konto_model', 'KontoModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $this->ci->KontoModel->addJoin('public.tbl_prestudent', 'person_id'); + $this->ci->KontoModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + + $result = $this->ci->KontoModel-> loadWhere(array( + 'buchungstyp_kurzbz' => 'StudiengebuehrErhoeht', + 'studiensemester_kurzbz' => $semester + )); + $data = $result->retval; + + $konto_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->start, + 'bis' => $item->ende + ]; + }, $data); + + return (object) array( + 'data' => $konto_data, + 'typeId' => 'prestudent_id' + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $this->ci->KontoModel->addSelect('prestudent_id'); + $this->ci->KontoModel->addSelect('start as von'); + $this->ci->KontoModel->addSelect('ende as bis'); + $this->ci->KontoModel->addJoin('public.tbl_prestudent', 'person_id'); + $this->ci->KontoModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + + $result = $this->ci->KontoModel-> loadWhere(array( + 'buchungstyp_kurzbz' => 'StudiengebuehrErhoeht', + 'studiensemester_kurzbz' => $semester, + 'prestudent_id' => $prestudent_id + )); + + if(hasData($result)) + { + return $result; + } + else + return null; + } + +} diff --git a/application/libraries/tags/CoreUnterbrecherTagLib.php b/application/libraries/tags/CoreUnterbrecherTagLib.php new file mode 100644 index 000000000..ddf8f33cd --- /dev/null +++ b/application/libraries/tags/CoreUnterbrecherTagLib.php @@ -0,0 +1,76 @@ +ci = get_instance(); + $this->ci->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + $this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + + $result = $this->ci->PrestudentstatusModel-> loadWhere(array( + 'status_kurzbz' => 'Unterbrecher', + 'studiensemester_kurzbz' => $semester + )); + $data = $result->retval; + + $unterbrecher_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->start, + 'bis' => $item->ende + ]; + }, $data); + + return (object) array( + 'data' => $unterbrecher_data, + 'typeId' => 'prestudent_id' + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $this->ci->PrestudentstatusModel->addSelect('prestudent_id'); + $this->ci->PrestudentstatusModel->addSelect('start as von'); + $this->ci->PrestudentstatusModel->addSelect('ende as bis'); + + $this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + $result = $this->ci->PrestudentstatusModel-> loadWhere(array( + 'status_kurzbz' => 'Unterbrecher', + 'studiensemester_kurzbz' => $semester, + 'prestudent_id' => $prestudent_id + )); + + if(hasData($result)) + { + return $result; + } + else + return null; + } + +} diff --git a/application/libraries/tags/CoreWiederholerTagLib.php b/application/libraries/tags/CoreWiederholerTagLib.php new file mode 100644 index 000000000..8f4eb1dd7 --- /dev/null +++ b/application/libraries/tags/CoreWiederholerTagLib.php @@ -0,0 +1,76 @@ +ci = get_instance(); + $this->ci->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); + } + + public function getZuordnungIds(array $params) + { + if(!isset($params['studiensemester_kurzbz'])) + { + return (object) array( + 'idArray' => [] + ); + } + + $semester = $params['studiensemester_kurzbz']; + + $this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + $result = $this->ci->PrestudentstatusModel-> loadWhere(array( + 'statusgrund_id' => 16, + 'studiensemester_kurzbz' => $semester + )); + $data = $result->retval; + + $wiederholer_data = array_map(function($item) { + return [ + 'id' => $item->prestudent_id, + 'von' => $item->start, + 'bis' => $item->ende + ]; + }, $data); + + return (object) array( + 'typeId' => 'prestudent_id', + 'data' => $wiederholer_data + ); + } + + public function isCriteriaSetFor(array $params) + { + if ( !isset($params['id'], $params['studiensemester_kurzbz'], $params['typeId']) || $params['typeId'] !== 'prestudent_id') + return false; + + $semester = $params['studiensemester_kurzbz']; + $prestudent_id = $params['id']; + + $this->ci->PrestudentstatusModel->addSelect('prestudent_id'); + $this->ci->PrestudentstatusModel->addSelect('start as von'); + $this->ci->PrestudentstatusModel->addSelect('ende as bis'); + + $this->ci->PrestudentstatusModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + + $result = $this->ci->PrestudentstatusModel->loadWhere(array( + 'statusgrund_id' => 16, + 'studiensemester_kurzbz' => $semester, + 'prestudent_id' => $prestudent_id, + )); + + if(hasData($result)) + { + return $result; + } + else + return null; + } +} diff --git a/application/libraries/treemenu/LvVwLib.php b/application/libraries/treemenu/LvVwLib.php new file mode 100644 index 000000000..d3c91a196 --- /dev/null +++ b/application/libraries/treemenu/LvVwLib.php @@ -0,0 +1,192 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * LvVw Menu library + */ +class LvVwLib +{ + protected $_ci = null; + + public $config = []; + + public function __construct() + { + // Get code igniter instance + $this->_ci =& get_instance(); + + // Load Tree-config + $this->_ci->load->config('treemenu/custom/lvvw'); + $this->config = $this->_ci->config->item('root'); + + // Load Libraries + $this->_ci->load->library('treemenu/base/StgLib'); + + // Load Model + $this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel'); + } + + public function stg($path_template) + { + $permittedStgs = $this->getPermittedStudiengangKzs(); + + if (!$permittedStgs) + return []; + + $this->_ci->StudiengangModel->addSelect("ARRAY['link', FORMAT('{" . + '"type": "verband",' . + '"id": "lv/%1$s",' . + '"gruppe_kurzbz": null,' . + '"semester": null,' . + '"verband": null,' . + '"gruppe": null,' . + '"lehrverband": true,' . + '"stg_kz": "%1$s"' . + "}', studiengang_kz)] AS draggable", false); + + return $this->_ci->stglib->studiengang($path_template, $permittedStgs); + } + + public function semester($path_template, $has_children, $stg) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + if (!$has_children) + $this->_ci->StudiengangModel->db->select('TRUE AS leaf', false); + $this->_ci->StudiengangModel->addSelect("ARRAY['link', FORMAT('{" . + '"type": "verband",' . + '"id": "lv/%1$s/%2$s",' . + '"gruppe_kurzbz": null,' . + '"semester": "%2$s",' . + '"verband": null,' . + '"gruppe": null,' . + '"lehrverband": true,' . + '"stg_kz": "%1$s"' . + "}', v.studiengang_kz, v.semester)] AS draggable", false); + + return $this->_ci->stglib->semester($path_template, $stg->studiengang_kz); + } + + public function verband($path_template, $stg, $semester) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + $this->_ci->StudiengangModel->addSelect("CASE WHEN MAX(gruppe)='' OR MAX(gruppe)=' ' THEN TRUE ELSE FALSE END AS leaf"); + $this->_ci->StudiengangModel->addSelect("ARRAY['link', FORMAT('{" . + '"type": "verband",' . + '"id": "lv/%1$s/%2$s/%3$s",' . + '"gruppe_kurzbz": null,' . + '"semester": "%2$s",' . + '"verband": "%3$s",' . + '"gruppe": null,' . + '"lehrverband": true,' . + '"stg_kz": "%1$s"' . + "}', v.studiengang_kz, v.semester, v.verband)] AS draggable", false); + $this->_ci->StudiengangModel->addGroupBy('v.studiengang_kz, v.semester'); + + return $this->_ci->stglib->verband($path_template, $stg->studiengang_kz, $semester); + } + + public function group($path_template, $stg, $semester, $verband) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + $this->_ci->StudiengangModel->db->select('TRUE AS leaf', false); + + if ($verband === null) { + $this->_ci->StudiengangModel->addSelect("ARRAY['link', FORMAT('{" . + '"type": "verband",' . + '"id": "grp/%1$s/%2$s/%3$s",' . + '"gruppe_kurzbz": "%3$s",' . + '"semester": "%2$s",' . + '"verband": null,' . + '"gruppe": null,' . + '"lehrverband": false,' . + '"stg_kz": "%1$s"' . + "}', studiengang_kz, semester, gruppe_kurzbz)] AS draggable", false); + + return $this->_ci->stglib->group($path_template, $stg->studiengang_kz, $semester); + } + + $this->_ci->StudiengangModel->addSelect("ARRAY['link', FORMAT('{" . + '"type": "verband",' . + '"id": "lv/%1$s/%2$s/%3$s/%4$s",' . + '"gruppe_kurzbz": null,' . + '"semester": "%2$s",' . + '"verband": "%3$s",' . + '"gruppe": "%4$s",' . + '"lehrverband": true,' . + '"stg_kz": "%1$s"' . + "}', v.studiengang_kz, v.semester, v.verband, v.gruppe)] AS draggable", false); + + return $this->_ci->stglib->verbandsgroup($path_template, $stg->studiengang_kz, $semester, $verband); + } + + public function orgform($path_template, $stg) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + if (!$stg->mischform) + return []; + + $this->_ci->StudiengangModel->db->select('TRUE AS leaf', false); + + return $this->_ci->stglib->orgform($path_template, $stg->studiengang_kz); + } + + protected function getPermittedStudiengangKzs() + { + $permittedStgs = $this->_ci->permissionlib->getSTG_isEntitledFor('admin') ?: []; + $permittedStgs = array_merge($permittedStgs, $this->_ci->permissionlib->getSTG_isEntitledFor('assistenz') ?: []); + + return $permittedStgs; + } + + protected function getStgFromUrl($stg) + { + $permittedStgs = $this->getPermittedStudiengangKzs(); + + if (!$permittedStgs) + return null; + + $this->_ci->StudiengangModel->db->where_in('studiengang_kz', $permittedStgs); + $stg = $this->_ci->StudiengangModel->loadWhere([ + 'LOWER(CONCAT(typ, kurzbz)) =' => $stg + ]); + + if (!hasData($stg)) + return null; + + return current(getData($stg)); + } +} diff --git a/application/libraries/treemenu/StudVwLib.php b/application/libraries/treemenu/StudVwLib.php new file mode 100644 index 000000000..ee3e2cbdc --- /dev/null +++ b/application/libraries/treemenu/StudVwLib.php @@ -0,0 +1,257 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * StudVw Menu library + */ +class StudVwLib +{ + protected $_ci = null; + + public $config = []; + + public $redirect_method = [ + 'interessenten' => 'filter', + 'bewerbungnichtabgeschickt' => 'filter', + 'bewerbungabgeschickt' => 'filter', + 'zgv' => 'filter', + 'statusbestaetigt' => 'filter', + 'statusbestaetigtrtnichtangemeldet' => 'filter', + 'statusbestaetigtrtangemeldet' => 'filter', + 'reihungstestnichtangemeldet' => 'filter', + 'reihungstestangemeldet' => 'filter', + 'bewerber' => 'filter', + 'bewerberrtnichtangemeldet' => 'filter', + 'bewerberrtangemeldet' => 'filter', + 'bewerberrtangemeldetteilgenommen' => 'filter', + 'bewerberrtangemeldetnichtteilgenommen' => 'filter', + 'aufgenommen' => 'filter', + 'warteliste' => 'filter', + 'absage' => 'filter', + 'outgoing' => 'inout', + 'shared_studies' => 'inout' + ]; + + public function __construct() + { + // Get code igniter instance + $this->_ci =& get_instance(); + + // Load Tree-config + $this->_ci->load->config('treemenu/stg_base'); + $this->config = $this->_ci->config->item('root'); + + // Load Libraries + $this->_ci->load->library('treemenu/base/StgLib'); + + // Load Model + $this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel'); + } + + public function stg($path_template) + { + $permittedStgs = $this->getPermittedStudiengangKzs(); + + if (!$permittedStgs) + return []; + + return $this->_ci->stglib->studiengang($path_template, $permittedStgs); + } + + public function prestudent($path_template, $stg) + { + // Check permission + if ($this->getStgFromUrl($stg) === null) + return []; + + return [ + [ + 'path' => sprintf($path_template, '1'), + 'name' => ['lehre', 'prestudent'], + 'no_sem_reload' => true + ] + ]; + } + + public function stdsem($path_template, $stg) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + return $this->_ci->stglib->studiensemester($path_template, $stg->studiengang_kz); + } + + public function semester($path_template, $stg, $orgform) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + if ($orgform !== null) { + $orgform = $this->getOrgform($orgform); + if ($orgform === null) + return []; + $orgform = $orgform->orgform_kurzbz; + } + + return $this->_ci->stglib->semester($path_template, $stg->studiengang_kz, $orgform); + } + + public function verband($path_template, $stg, $semester, $orgform) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + if ($orgform !== null) { + $orgform = $this->getOrgform($orgform); + if ($orgform === null) + return []; + $orgform = $orgform->orgform_kurzbz; + } + + $this->_ci->StudiengangModel->addSelect("CASE WHEN MAX(gruppe)='' OR MAX(gruppe)=' ' THEN TRUE ELSE FALSE END AS leaf"); + + return $this->_ci->stglib->verband($path_template, $stg->studiengang_kz, $semester, $orgform); + } + + public function group($path_template, $stg, $semester, $verband, $orgform) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + if ($orgform !== null) { + $orgform = $this->getOrgform($orgform); + if ($orgform === null) + return []; + $orgform = $orgform->orgform_kurzbz; + } + + $this->_ci->StudiengangModel->db->select('TRUE AS leaf', false); + + if ($verband === null) + return $this->_ci->stglib->group($path_template, $stg->studiengang_kz, $semester, $orgform); + + return $this->_ci->stglib->verbandsgroup($path_template, $stg->studiengang_kz, $semester, $verband, $orgform); + } + + public function orgform($path_template, $stg) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + if (!$stg->mischform) + return []; + + return $this->_ci->stglib->orgform($path_template, $stg->studiengang_kz); + } + + public function incoming($has_children, $path_template, $stg) + { + if ($stg === null) + return $this->inout('incoming', $has_children, $path_template); + + return $this->filter('incoming', $has_children, $path_template, $stg); + } + + public function filter($original_method, $has_children, $path_template, $stg) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + // TODO(chris): phrasen + $item = [ + 'path' => sprintf($path_template, '1'), + 'name' => ['treemenu', 'filter_' . $original_method] + ]; + + if (!$has_children) + $item['leaf'] = true; + + return [ $item ]; + } + + public function inout($original_method, $has_children, $path_template) + { + if (!$this->_ci->permissionlib->isBerechtigt('inout/uebersicht')) + return []; + + // TODO(chris): phrasen + $item = [ + 'path' => sprintf($path_template, '1'), + 'name' => ['treemenu', 'inout_' . $original_method] + ]; + + if (!$has_children) + $item['leaf'] = true; + + return [ $item ]; + } + + protected function getOrgform($orgform) + { + $this->_ci->load->model('codex/Orgform_model', 'OrgformModel'); + $orgform = $this->_ci->OrgformModel->loadWhere([ + 'LOWER(orgform_kurzbz) =' => $orgform + ]); + + if (!hasData($orgform)) + return null; + + return current(getData($orgform)); + } + + protected function getPermittedStudiengangKzs() + { + $permittedStgs = $this->_ci->permissionlib->getSTG_isEntitledFor('admin') ?: []; + $permittedStgs = array_merge($permittedStgs, $this->_ci->permissionlib->getSTG_isEntitledFor('assistenz') ?: []); + + return $permittedStgs; + } + + protected function getStgFromUrl($stg) + { + $permittedStgs = $this->getPermittedStudiengangKzs(); + + if (!$permittedStgs) + return null; + + $this->_ci->StudiengangModel->db->where_in('studiengang_kz', $permittedStgs); + $stg = $this->_ci->StudiengangModel->loadWhere([ + 'LOWER(CONCAT(typ, kurzbz)) =' => $stg + ]); + + if (!hasData($stg)) + return null; + + return current(getData($stg)); + } +} diff --git a/application/libraries/treemenu/base/StgLib.php b/application/libraries/treemenu/base/StgLib.php new file mode 100644 index 000000000..84665639d --- /dev/null +++ b/application/libraries/treemenu/base/StgLib.php @@ -0,0 +1,313 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * Stg Base library + */ +class StgLib +{ + protected $_ci = null; + + public function __construct() + { + // Get code igniter instance + $this->_ci =& get_instance(); + } + + public function studiengang($path_template, $permittedStgs = []) + { + $this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel'); + + $this->_ci->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); + + $this->_ci->StudiengangModel->addDistinct(); + $this->_ci->StudiengangModel->addSelect("FORMAT(" . $this->_ci->StudiengangModel->escape($path_template) . ", LOWER(CONCAT(typ, kurzbz))) AS path"); + $this->_ci->StudiengangModel->addSelect( + "CONCAT(kurzbzlang, ' (', UPPER(CONCAT(typ, kurzbz)), ') - ', tbl_studiengang.bezeichnung) AS name", + false + ); + $this->_ci->StudiengangModel->addSelect("studiengang_kz AS title"); + $this->_ci->StudiengangModel->addSelect("studiengang_kz AS search"); + $this->_ci->StudiengangModel->addSelect('erhalter_kz'); + $this->_ci->StudiengangModel->addSelect('typ'); + $this->_ci->StudiengangModel->addSelect('kurzbz'); + $this->_ci->StudiengangModel->addSelect('studiengang_kz'); + $this->_ci->StudiengangModel->addSelect('studiengang_kz AS stg_kz'); + + $this->_ci->StudiengangModel->addOrder('erhalter_kz'); + $this->_ci->StudiengangModel->addOrder('typ'); + $this->_ci->StudiengangModel->addOrder('kurzbz'); + + if ($permittedStgs) + $this->_ci->StudiengangModel->db->where_in('studiengang_kz', $permittedStgs); + + $result = $this->_ci->StudiengangModel->loadWhere(['v.aktiv' => true]); + + return getData($result) ?: []; + } + + public function semester($path_template, $studiengang_kz, $orgform_kurzbz = null) + { + $this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel'); + + $this->_ci->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); + + $this->_ci->StudiengangModel->addDistinct(); + $this->_ci->StudiengangModel->addSelect("FORMAT(" . $this->_ci->StudiengangModel->escape($path_template) . ", semester) AS path", false); + $this->_ci->StudiengangModel->addSelect("CONCAT( + UPPER(CONCAT(typ, kurzbz)), + '-', + semester, + ( + SELECT CASE WHEN bezeichnung IS NULL OR bezeichnung='' THEN ''::TEXT ELSE CONCAT(' (', bezeichnung, ')') END + FROM public.tbl_lehrverband + WHERE studiengang_kz=v.studiengang_kz AND semester=v.semester + ORDER BY verband, gruppe LIMIT 1 + ) + ) AS name", false); + + $this->_ci->StudiengangModel->addSelect('semester'); + $this->_ci->StudiengangModel->addSelect('v.studiengang_kz AS stg_kz'); + $this->_ci->StudiengangModel->addSelect("ARRAY['link-strict', 'student-collection'] AS droplink"); + + $this->_ci->StudiengangModel->addOrder('semester'); + + if ($orgform_kurzbz !== null) { + $this->_ci->StudiengangModel->addSelect($this->_ci->StudiengangModel->escape($orgform_kurzbz) . " AS orgform_kurzbz", false); + $this->_ci->StudiengangModel->addDistinct(); + $this->_ci->StudiengangModel->db->group_start(); + $this->_ci->StudiengangModel->db->where('v.semester', 0); + $this->_ci->StudiengangModel->db->or_where('v.orgform_kurzbz', $orgform_kurzbz); + $this->_ci->StudiengangModel->db->group_end(); + } + + $result = $this->_ci->StudiengangModel->loadWhere([ + 'studiengang_kz' => $studiengang_kz, + 'v.aktiv' => true + ]); + + + return getData($result) ?: []; + } + + public function group($path_template, $studiengang_kz, $semester, $orgform_kurzbz = null) + { + $this->_ci->load->model('organisation/Gruppe_model', 'GruppeModel'); + + $this->_ci->GruppeModel->addDistinct(); + $this->_ci->GruppeModel->addSelect("FORMAT(" . $this->_ci->GruppeModel->escape($path_template) . ", gruppe_kurzbz) AS path", false); + $this->_ci->GruppeModel->addSelect("CONCAT(gruppe_kurzbz, ' (', bezeichnung, ')') AS name", false); + + $this->_ci->GruppeModel->addSelect('sort'); + $this->_ci->GruppeModel->addSelect('gruppe_kurzbz'); + $this->_ci->GruppeModel->addSelect($this->_ci->GruppeModel->escape($studiengang_kz) . '::integer AS stg_kz', false); + $this->_ci->GruppeModel->addSelect("ARRAY['link-strict', 'student-collection'] AS droplink"); + + $this->_ci->GruppeModel->addOrder('sort'); + $this->_ci->GruppeModel->addOrder('gruppe_kurzbz'); + + $where = [ + 'studiengang_kz' => $studiengang_kz, + 'semester' => $semester, + 'lehre' => true, + 'sichtbar' => true, + 'aktiv' => true, + 'direktinskription' => false + ]; + + if ($orgform_kurzbz !== null) + $where['orgform_kurzbz'] = $orgform_kurzbz; + + $result = $this->_ci->GruppeModel->loadWhere($where); + + return getData($result) ?: []; + } + + public function verband($path_template, $studiengang_kz, $semester, $orgform_kurzbz = null) + { + $this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel'); + + $this->_ci->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); + + $this->_ci->StudiengangModel->addSelect("FORMAT(" . $this->_ci->StudiengangModel->escape($path_template) . ", verband) AS path", false); + $this->_ci->StudiengangModel->addSelect("CONCAT( + UPPER(CONCAT(typ, kurzbz)), + '-', + semester, + verband, + ( + SELECT + CASE + WHEN bezeichnung IS NULL OR bezeichnung='' + THEN ''::TEXT + ELSE CONCAT(' (', bezeichnung, ')') + END + FROM public.tbl_lehrverband + WHERE studiengang_kz=v.studiengang_kz + AND semester=v.semester + AND verband=v.verband + ORDER BY gruppe + LIMIT 1 + ) + ) AS name", false); + + $this->_ci->StudiengangModel->addSelect($this->_ci->StudiengangModel->escape($semester) . ' AS semester'); + $this->_ci->StudiengangModel->addSelect('verband'); + $this->_ci->StudiengangModel->addSelect($this->_ci->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); + $this->_ci->StudiengangModel->addSelect("ARRAY['link-strict', 'student-collection'] AS droplink"); + + $this->_ci->StudiengangModel->addOrder('verband'); + + $this->_ci->StudiengangModel->addGroupBy('path, name, verband'); + + $where = [ + 'v.studiengang_kz' => $studiengang_kz, + 'v.semester' => $semester, + 'v.verband !=' => '', + 'v.aktiv' => true + ]; + + if ($orgform_kurzbz !== null && $semester) // NOTE(chris): on semester 0 show all? + $where['v.orgform_kurzbz'] = $orgform_kurzbz; + + $result = $this->_ci->StudiengangModel->loadWhere($where); + + return getData($result) ?: []; + } + + public function verbandsgroup($path_template, $studiengang_kz, $semester, $verband, $orgform_kurzbz = null) + { + $this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel'); + + $this->_ci->StudiengangModel->addJoin('public.tbl_lehrverband v', 'studiengang_kz'); + + $this->_ci->StudiengangModel->addDistinct(); + $this->_ci->StudiengangModel->addSelect("FORMAT( + " . $this->_ci->StudiengangModel->escape($path_template) . ", + gruppe + ) AS path", false); + $this->_ci->StudiengangModel->addSelect("CONCAT( + UPPER(CONCAT(typ, kurzbz)), + '-', + semester, + verband, + gruppe, + ( + SELECT + CASE + WHEN bezeichnung IS NULL OR bezeichnung='' + THEN ''::TEXT + ELSE CONCAT(' (', bezeichnung, ')') + END + FROM public.tbl_lehrverband + WHERE studiengang_kz=v.studiengang_kz + AND semester=v.semester + AND verband=v.verband + AND gruppe=v.gruppe + ORDER BY gruppe + LIMIT 1 + ) + ) AS name", false); + + $this->_ci->StudiengangModel->addSelect('v.semester'); + $this->_ci->StudiengangModel->addSelect('v.verband'); + $this->_ci->StudiengangModel->addSelect('gruppe'); + $this->_ci->StudiengangModel->addSelect($this->_ci->StudiengangModel->escape($studiengang_kz) . '::integer AS stg_kz', false); + $this->_ci->StudiengangModel->addSelect("ARRAY['link-strict', 'student-collection'] AS droplink"); + + $this->_ci->StudiengangModel->addOrder('gruppe'); + + $where = [ + 'v.studiengang_kz' => $studiengang_kz, + 'v.semester' => $semester, + 'v.verband' => $verband, + 'v.gruppe !=' => '', + 'v.aktiv' => true + ]; + + if ($orgform_kurzbz !== null && $semester) // NOTE(chris): on semester 0 show all? + $where['v.orgform_kurzbz'] = $orgform_kurzbz; + + $result = $this->_ci->StudiengangModel->loadWhere($where); + + return getData($result) ?: []; + } + + public function orgform($path_template, $studiengang_kz) + { + $this->_ci->load->model('organisation/Studienordnung_model', 'StudienordnungModel'); + + $this->_ci->StudienordnungModel->addDistinct(); + $this->_ci->StudienordnungModel->addSelect("FORMAT( + " . $this->_ci->StudienordnungModel->escape($path_template) . ", + LOWER(p.orgform_kurzbz) + ) AS path"); + $this->_ci->StudienordnungModel->addSelect("p.orgform_kurzbz AS name"); + $this->_ci->StudienordnungModel->addSelect("studiengang_kz AS stg_kz"); + + $this->_ci->StudienordnungModel->addJoin('lehre.tbl_studienplan p', 'studienordnung_id'); + + $result = $this->_ci->StudienordnungModel->loadWhere([ + 'aktiv' => true, + 'studiengang_kz' => $studiengang_kz, + 'p.orgform_kurzbz !=' => 'DDP' + ]); + + + return getData($result) ?: []; + } + + public function studiensemester($path_template, $studiengang_kz) + { + $number_displayed_past_studiensemester = null; + + $this->_ci->load->model('system/Variable_model', 'VariableModel'); + + $result = $this->_ci->VariableModel->getVariables(getAuthUID(), ['number_displayed_past_studiensemester']); + if (isError($result)) + return []; + + $data = getData($result); + if ($data && isset($data['number_displayed_past_studiensemester'])) { + $number_displayed_past_studiensemester = $data['number_displayed_past_studiensemester']; + } else { + $this->_ci->load->config('stv'); + $number_displayed_past_studiensemester = $this->_ci->config->item('number_displayed_past_studiensemester_default'); + } + + $this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + + $this->_ci->StudiensemesterModel->addPlusMinus(null, $number_displayed_past_studiensemester); + + $this->_ci->StudiensemesterModel->addSelect("studiensemester_kurzbz AS name"); + $this->_ci->StudiensemesterModel->addSelect("FORMAT( + " . $this->_ci->StudiensemesterModel->escape($path_template) . ", + LOWER(studiensemester_kurzbz) + ) AS path", false); + $this->_ci->StudiensemesterModel->addSelect($this->_ci->StudiensemesterModel->escape($studiengang_kz) . " AS stg_kz", false); + + $this->_ci->StudiensemesterModel->addOrder('ende'); + + $result = $this->_ci->StudiensemesterModel->load(); + + return getData($result) ?: []; + } +} diff --git a/application/models/codex/Bisio_model.php b/application/models/codex/Bisio_model.php index 1cff1dc54..972661c8b 100644 --- a/application/models/codex/Bisio_model.php +++ b/application/models/codex/Bisio_model.php @@ -44,4 +44,110 @@ class Bisio_model extends DB_Model else return success("Bisio not found"); } + + /** + * Gets outgoing students of certain Semester + * @param String studiensemester_kurzbz + * @return array of prestudent_ids + */ + public function getOutgoingsOfSemester($studiensemester_kurzbz) + { + $query = " + SELECT DISTINCT ps.prestudent_id, tbl_bisio.von, tbl_bisio.bis + FROM bis.tbl_bisio + JOIN public.tbl_student USING (student_uid) + JOIN public.tbl_prestudent ps USING (prestudent_id) + JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id) + JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz) + WHERE ss.studiensemester_kurzbz = ? + AND nation_code <> 'A' + AND ( + tbl_bisio.von <= ss.ende + AND ( + tbl_bisio.bis >= ss.start + OR tbl_bisio.bis IS NULL + ) + ) + "; + + return $this->execQuery($query, array($studiensemester_kurzbz)); + } + + /** + * Gets incoming students of certain Semester + * @param String studiensemester_kurzbz + * @return array of prestudent_ids + */ + public function getIncomingsOfSemester($studiensemester_kurzbz) + { + $query = " + SELECT DISTINCT ps.prestudent_id, tbl_bisio.von, tbl_bisio.bis + FROM bis.tbl_bisio + JOIN public.tbl_student USING (student_uid) + JOIN public.tbl_prestudent ps USING (prestudent_id) + JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id) + JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz) + WHERE ss.studiensemester_kurzbz = ? + AND nation_code = 'A' + AND ( + tbl_bisio.von <= ss.ende + AND ( + tbl_bisio.bis >= ss.start + OR tbl_bisio.bis IS NULL + ) + ) + "; + + return $this->execQuery($query, array($studiensemester_kurzbz)); + } + + public function isPrestudentOutgoing($studiensemester_kurzbz, $prestudent_id) + { + $query = " + SELECT + ps.prestudent_id, tbl_bisio.von, tbl_bisio.bis + FROM bis.tbl_bisio + JOIN public.tbl_student USING (student_uid) + JOIN public.tbl_prestudent ps USING (prestudent_id) + JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id) + JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz) + WHERE ss.studiensemester_kurzbz = ? + AND nation_code <> 'A' + AND ( + tbl_bisio.von <= ss.ende + AND ( + tbl_bisio.bis >= ss.start + OR tbl_bisio.bis IS NULL + ) + ) + AND ps.prestudent_id = ? + "; + + return $this->execQuery($query, array($studiensemester_kurzbz, $prestudent_id)); + } + + public function isPrestudentIncoming($studiensemester_kurzbz, $prestudent_id) + { + $query = " + SELECT + ps.prestudent_id, tbl_bisio.von, tbl_bisio.bis + FROM bis.tbl_bisio + JOIN public.tbl_student USING (student_uid) + JOIN public.tbl_prestudent ps USING (prestudent_id) + JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id) + JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz) + WHERE ss.studiensemester_kurzbz = ? + AND nation_code = 'A' + AND ( + tbl_bisio.von <= ss.ende + AND ( + tbl_bisio.bis >= ss.start + OR tbl_bisio.bis IS NULL + ) + ) + AND ps.prestudent_id = ? + "; + + return $this->execQuery($query, array($studiensemester_kurzbz, $prestudent_id)); + } } diff --git a/application/models/crm/Dokument_model.php b/application/models/crm/Dokument_model.php index 8008202c1..d292068d8 100644 --- a/application/models/crm/Dokument_model.php +++ b/application/models/crm/Dokument_model.php @@ -57,7 +57,7 @@ class Dokument_model extends DB_Model $qry = " SELECT a.akte_id, - a.bezeichnung, + dok.bezeichnung, a.dokument_kurzbz, a.titel_intern, a.anmerkung_intern, @@ -83,6 +83,7 @@ class Dokument_model extends DB_Model inhalt FROM public.tbl_akte a + JOIN public.tbl_dokument dok ON (dok.dokument_kurzbz = a.dokument_kurzbz) WHERE a.person_id = ? AND a.dokument_kurzbz NOT IN ( diff --git a/application/models/education/Lehreinheit_model.php b/application/models/education/Lehreinheit_model.php index 2f955c295..808c4e443 100644 --- a/application/models/education/Lehreinheit_model.php +++ b/application/models/education/Lehreinheit_model.php @@ -719,7 +719,7 @@ EOSQL; return "tag_data_agg AS ( SELECT lehreinheit_id, - COALESCE(json_agg(tag ORDER BY done), '[]'::json) AS tags + COALESCE(json_agg(tag ORDER BY done, prioritaet), '[]'::json) AS tags FROM ( SELECT DISTINCT ON (public.tbl_notiz.notiz_id) tbl_notiz.notiz_id AS id, @@ -728,7 +728,9 @@ EOSQL; text AS notiz, style, erledigt AS done, - lehreinheit_id + lehreinheit_id, + prioritaet, + automatisiert FROM public.tbl_notizzuordnung JOIN public.tbl_notiz ON tbl_notizzuordnung.notiz_id = tbl_notiz.notiz_id JOIN public.tbl_notiz_typ ON tbl_notiz.typ = tbl_notiz_typ.typ_kurzbz diff --git a/application/models/education/Lehreinheitgruppe_model.php b/application/models/education/Lehreinheitgruppe_model.php index dee8bbfe1..139d639cb 100644 --- a/application/models/education/Lehreinheitgruppe_model.php +++ b/application/models/education/Lehreinheitgruppe_model.php @@ -341,7 +341,7 @@ class Lehreinheitgruppe_model extends DB_Model $this->db->where('lehreinheit_id', $lehreinheit_id); $this->db->where('studiengang_kz', $gruppen_array->studiengang_kz); - if (!isEmptyString($gruppen_array->semester)) + if (!isEmptyString((string)$gruppen_array->semester)) { $this->db->where('semester', $gruppen_array->semester); } @@ -444,30 +444,37 @@ class Lehreinheitgruppe_model extends DB_Model ) ELSE tbl_gruppe.beschreibung END AS beschreibung"); - $this->addSelect("CASE - WHEN tbl_lehreinheitgruppe.gruppe_kurzbz IS NULL THEN - ( - SELECT EXISTS ( - SELECT 1 - FROM lehre.tbl_stundenplandev - WHERE lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id - AND studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz - AND semester = tbl_lehreinheitgruppe.semester - AND TRIM(COALESCE(verband, '')) = TRIM(tbl_lehreinheitgruppe.verband) - AND TRIM(COALESCE(gruppe, '')) = TRIM(tbl_lehreinheitgruppe.gruppe) - AND (gruppe_kurzbz IS NULL OR gruppe_kurzbz = '') - ) - ) - ELSE + $this->addSelect(" + CASE + WHEN trim(COALESCE(tbl_lehreinheitgruppe.gruppe_kurzbz, '')) = '' THEN ( SELECT EXISTS ( SELECT 1 - FROM lehre.tbl_stundenplandev - WHERE lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id - AND gruppe_kurzbz = tbl_lehreinheitgruppe.gruppe_kurzbz + FROM lehre.tbl_stundenplandev sp + WHERE sp.lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id + AND sp.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz + AND sp.semester = tbl_lehreinheitgruppe.semester + AND trim(COALESCE(sp.verband, '')) = trim(COALESCE(tbl_lehreinheitgruppe.verband, '')) + AND trim(COALESCE(sp.gruppe, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe, '')) + AND trim(COALESCE(sp.gruppe_kurzbz, '')) = '' ) ) - END AS verplant"); + ELSE + ( + SELECT EXISTS ( + SELECT 1 + FROM lehre.tbl_stundenplandev sp + WHERE sp.lehreinheit_id = tbl_lehreinheitgruppe.lehreinheit_id + AND sp.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz + AND sp.semester = tbl_lehreinheitgruppe.semester + AND trim(COALESCE(sp.verband, '')) = trim(COALESCE(tbl_lehreinheitgruppe.verband, '')) + AND trim(COALESCE(sp.gruppe, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe, '')) + AND trim(COALESCE(sp.gruppe_kurzbz, '')) = trim(COALESCE(tbl_lehreinheitgruppe.gruppe_kurzbz, '')) + ) + ) + END AS verplant + "); + $this->addJoin('tbl_studiengang', 'studiengang_kz', 'LEFT'); $this->addJoin('public.tbl_gruppe', 'gruppe_kurzbz', 'LEFT'); diff --git a/application/models/education/Lehrveranstaltung_model.php b/application/models/education/Lehrveranstaltung_model.php index 5422c290e..bea676b98 100644 --- a/application/models/education/Lehrveranstaltung_model.php +++ b/application/models/education/Lehrveranstaltung_model.php @@ -1245,7 +1245,7 @@ class Lehrveranstaltung_model extends DB_Model { return " SELECT - lehrveranstaltung_id, tbl_lehrveranstaltung.kurzbz as lv_kurzbz, tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung, bezeichnung_english as lv_bezeichnung_english, studiengang_kz, + distinct on (lehrveranstaltung_id) lehrveranstaltung_id, tbl_lehrveranstaltung.kurzbz as lv_kurzbz, tbl_lehrveranstaltung.bezeichnung as lv_bezeichnung, bezeichnung_english as lv_bezeichnung_english, studiengang_kz, tbl_studienplan_lehrveranstaltung.semester, tbl_lehrveranstaltung.sprache, ects as lv_ects, semesterstunden, anmerkung, lehre, lehreverzeichnis as lv_lehreverzeichnis, tbl_lehrveranstaltung.aktiv, planfaktor as lv_planfaktor, planlektoren as lv_planlektoren, planpersonalkosten as lv_planpersonalkosten, diff --git a/application/models/education/Lvgesamtnote_model.php b/application/models/education/Lvgesamtnote_model.php index c30045ff0..0a7638869 100644 --- a/application/models/education/Lvgesamtnote_model.php +++ b/application/models/education/Lvgesamtnote_model.php @@ -29,10 +29,14 @@ class Lvgesamtnote_model extends DB_Model $this->addSelect("lv.bezeichnung AS lehrveranstaltung_bezeichnung"); $this->addSelect("lv.studiengang_kz"); $this->addSelect("UPPER(stg.typ || stg.kurzbz) AS studiengang"); + $this->addSelect("person.vorname"); + $this->addSelect("person.nachname"); $this->addJoin("lehre.tbl_note n", "note"); $this->addJoin("lehre.tbl_lehrveranstaltung lv", "lehrveranstaltung_id"); $this->addJoin("public.tbl_studiengang stg", "studiengang_kz"); + $this->addJoin("public.tbl_benutzer benutzer", "uid = student_uid", "LEFT"); + $this->addJoin("public.tbl_person person", "person_id", "LEFT"); $this->db->where($this->dbTable . ".freigabedatum <", "NOW()", false); diff --git a/application/models/education/Zeugnisnote_model.php b/application/models/education/Zeugnisnote_model.php index f32713dec..694c5d5c0 100644 --- a/application/models/education/Zeugnisnote_model.php +++ b/application/models/education/Zeugnisnote_model.php @@ -148,7 +148,7 @@ class Zeugnisnote_model extends DB_Model * * @return object */ - public function getZeugnisnoten($student_uid, $studiensemester_kurzbz) + public function getZeugnisnoten($student_uid, $studiensemester_kurzbz, $lehrveranstaltung_id = null) { $params = array(); $where=''; @@ -163,6 +163,11 @@ class Zeugnisnote_model extends DB_Model $where.=" AND vw_student_lehrveranstaltung.studiensemester_kurzbz= ?"; $params[] = $studiensemester_kurzbz; } + if($lehrveranstaltung_id != null) + { + $where .= " AND vw_student_lehrveranstaltung.lehrveranstaltung_id = ?"; + $params[] = $lehrveranstaltung_id; + } $where2=''; @@ -176,6 +181,11 @@ class Zeugnisnote_model extends DB_Model $where2 .= " AND studiensemester_kurzbz= ?"; $params[] = $studiensemester_kurzbz; } + if($lehrveranstaltung_id != null) + { + $where2 .=" AND lehrveranstaltung_id = ?"; + $params[] = $lehrveranstaltung_id; + } $qry = "SELECT a.*, @@ -188,7 +198,10 @@ class Zeugnisnote_model extends DB_Model lv.semester AS semester_lv, lv.ects AS ects_lv, lv.zeugnis, - lv.bezeichnung_english AS lehrveranstaltung_bezeichnung_english + lv.bezeichnung_english AS lehrveranstaltung_bezeichnung_english, + s.verband, + person.vorname, + person.nachname FROM ( SELECT vw_student_lehrveranstaltung.lehrveranstaltung_id, uid, vw_student_lehrveranstaltung.studiensemester_kurzbz, note, punkte, uebernahmedatum, benotungsdatum, @@ -231,6 +244,8 @@ class Zeugnisnote_model extends DB_Model ORDER BY sort ) a LEFT JOIN public.tbl_student s ON (a.uid = s.student_uid) + LEFT JOIN public.tbl_benutzer benutzer ON benutzer.uid = s.student_uid + LEFT JOIN public.tbl_person person ON benutzer.person_id = person.person_id LEFT JOIN public.tbl_studiengang stg1 ON (s.studiengang_kz = stg1.studiengang_kz) LEFT JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id) LEFT JOIN public.tbl_studiengang stg2 ON (lv.studiengang_kz = stg2.studiengang_kz)"; diff --git a/application/models/person/Benutzerfunktion_model.php b/application/models/person/Benutzerfunktion_model.php index dfed7c332..52236ba7c 100644 --- a/application/models/person/Benutzerfunktion_model.php +++ b/application/models/person/Benutzerfunktion_model.php @@ -348,5 +348,64 @@ class Benutzerfunktion_model extends DB_Model return success($funktionJson); } + /** + * Gets all Prestudents with details for a given Benutzerfunktion and optionally semester + * + * @param String $studiensemester_kurzbz + * @return object |null + */ + public function getPrestudentsOfJgv($semester) + { + $query = " + SELECT DISTINCT ps.prestudent_id, bf.datum_von, bf.datum_bis + FROM public.tbl_benutzerfunktion bf + JOIN public.tbl_benutzer bn USING (uid) + JOIN public.tbl_prestudent ps USING (person_id) + JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id) + JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz) + WHERE ss.studiensemester_kurzbz = ? + AND bf.funktion_kurzbz = 'jgv' + AND ( + bf.datum_von <= ss.ende + AND ( + bf.datum_bis >= ss.start + OR bf.datum_bis IS NULL + ) + ) + "; + + return $this->execQuery($query, array($semester)); + } + + /** + * Checks if a certain prestudent has the Benutzerfunktion jgv for a certain semester + * + * @param String $studiensemester_kurzbz + * @param $prestudent_id + * @return object |null + */ + public function isJgv($semester, $prestudent_id) + { + $query = " + SELECT ps.prestudent_id, ss.start as von, ss.ende as bis + FROM public.tbl_benutzerfunktion bf + JOIN public.tbl_benutzer bn USING (uid) + JOIN public.tbl_prestudent ps USING (person_id) + JOIN public.tbl_prestudentstatus pss ON (ps.prestudent_id = pss.prestudent_id) + JOIN public.tbl_studiensemester ss ON (pss.studiensemester_kurzbz = ss.studiensemester_kurzbz) + WHERE ss.studiensemester_kurzbz = ? + AND bf.funktion_kurzbz = 'jgv' + AND ( + bf.datum_von <= ss.ende + AND ( + bf.datum_bis >= ss.start + OR bf.datum_bis IS NULL + ) + ) + AND ps.prestudent_id = ? + "; + + return $this->execQuery($query, array($semester, $prestudent_id)); + } } diff --git a/application/models/person/Notiz_model.php b/application/models/person/Notiz_model.php index 64fce8944..1dc09caaf 100644 --- a/application/models/person/Notiz_model.php +++ b/application/models/person/Notiz_model.php @@ -296,4 +296,59 @@ class Notiz_model extends DB_Model return $this->loadWhere(array('anrechnung_id' => $anrechnung_id)); } + + /** + * check if a given Tag for a certain notizzuordnung id is valid + * + * @param $tag typ_kurzbz to check + * @param $typeId typeId to check + * @param $id id to check + * @param $von start of time period or NULL + * @param $bis end of time period or NULL + * + * @return array + */ + public function checkIfExistingTag($tag, $typeId, $id, $von=null, $bis=null) + { + $query = " + SELECT * + FROM public.tbl_notiz + JOIN public.tbl_notizzuordnung nz USING (notiz_id) + WHERE typ = ? + AND {$typeId} = ? + AND ( + start IS NULL + OR ende IS NULL + OR (start <= ? AND ende >= ?) + ) + "; + + return $this->execQuery($query, [$tag, $id, $bis, $von]); + } + + /** + * returns all existing tags of a certain tag within a time period + * + * @param $tag typ_kurzbz of tag + * @param $von start of time period or NULL + * @param $bis end of time period or NULL + * + * @return array + */ + public function getAllTags($tag, $von=null, $bis=null) + { + $query = " + SELECT * + FROM public.tbl_notiz + JOIN public.tbl_notizzuordnung nz USING (notiz_id) + WHERE typ = ? + AND ( + start IS NULL + OR ende IS NULL + OR (start <= ? AND ende >= ?) + ); + "; + + return $this->execQuery($query, array($tag, $bis, $von)); + } } diff --git a/application/models/person/Person_model.php b/application/models/person/Person_model.php index e72b24de4..c893c51de 100644 --- a/application/models/person/Person_model.php +++ b/application/models/person/Person_model.php @@ -149,7 +149,7 @@ class Person_model extends DB_Model * @param $filter Term to search for. * @return DB-result */ - public function searchPerson($filter) + public function searchPerson($filter, $mode=null) { $this->addSelect('vorname, nachname, gebdatum, person_id, titelpre, titelpost'); $this->addSelect("CASE @@ -161,6 +161,26 @@ class Person_model extends DB_Model THEN 'Student' ELSE 'Person' END AS status"); + + if($mode == 'mitMaUid') + { + $this->addSelect("( + SELECT m.mitarbeiter_uid + FROM public.tbl_benutzer b + JOIN public.tbl_mitarbeiter m + ON b.uid = m.mitarbeiter_uid + WHERE b.person_id = tbl_person.person_id + LIMIT 1 + ) + AS uid"); + $this->addOrder('uid, lower(nachname), lower(vorname)'); + } + else + { + $this->addOrder('lower(nachname), lower(vorname)'); + } + + $result = $this->loadWhere( 'lower(nachname) like '.$this->db->escape('%'.mb_strtolower($filter).'%')." OR lower(vorname) like ".$this->db->escape('%'.$filter.'%')." @@ -433,4 +453,33 @@ class Person_model extends DB_Model return $this->execReadOnlyQuery($qry, [$person_id]); } + + //just a test function for a person_id tag + //alle personen die innerhalb dieses Zeitraumens 55 werden + public function getFiftyFivers($von, $bis) + { + $qry = " + SELECT + p.person_id + FROM public.tbl_person p + WHERE p.gebdatum >= DATE ? - INTERVAL '55 years' + AND p.gebdatum <= DATE ? - INTERVAL '55 years'; + "; + return $this->execReadOnlyQuery($qry, [$von, $bis]); + } + + //just a test function for a person_id tag + //check if Person gets 55 in this time + public function isFiftyFive($von, $bis, $person_id) + { + $qry = " + SELECT + p.person_id + FROM public.tbl_person p + WHERE p.gebdatum >= DATE ? - INTERVAL '55 years' + AND p.gebdatum <= DATE ? - INTERVAL '55 years' + AND p.persond_id = ?; + "; + return $this->execReadOnlyQuery($qry, [$von, $bis, $person_id]); + } } diff --git a/application/models/ressource/Reservierung_model.php b/application/models/ressource/Reservierung_model.php index cf5f1e4f9..d96fe2f6f 100644 --- a/application/models/ressource/Reservierung_model.php +++ b/application/models/ressource/Reservierung_model.php @@ -135,4 +135,15 @@ class Reservierung_model extends DB_Model return $this->execQuery($query, [$uid, $uid]); } + public function lektorHasReservierung($uid, $datum, $stunde) + { + $qry = "SELECT reservierung_id, uid, stg_kurzbz, ort_kurzbz, semester, verband, gruppe, gruppe_kurzbz, datum, stunde + FROM lehre.vw_reservierung + WHERE uid = ? + AND datum = ? + AND stunde = ?"; + + return $this->execReadOnlyQuery($qry, [$uid, $datum, $stunde]); + } + } diff --git a/application/models/ressource/Stundenplan_model.php b/application/models/ressource/Stundenplan_model.php index 997451243..1eddd2e2f 100644 --- a/application/models/ressource/Stundenplan_model.php +++ b/application/models/ressource/Stundenplan_model.php @@ -333,6 +333,68 @@ class Stundenplan_model extends DB_Model ", [$start_date, $end_date, $lv_id]); } + public function getStundenplanLE($lehreinheit, $start_date, $end_date, $stundenplan) + { + $qry = " + WITH lehreinheiten AS ( + SELECT lehreinheit_id FROM lehre.tbl_lehreinheit WHERE lehreinheit_id = ? + ), " . $this->getStundenplanCTE($stundenplan) . " + SELECT * + FROM stundenplanentries + "; + + return $this->execReadOnlyQuery($qry, array($lehreinheit, $start_date, $end_date)); + } + + public function getStundenplanLV($lehrveranstaltung_id, $start_date, $end_date, $stundenplan) + { + $qry = " + WITH lehreinheiten AS ( + SELECT lehreinheit_id + FROM lehre.tbl_lehreinheit + JOIN tbl_studiensemester USING(studiensemester_kurzbz) + WHERE lehrveranstaltung_id = ? + AND tbl_studiensemester.start >= ? AND tbl_studiensemester.ende <= ? + ), " . $this->getStundenplanCTE($stundenplan) . " + SELECT * + FROM stundenplanentries + "; + + return $this->execReadOnlyQuery($qry, array($lehrveranstaltung_id, $start_date, $end_date, $start_date, $end_date)); + } + + private function getStundenplanCTE($stundenplan) + { + return "entries AS ( + SELECT + datum, min(stunde) as stunde_beginn, max(stunde) as stunde_ende, + array_agg(DISTINCT( + CASE WHEN gruppe_kurzbz is not null THEN gruppe_kurzbz + ELSE (UPPER(stg_typ || stg_kurzbz) || COALESCE(semester,'0') || COALESCE(verband,'') || COALESCE(gruppe,'')) + END)) as gruppen_kuerzel, + array_agg(DISTINCT CONCAT(vorname || ' ' || nachname)) as lektorname, + array_agg(DISTINCT stundenplan.ort_kurzbz) as ort_kurzbz, + array_agg(DISTINCT titel) as titel, + lehrfach_bez, stundenplan.lehreinheit_id, lehrveranstaltung_id + FROM lehre.vw_$stundenplan as stundenplan + JOIN public.tbl_mitarbeiter ON stundenplan.uid = tbl_mitarbeiter.mitarbeiter_uid + JOIN tbl_benutzer ON tbl_mitarbeiter.mitarbeiter_uid = tbl_benutzer.uid + JOIN tbl_person USING(person_id) + JOIN lehreinheiten ON stundenplan.lehreinheit_id = lehreinheiten.lehreinheit_id + WHERE datum >= ? AND datum <= ? + GROUP BY datum, unr, stundenplan.lehreinheit_id, lehrveranstaltung_id, lehrfach_bez, lehrfach_bez + ORDER BY datum, min(stunde), unr, lehreinheit_id + ), + stundenplanentries AS ( + SELECT + entries.*, + stundeb.beginn AS beginn, + stundee.ende AS ende + FROM entries + JOIN lehre.tbl_stunde stundeb ON stundeb.stunde = entries.stunde_beginn + JOIN lehre.tbl_stunde stundee ON stundee.stunde = entries.stunde_ende + )"; + } /** * queries Stundenplan and filters by assigned ma_kurzbz, very similar to get by LVA * diff --git a/application/models/ressource/Stundenplandev_model.php b/application/models/ressource/Stundenplandev_model.php index e718ba073..355283b90 100644 --- a/application/models/ressource/Stundenplandev_model.php +++ b/application/models/ressource/Stundenplandev_model.php @@ -241,4 +241,15 @@ class Stundenplandev_model extends DB_Model return $this->delete(array('lehreinheit_id' => $lehreinheit_id, 'mitarbeiter_uid' => $mitarbeiter_uid)); } + + public function lektorHasStundenplandevEintrag($uid, $datum, $stunde) + { + $qry = "SELECT stundenplandev_id, lektor, stg_kurzbz, ort_kurzbz, semester, verband, gruppe, gruppe_kurzbz, datum, stunde + FROM lehre.vw_stundenplandev + WHERE uid = ? + AND datum = ? + AND stunde = ?"; + + return $this->execReadOnlyQuery($qry, [$uid, $datum, $stunde]); + } } diff --git a/application/models/ressource/Zeitsperre_model.php b/application/models/ressource/Zeitsperre_model.php index 078d29d8b..50ba4cb1d 100644 --- a/application/models/ressource/Zeitsperre_model.php +++ b/application/models/ressource/Zeitsperre_model.php @@ -61,4 +61,40 @@ class Zeitsperre_model extends DB_Model return $this->execQuery($qry); } + + public function checkIfZeitsperreExists($uid, $datum, $stunde) + { + $this->db->select("*"); + + $this->db->where('mitarbeiter_uid', $uid); + $this->db->where('zeitsperretyp_kurzbz !=', 'ZVerfueg'); + + $this->db->group_start(); + + $this->db->where('vondatum <', $datum); + $this->db->or_group_start(); + $this->db->where('vondatum', $datum); + $this->db->group_start(); + $this->db->where('vonstunde <=', $stunde); + $this->db->or_where('vonstunde IS NULL', null, false); + $this->db->group_end(); + $this->db->group_end(); + $this->db->group_end(); + + + $this->db->group_start(); + $this->db->where('bisdatum >', $datum); + $this->db->or_group_start(); + $this->db->where('bisdatum', $datum); + $this->db->group_start(); + $this->db->where('bisstunde >=', $stunde); + $this->db->or_where('bisstunde IS NULL', null, false); + $this->db->group_end(); + + $this->db->group_end(); + $this->db->group_end(); + + return $this->load(); + + } } diff --git a/application/views/LVVerwaltung.php b/application/views/LVVerwaltung.php index 4cebae839..42d4631f1 100644 --- a/application/views/LVVerwaltung.php +++ b/application/views/LVVerwaltung.php @@ -31,6 +31,8 @@ lv-root="" :permissions="" :config="" + avatar-url="" + logout-url="" > diff --git a/application/views/Studentenverwaltung.php b/application/views/Studentenverwaltung.php index 8dd2dd93d..5b6138ff8 100644 --- a/application/views/Studentenverwaltung.php +++ b/application/views/Studentenverwaltung.php @@ -56,12 +56,13 @@ $configArray = [ diff --git a/application/views/jssnippets/tabulatorcolumns.js b/application/views/jssnippets/tabulatorcolumns.js new file mode 100644 index 000000000..44667e45b --- /dev/null +++ b/application/views/jssnippets/tabulatorcolumns.js @@ -0,0 +1,19 @@ +let result = []; +let current; + + + +current = (tabulatorcolumns => { +load->view($path); ?> +})(result); + +if (current) { + if (Array.isArray(current)) + result = result.concat(current); + else + result.push(current); +} + + + +export default result; \ No newline at end of file diff --git a/application/views/jssnippets/tabulatorcolumns/lvverwaltung.js b/application/views/jssnippets/tabulatorcolumns/lvverwaltung.js new file mode 100644 index 000000000..9b7c44212 --- /dev/null +++ b/application/views/jssnippets/tabulatorcolumns/lvverwaltung.js @@ -0,0 +1,264 @@ +return [ + { + title: 'kurzbz', + titlePhrase: 'lehre/kurzbz', + field: "lv_kurzbz", + headerFilterFuncParams: { field: 'lv_kurzbz' }, + headerFilter: true, + formatter(cell, formatterParams) { + const rowData = cell.getRow().getData(); + const iconKey = (rowData.lehrtyp_kurzbz || '').toLowerCase(); + const lvkurzbz = (cell.getValue()).toUpperCase(); + + const parentspan = document.createElement('span'); + const span = document.createElement('span'); + + span.classList.add('lv_table_icon', `icon-${iconKey}`); + span.title = iconKey || 'LV-Teil'; + + parentspan.appendChild(span); + parentspan.appendChild(document.createTextNode(` ${lvkurzbz}`)); + + return parentspan; + }, + cellClick(e, cell) { + cell.getRow().treeToggle(); + } + }, + { + title: 'Tags', + field: 'tags', + tooltip: false, + headerFilter: "input", + headerFilterFunc: "tagHeaderFilter", + headerFilterFuncParams: { field: 'tags' }, + formatter: "tagFormatter", + width: 150, + }, + { + title: 'lehrveranstaltung_id', + titlePhrase: 'lehre/lehrveranstaltung_id', + field: "lehrveranstaltung_id", + headerFilterFuncParams: { field: 'lehrveranstaltung_id' }, + headerFilter: true, + visible: false + }, + { + title: 'bezeichnung', + titlePhrase: 'ui/bezeichnung', + field: "lv_bezeichnung", + headerFilter: true, + headerFilterFuncParams: { field: 'lv_bezeichnung' } + }, + { + title: 'bezeichnungeng', + titlePhrase: 'lehre/bezeichnungeng', + field: "lv_bezeichnung_english", + headerFilter: true, + headerFilterFuncParams: { field: 'lv_bezeichnung_english' }, + visible: false + }, + { + title: 'studiengangskennzahlLehre', + titlePhrase: 'lehre/studiengangskennzahlLehre', + field: "lv_studiengang_kz", + headerFilter: true, + headerFilterFuncParams: { field: 'lv_studiengang_kz' }, + visible: false + }, + { + title: 'studiengang', + titlePhrase: 'lehre/studiengang', + field: "studiengang", + headerFilter: true, + headerFilterFuncParams: { field: 'studiengang' }, + visible: false + }, + { + title: 'semester', + titlePhrase: 'lehre/semester', + field: "semester", + headerFilter: true, + headerFilterFuncParams: { field: 'semester' } + }, + { + title: 'sprache', + titlePhrase: 'global/sprache', + field: "sprache", + headerFilter: true, + headerFilterFuncParams: { field: 'sprache' }, + visible: false + }, + { + title: 'ects', + titlePhrase: 'lehre/ects', + field: "lv_ects", + headerFilter: true, + headerFilterFuncParams: { field: 'lv_ects' }, + visible: false + }, + { + title: 'semesterstunden', + titlePhrase: 'lehre/semesterstunden', + field: "semesterstunden", + headerFilter: true, + headerFilterFuncParams: { field: 'semesterstunden' }, + visible: false + }, + { + title: 'anmerkung', + titlePhrase: 'global/anmerkung', + field: "anmerkung", + headerFilter: true, + headerFilterFuncParams: { field: 'anmerkung' }, + visible: false + }, + { + title: 'lehre', + titlePhrase: 'lehre/lehre', + field: "lehre", + headerFilter: true, + headerFilterFuncParams: { field: 'lehre' }, + formatter: 'ja_nein', + visible: false + }, + { + title: 'aktiv', + titlePhrase: 'person/aktiv', + field: "aktiv", + headerFilter: true, + headerFilterFuncParams: { field: 'aktiv' }, + formatter: 'ja_nein', + visible: false + }, + { + title: 'organisationsform', + titlePhrase: 'lehre/organisationsform', + field: "orgform_kurzbz", + headerFilter: true, + headerFilterFuncParams: { field: 'orgform_kurzbz' } + }, + { + title: 'studienplan_id', + titlePhrase: 'ui/studienplan_id', + field: "studienplan_id", + headerFilter: true, + headerFilterFuncParams: { field: 'studienplan_id' }, + visible: false + }, + { + title: 'studienplan', + titlePhrase: 'lehre/studienplan', + field: "studienplan_bezeichnung", + headerFilter: true, + headerFilterFuncParams: { field: 'studienplan_bezeichnung' }, + visible: false + }, + { + title: 'lehrtyp', + titlePhrase: 'lehre/lehrtyp', + field: "lehrtyp_kurzbz", + headerFilter: true, + headerFilterFuncParams: { field: 'lehrtyp_kurzbz' }, + visible: false + }, + { + title: 'lehrform', + titlePhrase: 'lehre/lehrform', + field: "lehrform_kurzbz", + headerFilter: true, + headerFilterFuncParams: { field: 'lehrform_kurzbz'} }, + { + title: 'leplanstunden', + titlePhrase: 'lehre/leplanstunden', + field: "le_planstunden", + headerFilter: true, + headerFilterFuncParams: { field: 'le_planstunden' }, + visible: false + }, + { + title: 'lehreinheit_id', + titlePhrase: 'lehre/lehreinheit_id', + field: "lehreinheit_id", + headerFilter: true, + headerFilterFuncParams: { field: 'lehreinheit_id' }, + visible: false + }, + { + title: 'studiensemester', + titlePhrase: 'lehre/studiensemester', + field: "studiensemester_kurzbz", + headerFilter: true, + headerFilterFuncParams: { field: 'studiensemester_kurzbz' }, + visible: false + }, + { + title: 'unr', + titlePhrase: 'lehre/unr', + field: "unr", + headerFilter: true, + headerFilterFuncParams: { field: 'unr' }, + visible: false + }, + { + title: 'organisationseinheit', + titlePhrase: 'lehre/organisationseinheit', + field: "fachbereich", + headerFilter: true, + headerFilterFuncParams: { field: 'fachbereich' }, + visible: false + }, + { + title: 'stundenblockung', + titlePhrase: 'lehre/stundenblockung', + field: "stundenblockung", + headerFilter: true, + headerFilterFuncParams: { field: 'stundenblockung' }, + visible: false + }, + { + title: 'wochenrhythmus', + titlePhrase: 'lehre/wochenrhythmus', + field: "wochenrythmus", + headerFilter: true, + headerFilterFuncParams: { field: 'wochenrythmus' }, + visible: false + }, + { + title: 'startkw', + titlePhrase: 'lehre/startkw', + field: "start_kw", + headerFilter: true, + headerFilterFuncParams: { field: 'startkw' }, + visible: false + }, + { + title: 'raumtyp', + titlePhrase: 'lehre/raumtyp', + field: "raumtyp", + headerFilter: true, + headerFilterFuncParams: { field: 'raumtyp' }, + visible: false + }, + { + title: 'raumtypalternativ', + titlePhrase: 'lehre/raumtypalternativ', + field: "raumtypalternativ", + headerFilter: true, + headerFilterFuncParams: { field: 'raumtypalternativ' }, + visible: false + }, + { + title: 'gruppen', + titlePhrase: 'lehre/gruppen', + field: "gruppen", + headerFilter: true, + headerFilterFuncParams: { field: 'gruppen'} }, + { + title: 'lehrende', + titlePhrase: 'lehre/lehrende', + field: "lektoren", + headerFilter: true, + headerFilterFuncParams: { field: ['lektoren', 'vorname', 'nachname'] } + }, +]; diff --git a/application/views/jssnippets/tabulatorcolumns/stv.js b/application/views/jssnippets/tabulatorcolumns/stv.js new file mode 100644 index 000000000..8270defe1 --- /dev/null +++ b/application/views/jssnippets/tabulatorcolumns/stv.js @@ -0,0 +1,343 @@ +return [ + { + title: "UID", + titlePhrase: 'person/uid', + field: "uid", + headerFilter: true + }, + { + title: "TitelPre", + titlePhrase: 'person/titelpre', + field: "titelpre", + headerFilter: "list", + headerFilterParams: { + valuesLookup: true, + listOnEmpty: true, + autocomplete: true, + sort: "asc" + } + }, + { + title: "Nachname", + titlePhrase: 'person/nachname', + field: "nachname", + headerFilter: true + }, + { + title: "Vorname", + titlePhrase: 'person/vorname', + field: "vorname", + headerFilter: true + }, + { + title: "Wahlname", + titlePhrase: 'person/wahlname', + field: "wahlname", + visible: false, + headerFilter: true + }, + { + title: "Vornamen", + titlePhrase: 'person/vornamen', + field: "vornamen", + visible: false, + headerFilter: true + }, + { + title: "TitelPost", + titlePhrase: 'person/titelpost', + field: "titelpost", + headerFilter: "list", + headerFilterParams: { + valuesLookup: true, + listOnEmpty: true, + autocomplete: true, + sort: "asc" + } + }, + { + title: "Ersatzkennzeichen", + titlePhrase: 'person/ersatzkennzeichen', + field: "ersatzkennzeichen", + headerFilter: true + }, + { + title: "Geburtsdatum", + titlePhrase: 'person/geburtsdatum', + field: "gebdatum", + formatter: 'datetime', + formatterParams: { + inputFormat: "iso", + outputFormat: "dd.MM.yyyy", + invalidPlaceholder: " ", + timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone + }, + headerFilter: 'input', + headerFilterFunc: 'dateinput' + }, + { + title: "Geschlecht", + titlePhrase: 'person/geschlecht', + field: "geschlecht", + headerFilter: "list", + headerFilterParams: { + values: { + 'm': 'männlich', + 'w': 'weiblich', + 'x': 'divers', + 'u': 'unbekannt' + }, + listOnEmpty: true, + autocomplete: true + } + }, + { + title: "Sem.", + titlePhrase: 'lehre/sem', + field: "semester_berechnet", + headerFilter: "list", + headerFilterParams: { + valuesLookup: true, + listOnEmpty: true, + autocomplete: true, + sort: "asc" + } + }, + { + title: "Verb.", + titlePhrase: 'lehre/verb', + field: "verband", + headerFilter: "list", + headerFilterParams: { + valuesLookup: true, + listOnEmpty: true, + autocomplete: true, + sort: "asc" + } + }, + { + title: "Grp.", + titlePhrase: 'lehre/grp', + field: "gruppe", + headerFilter: "list", + headerFilterParams: { + valuesLookup: true, + listOnEmpty: true, + autocomplete: true, + sort: "asc" + } + }, + { + title: "Studiengang", + titlePhrase: 'lehre/studiengang', + field: "studiengang", + headerFilter: "list", + headerFilterParams: { + valuesLookup: true, + listOnEmpty: true, + autocomplete: true, + sort: "asc" + } + }, + { + title: "Studiengang_kz", + titlePhrase: 'lehre/studiengang_kz', + field: "studiengang_kz", + visible: false, + headerFilter: true + }, + { + title: "Personenkennzeichen", + titlePhrase: 'person/personenkennzeichen', + field: "matrikelnr", + headerFilter: true + }, + { + title: "PersonID", + titlePhrase: 'person/person_id', + field: "person_id", + headerFilter: true + }, + { + title: "Status", + titlePhrase: 'global/status', + field: "status", + headerFilter: "list", + headerFilterParams: { + valuesLookup: true, + listOnEmpty: true, + autocomplete: true, + sort: "asc" + } + }, + { + title: "Status Datum", + titlePhrase: 'profilUpdate/statusDate', + field: "status_datum", + visible: false, + formatter: 'datetime', + formatterParams: { + inputFormat: "iso", + outputFormat: "dd.MM.yyyy", + invalidPlaceholder: " ", + timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone + } + }, + { + title: "Status Bestaetigung", + titlePhrase: 'global/status_bestaetigung', + field: "status_bestaetigung", + visible: false, + formatter: 'datetime', + formatterParams: { + inputFormat: "iso", + outputFormat: "dd.MM.yyyy", + invalidPlaceholder: " ", + timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone + }, + headerFilter: 'input', + headerFilterFunc: 'dateinput' + }, + { + title: "EMail (Privat)", + titlePhrase: 'person/email_private', + field: "mail_privat", + visible: false, + headerFilter: true + }, + { + title: "EMail (Intern)", + titlePhrase: 'person/email_intern', + field: "mail_intern", + visible: false, + headerFilter: true + }, + { + title: "Anmerkungen", + titlePhrase: 'stv/notes_person', + field: "anmerkungen", + visible: false, + headerFilter: true + }, + { + title: "AnmerkungPre", + titlePhrase: 'stv/notes_prestudent', + field: "anmerkung", + visible: false, + headerFilter: true + }, + { + title: "OrgForm", + titlePhrase: 'lehre/orgform', + field: "orgform_kurzbz", + headerFilter: "list", + headerFilterParams: { + valuesLookup: true, + listOnEmpty: true, + autocomplete: true, + sort: "asc" + } + }, + { + title: "Aufmerksamdurch", + titlePhrase: 'person/aufmerksamDurch', + field: "aufmerksamdurch_kurzbz", + visible: false + }, + { + title: "Gesamtpunkte", + titlePhrase: 'admission/gesamtpunkte', + field: "punkte", + visible: false + }, + { + title: "Aufnahmegruppe", + titlePhrase: 'stv/aufnahmegruppe_kurzbz', + field: "aufnahmegruppe_kurzbz", + visible: false + }, + { + title: "Dual", + titlePhrase: 'lehre/dual_short', + field: "dual", + visible: false, + formatter: 'tickCross', + formatterParams: { + tickElement: '', + crossElement: '' + }, + headerFilter: "tickCross", + headerFilterParams: { + tristate: true, + elementAttributes: { + value: "true" + } + }, + headerFilterEmptyCheck(value) { + return value === null + } + }, + { + title: "Matrikelnummer", + titlePhrase: 'person/matrikelnummer', + field: "matr_nr", + visible: false, + headerFilter: true + }, + { + title: "Studienplan", + titlePhrase: 'lehre/studienplan', + field: "studienplan_bezeichnung", + headerFilter: "list", + headerFilterParams: { + valuesLookup: true, + listOnEmpty: true, + autocomplete: true, + sort: "asc" + } + }, + { + title: "PreStudentInnenID", + titlePhrase: 'ui/prestudent_id', + field: "prestudent_id", + headerFilter: true + }, + { + title: "Priorität", + titlePhrase: 'lehre/prioritaet', + field: "priorisierung_relativ" + }, + { + title: "Mentor", + titlePhrase: 'stv/mentor', + field: "mentor", + visible: false + }, + { + title: "Aktiv", + titlePhrase: 'person/aktiv', + field: "bnaktiv", + visible: false, + formatter: 'tickCross', + formatterParams: { + allowEmpty: true, + tickElement: '', + crossElement: '' + }, + headerFilter: "tickCross", + headerFilterParams: { + tristate: true, + elementAttributes: { + value: "true" + } + }, + headerFilterEmptyCheck(value) { + return value === null + } + }, + { + title: "Unruly", + field: "unruly", + visible: false + } +]; diff --git a/application/views/templates/FHC-Footer.php b/application/views/templates/FHC-Footer.php index d2eb229f1..66ed4bd51 100644 --- a/application/views/templates/FHC-Footer.php +++ b/application/views/templates/FHC-Footer.php @@ -154,6 +154,7 @@ generateJSsInclude('vendor/npm-asset/primevue/confirmdialog/confirmdialog.min.js'); generateJSsInclude('vendor/npm-asset/primevue/confirmationservice/confirmationservice.min.js'); generateJSsInclude('vendor/npm-asset/primevue/tieredmenu/tieredmenu.min.js'); + generateJSsInclude('vendor/npm-asset/primevue/progressbar/progressbar.min.js'); } if($vuedatepicker11) generateJSsInclude('vendor/vuejs/vuedatepicker_js11/vue-datepicker.iife.js'); diff --git a/application/views/templates/FHC-Header.php b/application/views/templates/FHC-Header.php index 7b53cbf5d..4203aa571 100644 --- a/application/views/templates/FHC-Header.php +++ b/application/views/templates/FHC-Header.php @@ -132,7 +132,11 @@ if ($cis === true) generateCSSsInclude(defined('CIS4') ? 'public/css/cis4.css' : 'public/css/cis_bs5.css'); //Tags - if ($tags === true) generateCSSsInclude('public/css/tags.css'); + if ($tags === true) + { + generateCSSsInclude('public/css/tags.css'); + generateCSSsIncludeIfExtensionCssExists('tags.css'); + } $extapphelper = ExtendableAppsHelper::getInstance(); $extapphelper->init($customCSSs, $customJSs, $customJSModules); diff --git a/composer.json b/composer.json index 4d37abdbb..0feee3c63 100644 --- a/composer.json +++ b/composer.json @@ -109,9 +109,9 @@ "type": "package", "package": { "name": "fortawesome/font-awesome6", - "version": "6.1.1", + "version": "6.7.2", "dist": { - "url": "https://github.com/FortAwesome/Font-Awesome/archive/refs/tags/6.1.1.zip", + "url": "https://github.com/FortAwesome/Font-Awesome/archive/refs/tags/6.7.2.zip", "type": "zip" } } @@ -468,7 +468,7 @@ "fgelinas/timepicker": "0.3.3", "fortawesome/font-awesome4": "4.7.*", - "fortawesome/font-awesome6": "6.1.*", + "fortawesome/font-awesome6": "6.7.*", "fzaninotto/faker": "1.*", "firebase/php-jwt": "^6.0", @@ -496,7 +496,7 @@ "netcarver/textile": "3.7.*", "nicolaskruchten/pivottable": "2.23.0", "npm-asset/primevue": "3.29.1", - "npm-asset/primeicons": "5.0.0", + "npm-asset/primeicons": "6.0.1", "olifolkerd/tabulator4": "4.9.*", "olifolkerd/tabulator5": "5.5.*", diff --git a/composer.lock b/composer.lock index 212eea3be..39042460f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "869cbc35bd1ba90ab90934fcb41b0f51", + "content-hash": "bc21de2a48b960781c1ed028c4b8d7b9", "packages": [ { "name": "afarkas/html5shiv", @@ -960,10 +960,10 @@ }, { "name": "fortawesome/font-awesome6", - "version": "6.1.1", + "version": "6.7.2", "dist": { "type": "zip", - "url": "https://github.com/FortAwesome/Font-Awesome/archive/refs/tags/6.1.1.zip" + "url": "https://github.com/FortAwesome/Font-Awesome/archive/refs/tags/6.7.2.zip" }, "type": "library" }, @@ -1565,10 +1565,10 @@ }, { "name": "npm-asset/primeicons", - "version": "5.0.0", + "version": "6.0.1", "dist": { "type": "tar", - "url": "https://registry.npmjs.org/primeicons/-/primeicons-5.0.0.tgz" + "url": "https://registry.npmjs.org/primeicons/-/primeicons-6.0.1.tgz" }, "type": "npm-asset", "license": [ diff --git a/content/statistik/lehrauftragsliste_gst.xls.php b/content/statistik/lehrauftragsliste_gst.xls.php index 990216c09..d44c91e63 100644 --- a/content/statistik/lehrauftragsliste_gst.xls.php +++ b/content/statistik/lehrauftragsliste_gst.xls.php @@ -41,6 +41,12 @@ else $user = get_uid(); loadVariables($user); +if (isset($_GET['studiensemester_kurzbz'])) +{ + if (check_stsem($_GET['studiensemester_kurzbz'])) + $semester_aktuell = $_GET['studiensemester_kurzbz']; +} + //Studiengang laden $studiengang = new studiengang($studiengang_kz); diff --git a/content/statistik/lvplanung.php b/content/statistik/lvplanung.php index b0169dade..fe69e4160 100644 --- a/content/statistik/lvplanung.php +++ b/content/statistik/lvplanung.php @@ -50,6 +50,12 @@ else $user = get_uid(); loadVariables($user); +if (isset($_GET['studiensemester_kurzbz'])) +{ + if (check_stsem($_GET['studiensemester_kurzbz'])) + $semester_aktuell = $_GET['studiensemester_kurzbz']; +} + $rechte = new benutzerberechtigung(); $rechte->getBerechtigungen($user); diff --git a/public/css/Lvverwaltung.css b/public/css/Lvverwaltung.css index 6e0f4c760..b8152d39a 100644 --- a/public/css/Lvverwaltung.css +++ b/public/css/Lvverwaltung.css @@ -38,3 +38,9 @@ textarea[name="anmerkung"] { { border-color: black; } + +.node-verband { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; +} diff --git a/public/css/Studentenverwaltung.css b/public/css/Studentenverwaltung.css index 86e3d6be3..870a58bdf 100644 --- a/public/css/Studentenverwaltung.css +++ b/public/css/Studentenverwaltung.css @@ -2,6 +2,7 @@ @import './SvgIcons.css'; @import './components/searchbar/searchbar.css'; @import './components/verticalsplit.css'; +@import './components/horizontalsplit.css'; @import './components/FilterComponent.css'; @import './components/Tabs.css'; @import './components/Notiz.css'; @@ -95,6 +96,13 @@ html.fs_huge { overflow: visible !important; } +/* to enable scrolling for many student entries*/ +.stv-details-abschlusspruefung-student-names { + max-height: 80px; + overflow-y: auto; + overflow-x: hidden; +} + .p-dropdown-item{ z-index: 1201 !important; } diff --git a/public/css/components/horizontalsplit.css b/public/css/components/horizontalsplit.css new file mode 100644 index 000000000..baf8632c0 --- /dev/null +++ b/public/css/components/horizontalsplit.css @@ -0,0 +1,75 @@ +:root { + --fhc-horizontalsplit-hsplitter-bg-color: var(--fhc-background, #eee); + --fhc-horizontalsplit-hsplitter-border-color: var(--fhc-border, #eee); + --fhc-horizontalsplit-hsplitter-splitactions-color: var(--fhc-dark, #000); +} + +.horizontalsplit-container { + display: flex; + flex-direction: row; + overflow: hidden; + max-height: 100%; + padding: 0px; +} + +.horizontalsplitted { + overflow: auto; + flex-shrink: 0; +} + +.horizontalsplitter { + flex-shrink: 0; + width: 16px; + cursor: col-resize; + user-select: none; + display: flex; + align-items: center; + justify-content: center; +} + +.horizontalsplitter.left { + border-left: solid 3px var(--fhc-horizontalsplit-hsplitter-border-color); + margin-right: 3px; +} + +.horizontalsplitter.right { + border-right: solid 3px var(--fhc-horizontalsplit-hsplitter-border-color); + margin-left: 3px; +} + +.splitactions.horizontal { + background-color: var(--fhc-horizontalsplit-hsplitter-bg-color); + color: var(--fhc-horizontalsplit-hsplitter-splitactions-color); + display: flex; + flex-direction: column; + padding: 5px 0 5px 0; +} + +.splitactions.horizontal.left { + border-radius: 0 40% 40% 0; +} + +.splitactions.horizontal.right { + border-radius: 40% 0 0 40%; +} + +.splitactions.horizontal .splitaction { + width: 14px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; +} + +.splitactions.horizontal .splitaction.resize { + cursor: col-resize; +} + +#content { + padding-top: 0 !important; + padding-bottom: 0 !important; +} + +#content > div:first-child { + margin-top: 30px; +} \ No newline at end of file diff --git a/public/css/tags.css b/public/css/tags.css index e92f415b2..0634c5ce3 100644 --- a/public/css/tags.css +++ b/public/css/tags.css @@ -46,7 +46,6 @@ background-color: #ffA500ff; } - .tag_braun { background-color: #6d4c41; } @@ -69,12 +68,46 @@ .tag_limette { background-color: #D3FFCE; + color: black; +} + +.tag_turquoise { + background-color: #89cccf; + color: black; +} + +.tag_bordeaux { + background-color: #7B1B38; + color: white; +} + +.tag_olive { + background-color: #868d07; + color: white; +} + +.tag_beige { + background-color: #E4D5B4; + color: black; +} + +.tag_skyblue { + background-color: #90D5FF; + color: black; +} +.tag_blush { + background-color: #A83E3E; + color: white; } .tag_done { text-decoration: line-through; } +.tag_auto { + border: 1px dashed white; +} + .display_all { background-color: darkgrey !important; border: none; @@ -111,4 +144,4 @@ .copy-btn { float: right; margin-top: 3px; -} \ No newline at end of file +} diff --git a/public/js/api/factory/detailHeader.js b/public/js/api/factory/detailHeader.js index 3c7d95bb1..02a371f15 100644 --- a/public/js/api/factory/detailHeader.js +++ b/public/js/api/factory/detailHeader.js @@ -34,4 +34,10 @@ export default { url: 'api/frontend/v1/detailheader/detailheader/getLeitungOrg/' + oekurzbz, }; }, + getSemesterStati(prestudent_id){ + return { + method: 'get', + url: 'api/frontend/v1/detailheader/detailheader/getSemesterStati/' + prestudent_id, + }; + }, } \ No newline at end of file diff --git a/public/js/api/factory/studiensemester.js b/public/js/api/factory/studiensemester.js index 7fe4a22d3..f57c10763 100644 --- a/public/js/api/factory/studiensemester.js +++ b/public/js/api/factory/studiensemester.js @@ -29,5 +29,20 @@ export default { url: 'api/frontend/v1/organisation/studiensemester/getAll', params: { order, start } }; + }, + current() { + return { + method: 'get', + url: 'api/frontend/v1/organisation/studiensemester/current' + }; + }, + set(studiensemester_kurzbz) { + return { + method: 'post', + url: 'api/frontend/v1/organisation/studiensemester/set', + params: { + studiensemester_kurzbz + } + }; } }; \ No newline at end of file diff --git a/public/js/api/factory/stv.js b/public/js/api/factory/stv.js index 95bc227f5..15ae15649 100644 --- a/public/js/api/factory/stv.js +++ b/public/js/api/factory/stv.js @@ -17,7 +17,6 @@ import app from './stv/app.js'; import lists from './stv/lists.js'; -import verband from './stv/verband.js'; import students from './stv/students.js'; import filter from './stv/filter.js'; import konto from './stv/konto.js'; @@ -34,7 +33,6 @@ import admissionDates from './stv/admissionDates.js'; export default { app, lists, - verband, students, filter, konto, diff --git a/public/js/api/factory/stv/coursedates.js b/public/js/api/factory/stv/coursedates.js index 71ded7285..dc047f815 100644 --- a/public/js/api/factory/stv/coursedates.js +++ b/public/js/api/factory/stv/coursedates.js @@ -16,16 +16,20 @@ */ export default { - getCourselist(params) { + getCourselist(student_uid, start_date, end_date, stundenplan) { return { method: 'get', - url: 'api/frontend/v1/stv/LvTermine/getStundenplan/' + params.student_uid + '/' - + params.start_date + '/' - + params.end_date + '/' - + params.group_consecutiveHours + '/' - + params.dbStundenplanTable + url: 'api/frontend/v1/stv/LvTermine/getStundenplan/' + encodeURIComponent(student_uid) + '/' + + encodeURIComponent(start_date) + '/' + + encodeURIComponent(end_date) + '/' + + encodeURIComponent(stundenplan) + '/' + + encodeURIComponent(true) }; }, + exportCalendar(student_uid, stundenplan) + { + return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'content/statistik/termine.xls.php?student_uid=' + encodeURIComponent(student_uid) + '&db_stpl_table='+encodeURIComponent(stundenplan); + }, getStudiensemester(){ return { method: 'get', diff --git a/public/js/api/factory/stv/students.js b/public/js/api/factory/stv/students.js index c17e32ff9..98360ea13 100644 --- a/public/js/api/factory/stv/students.js +++ b/public/js/api/factory/stv/students.js @@ -17,9 +17,9 @@ export default { uid(uid, studiensemester_kurzbz) { - let url = 'api/frontend/v1/stv/students/' + let url = 'api/frontend/v1/treemenudata/stv/stdsem/' + encodeURIComponent(studiensemester_kurzbz) - + '/uid/' + + '/student/' + encodeURIComponent(uid); return { method: 'get', @@ -27,7 +27,7 @@ export default { }; }, prestudent(prestudent_id, studiensemester_kurzbz) { - let url = 'api/frontend/v1/stv/students/' + let url = 'api/frontend/v1/treemenudata/stv/stdsem/' + encodeURIComponent(studiensemester_kurzbz) + '/prestudent/' + encodeURIComponent(prestudent_id); @@ -37,7 +37,7 @@ export default { }; }, person(person_id, studiensemester_kurzbz) { - let url = 'api/frontend/v1/stv/students/' + let url = 'api/frontend/v1/treemenudata/stv/stdsem/' + encodeURIComponent(studiensemester_kurzbz) + '/person/' + encodeURIComponent(person_id); diff --git a/public/js/api/factory/stv/tag.js b/public/js/api/factory/stv/tag.js index 29675f6d4..4a22a009e 100644 --- a/public/js/api/factory/stv/tag.js +++ b/public/js/api/factory/stv/tag.js @@ -51,4 +51,20 @@ export default { params: data }; }, + + getAllTagsPrestudent(prestudent_id){ + return { + method: 'get', + url: 'api/frontend/v1/stv/Tags/getAllTags', + params: prestudent_id + }; + }, + + rebuildTagsforTypeId(data){ + return { + method: 'post', + url: 'api/frontend/v1/stv/Tags/rebuildTagsForTypeId/', + params: data + }; + } }; \ No newline at end of file diff --git a/public/js/api/lehrveranstaltung/studiengangtree.js b/public/js/api/factory/treemenu.js similarity index 68% rename from public/js/api/lehrveranstaltung/studiengangtree.js rename to public/js/api/factory/treemenu.js index fb63db729..662297d22 100644 --- a/public/js/api/lehrveranstaltung/studiengangtree.js +++ b/public/js/api/factory/treemenu.js @@ -1,5 +1,5 @@ /** - * Copyright (C) 2025 fhcomplete.org + * Copyright (C) 2026 fhcomplete.org * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,26 +16,29 @@ */ export default { - get(path) { - let url = 'api/frontend/v1/lv/StgTree'; - if (path) - url += '/' + path; + get(config, path = '') { return { method: 'get', - url + url: '/api/frontend/v1/treemenu/' + config + '/' + path + }; + }, + data(config, path = '') { + return { + method: 'get', + url: '/api/frontend/v1/treemenudata/' + config + '/' + path }; }, favorites: { - get() { + get(config) { return { method: 'get', - url: 'api/frontend/v1/lv/favorites' + url: 'api/frontend/v1/favorites/get/' + config }; }, - set(favorites) { + set(config, favorites) { return { method: 'post', - url: 'api/frontend/v1/lv/favorites/set', + url: 'api/frontend/v1/favorites/set/' + config, params: { favorites } }; } diff --git a/public/js/api/lehrveranstaltung/config.js b/public/js/api/lehrveranstaltung/config.js new file mode 100644 index 000000000..40d275145 --- /dev/null +++ b/public/js/api/lehrveranstaltung/config.js @@ -0,0 +1,15 @@ +export default { + get() { + return { + method: 'get', + url: '/api/frontend/v1/lv/config/get' + }; + }, + set(params) { + return { + method: 'post', + url: 'api/frontend/v1/lv/config/set', + params + }; + } +}; diff --git a/public/js/api/lehrveranstaltung/gruppe.js b/public/js/api/lehrveranstaltung/gruppe.js index 44340bec7..23db4c069 100644 --- a/public/js/api/lehrveranstaltung/gruppe.js +++ b/public/js/api/lehrveranstaltung/gruppe.js @@ -18,6 +18,14 @@ export default { params: newData }; }, + getGruppe(gruppe) + { + return { + method: 'post', + url: '/api/frontend/v1/lv/gruppe/getGruppe/', + params: gruppe + }; + }, getByLehreinheit(lehreinheit_id) { return { diff --git a/public/js/api/lehrveranstaltung/letermine.js b/public/js/api/lehrveranstaltung/letermine.js new file mode 100644 index 000000000..e5b3093f5 --- /dev/null +++ b/public/js/api/lehrveranstaltung/letermine.js @@ -0,0 +1,19 @@ +export default { + getCourselist(le_id, start_date, end_date, stundenplan) + { + return { + method: 'get', + url: '/api/frontend/v1/lvPlan/getLeEvents/' + encodeURIComponent(le_id) + "/" + encodeURIComponent(start_date) + "/" + encodeURIComponent(end_date) + "/" + encodeURIComponent(stundenplan), + }; + }, + exportCalendar(le_id, stundenplan) + { + return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'content/statistik/termine.xls.php?lehreinheit_id=' + encodeURIComponent(le_id) + '&db_stpl_table='+encodeURIComponent(stundenplan); + }, + getStudiensemester(){ + return { + method: 'get', + url: '/api/frontend/v1/lv/setup/getStudiensemester/' + }; + }, +}; diff --git a/public/js/api/lehrveranstaltung/lvtermine.js b/public/js/api/lehrveranstaltung/lvtermine.js new file mode 100644 index 000000000..35fdc5ca8 --- /dev/null +++ b/public/js/api/lehrveranstaltung/lvtermine.js @@ -0,0 +1,19 @@ +export default { + getCourselist(lv_id, start_date, end_date, stundenplan) + { + return { + method: 'get', + url: '/api/frontend/v1/lvPlan/getLvEvents/' + encodeURIComponent(lv_id) + "/" + encodeURIComponent(start_date) + "/" + encodeURIComponent(end_date) + "/" + encodeURIComponent(stundenplan), + }; + }, + exportCalendar(lv_id, stundenplan) + { + return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'content/statistik/termine.xls.php?lehrveranstaltung_id=' + encodeURIComponent(lv_id) + '&db_stpl_table='+encodeURIComponent(stundenplan); + }, + getStudiensemester(){ + return { + method: 'get', + url: '/api/frontend/v1/lv/setup/getStudiensemester/' + }; + }, +}; diff --git a/public/js/api/lehrveranstaltung/noten.js b/public/js/api/lehrveranstaltung/noten.js new file mode 100644 index 000000000..fdcbb6f1e --- /dev/null +++ b/public/js/api/lehrveranstaltung/noten.js @@ -0,0 +1,25 @@ +import Grades from '../factory/stv/grades.js'; + +export default { + ...Grades, + getCertificate(lv_id, studiensemester_kurzbz) { + let url = 'api/frontend/v1/lv/noten/getCertificate/' + encodeURIComponent(lv_id); + if (!!studiensemester_kurzbz) { + url = url + '/' + encodeURIComponent(studiensemester_kurzbz); + } + return { + method: 'get', + url: url + }; + }, + getTeacherProposal(lv_id, studiensemester_kurzbz) { + let url = 'api/frontend/v1/lv/noten/getTeacherProposal/' + encodeURIComponent(lv_id); + if (!!studiensemester_kurzbz) { + url = url + '/' + encodeURIComponent(studiensemester_kurzbz); + } + return { + method: 'get', + url: url + }; + }, +}; \ No newline at end of file diff --git a/public/js/api/lehrveranstaltung/setup.js b/public/js/api/lehrveranstaltung/setup.js index 78f78b05b..dab1062a4 100644 --- a/public/js/api/lehrveranstaltung/setup.js +++ b/public/js/api/lehrveranstaltung/setup.js @@ -1,9 +1,16 @@ export default { - getTabs() + getLETabs() { return { method: 'get', - url: '/api/frontend/v1/lv/setup/getTabs/' + url: '/api/frontend/v1/lv/setup/getLETabs/' + }; + }, + getLVTabs() + { + return { + method: 'get', + url: '/api/frontend/v1/lv/setup/getLVTabs/' }; }, } diff --git a/public/js/api/stv.js b/public/js/api/stv.js index 8a4e568ea..19125b1bf 100644 --- a/public/js/api/stv.js +++ b/public/js/api/stv.js @@ -1,4 +1,3 @@ -import verband from './stv/verband.js'; import students from './stv/students.js'; import filter from './stv/filter.js'; import konto from './stv/konto.js'; @@ -18,7 +17,6 @@ import jointstudies from "./stv/jointstudies.js"; import courselist from './stv/courselist.js'; export default { - verband, students, filter, konto, diff --git a/public/js/api/stv/abschlusspruefung.js b/public/js/api/stv/abschlusspruefung.js index dccc10085..26d9c17ab 100644 --- a/public/js/api/stv/abschlusspruefung.js +++ b/public/js/api/stv/abschlusspruefung.js @@ -35,9 +35,6 @@ export default { getMitarbeiter(searchString){ return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getMitarbeiter/' + searchString); }, - getPruefer(searchString){ - return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getPruefer/' + searchString); - }, getNoten(){ return this.$fhcApi.get('api/frontend/v1/stv/abschlusspruefung/getNoten/'); }, diff --git a/public/js/api/stv/verband.js b/public/js/api/stv/verband.js deleted file mode 100644 index f3479c81b..000000000 --- a/public/js/api/stv/verband.js +++ /dev/null @@ -1,15 +0,0 @@ -export default { - get() { - return this.$fhcApi.get('api/frontend/v1/stv/verband'); - }, - favorites: { - get() { - return this.$fhcApi.get('api/frontend/v1/stv/favorites'); - }, - set(favorites) { - return this.$fhcApi.post('api/frontend/v1/stv/favorites/set', { - favorites - }); - } - } -} \ No newline at end of file diff --git a/public/js/apps/LVVerwaltung.js b/public/js/apps/LVVerwaltung.js index 18f8a072f..b9ab52ef6 100644 --- a/public/js/apps/LVVerwaltung.js +++ b/public/js/apps/LVVerwaltung.js @@ -12,74 +12,13 @@ const router = VueRouter.createRouter({ component: LVVerwaltung }, { - name: `byEmp`, - path: `/emp/:studiensemester_kurzbz/:emp/:stg?/:semester?`, + name: 'stdsem', + path: '/stdsem/:stdsem', component: LVVerwaltung, - props: route => { - let {emp, stg, semester, studiensemester_kurzbz} = route.params; - - if (emp === '') - emp = undefined; - - if (stg === '') - stg = undefined; - - if (studiensemester_kurzbz === '') - studiensemester_kurzbz = undefined; - - return { - studiensemester_kurzbz: studiensemester_kurzbz, - emp: emp, - stg: stg, - semester: semester - }; - }, - beforeEnter: (to, from, next) => { - const { studiensemester_kurzbz } = to.params; - const isSemester = /^(SS|WS)\d{4}$/.test(studiensemester_kurzbz); - - if (!isSemester) - return next({ path: '/' }); - else - next(); - } - }, - /*{ - name: `byFachbereich`, - path: `/fachbereich/:fachbereich/:emp?`, - component: LVVerwaltung - },*/ - { - name: `byStg`, - path: '/stg/:studiensemester_kurzbz/:stg?/:semester?/', - component: LVVerwaltung, - props: route => { - let { studiensemester_kurzbz, stg, semester } = route.params; - - if (semester === '') - semester = undefined; - - if (studiensemester_kurzbz === '') - studiensemester_kurzbz = undefined; - - if (stg === '') - stg = undefined; - - return { - studiensemester_kurzbz: studiensemester_kurzbz, - stg: stg, - semester: semester, - }; - }, - beforeEnter: (to, from, next) => { - const studiensemester_kurzbz = to.params?.studiensemester_kurzbz - const isSemester = /^(SS|WS)\d{4}$/.test(studiensemester_kurzbz); - - if (!isSemester) - return next({ path: '/' }); - else - next(); - } + children: [ + { name: 'emp', path: 'emp/:emp/:treemenu(.*)*', component: LVVerwaltung }, + { name: 'treemenu', path: ':treemenu(.*)*', component: LVVerwaltung } + ] }, { path: '/:pathMatch(.*)*', @@ -91,7 +30,9 @@ const router = VueRouter.createRouter({ FhcApps.router.makeExtendable(router); -const app = Vue.createApp(); +const app = Vue.createApp({ + name: 'LvVwApp' +}); FhcApps.makeExtendable(app); diff --git a/public/js/apps/Studentenverwaltung.js b/public/js/apps/Studentenverwaltung.js index 47490bfdd..7687eaf90 100644 --- a/public/js/apps/Studentenverwaltung.js +++ b/public/js/apps/Studentenverwaltung.js @@ -24,167 +24,27 @@ import PluginsPhrasen from "../plugins/Phrasen.js"; const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router; const router = VueRouter.createRouter({ - history: VueRouter.createWebHistory(), + history: VueRouter.createWebHistory('/' + ciPath), routes: [ { name: 'index', - path: `/${ciPath}/studentenverwaltung`, - component: FhcStudentenverwaltung - }, - { - name: 'studiensemester', - path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz`, + path: '/studvw', component: FhcStudentenverwaltung, - props: (route) => { - return { - url_studiensemester_kurzbz: route.params.studiensemester_kurzbz - }; - }, - beforeEnter: (to, from, next) => { - const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); - if (!isSemester) { - return next({name: 'index'}); + children: [ + { + name: 'stdsem', + path: 'stdsem/:stdsem', + component: FhcStudentenverwaltung, + children: [ + { name: 'search', path: 'search/:query', component: FhcStudentenverwaltung }, + { name: 'searchtypes', path: 'search/:types/:query', component: FhcStudentenverwaltung }, + { name: 'prestudent', path: 'prestudent/:prestudent_id', component: FhcStudentenverwaltung }, + { name: 'student', path: 'student/:student_uid', component: FhcStudentenverwaltung }, + { name: 'person', path: 'person/:person_id', component: FhcStudentenverwaltung }, + { name: 'treemenu', path: ':treemenu(.*)*', component: FhcStudentenverwaltung }, + ] } - next(); - } - }, - { - name: 'studiengang', - path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/:studiengang`, - component: FhcStudentenverwaltung, - props: (route) => { - return { - url_studiensemester_kurzbz: route.params.studiensemester_kurzbz, - url_studiengang: route.params.studiengang, - }; - }, - beforeEnter: (to, from, next) => { - const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); - if (!isSemester) { - return next({name: 'index'}); - } - const isStudiengang = /^[A-Z]{3}/.test(to.params.studiengang); - if (!isStudiengang) { - return next({ - name: 'studiensemester', - params: { - studiensemester_kurzbz: to.params.studiensemester_kurzbz - } - }); - } - next(); - } - }, - { - path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/prestudent/:prestudent_id`, - component: FhcStudentenverwaltung, - props: (route) => { - return { - url_studiensemester_kurzbz: route.params.studiensemester_kurzbz, - url_mode: 'prestudent', - url_prestudent_id: route.params.prestudent_id - }; - }, - beforeEnter: (to, from, next) => { - const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); - if (!isSemester) { - return next({name: 'index'}); - } - next(); - } - }, - { - path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/prestudent/:prestudent_id/:tab`, - component: FhcStudentenverwaltung, - props: (route) => { - return { - url_studiensemester_kurzbz: route.params.studiensemester_kurzbz, - url_mode: 'prestudent', - url_prestudent_id: route.params.prestudent_id, - url_tab: route.params.tab - }; - }, - beforeEnter: (to, from, next) => { - const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); - if (!isSemester) { - return next({name: 'index'}); - } - next(); - } - }, - { - path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/student/:id`, - component: FhcStudentenverwaltung, - props: (route) => { - return { - url_studiensemester_kurzbz: route.params.studiensemester_kurzbz, - url_mode: 'student', - url_student_id: route.params.id - }; - }, - beforeEnter: (to, from, next) => { - const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); - if (!isSemester) { - return next({name: 'index'}); - } - next(); - } - }, - { - path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/person/:person_id`, - component: FhcStudentenverwaltung, - props: (route) => { - return { - url_studiensemester_kurzbz: route.params.studiensemester_kurzbz, - url_mode: 'person', - url_prestudent_id: route.params.person_id - }; - }, - beforeEnter: (to, from, next) => { - const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); - if (!isSemester) { - return next({name: 'index'}); - } - next(); - } - }, - { - name: 'search', - path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/search/:searchstr`, - component: FhcStudentenverwaltung, - props(route) { - return { - url_studiensemester_kurzbz: route.params.studiensemester_kurzbz, - url_mode: 'search', - url_prestudent_id: route.params.searchstr - }; - }, - beforeEnter(to, from, next) { - const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); - if (!isSemester) { - return next({name: 'index'}); - } - next(); - } - }, - { - name: 'search_w_types', - path: `/${ciPath}/studentenverwaltung/:studiensemester_kurzbz/search/:types/:searchstr`, - component: FhcStudentenverwaltung, - props(route) { - return { - url_studiensemester_kurzbz: route.params.studiensemester_kurzbz, - url_mode: 'search', - url_prestudent_id: route.params.type + '/' + route.params.searchstr - }; - }, - beforeEnter(to, from, next) { - const isSemester = /^[WS]S\d{4}$/.test(to.params.studiensemester_kurzbz); - if (!isSemester) { - return next({name: 'index'}); - } - next(); - } + ] }, { path: '/:pathMatch(.*)*', @@ -197,11 +57,16 @@ const router = VueRouter.createRouter({ router.afterEach((to, from, failure) => { let title = 'Studierendenverwaltung FH-Complete'; - if (to.params.studiengang) { - title = to.params.studiengang + ' ' + title; + if (to.params.treemenu) { + const index = to.params.treemenu.findIndex( + (e, i) => i%2 == 0 && e == 'stg' + ); + if (index >= 0) { + title = to.params.treemenu[index + 1].toUpperCase() + ' ' + title; + } } - if (to.params.studiensemester_kurzbz) { - title = to.params.studiensemester_kurzbz + ' ' + title; + if (to.params.stdsem) { + title = to.params.stdsem.toUpperCase() + ' ' + title; } document.title = title; }); @@ -209,7 +74,7 @@ router.afterEach((to, from, failure) => { FhcApps.router.makeExtendable(router); const app = Vue.createApp({ - name: 'StudentenverwaltungApp' + name: 'StudVwApp' }); FhcApps.makeExtendable(app); diff --git a/public/js/components/Base/Treemenu.js b/public/js/components/Base/Treemenu.js new file mode 100644 index 000000000..5fe46606c --- /dev/null +++ b/public/js/components/Base/Treemenu.js @@ -0,0 +1,363 @@ +import TreemenuEntry from './Treemenu/Entry.js'; + +import dragClick from '../../directives/dragClick.js'; + +import ApiTreemenu from '../../api/factory/treemenu.js'; + +export default { + components: { + PvTreetable: primevue.treetable, + PvColumn: primevue.column, + TreemenuEntry + }, + directives: { + dragClick + }, + emits: [ + 'selectEntry', + 'drop' + ], + props: { + config: { + type: String, + required: true, + }, + preselectedKey: { + type: Array, + default: null + } + }, + data() { + return { + loading: true, + nodes: [], + selectedKey: [], + expandedKeys: {}, + filters: {}, // TODO(chris): filter only 1st level? + favorites: {on: false, list: []} + } + }, + computed: { + filteredNodes() { + if (this.favorites.on) + return this.nodes.filter(node => this.favorites.list.includes(node.data.path)); + + return this.nodes; + } + }, + watch: { + preselectedKey(newVal, oldVal) { + if (newVal !== oldVal) { + this.setPreselection(); + } + } + }, + methods: { + reloadNodesWithProp(prop, nodes = undefined) { + if (!nodes) + nodes = this.nodes; + + nodes.forEach(node => { + if (node.data[prop]) { + // reload + delete node.children; + this.onExpandTreeNode(node); + } else if (node.children) { + this.reloadNodesWithProp(prop, node.children); + } + }); + }, + findNodeByKey(key, arr) { + if (!arr) + arr = this.nodes; + let res = arr.filter(n => n.key == key); + if (res.length) + return res.pop(); + res = arr.map(n => n.children ? this.findNodeByKey(key, n.children) : null).filter(a => a); + if (res.length) + return res.pop(); + return null; + }, + async onExpandTreeNode(node) { + if (!node.children) { + if (node.data.path) { + /** + * NOTE(chris): activeEl is for keyboard navigation to + * prevent the focus jumping down to the next parent + * instead of the current submenu entry (which is not yet + * loaded) + */ + let activeEl = null; + this.$nextTick(() => { + this.$nextTick(() => { + activeEl = document.activeElement; + }); + }); + this.loading = true; + + return this.$api + .call(ApiTreemenu.get(this.config, node.data.path)) + .then(result => { + const subNodes = result.data.map(this.mapResultToTreeData); + const realNode = this.findNodeByKey(node.key); + if (realNode) + realNode.children = subNodes; + else + node.children = subNodes; // NOTE(chris): fallback should never be the case + + this.$nextTick(() => { + if (activeEl != document.activeElement) + return; + + let treeitem = this.$refs.tree.$el.querySelector('[data-tree-item-key="' + node.key + '"]'); + if (!treeitem) + return; + + treeitem = treeitem.closest('[role="row"]'); + + if (!treeitem) + return; + + treeitem.dispatchEvent(new KeyboardEvent('keydown', { + code: 'ArrowDown', + key: 'ArrowDown' + })); + }); + + this.loading = false; + }) + .catch(this.$fhcAlert.handleSystemError); + } + } + }, + onSelectTreeNode(node) { + this.$emit('selectEntry', node.data); + }, + mapNodesToNoSemReloadNodes(result, node) { + if (node.data.no_sem_reload) + result.push(node); + if (node.children) + result = node.children.reduce(this.mapNodesToNoSemReloadNodes, result); + return result; + }, + mapResultToTreeData(el) { + const cp = { + key: ("" + el.path).replace(/\//g, '-'), + data: el, + label: el.name // TODO(chris): phrase + }; + + if (el.children) + cp.children = el.children.map(this.mapResultToTreeData); + else + cp.leaf = el.leaf || false; + + return cp; + }, + async setPreselection() + { + if (!this.preselectedKey || !this.preselectedKey.length) + { + this.selectedKey = null; + return; + } + + if (!this.preselectedKey || !Array.isArray(this.preselectedKey)) + return; + + // [1, 2, 3, 4, ...] => ['1-2', '3-4', ...] + const parts = this.preselectedKey.reduce((r, c, i, a) => (i%2 && r.push(a[i-1] + '-' + c), r), []); + let currentKey = parts[0]; + let currentNode = this.findNodeByKey(currentKey); + + if (!currentNode) + return; + + if(this.selectedKey) + { + const currentSelectedKey = Object.keys(this.selectedKey).find(Boolean); + if (currentSelectedKey) { + if (currentSelectedKey == currentKey) + return; + /** + * Do not select a new entry if the current is a child of the new one. + * This happens if a child entry of a new stg is selected and the router + * tries to select the stg root entry (because subtrees do not have + * routes yet) + */ + const isChild = this.findNodeByKey( + currentSelectedKey, + currentNode.children || [] + ); + if (isChild) + return; + } + } + + for (let i = 1; i < parts.length; i++) + { + this.expandedKeys[currentNode.key] = true; + + await this.onExpandTreeNode(currentNode); + + currentKey += '-' + parts[i]; + currentNode = this.findNodeByKey(currentKey); + + if (!currentNode) + { + return; + } + } + + this.selectedKey = {[currentNode.key]: true}; + this.onSelectTreeNode(currentNode); + }, + async toggleTreeNode(node) { + if (this.expandedKeys[node.key]) { + delete this.expandedKeys[node.key]; + } else if (!node.leaf) { + await this.onExpandTreeNode(node); + this.expandedKeys[node.key] = true; + } + }, + filterFav() { + this.favorites.on = !this.favorites.on; + this.$api + .call(ApiTreemenu.favorites.set( + this.config, + JSON.stringify(this.favorites) + )); + }, + markFav(key) { + let index = this.favorites.list.indexOf(key.data.path + ''); + + if (index != -1) { + this.favorites.list.splice(index, 1); + } else { + this.favorites.list.push(key.data.path + ''); + } + + this.$api + .call(ApiTreemenu.favorites.set( + this.config, + JSON.stringify(this.favorites) + )); + }, + unsetFavFocus(e) { + if (e.target.dataset?.linkFavAdd !== undefined) { + e.target.tabIndex = -1; + } else { + let items = e.target.querySelectorAll('[data-link-fav-add]:not([tabindex="-1"])'); + items.forEach(el => el.tabIndex = document.activeElement == el ? 0 : -1); + } + }, + setFavFocus(e) { + if (e.target.dataset?.linkFavAdd !== undefined) { + e.target.tabIndex = 0; + } else { + let items = e.target.querySelectorAll('[data-link-fav-add][tabindex="-1"]'); + items.forEach(el => el.tabIndex = 0); + } + } + }, + mounted() { + this.$api + .call(ApiTreemenu.get(this.config)) + .then(result => { + this.nodes = result.data.map(el => { + el.root = true; + return this.mapResultToTreeData(el); + }); + this.setPreselection(); + this.loading = false; + }) + .catch(this.$fhcAlert.handleSystemError); + + this.$api + .call(ApiTreemenu.favorites.get(this.config)) + .then(result => { + if (result.data) { + this.favorites = JSON.parse(result.data); + } + }) + .catch(this.$fhcAlert.handleSystemError); + }, + template: /* html */` + + + + + + + + + + + ` +}; diff --git a/public/js/components/Base/Treemenu/Entry.js b/public/js/components/Base/Treemenu/Entry.js new file mode 100644 index 000000000..c09f413ff --- /dev/null +++ b/public/js/components/Base/Treemenu/Entry.js @@ -0,0 +1,63 @@ +import drop from '../../../directives/drop.js'; +import draggable from '../../../directives/draggable.js'; + +export default { + directives: { + drop, + draggable + }, + emits: [ + 'drop' + ], + props: { + node: { + type: Object, + required: true + } + }, + computed: { + name() { + if (Array.isArray(this.node.data.name)) + return this.$p.t(this.node.data.name); + + return this.node.data.name; + }, + title() { + if (!this.node.data.title) + return this.name; + + if (Array.isArray(this.node.data.title)) + return this.$p.t(this.node.data.title); + + return this.node.data.title; + }, + dropConfig() { + if (!this.node.data?.droplink) + return null; + + const allowed = [ ...this.node.data.droplink ]; + const effect = allowed.shift(); + + return { effect, allowed }; + }, + dragConfig() { + if (!this.node.data?.draggable) + return null; + + let config = [ ...this.node.data.draggable ]; + const effect = config.shift(); + const value = JSON.parse(config.shift()); + + return { effect, value }; + } + }, + template: /* html */` + + {{ name }} + ` +}; diff --git a/public/js/components/DetailHeader/DetailHeader.js b/public/js/components/DetailHeader/DetailHeader.js index 0b846df9c..396e1245a 100644 --- a/public/js/components/DetailHeader/DetailHeader.js +++ b/public/js/components/DetailHeader/DetailHeader.js @@ -1,11 +1,17 @@ import ApiDetailHeader from "../../api/factory/detailHeader.js"; import ApiHandleFoto from "../../api/factory/fotoHandling.js"; import ModalUploadFoto from "./Modal/UploadFoto.js"; +import PvSkeleton from "../../../../index.ci.php/public/js/components/primevue/skeleton/skeleton.esm.min.js"; +import CoreTag from "../Tag/Tag.js"; +import ApiTag from "../../api/factory/stv/tag.js"; +import { idTagFormatter } from "../Tag/tagFormatter.js"; export default { name: 'DetailHeader', components: { - ModalUploadFoto + ModalUploadFoto, + PvSkeleton, + CoreTag }, props: { headerData: { @@ -38,8 +44,26 @@ export default { 'mitarbeiter', ].includes(value) } + }, + currentSemester: { + type: String, + default: '' + }, + isLoading: { //if true, then parent isLoading + type: Boolean, + default: false } }, + inject: { + tagsEnabled: { + from: 'configStvTagsEnabled', + default: false + }, + lists: { + from: 'lists', + required: true + }, + }, computed: { appRoot() { return FHC_JS_DATA_STORAGE_OBJECT.app_root; @@ -61,17 +85,30 @@ export default { hasTileUIDSlot() { return !!this.$slots.uid }, - + prestudentIds() { + if (this.headerData[0].prestudent_id) + { + return [this.headerData[0].prestudent_id]; + } + }, + semesterDates(){ + return this.lists.studiensemester?.find(item => item.studiensemester_kurzbz === this.currentSemester) || {}; + } }, created(){ if (this.typeHeader === 'student') { if (!this.headerData) { throw new Error('[DetailHeader] "headerData" is required.') } + + if(this.tagsEnabled) { + this.loadTagsAndRender(this.headerData[0].prestudent_id); + } + } else if (this.typeHeader === 'mitarbeiter') { if (!this.person_id || !this.mitarbeiter_uid || !this.domain) { throw new Error( - '[DetailHeader] "person_id", "mitarbeiter_uid", and "domain" are requried.' + '[DetailHeader] "person_id", "mitarbeiter_uid", and "domain" are required.' ) } this.loadHeaderData(this.person_id, this.mitarbeiter_uid); @@ -86,13 +123,34 @@ export default { }, deep: true, }, + headerData: { + handler(newVal) { + if (this.typeHeader === 'student' && newVal?.length) { + this.getSemesterStati(newVal[0].prestudent_id); + } + + if(this.tagsEnabled) { + this.loadTagsAndRender(this.headerData[0].prestudent_id); + } + }, + deep: true, + immediate: true + }, }, data(){ return{ headerDataMa: {}, departmentData: {}, leitungData: {}, - isFetchingIssues: false + isFetchingIssues: false, + noCurrentStatus: false, + semesterStatiLoading: false, + leitungOrgLoading: false, + departmentDataLoading: false, + headerDataMaLoading: false, + tagEndpoint: ApiTag, + tagData: null, + rebuildData: null, }; }, methods: { @@ -108,29 +166,40 @@ export default { }); }, getHeader(person_id) { + this.headerDataMaLoading = true; return this.$api .call(ApiDetailHeader.getHeader(person_id)) .then(result => { this.headerDataMa = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.headerDataMaLoading = false; + }); }, loadDepartmentData(mitarbeiter_uid) { + this.departmentDataLoading = true; return this.$api .call(ApiDetailHeader.getPersonAbteilung(mitarbeiter_uid)) .then(result => { this.departmentData = result.data; }) - .catch(this.$fhcAlert.handleSystemError); + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.departmentDataLoading = false; + }); }, getLeitungOrg(oekurzbz){ + this.leitungOrgLoading = true; return this.$api .call(ApiDetailHeader.getLeitungOrg(oekurzbz)) .then(result => { this.leitungData = result.data; }) - .catch(this.$fhcAlert.handleSystemError); + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.leitungOrgLoading = false; + }); }, async goToLeitung() { this.loadHeaderData(this.leitungData.person_id, this.leitungData.uid); @@ -179,7 +248,98 @@ export default { } else { return 'data:image/jpeg;base64,' + foto; } - } + }, + getSemesterStati(prestudent_id){ + this.semesterStatiLoading = true; + this.$api + .call(ApiDetailHeader.getSemesterStati(prestudent_id)) + .then(result => { + this.semesterStati = result.data; + this.setNoCurrentStatus(); + }) + .catch(this.$fhcAlert.handleSystemError) + .finally(() => { + this.semesterStatiLoading = false; + }); + }, + setNoCurrentStatus() { + if(!Array.isArray(this.semesterStati)) + { + this.noCurrentStatus = false; + } + + if(!this.semesterStati.some(item => item.studiensemester_kurzbz === this.currentSemester)) { + this.noCurrentStatus = true; + } + else + { + this.noCurrentStatus = false; + } + }, + //methods tags + async loadTagsAndRender(prestudent_id) { + await this.getAllTags(prestudent_id); + + const container = idTagFormatter( + prestudent_id, + this.tagData, + this.$refs.tagComponent, + 'prestudent_id', + this.semesterDates.start, + this.semesterDates.ende + ); + + if(this.headerData.length == 1) { + this.$refs.tagWrapper.innerHTML = ''; + this.$refs.tagWrapper.appendChild(container); + } + }, + getAllTags(prestudent_id){ + return this.$api + .call(ApiTag.getAllTagsPrestudent({prestudent_id})) + .then(result => { + this.tagData = result.data.sort((a, b) => { + let adone = a.done ? 1 : 0; + let bdone = b.done ? 1 : 0; + + if (adone !== bdone) { + return adone - bdone; + } + return a.prioritaet - b.prioritaet; + }); + }) + .catch(this.$fhcAlert.handleSystemError); + }, + addedTag(addedTag) + { + this.reload(); + }, + deletedTag(id) + { + this.reload(); + }, + updatedTag(updatedTag) + { + this.reload(); + }, + rebuildPrestudentTags(){ + const id = [this.headerData[0].prestudent_id]; + + const params = { + ids: id, + typeId: 'prestudent_id', + sem: this.currentSemester + }; + + return this.$api + .call(ApiTag.rebuildTagsforTypeId(params)) + .then(result => { + this.rebuildData = result.data; + console.log("Rebuild manually triggered"); + this.reload(); + }) + .catch(this.$fhcAlert.handleSystemError); + }, }, template: `
@@ -200,7 +360,6 @@ export default { @@ -330,27 +560,51 @@ export default {
-

{{headerDataMa.titelpre}} {{headerDataMa.vorname}} {{headerDataMa.nachname}}, {{headerDataMa.titelpost}}

- {{departmentData.organisationseinheittyp_kurzbz}} - {{departmentData.bezeichnung}} - | - Vorgesetzte*r - - {{leitungData.titelpre}} {{leitungData.vorname}} {{leitungData.nachname}} - -

- Email - - - {{ mitarbeiter_uid }}@{{ domain }} +

{{headerDataMa.titelpre}} {{headerDataMa.vorname}} {{headerDataMa.nachname}}, {{headerDataMa.titelpost}}

+

+
+ +
+
+ Email + + +
+ +
+ | + DW + + +
+
+ +
@@ -360,20 +614,33 @@ export default {

-
+
+ + +

-
+
+ + +

-
+
+ + +

UID

-
+
+ + +
+
diff --git a/public/js/components/Form/Input.js b/public/js/components/Form/Input.js index 4ad555ab7..62c6217e6 100644 --- a/public/js/components/Form/Input.js +++ b/public/js/components/Form/Input.js @@ -29,7 +29,8 @@ export default { label: String, // NOTE(chris): remove these from $attrs array to prevent doubled event listeners onInput: [Array, Function], - 'onUpdate:modelValue': [Array, Function] + 'onUpdate:modelValue': [Array, Function], + titleActionButton: String }, data() { return { @@ -317,6 +318,7 @@ export default { :id="idCmp" :name="name" :class="validationClass" + :titleActionButton="titleActionButton" @update:model-value="clearValidationForThisName" > diff --git a/public/js/components/Form/Upload/Image.js b/public/js/components/Form/Upload/Image.js index b0b8d5b78..4b96d4c85 100644 --- a/public/js/components/Form/Upload/Image.js +++ b/public/js/components/Form/Upload/Image.js @@ -1,9 +1,14 @@ export default { emits: [ - 'update:modelValue' + 'update:modelValue', + 'actionbutton-clicked' ], props: { - modelValue: String + modelValue: String, + titleActionButton: { + type: String, + default: "" + } }, computed: { valueAsBase64DataString() { @@ -28,6 +33,9 @@ export default { }, deleteImage() { this.$emit('update:modelValue', ''); + }, + emitAction(){ + this.$emit('actionbutton-clicked', this.modelValue); } }, template: ` @@ -42,6 +50,7 @@ export default { + diff --git a/public/js/components/LVVerwaltung/Details/Gruppen.js b/public/js/components/LVVerwaltung/Details/Gruppen.js index 024bf6468..5244136cc 100644 --- a/public/js/components/LVVerwaltung/Details/Gruppen.js +++ b/public/js/components/LVVerwaltung/Details/Gruppen.js @@ -2,6 +2,7 @@ import {CoreFilterCmpt} from "../../filter/Filter.js"; import FormForm from '../../Form/Form.js'; import FormInput from '../../Form/Input.js'; import ApiGruppe from "../../../api/lehrveranstaltung/gruppe.js"; +import drop from '../../../directives/drop.js'; export default{ name: "LVGruppen", components: { @@ -9,6 +10,9 @@ export default{ FormForm, FormInput }, + directives: { + drop + }, props: { lehreinheit_id: Number }, @@ -187,28 +191,48 @@ export default{ this.reload(); }) }, + onDropVerband(node, gruppe) { + this.$api.call(ApiGruppe.getGruppe(gruppe)) + .then(result => result.data) + .then(result => { + + let newData = { + 'gid': result, + 'lehreinheit_id': this.lehreinheit_id, + 'lehrverband': gruppe.lehrverband, + } + this.$api.call(ApiGruppe.add(newData)) + .then(result => { + this.reload() + }) + }); + + }, + }, template: ` - - - - +
+ + + + +
` }; \ No newline at end of file diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index 42c50bdfa..6d9331d38 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -1,16 +1,18 @@ import CoreSearchbar from "../searchbar/searchbar.js"; import VerticalSplit from "../verticalsplit/verticalsplit.js"; +import HorizontalSplit from "../horizontalsplit/horizontalsplit.js"; import AppMenu from "../AppMenu.js"; -import StvVerband from "../Stv/Studentenverwaltung/Verband.js"; +import BaseTreemenu from '../Base/Treemenu.js'; import StvStudiensemester from "../Stv/Studentenverwaltung/Studiensemester.js"; import LvTable from "./Setup/Table.js"; import LvTabs from "./Setup/Tabs.js"; +import NavLanguage from "../navigation/Language.js"; import ApiDetails from "../../api/lehrveranstaltung/details.js"; import ApiLektor from "../../api/lehrveranstaltung/lektor.js"; -import ApiGruppe from "../../api/lehrveranstaltung/gruppe.js"; -import ApiStudiengangTree from "../../api/lehrveranstaltung/studiengangtree.js"; import ApiSearchbar from "../../api/factory/searchbar.js"; +import AppConfig from "../AppConfig.js"; +import ApiLvConfig from "../../api/lehrveranstaltung/config.js"; export default { @@ -18,23 +20,23 @@ export default { components: { CoreSearchbar, VerticalSplit, + HorizontalSplit, AppMenu, - StvVerband, + BaseTreemenu, StvStudiensemester, LvTable, LvTabs, + AppConfig, + NavLanguage }, props: { + avatarUrl: String, + logoutUrl: String, defaultSemester: String, lvRoot: String, permissions: Object, config: Object, - stg: { type: String, required: false }, - semester: { type: [Number, String], required: false, default: null }, - studiensemester_kurzbz: { type: String, required: false, default: null }, - emp: { type: String, required: false, default: null } }, - provide() { return { currentSemester: Vue.computed(() => this.selectedStudiensemester), @@ -44,46 +46,27 @@ export default { lehreinheitAnmerkungDefault: (this.config.lehreinheitAnmerkungDefault || '').replace(/\\n/g, '\n'), lehreinheitRaumtypDefault: this.config.lehreinheitRaumtypDefault, lehreinheitRaumtypAlternativeDefault: this.config.lehreinheitRaumtypAlternativeDefault, - permissionLehrveranstaltung: this.permissions['lehre/lehrveranstaltung'], permissionGruppenEntfernen: this.permissions['lv-plan/gruppenentfernen'], permissionLektorEntfernen: this.permissions['lv-plan/lektorentfernen'], - } - }, - mounted() { - this.updateFilter(); - }, - watch: { - stg() { - this.updateFilter(); - }, - semester() { - this.updateFilter(); - }, - selectedStudiensemester() { - this.updateFilter(); - }, - emp() { - this.updateFilter(); - }, - studiensemester_kurzbz(newVal) { - this.selectedStudiensemester = newVal ?? this.defaultSemester; + language: Vue.computed(() => this.$p.user_language), + isMobile: false, } }, data() { return { + sidebarCollapsed: false, + appconfig:{}, + configEndpoints: ApiLvConfig, selected: [], - studiengang: "", - filter: {}, - selectedStudiensemester: this.studiensemester_kurzbz ?? this.defaultSemester, - endpoint: ApiStudiengangTree, + studiengang_kz: null, + selectedStudiensemester: this.defaultSemester, dropdowns: { studiensemester_array: [], sprachen_array: [], lehrform_array: [], raumtyp_array: [], }, - selectedStudiengang: '', searchbaroptions: { origin: 'lvverwaltung', cssclass: "position-relative", @@ -107,28 +90,112 @@ export default { }, } }, + computed: { + filter() { + let filter = { + emp: this.$route.params.emp, + studiensemester_kurzbz: this.selectedStudiensemester + }; + let index; + if (this.$route.params.treemenu) { + index = this.$route.params.treemenu.indexOf('stg'); + if (index > -1) + filter.stg = this.$route.params.treemenu[index+1]; + index = this.$route.params.treemenu.indexOf('semester'); + if (index > -1) + filter.semester = this.$route.params.treemenu[index+1]; + } + filter.activeFilter = filter.emp ? 'employee' : filter.stg ? 'verband' : null; + return filter; + }, + emp() { + return this.filter.emp; + }, + stg() { + return this.filter.stg; + }, + semester() { + return this.filter.semester; + }, + appMenuExtraItems() + { + let extraItems = []; + + const studiengang_kz = this.studiengang_kz || ''; + const studiensemester = this.selectedStudiensemester; + const semester = this.semester || ''; + const uid = this.emp || ''; + + + extraItems.push( + { + description: 'lehre/berichte', + requires: ['studiengang_kz'], + children: [ + { + link: FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'content/statistik/lvplanung.xls.php' + + '?studiengang_kz=' + studiengang_kz + + '&studiensemester_kurzbz=' + studiensemester + + '&semester=' + semester, + description: 'lehre/lvplanung', + requires: ['studiengang_kz'] + }, + { + link: FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'content/statistik/lehrauftragsliste_gst.xls.php' + + '?studiengang_kz=' + studiengang_kz + + '&studiensemester_kurzbz=' + studiensemester + + '&semester=' + semester, + description: 'lehre/lehrauftragsliste', + requires: ['studiengang_kz'] + }, + { + link: FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'content/pdfExport.php?xml=lehrauftrag.xml.php' + + '&xsl=Lehrauftrag' + + '&stg_kz=' + studiengang_kz + + '&ss=' + studiensemester, + description: 'lehre/lehrauftraege', + requires: ['studiengang_kz'] + }, + { + link: FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'content/pdfExport.php?xml=lehrauftrag.xml.php' + + '&xsl=Lehrauftrag' + + '&stg_kz=' + studiengang_kz + + '&ss=' + studiensemester + + '&uid=' + uid, + description: 'lehre/lehrauftragslisteemp', + requires: ['emp'] + } + ] + }, + { + link: FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'vilesci/lehre/lehrveranstaltung.php' + + '?stg_kz=' + studiengang_kz, + description: 'lehre/extrakvverwaltung', + requires: ['studiengang_kz'] + } + ); + + return extraItems; + } + }, + + watch: { + sidebarCollapsed(newVal) { + if(newVal) + this.$refs.hSplit.collapseLeft() + else + this.$refs.hSplit.showBoth() + } + }, methods: { updateFilter() { - const filter = { - stg: this.stg, - emp: this.emp, - semester: this.semester, - studiensemester_kurzbz: this.selectedStudiensemester, - activeFilter: this.emp ? 'employee' : this.stg ? 'verband' : null - } - - if (this.stg !== undefined) - { - this.selectedStudiengang = this.semester !== '' && this.semester - ? `${this.stg}/${this.semester}` - : this.stg; - } - else - { - this.selectedStudiengang = ''; - } - this.filter = filter; + // deprecated }, handleRowClicked(data) { @@ -136,77 +203,107 @@ export default { }, onSelectEmployee(emp) { - const { stg, semester } = this.filter; - - let studiensemester_kurzbz = this.selectedStudiensemester; - const params = { emp }; - - if (stg) - params.stg = stg; - if (semester !== null) - params.semester = semester; - if (studiensemester_kurzbz) - params.studiensemester_kurzbz = studiensemester_kurzbz; - - this.$router.push({ name: 'byEmp', params }) + this.$router.push({ + name: 'emp', + params: { + ...this.$route.params, + emp + } + }); }, - - onSelectVerband({link}) + onSelectVerband({ path: link, stg_kz }) { - let stg = null; - let semester = null; - let studiensemester_kurzbz = this.selectedStudiensemester; - - if (typeof link === 'number') - stg = link; - else if (typeof link === 'string') - { - [stg, semester] = link.split('/'); - } - - const routeName = this.filter.emp ? 'byEmp' : 'byStg'; - const params = { stg }; - - params.semester = ''; - if (semester !== null) - params.semester = semester; - if (studiensemester_kurzbz) - params.studiensemester_kurzbz = studiensemester_kurzbz; - if (this.filter.emp) - params.emp = this.filter.emp; - this.$router.push({ name: routeName, params }); + this.$router.push({ + name: this.$route.name == 'emp' ? 'emp' : 'treemenu', + params: { + ...this.$route.params, + treemenu: link.split('/') + } + }); this.selected = []; + this.studiengang_kz = stg_kz; }, resetEmployeeFilter() { - const newParams = { ...this.filter, activeFilter: 'verband' }; - if (newParams.stg === '') - this.$router.replace({ name: 'index' }); - else - { - delete newParams.emp; - - this.$router.replace({ name: 'byStg', params: newParams }); - } + let params = { ...this.$route.params }; + + delete params.emp; + + this.$router.replace({ + name: params.treemenu ? 'treemenu' : 'stdsem', + params + }); }, resetStgFilter() { - const newParams = { ...this.filter, activeFilter: 'emp' }; - delete newParams.stg; - this.selectedStudiengang = ''; - this.$router.replace({ name: 'byEmp', params: newParams }); + let params = { ...this.$route.params }; + let index = params.treemenu.indexOf('stg'); + if (index > -1) + params.treemenu = params.treemenu.slice(0, index); + if (params.treemenu.length) { + this.$router.replace({ + params: { + treemenu: params.treemenu + } + }); + } else { + delete params.treemenu; + this.$router.replace({ + name: params.emp ? 'emp' : 'stdsem', + params + }); + } }, - searchfunction(params) { - return this.$api.call(ApiSearchbar.search(params)); + searchfunction(params, config) { + return this.$api.call(ApiSearchbar.search(params), config); }, - studiensemesterChanged(newValue) { - const routeName = this.filter.activeFilter === 'employee' ? 'byEmp' : 'byStg'; - const newParams = {...this.filter, studiensemester_kurzbz: newValue}; - this.$router.push({ name: routeName, params: newParams }); + studiensemesterChanged(stdsem) { + if (!stdsem) { + // no valid studiensemester in url + this.$router.replace({ + params: { + stdsem: this.defaultSemester.toLowerCase() + } + }); + return; + } + if (stdsem.toLowerCase() != this.selectedStudiensemester?.toLowerCase()) { + this.$router.push({ + params: { + stdsem: stdsem.toLowerCase() + } + }); + } + this.selectedStudiensemester = stdsem; this.selected = []; }, + isDisabled(item) + { + if (!item?.requires?.length) + return false; + + const values = { + studiengang_kz: this.studiengang_kz, + stg: this.stg, + emp: this.emp, + studiensemester: this.selectedStudiensemester, + semester: this.semester + }; + + return item.requires.some(req => !values[req]); + + } }, created() { + if (!this.$route.params.stdsem) { + this.$router.replace({ + name: 'stdsem', + params: { + stdsem: this.defaultSemester.toLowerCase() + } + }); + } + this.$p.loadCategory(['lehre', 'person', 'global']) this.$api.call(ApiDetails.getStudiensemester()) @@ -239,9 +336,8 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); }, - template: /* html */` -
+
@@ -278,54 +419,99 @@ export default {
- + + +
- - -
- - - + {{this.$p.t('global','details')}}

[{{$p.t('ui', 'neu')}}]

+
- +
+ + @@ -725,6 +870,7 @@ export default { @@ -782,6 +928,7 @@ export default { >
-
+

{{$p.t('abschlusspruefung', 'zurBeurteilung')}}

@@ -810,8 +957,9 @@ export default { diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Details.js b/public/js/components/Stv/Studentenverwaltung/Details/Details.js index 199a79d38..e457c2773 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Details.js @@ -158,6 +158,16 @@ export default { }, reload(){ this.updateStudent(this.modelValue); + }, + sendInfomail(){ + const subject = this.$p.t('person', 'betreffProfilfoto'); + const subjectEncoded = encodeURIComponent(subject); + + const body = this.$p.t('person', 'mailText_profilfoto'); + const bodyWithNewLines = body.replace(/\\n/g, '\n'); + const bodyEncoded = encodeURIComponent(bodyWithNewLines); + + window.location.href = "mailto:" + this.modelValue.mail_intern + "?subject=" + subjectEncoded + "&body=" + bodyEncoded; } }, created() { @@ -387,8 +397,10 @@ export default { container-class="col stv-details-details-foto" :label="$p.t('person', 'foto')" type="UploadImage" + titleActionButton="Infomail" v-model="data.foto" name="foto" + @actionbutton-clicked="sendInfomail" > No Image diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/List/Unaccepted.js b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/List/Unaccepted.js index ddee23bc7..f92e6bd88 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/List/Unaccepted.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Dokumente/List/Unaccepted.js @@ -20,7 +20,6 @@ export default { layoutColumnsOnNewData: false, height: 300, listDocuments: [], - prestudentDocumentData: [], } }, computed: { @@ -68,21 +67,36 @@ export default { let value = cell.getValue(); let rowData = cell.getRow().getData(); // Zugriff auf gesamte Zeile let dueDate = rowData["nachgereicht_am"]; - const date = new Date(dueDate); - let dateFormatted = date.toLocaleString("de-DE", { + + let vorhanden = rowData["vorhanden"]; + let hochgeladenamum = vorhanden ? rowData["hochgeladenamum"] : ''; + + const dateDue = new Date(dueDate); + let dateFormatted = dateDue.toLocaleString("de-DE", { day: "2-digit", month: "2-digit", year: "numeric", hour12: false }); + let hochgeladenamumFormatted = hochgeladenamum + ? new Date(hochgeladenamum.replace(" ", "T")).toLocaleDateString("de-DE", { + day: "2-digit", + month: "2-digit", + year: "numeric" + }) + : dateFormatted; + + + const date = vorhanden && hochgeladenamum ? hochgeladenamumFormatted : dateFormatted; + let tickCrossIcon = value ? '' : ''; - // if nachgereicht_am show datum + // if nachgereicht_am show datum nachgereicht_am oder hochgeladenamum let pill = dueDate - ? `${dateFormatted}` + ? `${date}` : ''; return `${tickCrossIcon} ${pill}`; @@ -206,7 +220,7 @@ export default { setHeader('anmerkung_intern', this.$p.t('global', 'anmerkung')); setHeader('akte_id', this.$p.t('global', 'akte_id')); setHeader('pflicht', this.$p.t('ampeln', 'mandatory')); - setHeader('nachgereicht_am', this.$p.t('global', 'dokument')); + setHeader('nachgereicht_am', this.$p.t('dokumente', 'nachreichungAm')); } }, { diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js b/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js index ef61ef1b9..6d89d6b12 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js @@ -1,6 +1,14 @@ -import { splitMailsHelper } from "../../../../helpers/EmailHelpers.js" +import { splitMailsHelper } from "../../../../helpers/EmailHelpers.js"; +import { splitMailLinks } from "../../../../helpers/EmailHelpers.js"; +import FormInput from "../../../Form/Input.js"; +import FormForm from "../../../Form/Form.js"; + export default { name: "Kontaktieren", + components: { + FormInput, + FormForm + }, computed: { internMails() { if (this.modelValue.mail_intern) @@ -21,38 +29,150 @@ export default { props: { modelValue: Object }, - + inject: { + authUid: { + from: 'authUid', + required: true + }, + }, + data(){ + return { + mailLinks: [], + mailLinksClicked: [], + showMailDialog: false, + showDivKomp: false, + showDivKompLocalStorageId: 'studvw-contact-showDivKomp' + } + }, + activated() { + const showDivCompLocalStorage = window.localStorage.getItem(this.showDivKompLocalStorageId); + this.showDivKomp = (showDivCompLocalStorage === 'true') ? true : false; + }, methods: { internMail(event) { if (this.internMails.length) { - splitMailsHelper(this.internMails, event, null, null, this.$fhcAlert, this.$p) + splitMailsHelper(this.internMails, event, null, null, this.$fhcAlert, this.$p, this.authUid) } }, privateMail(event) { if (this.privateMails.length) { - splitMailsHelper(this.privateMails, event, null,null, this.$fhcAlert, this.$p) + splitMailsHelper(this.privateMails, event, null, null, this.$fhcAlert, this.$p, this.authUid) } + }, + createMailLinks(event, mailGroup){ + let recipients = mailGroup == 'private' ? this.privateMails : this.internMails; + this.mailLinks = splitMailLinks( + recipients, + "", + event.ctrlKey || event.metaKey, + this.authUid + ); + this.mailLinksClicked = []; + + this.showMailDialog = true; + }, + handleMailLink(link, index){ + this.mailLinksClicked.push(index); + window.open(link, '_blank', 'noopener'); + }, + reset(){ + this.showMailDialog = false; + }, + onSwitchChange(){ + window.localStorage.setItem(this.showDivKompLocalStorageId, this.showDivKomp); } }, template: ` -
-
-
-
- +
+
+ + + +
+
+
+ +
+
+
+
+ +
- -
-
- + +
+ +
+
+ +
+
+
+ +
+
+ +
+
+

{{$p.t('stv', 'zuvieleEMails')}}

+ + +
+ +
+
-
` -}; \ No newline at end of file +
+ ` +}; diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine.js b/public/js/components/Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine.js index 6845613ab..6eb324f27 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine.js @@ -1,4 +1,5 @@ import TableLvList from "./Lehrveranstaltungstermine/ListLehrveranstaltungstermine.js"; +import ApiStvCoursedates from "../../../../api/factory/stv/coursedates.js"; export default { name: "TabCourseList", @@ -8,11 +9,13 @@ export default { props: { modelValue: Object, }, - data(){ - return {} + data() { + return { + endpoint: ApiStvCoursedates + }; }, template: `
- +
` }; \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine/ListLehrveranstaltungstermine.js b/public/js/components/Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine/ListLehrveranstaltungstermine.js index b664964f7..855adc26b 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine/ListLehrveranstaltungstermine.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Lehrveranstaltungstermine/ListLehrveranstaltungstermine.js @@ -2,8 +2,6 @@ import {CoreFilterCmpt} from "../../../../filter/Filter.js"; import FormInput from "../../../../Form/Input.js"; import FormForm from '../../../../Form/Form.js'; -import ApiStvCoursedates from "../../../../../api/factory/stv/coursedates.js"; - export default { name: "TblCourseList", components: { @@ -17,7 +15,14 @@ export default { }, }, props: { - student: Object + id: { + type: [Number, String], + required: true + }, + endpoint: { + type: Object, + required: true + }, }, data(){ return { @@ -48,14 +53,7 @@ export default { initTabulatorOptions(){ this.tabulatorOptions = { ajaxURL: 'dummy', - ajaxRequestFunc: () => this.$api.call( - ApiStvCoursedates.getCourselist({ - student_uid: this.student.uid, - start_date: this.dataSem.start, - end_date: this.dataSem.ende, - group_consecutiveHours: true, - dbStundenplanTable: this.dbStundenplanTable}) - ), + ajaxRequestFunc: () => this.$api.call(this.endpoint.getCourselist(this.id, this.dataSem.start, this.dataSem.ende, this.dbStundenplanTable)), ajaxResponse: (url, params, response) => { return response.data; }, @@ -131,7 +129,7 @@ export default { this.dataSem = this.listStudiensemester.find(item => item.studiensemester_kurzbz === studiensemester_kurzbz); }, exportToExcel(){ - window.open(this.downloadLink, '_blank'); + window.open(this.endpoint.exportCalendar(this.id, this.dbStundenplanTable), '_blank'); }, reload() { this.$refs.table.reloadTable(); @@ -145,10 +143,13 @@ export default { currentSemester(newVal, oldVal) { this.getDatesOfSemester(newVal); }, + id() { + this.reload(); + } }, created(){ this.$api - .call(ApiStvCoursedates.getStudiensemester()) + .call(this.endpoint.getStudiensemester()) .then(result => { this.listStudiensemester = result.data; this.getDatesOfSemester(this.currentSemester); diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js index 71b8096ec..48863da9c 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js @@ -2,6 +2,9 @@ import NotenZeugnis from './Noten/Zeugnis.js'; import NotenTeacher from './Noten/Teacher.js'; import NotenRepeater from './Noten/Repeater.js'; +import ApiStvGrades from '../../../../api/factory/stv/grades.js'; +import { highlightGesamtnote } from "../../../../helpers/DocumentHelper.js"; + const LOCAL_STORAGE_ID = 'stv_details_noten_2024-11-25_stdsem_all'; export default { @@ -22,7 +25,10 @@ export default { }, data() { return { - stdsem: '' + stdsem: '', + endpoint: ApiStvGrades, + zeugnisLoaded: false, + teacherLoaded: false, }; }, methods: { @@ -33,6 +39,30 @@ export default { }, saveStdsem(event) { window.localStorage.setItem(LOCAL_STORAGE_ID, event.target.value); + }, + onZeugnisLoaded() { + this.zeugnisLoaded = true; + this.checkHighlight(); + }, + onTeacherLoaded() { + this.teacherLoaded = true; + this.checkHighlight(); + }, + checkHighlight() + { + if (!this.zeugnisLoaded || !this.teacherLoaded) + return; + + if (!this.$refs.zeugnis || !this.$refs.teacher) + return; + + let zeugnisTable = this.$refs.zeugnis.$refs.table.tabulator; + let teacherTable = this.$refs.teacher.$refs.table.tabulator; + + if (!zeugnisTable || !teacherTable) + return; + + highlightGesamtnote(zeugnisTable, teacherTable); } }, created() { @@ -49,10 +79,10 @@ export default {
- +
- +
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Repeater.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Repeater.js index eaa34c1c7..ab7dedb3e 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Repeater.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Repeater.js @@ -21,6 +21,7 @@ export default { }, data() { return { + renderTabulator: false, tabulatorEvents: [] }; }, @@ -118,13 +119,13 @@ export default { created() { this.$p.loadCategory(['global', 'stv', 'lehre', 'profil']) .then(() => { - if (this.$refs.table.tableBuilt) - this.$refs.table.tabulator.columnManager.setColumns(this.tabulatorOptions.columns); + this.renderTabulator = true }); }, template: `
this.$emit("loaded"), + }, + { + event: "tableBuilt", + handler: () => { + if(this.optionalTabulatorOptions?.visibleColumns + && this.setOptinalTabulatorOptionsVisibility) + { + for(let key in this.optionalTabulatorOptions.visibleColumns) { + if(this.optionalTabulatorOptions.visibleColumns[key]) + this.$refs.table.tabulator.showColumn(key); + else + this.$refs.table.tabulator.hideColumn(key); + } + } + } + } + ] }; }, computed: { tabulatorOptions() { return { ajaxURL: 'dummy', - ajaxRequestFunc: () => this.$api.call(ApiStvGrades.getTeacherProposal( - this.student.prestudent_id, + ajaxRequestFunc: () => this.$api.call(this.endpoint.getTeacherProposal( + this.id, (!this.allSemester ? this.currentSemester : null) )), ajaxResponse: (url, params, response) => { @@ -74,19 +103,24 @@ export default { { field: 'studiensemester_kurzbz', title: this.$p.t('lehre/studiensemester'), visible: false }, { field: 'note', title: this.$p.t('stv/grades_numericgrade'), visible: false }, { field: 'student_uid', title: this.$p.t('profil/studentIn'), visible: false }, + { field: 'vorname', title: this.$p.t('person/vorname'), visible: false }, + { field: 'nachname', title: this.$p.t('person/nachname'), visible: false }, { field: 'lehrveranstaltung_id', title: this.$p.t('lehre/lehrveranstaltung_id'), visible: false }, { field: 'punkte', title: this.$p.t('stv/grades_points'), visible: false } ], + columnDefaults: { + headerFilter: this.optionalTabulatorOptions?.headerFilter ?? false, + }, layout: 'fitDataStretch', height: '100%', selectableRows: true, selectableRowsRangeMode: 'click', - persistenceID: 'stv-details-noten-teacher-20260217' + persistenceID: this.optionalTabulatorOptions?.persistenceTeacherID ?? 'stv-details-noten-teacher-2025120401', }; } }, watch: { - student(n) { + id() { this.$refs.table.reloadTable(); }, allSemester(n) { @@ -97,7 +131,7 @@ export default { copyGrades(selected) { const promises = selected.map( grade => this.$api - .call(ApiStvGrades.copyTeacherProposalToCertificate(grade), { + .call(this.endpoint.copyTeacherProposalToCertificate(grade), { errorHeader: grade.lehrveranstaltung_bezeichnung }) .then(() => { @@ -117,13 +151,19 @@ export default { created() { this.$p.loadCategory(['stv', 'lehre', 'profil']) .then(() => { - if (this.$refs.table.tableBuilt) - this.$refs.table.tabulator.columnManager.setColumns(this.tabulatorOptions.columns); + this.renderTabulator = true; }); + + if(this?.optionalTabulatorOptions) + { + const localStorageKey = 'tabulator-' + this.optionalTabulatorOptions.persistenceTeacherID + '-columns'; + this.setOptinalTabulatorOptionsVisibility = (window.localStorage.getItem(localStorageKey) === null) ? true : false; + } }, template: `
this.$emit("loaded") + }, { event: "rowSelected", handler: row => row.getElement().style.zIndex = 12 @@ -42,6 +55,21 @@ export default { { event: "rowDeselected", handler: row => row.getElement().style.zIndex = '' + }, + { + event: "tableBuilt", + handler: () => { + if(this.optionalTabulatorOptions?.visibleColumns + && this.setOptinalTabulatorOptionsVisibility) + { + for(let key in this.optionalTabulatorOptions.visibleColumns) { + if(this.optionalTabulatorOptions.visibleColumns[key]) + this.$refs.table.tabulator.showColumn(key); + else + this.$refs.table.tabulator.hideColumn(key); + } + } + } } ], stdsem: '', @@ -53,7 +81,7 @@ export default { computed: { tabulatorOptions() { const listPromise = this.$api - .call(ApiStvGrades.list()) + .call(this.endpoint.list()) .then(res => res.data.map(({bezeichnung: label, note: value}) => ({label, value}))); let gradeField = { @@ -86,7 +114,7 @@ export default { note_bezeichnung })) // send to backend - .then(data => this.$api.call(ApiStvGrades.updateCertificate(data))) + .then(data => this.$api.call(this.endpoint.updateCertificate(data))) // get bezeichnung again .then(() => listPromise) .then(list => list.find(el => el.value == note)) @@ -109,7 +137,7 @@ export default { if (filterTerm) { return this.$api .call( - ApiStvGrades.getGradeFromPoints( + this.endpoint.getGradeFromPoints( filterTerm, cell.getData().lehrveranstaltung_id, this.currentSemester @@ -142,6 +170,8 @@ export default { .then(() => node.innerText = this.$p.t('ui/loading')) .catch(this.$fhcAlert.handleSystemError); gradeField.editorParams.placeholderLoading = node; + gradeField.visible = this.optionalTabulatorOptions?.visibleColumns?.note ?? true + gradeField.headerFiler = this.optionalTabulatorOptions?.headerFilter?.note || this.optionalTabulatorOptions?.note || false } const columns = [ @@ -192,7 +222,10 @@ export default { { field: 'lehrform', title: this.$p.t('lehre/lehrform'), visible: false }, { field: 'kurzbz', title: this.$p.t('lehre/kurzbz'), visible: false }, { field: 'punkte', title: this.$p.t('stv/grades_points'), visible: false }, - { field: 'lehrveranstaltung_bezeichnung_english', title: this.$p.t('stv/grades_lehrveranstaltung_bezeichnung_english'), visible: false } + { field: 'lehrveranstaltung_bezeichnung_english', title: this.$p.t('stv/grades_lehrveranstaltung_bezeichnung_english'), visible: false }, + { field: 'uid', title: this.$p.t('person/uid'), visible: false }, + { field: 'vorname', title: this.$p.t('person/vorname'), visible: false }, + { field: 'nachname', title: this.$p.t('person/nachname'), visible: false } ]; const hasDocuments = ['both', 'inline'].includes(this.config.documents); @@ -202,6 +235,7 @@ export default { columns.push({ field: 'actions', title: this.$p.t('global/actions'), + minWidth: 120, cssClass: "overflow-visible", headerSort: false, formatter: cell => { @@ -238,8 +272,8 @@ export default { return { ajaxURL: 'dummy', - ajaxRequestFunc: () => this.$api.call(ApiStvGrades.getCertificate( - this.student.prestudent_id, + ajaxRequestFunc: () => this.$api.call(this.endpoint.getCertificate( + this.id, (!this.allSemester ? this.currentSemester : null) )), ajaxResponse: (url, params, response) => { @@ -248,9 +282,10 @@ export default { columns, height: '100%', layout: 'fitDataStretchFrozen', - selectableRows: 1, + selectable: true, selectableRowsRangeMode: 'click', - persistenceID: 'stv-details-noten-zeugnis-20260217', + selectableRows: true, + persistenceID: this.optionalTabulatorOptions?.persistenceZeugnisID ?? 'stv-details-noten-zeugnis-20260217', persistence:{ columns: ["width", "visible", "frozen"] } @@ -258,7 +293,8 @@ export default { } }, watch: { - student(n) { + id() + { this.$refs.table.reloadTable(); }, allSemester(n) { @@ -269,7 +305,7 @@ export default { setGrade(data) { this.$api .call( - ApiStvGrades.updateCertificate(data), + this.endpoint.updateCertificate(data), { errorHeader: data.lehrveranstaltung_bezeichnung } ) .then(this.$refs.table.reloadTable) @@ -282,7 +318,7 @@ export default { .confirmDelete() .then(result => result ? data : Promise.reject({handled:true})) .then(data => this.$api.call( - ApiStvGrades.deleteCertificate(data), + this.endpoint.deleteCertificate(data), { errorHeader: data.lehrveranstaltung_bezeichnung } )) .then(this.$refs.table.reloadTable) @@ -291,15 +327,21 @@ export default { } }, created() { - this.$p.loadCategory(['global', 'stv', 'lehre']) + this.$p.loadCategory(['global', 'stv', 'lehre', 'person']) .then(() => { - if (this.$refs.table.tableBuilt) - this.$refs.table.tabulator.columnManager.setColumns(this.tabulatorOptions.columns); + this.renderTabulator = true; }); + + if(this?.optionalTabulatorOptions) + { + const localStorageKey = 'tabulator-' + this.optionalTabulatorOptions.persistenceZeugnisID + '-columns'; + this.setOptinalTabulatorOptionsVisibility = (window.localStorage.getItem(localStorageKey) === null) ? true : false; + } }, template: `
', - crossElement: '' - }, - headerFilter:"tickCross", headerFilterParams: { - "tristate":true, elementAttributes:{"value":"true"} - }, headerFilterEmptyCheck:function(value){return value === null} - }, - {title:"Matrikelnummer", field:"matr_nr", visible:false, headerFilter: true}, - {title:"Studienplan", field:"studienplan_bezeichnung", headerFilter: "list", headerFilterParams: {valuesLookup:true, listOnEmpty:true, autocomplete:true, sort:"asc"}}, - {title:"PreStudentInnenID", field:"prestudent_id", headerFilter: true}, - {title:"Priorität", field:"priorisierung_relativ"}, - {title:"Mentor", field:"mentor", visible:false}, - {title:"Aktiv", field:"bnaktiv", visible:false, - formatter:'tickCross', formatterParams: { - allowEmpty:true, - tickElement: '', - crossElement: '' - }, - headerFilter:"tickCross", headerFilterParams: { - "tristate":true, elementAttributes:{"value":"true"} - }, headerFilterEmptyCheck:function(value){return value === null} - }, - {title:"Unruly", field:"unruly", visible:false}, + ajaxRequestFunc: (url, config, params) => { + if ( url === '' ) { + return Promise.resolve({ data: []}); + } + /** + * NOTE(chris): Because of a bug in Tabulator + * we need to get the params from elsewhere. + * @see https://github.com/olifolkerd/tabulator/issues/4318 + */ + const apiconfig = { + ...this.tabulatorOptions.ajaxConfig, + url: this.tabulatorOptions.ajaxURL, + params: this.tabulatorOptions.ajaxParams + }; + return this.$api.call(apiconfig); + }, + ajaxResponse: (url, params, response) => { + return response?.data; + }, + + layout: 'fitDataStretch', + layoutColumnsOnNewData: false, + height: '100%', + selectableRows: true, + selectableRowsRangeMode: 'click', + index: 'prestudent_id', + persistence: { + sort: true, + columns: ["width", "visible"], + filter: false, + headerFilter: false, + group: false, + page: false, + }, + persistenceID: 'stv-list-20260223_01' + }, + tabulatorEvents: [ + { + event: 'rowSelectionChanged', + handler: this.rowSelectionChanged + }, + { + event: 'dataLoading', + handler: this.handleDataLoading + }, + { + event: 'renderComplete', + handler: this.handleRenderComplete + }, + { + event: 'dataProcessed', + handler: (data) => { + this.getAllRows() + this.autoSelectRows(data) + } + }, + { + event: 'dataLoaded', + handler: data => { + if (Array.isArray(data)) { + this.count = data.length; + this.allPrestudents = data.map(item => item.prestudent_id); + } else { + this.count = 0; + this.allPrestudents = []; + } + syncTagHeaderFilterOptions( + Array.isArray(data) ? data : [], + this.tagFilterState.initialOptions, + this.tagFilterState.selectedOptions, + ); + } + }, + { + event: 'dataFiltered', + handler: (filters, rows) => { + this.filteredcount = rows.length; + syncSelectedTagOptionsWithHeaderFilters( + filters, + this.tagFilterState.selectedOptions, + this.tagsEnabled, + ); + }, + }, + { + event: 'rowClick', + handler: this.handleRowClick // TODO(chris): this should be in the filter component + }, + { + event: 'dataTreeRowExpanded', + handler: (data) => { + this.getExpandedRows() + } + }, + { + event: 'dataTreeRowCollapsed', + handler: (data) => { + this.getExpandedRows() + } + }, + { + event: 'rowMouseDown', + handler: this.handleMouseDown + }, + { + event: "columnWidth", + handler: (column) => { + if (column.getField() !== "tags") return; + + column.getCells().forEach((cell) => { + cell.getElement().firstElementChild?.fitTags?.(); + }); + }, + }, + ], + focusObj: null, // TODO(chris): this should be in the filter component + lastSelected: null, + filter: [], + count: 0, + filteredcount: 0, + selectedcount: 0, + //tags + expanded: [], + tagEndpoint: ApiTag, + currentEndpoint: null, + headerFilterActive: false, + dragSource: [], + oldScrollUrl: '', + oldScrollLeft: 0, + oldScrollTop: 0, + allPrestudents: [], + rebuildData: [], + isLoading: false, + progress: -1, + total: -1, + processed: -1 + } + }, + computed: { + generalPresets: function() { + return [ + { + "id": null, + "name": "Standard", + "displayedColumns": [ + "uid","titelpre","tags","nachname","vorname","titelpost", + "ersatzkennzeichen","gebdatum","geschlecht", + "semester_berechnet","verband","gruppe","studiengang", + "matrikelnr","person_id","status","orgform_kurzbz", + "studienplan_bezeichnung","prestudent_id","priorisierung_relativ" ], - rowFormatter(row) { - if (row.getData().bnaktiv === false) { - row.getElement().classList.add('text-black','text-opacity-50','fst-italic'); - } - row.getElement().draggable = true - }, - - ajaxRequestFunc: (url, config, params) => { - if( url === '' ) - { - return Promise.resolve({ data: []}); - } - /** - * NOTE(chris): Because of a bug in Tabulator - * we need to get the params from elsewhere. - * @see https://github.com/olifolkerd/tabulator/issues/4318 - */ - const apiconfig = { - ...this.tabulatorOptions.ajaxConfig, - url: this.tabulatorOptions.ajaxURL, - params: this.tabulatorOptions.ajaxParams - }; - return this.$api.call(apiconfig); - }, - ajaxResponse: (url, params, response) => { - return response?.data; - }, - - layout: 'fitDataStretch', - layoutColumnsOnNewData: false, - height: '100%', - selectableRows: true, - selectableRowsRangeMode: 'click', - index: 'prestudent_id', - persistenceID: 'stv-list', - }, - tabulatorEvents: [ - { - event: 'rowSelectionChanged', - handler: this.rowSelectionChanged - }, - { - event: 'dataLoading', - handler: this.handleDataLoading - }, - { - event: 'renderComplete', - handler: this.handleRenderComplete - }, - { - event: 'dataProcessed', - handler: (data) => { - this.getAllRows() - this.autoSelectRows(data) - } - }, - { - event: 'dataLoaded', - handler: data => this.count = data.length - }, - { - event: 'dataFiltered', - handler: (filters, rows) => this.filteredcount = rows.length - }, - { - event: 'rowClick', - handler: this.handleRowClick // TODO(chris): this should be in the filter component - }, - { - event: 'dataTreeRowExpanded', - handler: (data) => { - this.getExpandedRows() - } - }, - { - event: 'dataTreeRowCollapsed', - handler: (data) => { - this.getExpandedRows() - } - }, - { - event: 'rowMouseDown', - handler: this.handleMouseDown - } - ], - focusObj: null, // TODO(chris): this should be in the filter component - lastSelected: null, - filter: [], - count: 0, - filteredcount: 0, - selectedcount: 0, - //tags - expanded: [], - selectedColumnValues: [], - tagEndpoint: ApiTag, - currentEndpoint: null, - headerFilterActive: false, - dragSource: [], - oldScrollUrl: '', - oldScrollLeft: 0, - oldScrollTop: 0 - } + "headerFilters": [], + "sort": null + } + ]; }, - computed: { - countsToHTML: function() { - return this.$p.t('global/ausgewaehlt') - + ': ' + (this.selectedcount || 0) + '' - + ' | ' - + this.$p.t('global/gefiltert') - + ': ' - + '' + (this.filteredcount || 0) + '' - + ' | ' - + this.$p.t('global/gesamt') - + ': ' + (this.count || 0) + ''; - }, - selectedDragObject() { - let items = this.dragSource?.length ? this.dragSource : this.selected; + countsToHTML: function() { + return this.$p.t('global/ausgewaehlt') + + ': ' + (this.selectedcount || 0) + '' + + ' | ' + + this.$p.t('global/gefiltert') + + ': ' + + '' + (this.filteredcount || 0) + '' + + ' | ' + + this.$p.t('global/gesamt') + + ': ' + (this.count || 0) + ''; + }, + selectedDragObject() { + let items = this.dragSource?.length ? this.dragSource : this.selected; - return items.map(item => { - let type, id; - if (item.uid) { - type = 'student'; - id = item.uid; - } else if (item.prestudent_id) { - type = 'prestudent'; - id = item.prestudent_id; - } else if (item.person_id) { - type = 'person'; - id = item.person_id; - } - return { - ...item, - type, - id - }; - }); - }, - downloadConfig() { - return { - csv: { - formatter: 'csv', - file: this.fileString, - options: { - delimiter: ';', - bom: true, - } - } + return items.map(item => { + let type, id; + if (item.uid) { + type = 'student'; + id = item.uid; + } else if (item.prestudent_id) { + type = 'prestudent'; + id = item.prestudent_id; + } else if (item.person_id) { + type = 'person'; + id = item.person_id; + } + return { + ...item, + type, + id + }; + }); + }, + downloadConfig() { + return { + csv: { + formatter: 'csv', + file: this.fileString, + options: { + delimiter: ';', + bom: true, + }, + rowRange: this.selectedcount > 0 ? "selected" : "all", + }, + }; + }, + fileString() { + let today = new Date().toLocaleDateString('en-GB') + .replace(/\//g, '_'); + return "StudentList_" + today + ".csv"; + }, + selectedPrestudents() { + if (this.selected && this.selected.length > 0) { + return this.selected.map(item => item.prestudent_id); + } else { + // fallback whole list of prestudents + return this.allPrestudents || []; + } + }, + + linkXLS(){ + return FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'content/statistik/studentenexportextended.xls.php?' + + '&studiensemester_kurzbz=' + this.currentSemester + + '&data=' + this.selectedPrestudents.join(";"); + }, + }, + watch: { + "$p.user_language.value"(n, o) { + if (n !== o && o !== undefined && this.$refs.table.tableBuilt) { + this.translateTabulator(); + } + }, + "tagFilterState.selectedOptions": { + handler() { + const selectedOptions = this.tagFilterState.selectedOptions; + const combinedFilterStatement = + buildTagHeaderFilterExpression(selectedOptions); + + setTagHeaderFilterValue( + combinedFilterStatement, + this.$refs.table.tabulator, + ); + }, + deep: true, + }, + }, + methods: { + translateTabulator() { + this.$p + .loadCategory([ + "global", + "person", + "lehre", + "ui", + "profilUpdate", + "admission", + "stv", + ]) + .then(() => { + this.updateTagFilterLabels(); + + const translations = { + uid: capitalize(this.$p.t("person/uid")), + titelpre: capitalize(this.$p.t("person/titelpre")), + nachname: capitalize(this.$p.t("person/nachname")), + vorname: capitalize(this.$p.t("person/vorname")), + wahlname: capitalize(this.$p.t("person/wahlname")), + vornamen: capitalize(this.$p.t("person/vornamen")), + titelpost: capitalize(this.$p.t("person/titelpost")), + ersatzkennzeichen: capitalize( + this.$p.t("person/ersatzkennzeichen"), + ), + gebdatum: capitalize(this.$p.t("person/geburtsdatum")), + geschlecht: capitalize(this.$p.t("person/geschlecht")), + semester_berechnet: capitalize(this.$p.t("lehre/sem")), + verband: capitalize(this.$p.t("lehre/verb")), + gruppe: capitalize(this.$p.t("lehre/grp")), + studiengang: capitalize(this.$p.t("lehre/studiengang")), + studiengang_kz: capitalize(this.$p.t("lehre/studiengang_kz")), + matrikelnr: capitalize(this.$p.t("person/personenkennzeichen")), + person_id: capitalize(this.$p.t("person/person_id")), + status: capitalize(this.$p.t("global/status")), + status_datum: capitalize(this.$p.t("profilUpdate/statusDate")), + status_bestaetigung: capitalize( + this.$p.t("global/status_bestaetigung"), + ), + mail_privat: capitalize(this.$p.t("person/email_private")), + mail_intern: capitalize(this.$p.t("person/email_intern")), + anmerkungen: capitalize(this.$p.t("stv/notes_person")), + anmerkung: capitalize(this.$p.t("stv/notes_prestudent")), + orgform_kurzbz: capitalize(this.$p.t("lehre/orgform")), + aufmerksamdurch_kurzbz: capitalize( + this.$p.t("person/aufmerksamDurch"), + ), + punkte: capitalize(this.$p.t("admission/gesamtpunkte")), + aufnahmegruppe_kurzbz: capitalize( + this.$p.t("stv/aufnahmegruppe_kurzbz"), + ), + dual: capitalize(this.$p.t("lehre/dual_short")), + matr_nr: capitalize(this.$p.t("person/matrikelnummer")), + studienplan_bezeichnung: capitalize(this.$p.t("lehre/studienplan")), + prestudent_id: capitalize(this.$p.t("ui/prestudent_id")), + priorisierung_relativ: capitalize(this.$p.t("lehre/prioritaet")), + mentor: capitalize(this.$p.t("stv/mentor")), + bnaktiv: capitalize(this.$p.t("person/aktiv")), + }; + + /** NOTE(chris): + * use this approach because updateDefinition + * on the Tabulator columns is way slower and + * freezes up the GUI. + */ + // Overwrite definition for column show/hide + this.$refs.table.tabulator.getColumns().forEach((col) => { + const trans = translations[col.getField()]; + if (!trans) return; + col.getDefinition().title = trans; + }); + // Overwrite node in dom + this.$refs.table.tabulator.element + .querySelectorAll(".tabulator-col[tabulator-field]") + .forEach((el) => { + const field = el.getAttribute("tabulator-field"); + if (!translations[field]) return; + + const title = el.querySelector(".tabulator-col-title"); + if (!title) return; + + title.innerText = translations[field]; + }); + }); + }, + reload() { + this.$refs.table.reloadTable(); + }, + actionNewPrestudent() { + this.$refs.new.open(); + }, + rowSelectionChanged(data, rows, selected, deselected) { + this.selectedcount = data.length; + + //in case of empty selection (eg. in future or past semester of selected student without sem) + if (selected.length == 0 ) { + this.lastSelected = this.selected; + } + + if (selected.length > 0 || deselected.length > 0) { + this.lastSelected = this.selected; + + //for tags + this.selectedRows = this.$refs.table.tabulator.getSelectedRows(); + this.selectedColumnValues = this.selectedRows.filter( + row => row.getData().prestudent_id !== undefined + && row.getData().prestudent_id + ).map( + row => row.getData().prestudent_id + ); + + this.$emit('update:selected', data); + } + }, + autoSelectRows(data) { + if (Array.isArray(this.lastSelected) && this.lastSelected.length){ + // NOTE(chris): reselect rows on refresh + let selected = this.lastSelected.map(el => this.$refs.table.tabulator.getRow(el.prestudent_id)) + // TODO(chris): unselect current item if it's no longer in the table? + // or maybe reselect only the last one? + selected = selected.filter(el => el); + + if (selected.length) + this.$refs.table.tabulator.selectRow(selected); + } else if(data && this.lastSelected === undefined) { + // NOTE(chris): select row if it's the only one (preferably only on startup) + if (data.length == 1) { + this.$refs.table.tabulator.selectRow(this.$refs.table.tabulator.getRows()); + } + } + }, + updateFilter(filter) { + this.filter = filter; + this.$emit('filterActive', filter); + this.updateUrl(); + }, + updateTagFilterLabels() { + this.tagFilterLabels.clear = this.$p.t("ui/filterdelete"); + this.tagFilterLabels.tag = "Tag"; + this.tagFilterLabels.connectors.AND = this.$p + .t("ui/andCondition") + .toUpperCase(); + this.tagFilterLabels.connectors.OR = this.$p + .t("ui/orCondition") + .toUpperCase(); + this.tagFilterLabels.connectors.NOT = this.$p + .t("ui/notCondition") + .toUpperCase(); + }, + updateUrl(endpoint, first) { + this.lastSelected = first ? undefined : this.selected; + + if (endpoint === undefined && this.currentEndpoint === null) { + endpoint = { url: '' }; + } else if (endpoint === undefined) { + endpoint = JSON.parse(JSON.stringify(this.currentEndpoint)); + } else { + this.currentEndpoint = JSON.parse(JSON.stringify(endpoint)); + } + + endpoint.url = endpoint.url.replace( + 'CURRENT_SEMESTER', + encodeURIComponent(this.currentSemester) + ); + + const params = (endpoint?.params !== undefined) ? endpoint.params : {}; + let method = (endpoint?.method !== undefined) ? endpoint.method : 'get'; + if (this.filter.length && !endpoint.url.match(/\/search\//)) { + params.filter = this.filter; + method = 'post'; + } + + this.tabulatorOptions.ajaxURL = endpoint.url; + this.tabulatorOptions.ajaxParams = { ...params }; + this.tabulatorOptions.ajaxConfig = { method }; + + if (!this.$refs.table.tableBuilt) { + if (this.$refs.table.tabulator) { + this.$refs.table.tabulator.on("tableBuilt", () => { + this.$refs.table.tabulator.setData(endpoint.url, params, method); + }); + } + } else + this.$refs.table.tabulator.setData(endpoint.url, params, method); + }, + dragCleanup(evt) { + this.dragSource = []; + if (evt.dataTransfer.dropEffect == 'none') + return; // aborted or wrong target + + this.$reloadList(); + }, + onKeydown(e) { // TODO(chris): this should be in the filter component + + if ((e.ctrlKey || e.metaKey) && e.code === "KeyA") { + e.preventDefault(); + + this.$refs.table.tabulator.deselectRow(); + this.$refs.table.tabulator.selectRow('active'); + } + + if (!this.focusObj) + return; + + // Ignore typing inside editable elements + if ( + e.target instanceof HTMLInputElement || + e.target instanceof HTMLTextAreaElement || + e.target.isContentEditable + ) + return; + + var next; + switch (e.code) { + case 'Enter': + case 'Space': + e.preventDefault(); + var e2 = new Event('click', e); + e2.altKey = e.altKey; + e2.ctrlKey = e.ctrlKey; + e2.shiftKey = e.shiftKey; + this.focusObj.dispatchEvent(e2); + //row.component.toggleSelect(); + break; + case 'ArrowUp': + e.preventDefault(); + next = this.focusObj.previousElementSibling; + if (next) + this.changeFocus(this.focusObj, next); + break; + case 'ArrowDown': + e.preventDefault(); + next = this.focusObj.nextElementSibling; + if (next) + this.changeFocus(this.focusObj, next); + break; + } + }, + changeFocus(a, b) { // TODO(chris): this should be in the filter component + if (b) { + b.tabIndex = 0; + this.focusObj = b; + b.focus(); + } else { + this.focusObj = null; + } + a.tabIndex = -1; + return this.focusObj; + }, + onFocus(e) { // TODO(chris): this should be in the filter component + if (!this.focusObj) { + var container, target; + if (e.target.classList.contains('tabulator-container')) { + container = e.target; + target = container.querySelector('.tabulator-row'); + } else if (e.target.classList.contains('tabulator-row')) { + container = e.target.closest('.tabulator-container'); + target = e.target; + } + if (container && target) { + this.changeFocus(container, target); + } + } + }, + handleRowClick(e, row) { // TODO(chris): this should be in the filter component + if (this.focusObj) { + let el = row.getElement(); + if (el != this.focusObj) + this.changeFocus(this.focusObj, el); + } + }, + clearSelection() { + this.lastSelected = []; + this.$emit('update:selected',[]); + }, + //methods tags + addedTag(addedTag) { + addTagInTable(addedTag, this.allRows, 'prestudent_id') + syncTagHeaderFilterOptions( + this.$refs.table?.tabulator?.getData() || [], + this.tagFilterState.initialOptions, + this.tagFilterState.selectedOptions, + ); + }, + deletedTag(deletedTag) { + deleteTagInTable(deletedTag, this.allRows); + syncTagHeaderFilterOptions( + this.$refs.table?.tabulator?.getData() || [], + this.tagFilterState.initialOptions, + this.tagFilterState.selectedOptions, + ); + }, + updatedTag(updatedTag) { + updateTagInTable(updatedTag, this.allRows) + syncTagHeaderFilterOptions( + this.$refs.table?.tabulator?.getData() || [], + this.tagFilterState.initialOptions, + this.tagFilterState.selectedOptions, + ); + }, + getAllRows() { + this.allRows = this.$refs.table.tabulator.getRows(); + }, + resetFilter() { + this.clearSelectedTagHeaderFilters(); + this.$refs.listfilter.resetFilter(); + this.$refs.table.clearFilters(); + }, + clearSelectedTagHeaderFilters() { + this.tagFilterState.selectedOptions.splice( + 0, + this.tagFilterState.selectedOptions.length + ); + }, + handleHeaderFilter(filterActive) { + this.headerFilterActive = filterActive; + }, + handleMouseDown(e, row) { + let data = row.getData(); + let id = data.uid ?? data.prestudent_id ?? data.person_id; + + const isAlreadySelected = this.selected?.some( + row => (row.uid ?? row.prestudent_id ?? row.person_id) === id + ); + + this.dragSource = + isAlreadySelected && this.selected?.length ? this.selected : [data]; + }, + handleDataLoading() { + this.oldScrollLeft = this.$refs.table.tabulator.rowManager.scrollLeft; + this.oldScrollTop = this.$refs.table.tabulator.rowManager.scrollTop; + }, + handleRenderComplete() { + const table = this.$refs.table.tabulator.element.querySelector( + '.tabulator-tableholder' + ); + if (table) { + const curAjaxUrl = this.$refs.table.tabulator.getAjaxUrl(); + if (this.oldScrollUrl === curAjaxUrl) { + table.scrollLeft = this.oldScrollLeft; + table.scrollTop = this.oldScrollTop; + } else { + this.oldScrollLeft = table.scrollLeft; + this.oldScrollTop = table.scrollTop; + } + this.oldScrollUrl = this.$refs.table.tabulator.getAjaxUrl(); + } + }, + async rebuild(selected){ + const maxbulksize = 25; + const minbulksize = 5; + let bulksize = minbulksize; + this.isLoading = true; + let prestudentIds = []; + if(!selected.length) + prestudentIds = this.$refs.table.tabulator + .getData() + .map(row => row.prestudent_id); + else + prestudentIds = selected.map(item => item.prestudent_id); + + this.progress = 0; + this.total = prestudentIds.length; + this.processed = 0; + this.rebuildData = [ + [], + [], + [] + ]; + + this.showModal(); + + while(this.processed < this.total) + { + let iteration_prestudentIds = prestudentIds.slice( + this.processed, + (this.processed + bulksize) + ); + const params = { + ids: iteration_prestudentIds, + typeId: 'prestudent_id', + sem: this.studiensemesterKurzbz }; - }, - fileString() { - let today = new Date().toLocaleDateString('en-GB') - .replace(/\//g, '_'); - return "StudentList_" + today + ".csv"; - }, - }, - created: function() { - if(this.tagsEnabled) { - const coltags = { - title: 'Tags', - field: 'tags', - tooltip: false, - headerFilter: "input", - headerFilterFunc: tagHeaderFilter, - headerFilterFuncParams: {field: 'tags'}, - formatter: (cell) => tagFormatter(cell, this.$refs.tagComponent), - width: 150, + + try + { + const result = await this.$api.call(ApiTag.rebuildTagsforTypeId(params)); + this.rebuildData[1] = [...this.rebuildData[1], ...result.data[1]]; + this.rebuildData[2] = [...this.rebuildData[2], ...result.data[2]]; + } + catch(error) { + this.$fhcAlert.handleSystemError(error); }; - this.tabulatorOptions.columns.splice(2, 0, coltags); + + this.processed += iteration_prestudentIds.length; + this.progress = Math.round(this.processed * 100 / this.total); + + bulksize = bulksize * 2; + if(bulksize > maxbulksize) bulksize = maxbulksize; } - }, - watch: { - '$p.user_language.value'(n, o) { - if (n !== o && o !== undefined && this.$refs.table.tableBuilt) { - this.translateTabulator(); - } + + if(this.rebuildData[1].length > 0) + { + this.$fhcAlert.alertSuccess(this.$p.t('tag', 'alertSuccessRebuild', { count: this.rebuildData[1].length })); } + if(this.rebuildData[2].length > 0) + { + this.$fhcAlert.alertError(this.$p.t('tag', 'alertErrorRebuild') + this.rebuildData[2].toString()); + } + + this.isLoading = false; + this.$reloadList(); }, - methods: { - translateTabulator() { - this.$p - .loadCategory(['global', 'person', 'lehre', 'ui', 'profilUpdate', 'admission', 'stv']) - .then(() => { - const translations = { - uid: capitalize(this.$p.t('person/uid')), - titelpre: capitalize(this.$p.t('person/titelpre')), - nachname: capitalize(this.$p.t('person/nachname')), - vorname: capitalize(this.$p.t('person/vorname')), - wahlname: capitalize(this.$p.t('person/wahlname')), - vornamen: capitalize(this.$p.t('person/vornamen')), - titelpost: capitalize(this.$p.t('person/titelpost')), - ersatzkennzeichen: capitalize(this.$p.t('person/ersatzkennzeichen')), - gebdatum: capitalize(this.$p.t('person/geburtsdatum')), - geschlecht: capitalize(this.$p.t('person/geschlecht')), - semester: capitalize(this.$p.t('lehre/sem')), - verband: capitalize(this.$p.t('lehre/verb')), - gruppe: capitalize(this.$p.t('lehre/grp')), - studiengang: capitalize(this.$p.t('lehre/studiengang')), - studiengang_kz: capitalize(this.$p.t('lehre/studiengang_kz')), - matrikelnr: capitalize(this.$p.t('person/personenkennzeichen')), - person_id: capitalize(this.$p.t('person/person_id')), - status: capitalize(this.$p.t('global/status')), - status_datum: capitalize(this.$p.t('profilUpdate/statusDate')), - status_bestaetigung: capitalize(this.$p.t('global/status_bestaetigung')), - mail_privat: capitalize(this.$p.t('person/email_private')), - mail_intern: capitalize(this.$p.t('person/email_intern')), - anmerkungen: capitalize(this.$p.t('stv/notes_person')), - anmerkung: capitalize(this.$p.t('stv/notes_prestudent')), - orgform_kurzbz: capitalize(this.$p.t('lehre/orgform')), - aufmerksamdurch_kurzbz: capitalize(this.$p.t('person/aufmerksamDurch')), - punkte: capitalize(this.$p.t('admission/gesamtpunkte')), - aufnahmegruppe_kurzbz: capitalize(this.$p.t('stv/aufnahmegruppe_kurzbz')), - dual: capitalize(this.$p.t('lehre/dual_short')), - matr_nr: capitalize(this.$p.t('person/matrikelnummer')), - studienplan_bezeichnung: capitalize(this.$p.t('lehre/studienplan')), - prestudent_id: capitalize(this.$p.t('ui/prestudent_id')), - priorisierung_relativ: capitalize(this.$p.t('lehre/prioritaet')), - mentor: capitalize(this.$p.t('stv/mentor')), - bnaktiv: capitalize(this.$p.t('person/aktiv')) - }; - - /** NOTE(chris): - * use this approach because updateDefinition - * on the Tabulator columns is way slower and - * freezes up the GUI. - */ - // Overwrite definition for column show/hide - this.$refs.table.tabulator.getColumns().forEach(col => { - const trans = translations[col.getField()]; - if (!trans) - return; - col.getDefinition().title = trans; - }); - // Overwrite node in dom - this.$refs.table.tabulator.element - .querySelectorAll('.tabulator-col[tabulator-field]') - .forEach(el => { - const field = el.getAttribute('tabulator-field'); - if (!translations[field]) - return; - - const title = el.querySelector('.tabulator-col-title'); - if (!title) - return; - - title.innerText = translations[field]; - }); - }); - }, - reload() { - this.$refs.table.reloadTable(); - }, - actionNewPrestudent() { - this.$refs.new.open(); - }, - rowSelectionChanged(data, rows) { - this.selectedcount = data.length; - this.lastSelected = this.selected; - - //for tags - this.selectedRows = this.$refs.table.tabulator.getSelectedRows(); - this.selectedColumnValues = this.selectedRows.filter(row => row.getData().prestudent_id !== undefined && row.getData().prestudent_id).map(row => row.getData().prestudent_id); - - this.$emit('update:selected', data); - }, - autoSelectRows(data) { - if (Array.isArray(this.lastSelected) && this.lastSelected.length){ - // NOTE(chris): reselect rows on refresh - let selected = this.lastSelected.map(el => this.$refs.table.tabulator.getRow(el.prestudent_id)) - // TODO(chris): unselect current item if it's no longer in the table? - // or maybe reselect only the last one? - selected = selected.filter(el => el); - - if (selected.length) - this.$refs.table.tabulator.selectRow(selected); - } else if(data && this.lastSelected === undefined) { - // NOTE(chris): select row if it's the only one (preferably only on startup) - if (data.length == 1) { - this.$refs.table.tabulator.selectRow(this.$refs.table.tabulator.getRows()); - } - } - }, - updateFilter(filter) { - this.filter = filter; - this.$emit('filterActive', filter); - this.updateUrl(); - }, - updateUrl(endpoint, first) { - this.lastSelected = first ? undefined : this.selected; - -/* console.log('function param endpoint: ' + JSON.stringify(endpoint)); - console.log('current endpoint: ' + JSON.stringify(this.currentEndpoint));*/ - - if( endpoint === undefined && this.currentEndpoint === null) - { - endpoint = { url: '' }; - } - else if( endpoint === undefined ) - { - endpoint = JSON.parse(JSON.stringify(this.currentEndpoint)); - } - else - { - this.currentEndpoint = JSON.parse(JSON.stringify(endpoint)); - } - - endpoint.url = endpoint.url.replace( - 'CURRENT_SEMESTER', - encodeURIComponent(this.currentSemester) - ); - - const params = (endpoint?.params !== undefined) ? endpoint.params : {}; - let method = (endpoint?.method !== undefined) ? endpoint.method : 'get'; - if (this.filter.length && !endpoint.url.match(/\/search\//)) - { - params.filter = this.filter; - method = 'post'; - } - - this.tabulatorOptions.ajaxURL = endpoint.url; - this.tabulatorOptions.ajaxParams = { ...params }; - this.tabulatorOptions.ajaxConfig = {method}; - - if (!this.$refs.table.tableBuilt) { - if (this.$refs.table.tabulator) { - this.$refs.table.tabulator.on("tableBuilt", () => { - this.$refs.table.tabulator.setData(endpoint.url, params, method); - }); - } - } else - this.$refs.table.tabulator.setData(endpoint.url, params, method); - }, - dragCleanup(evt) { - this.dragSource = []; - if (evt.dataTransfer.dropEffect == 'none') - return; // aborted or wrong target - - this.$reloadList(); - }, - onKeydown(e) { // TODO(chris): this should be in the filter component - if (!this.focusObj) - return; - - // Ignore typing inside editable elements - if ( - e.target instanceof HTMLInputElement || - e.target instanceof HTMLTextAreaElement || - e.target.isContentEditable - ) - return; - - var next; - switch (e.code) { - case 'Enter': - case 'Space': - e.preventDefault(); - var e2 = new Event('click', e); - e2.altKey = e.altKey; - e2.ctrlKey = e.ctrlKey; - e2.shiftKey = e.shiftKey; - this.focusObj.dispatchEvent(e2); - //row.component.toggleSelect(); - break; - case 'ArrowUp': - e.preventDefault(); - next = this.focusObj.previousElementSibling; - if (next) - this.changeFocus(this.focusObj, next); - break; - case 'ArrowDown': - e.preventDefault(); - next = this.focusObj.nextElementSibling; - if (next) - this.changeFocus(this.focusObj, next); - break; - } - }, - changeFocus(a, b) { // TODO(chris): this should be in the filter component - if (b) { - b.tabIndex = 0; - this.focusObj = b; - b.focus(); - } else { - this.focusObj = null; - } - a.tabIndex = -1; - return this.focusObj; - }, - onFocus(e) { // TODO(chris): this should be in the filter component - if (!this.focusObj) { - var container, target; - if (e.target.classList.contains('tabulator-container')) { - container = e.target; - target = container.querySelector('.tabulator-row'); - } else if (e.target.classList.contains('tabulator-row')) { - container = e.target.closest('.tabulator-container'); - target = e.target; - } - if (container && target) { - this.changeFocus(container, target); - } - } - }, - handleRowClick(e, row) { // TODO(chris): this should be in the filter component - if (this.focusObj) { - let el = row.getElement(); - if (el != this.focusObj) - this.changeFocus(this.focusObj, el); - } - }, - //methods tags - addedTag(addedTag) - { - addTagInTable(addedTag, this.allRows, 'prestudent_id') - }, - deletedTag(deletedTag) - { - deleteTagInTable(deletedTag, this.allRows); - }, - updatedTag(updatedTag) - { - updateTagInTable(updatedTag, this.allRows) - }, - getAllRows() { - this.allRows = this.$refs.table.tabulator.getRows(); - }, - resetFilter(){ - this.$refs.listfilter.resetFilter(); - this.$refs.table.clearFilters(); - }, - handleHeaderFilter(filterActive){ - this.headerFilterActive = filterActive; - }, - handleMouseDown(e, row) - { - let data = row.getData(); - let id = data.uid ?? data.prestudent_id ?? data.person_id; - - const isAlreadySelected = this.selected?.some(row => (row.uid ?? row.prestudent_id ?? row.person_id) === id); - - this.dragSource = (isAlreadySelected && this.selected?.length) ? this.selected : [data]; - }, - handleDataLoading() { - this.oldScrollLeft = this.$refs.table.tabulator.rowManager.scrollLeft; - this.oldScrollTop = this.$refs.table.tabulator.rowManager.scrollTop; - }, - handleRenderComplete() { - const table = this.$refs.table.tabulator.element.querySelector('.tabulator-tableholder'); - if(table) { - const curAjaxUrl = this.$refs.table.tabulator.getAjaxUrl(); - if(this.oldScrollUrl === curAjaxUrl) { - table.scrollLeft = this.oldScrollLeft; - table.scrollTop = this.oldScrollTop; - } else { - this.oldScrollLeft = table.scrollLeft; - this.oldScrollTop = table.scrollTop; - } - this.oldScrollUrl = this.$refs.table.tabulator.getAjaxUrl(); - } - }, + showModal(){ + this.$refs.modalLocked.open(); }, - // TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component - // TODO(chris): filter component column chooser has no accessibilty features - template: ` -
-
- tagFormatter(cell, this.$refs.tagComponent, onRendered), + width: 150, + headerSort: false, + }; + this.tabulatorOptions.columns.splice(2, 0, coltags); + this.$p.loadCategory("ui").then(() => { + this.updateTagFilterLabels(); + }); + } + }, + // TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component + // TODO(chris): filter component column chooser has no accessibilty features + template: /* html */` +
+ + + +
+ + + + + + + - - -
- -
` + +
+
+ +
` }; diff --git a/public/js/components/Stv/Studentenverwaltung/List/ModalLoading.js b/public/js/components/Stv/Studentenverwaltung/List/ModalLoading.js new file mode 100644 index 000000000..99368173e --- /dev/null +++ b/public/js/components/Stv/Studentenverwaltung/List/ModalLoading.js @@ -0,0 +1,84 @@ +import BsModal from "../../../Bootstrap/Modal.js"; +import FormForm from "../../../Form/Form.js"; + +export default { + name: "modalLoading", + components: { + BsModal, + FormForm, + PvProgressBar: primevue.progressbar + }, + props: { + isLoading: { + type: Boolean, + required: true + }, + message: { + type: String, + required: true + }, + progress: { + type: Number, + default: -1 + }, + total: { + type: Number, + default: -1 + }, + processed: { + type: Number, + default: -1 + } + }, + data(){ + return{} + }, + methods:{ + open(){ + this.$refs.modalLoading.show(); + }, + closeModal(){ + this.$refs.modalLoading.hide(); + } + }, + template: ` + + + + +
+
+ {{message}} +
+
+ {{$p.t('ui', 'fenster_schliessen')}} +
+
+
+ + {{processed}} / {{total}} + + + +
+ `, +} \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Studiensemester.js b/public/js/components/Stv/Studentenverwaltung/Studiensemester.js index 32b0a831d..33230b54a 100644 --- a/public/js/components/Stv/Studentenverwaltung/Studiensemester.js +++ b/public/js/components/Stv/Studentenverwaltung/Studiensemester.js @@ -1,4 +1,4 @@ -import {CoreRESTClient} from '../../../RESTClient.js'; +import ApiStudiensemester from '../../../api/factory/studiensemester.js'; export default { emits: [ @@ -18,6 +18,11 @@ export default { today: -1 }; }, + computed: { + lowerCaseList() { + return this.list.map(i => i.toLowerCase()); + } + }, methods: { set(n) { this.loading = true; @@ -47,25 +52,18 @@ export default { } this.loading = true; - CoreRESTClient - .get('components/stv/studiensemester/now') - .then(result => CoreRESTClient.getData(result.data)) + this.$api + .call(ApiStudiensemester.current()) .then(result => { - this.today = this.list.indexOf(result); - if (this.today >= 0) { - if (this.today != this.current) - this.set(this.today); - } else { - // TODO(chris): handle error (list might not be loaded yet) - } + this.today = this.list.indexOf(result.data); + if (this.today >= 0 && this.today != this.current) + this.set(this.today); }) .catch(this.$fhcAlert.handleSystemError); }, save(fallback) { - CoreRESTClient - .post('components/stv/studiensemester/set', { - studiensemester: this.list[this.current] - }) + this.$api + .call(ApiStudiensemester.set(this.list[this.current])) .then(() => { this.loading = false; this.$emit('update:studiensemesterKurzbz', this.list[this.current]); @@ -73,27 +71,42 @@ export default { .catch(error => { this.current = fallback; this.loading = false; - this.$fhcAlert.handleFormValidation(error); + this.$fhcAlert.handleSystemError(error); }); + }, + index() { + let index = this.list.indexOf(this.studiensemesterKurzbz); + if (index >= 0) + return index; + + // check if input is lower cases (because it came from an url) + index = this.lowerCaseList.indexOf(this.studiensemesterKurzbz); + if (index >= 0) { + this.$emit('update:studiensemesterKurzbz', this.list[index]); + return index; + } + + this.$emit('update:studiensemesterKurzbz', null); + return -1; } }, watch: { - 'studiensemesterKurzbz': function () { - this.current = this.list.indexOf(this.studiensemesterKurzbz); + studiensemesterKurzbz() { + if (!this.loading) + this.current = this.index(this.studiensemesterKurzbz); } }, created() { - CoreRESTClient - .get('components/stv/studiensemester') - .then(result => CoreRESTClient.getData(result.data) || []) + this.$api + .call(ApiStudiensemester.getAll()) .then(result => { - this.list = result.map(el => el.studiensemester_kurzbz); + this.list = result.data.map(el => el.studiensemester_kurzbz); this.loading = false; - this.current = this.list.indexOf(this.studiensemesterKurzbz); + this.current = this.index(this.studiensemesterKurzbz); }) .catch(this.$fhcAlert.handleSystemError); }, - template: ` + template: /* html */`
diff --git a/public/js/components/Tag/tagFormatter.js b/public/js/components/Tag/tagFormatter.js new file mode 100644 index 000000000..072906be8 --- /dev/null +++ b/public/js/components/Tag/tagFormatter.js @@ -0,0 +1,111 @@ +export function idTagFormatter (id, tagData, tagComponent, typeId, semesterStart=null, semesterEnd=null) +{ + if (!id) return; + + const hasSemesterFilter = !!(semesterStart && semesterEnd); + + const semStart = hasSemesterFilter ? new Date(semesterStart) : null; + const semEnd = hasSemesterFilter ? new Date(semesterEnd) : null; + + const parsedTags = tagData.map(tag => ({ + id: tag.notiz_id, + typ_kurzbz: tag.titel?.toLowerCase(), + beschreibung: tag.bezeichnung, + notiz: tag.text || "", + style: tag.style, + done: tag.done, + automatisiert: tag.automatisiert, + typeId: id, + validFrom: tag.start ? new Date(tag.start) : null, + validTo: tag.ende ? new Date(tag.ende) : null + })); + + const isInSemester = (tag) => { + if (!hasSemesterFilter) return true; + + if (!tag.validFrom && !tag.validTo) return true; + + if (!tag.validFrom && !tag.validTo) return true; + + if (tag.validFrom && tag.validTo) { + return tag.validFrom <= semEnd && tag.validTo >= semStart; + } + + if (tag.validFrom && !tag.validTo) { + return tag.validFrom <= semEnd; + } + + if (!tag.validFrom && tag.validTo) { + return tag.validTo >= semStart; + } + + return false; + }; + + let container = document.createElement('div'); + container.className = "d-flex gap-1"; + + let maxVisibleTags = 5; + let expanded = false; + const renderTags = () => { + container.innerHTML = ''; + + let filtered = parsedTags + .filter(t => t != null) + .filter(isInSemester); + + filtered.sort((a, b) => { + let adone = a.done ? 1 : 0; + let bdone = b.done ? 1 : 0; + + if (adone !== bdone) return adone - bdone; + return a.prioritaet - b.prioritaet; + }); + + const tagsToShow = expanded + ? filtered + : filtered.slice(0, maxVisibleTags); + + tagsToShow.forEach(tag => { + let tagElement = document.createElement('span'); + + if(tag.automatisiert) + tagElement.innerHTML = " " + tag.beschreibung; + else + tagElement.innerText = tag.beschreibung; + tagElement.title = tag.notiz; + tagElement.className = "tag " + tag.style; + + if (tag.done) { + tagElement.className += " tag_done"; + } + if (tag.automatisiert) + tagElement.className += " tag_auto"; + + tagElement.addEventListener('click', (event) => { + event.stopPropagation(); + event.preventDefault(); + tagComponent.editTag(tag.id); + }); + + container.appendChild(tagElement); + }); + + if (filtered.length > maxVisibleTags) { + let toggle = document.createElement('button'); + toggle.innerText = (expanded ? '- ' : '+ ') + (filtered.length - maxVisibleTags); + toggle.className = "display_all"; + + toggle.addEventListener('click', () => { + expanded = !expanded; + renderTags(); + }); + + container.appendChild(toggle); + } + }; + + renderTags(); + + return container; +} \ No newline at end of file diff --git a/public/js/components/filter/Table/Download.js b/public/js/components/filter/Table/Download.js index 057fdad5e..1480a00ba 100644 --- a/public/js/components/filter/Table/Download.js +++ b/public/js/components/filter/Table/Download.js @@ -202,7 +202,7 @@ export default { return true; }, download(config) { - this.tabulator.download(config.formatter, config.file, config.options) + this.tabulator.download(config.formatter, config.file, config.options, config.rowRange) } }, template: ` diff --git a/public/js/components/horizontalsplit/horizontalsplit.js b/public/js/components/horizontalsplit/horizontalsplit.js new file mode 100644 index 000000000..2d91cc6a9 --- /dev/null +++ b/public/js/components/horizontalsplit/horizontalsplit.js @@ -0,0 +1,147 @@ +export default { + name: 'HorizontalSplit', + props: { + defaultRatio: { + type: Array, + default: () => [50, 50] + } + }, + data: function () { + return { + availWidth: 0, + leftwidth: 0, + rightwidth: 0, + mousePosX: 0, + resize: false, + hsplitterOffset: 0, + selfOffsetLeft: 0 + }; + }, + template: ` +
+
+ +

Left Panel

+
+
+
+
+ + + + + + + + + +
+
+
+ +

+ +

+
+ `, + mounted: function () { + this.calcWidths(); + this.trackHorizontalSplitterOffsetLeft(); + window.addEventListener('resize', this.calcWidths); + }, + updated: function () { + this.trackHorizontalSplitterOffsetLeft(); + }, + beforeDestroy: function () { + window.removeEventListener('resize', this.calcWidths); + }, + methods: { + calcWidths: function () { + var oldavailWidth = this.availWidth; + this.selfOffsetLeft = this.$refs.horizontalsplit.offsetLeft; + this.availWidth = this.$refs.horizontalsplit.offsetWidth - this.$refs.hsplitter.offsetWidth; + + if ((this.leftwidth === 0 && this.rightwidth === 0) || oldavailWidth === 0) { + this.leftwidth = Math.floor(this.availWidth * (this.defaultRatio[0] / 100)); + } else { + this.leftwidth = Math.floor(((this.leftwidth * 100) / oldavailWidth) / 100 * this.availWidth); + } + this.rightwidth = this.availWidth - this.leftwidth; + }, + collapseLeft: function () { + this.calcWidths(); + this.leftwidth = 0; + this.rightwidth = this.availWidth; + }, + collapseRight: function () { + this.calcWidths(); + this.leftwidth = this.availWidth; + this.rightwidth = 0; + }, + showBoth: function () { + this.leftwidth = Math.floor(this.availWidth * (this.defaultRatio[0] / 100)); + this.rightwidth = this.availWidth - this.leftwidth; + }, + isCollapsed: function () { + if (this.leftwidth === 0) { + return 'left'; + } else if (this.rightwidth === 0) { + return 'right'; + } else { + return false; + } + }, + dragStart: function (e) { + e.preventDefault(); + e.stopPropagation(); + window.addEventListener('mouseup', this.dragEnd); + window.addEventListener('mousemove', this.drag); + this.resize = true; + this.mousePosX = e.clientX; + }, + drag: function (e) { + if (!this.resize) { + return; + } + e.preventDefault(); + e.stopPropagation(); + var offsetX = e.clientX - this.mousePosX; + this.leftwidth = this.leftwidth + offsetX; + if (this.leftwidth < 0) { + this.leftwidth = 0; + } + if (this.leftwidth > this.availWidth) { + this.leftwidth = this.availWidth; + } + this.rightwidth = this.availWidth - this.leftwidth; + this.mousePosX = e.clientX; + }, + dragEnd: function (e) { + e.preventDefault(); + e.stopPropagation(); + window.removeEventListener('mousemove', this.drag); + window.removeEventListener('mouseup', this.dragEnd); + this.resize = false; + this.mousePosX = e.clientX; + }, + trackHorizontalSplitterOffsetLeft: function () { + this.hsplitterOffset = this.$refs.hsplitter.offsetLeft; + } + }, + computed: { + leftOrRightClass: function () { + return ((this.hsplitterOffset - this.selfOffsetLeft) <= Math.floor(this.availWidth / 2)) + ? 'left' + : 'right'; + }, + leftwidthcss: function () { + return this.leftwidth + 'px'; + }, + rightwidthcss: function () { + return this.rightwidth + 'px'; + } + } +}; \ No newline at end of file diff --git a/public/js/components/verticalsplit/verticalsplit.js b/public/js/components/verticalsplit/verticalsplit.js index e712d1ecb..91561a1d3 100644 --- a/public/js/components/verticalsplit/verticalsplit.js +++ b/public/js/components/verticalsplit/verticalsplit.js @@ -1,5 +1,11 @@ export default { name: 'VerticalSplit', + props: { + defaultRatio: { + type: Array, + default: () => [50, 50] + } + }, data: function() { return { availHeight: 0, @@ -50,17 +56,22 @@ export default { updated: function() { this.trackVerticalSplitterOffsetTop(); }, + beforeDestroy: function () { + window.removeEventListener('resize', this.calcHeights); + }, methods: { calcHeights: function() { var windowheight = window.innerHeight; var oldavailHeight = this.availHeight; this.selfOffsetTop = this.$refs.verticalsplit.offsetTop; this.availHeight = windowheight - this.selfOffsetTop - this.$refs.vsplitter.offsetHeight; + if( (this.topheight === 0 && this.bottomheight === 0) || oldavailHeight === 0 ) { - this.topheight = Math.floor(this.availHeight/2); + this.topheight = Math.floor(this.availHeight * (this.defaultRatio[0] / 100)); } else { this.topheight = Math.floor( ((((this.topheight * 100) / oldavailHeight) / 100) * this.availHeight) ); } + this.bottomheight = this.availHeight - this.topheight; }, collapseTop: function() { @@ -74,8 +85,8 @@ export default { this.bottomheight = 0; }, showBoth: function() { - this.topheight = Math.floor(this.availHeight/2); - this.bottomheight = Math.floor(this.availHeight/2); + this.topheight = Math.floor(this.availHeight * (this.defaultRatio[0] / 100)); + this.bottomheight = this.availHeight - this.topheight }, isCollapsed: function() { if( this.topheight === 0 ) { diff --git a/public/js/directives/draggable.js b/public/js/directives/draggable.js index d534b9703..7ce93b428 100644 --- a/public/js/directives/draggable.js +++ b/public/js/directives/draggable.js @@ -28,7 +28,7 @@ export default { // i.e: for dragging multiple elements // otherwise set draggable attribute if (!binding.modifiers.capture) { - el.draggable = true; + updateAttributes(el); } const bcc = new BroadcastChannel('fhc-dnd'); @@ -78,11 +78,21 @@ export default { el.fhcDraggableCleanup = () => { el.removeEventListener('dragstart', onStart, binding.modifiers.capture); el.removeEventListener('dragend', onEnd, true); + if (el.dataset.fhcDraggableValue) { + delete el.dataset.fhcDraggableValue; + } + if (el.dataset.fhcEffectAllowed) { + delete el.dataset.fhcEffectAllowed; + } + updateAttributes(el); }; }, updated(el, binding) { updateValue(el, binding.value); updateEffectAllowed(el, binding.arg); + if (!binding.modifiers.capture) { + updateAttributes(el); + } }, beforeUnmount(el) { el.fhcDraggableCleanup(); @@ -92,7 +102,8 @@ export default { // Helper functions function updateValue(el, value) { - value = convertToValidDragObject(value); + if (value) + value = convertToValidDragObject(value); if (value) { el.dataset.fhcDraggableValue = JSON.stringify(value); } else if (el.dataset.fhcDraggableValue) { @@ -106,3 +117,9 @@ function updateEffectAllowed(el, effectAllowed) { delete el.dataset.fhcEffectAllowed; } } +function updateAttributes(el) { + if (el.dataset.fhcEffectAllowed && el.dataset.fhcDraggableValue) + el.draggable = true; + else if (el.hasAttribute('draggable')) + el.removeAttribute('draggable'); +} diff --git a/public/js/directives/drop.js b/public/js/directives/drop.js index 4e14c5296..63ea4826e 100644 --- a/public/js/directives/drop.js +++ b/public/js/directives/drop.js @@ -16,6 +16,26 @@ const EFFECTS = [ export default { mounted(el, binding) { + if (!binding.arg) { + binding.arg = 'none'; + } else if (typeof binding.arg === 'object' && !Array.isArray(binding.arg)) { + // NOTE(chris): allow object as arg and map it to arg and + // modifiers to allow dynamic modifiers. + if (binding.arg.allowed) { + binding.modifiers = binding.arg.allowed.reduce((a, c) => { + a[c] = true; + return a; + }, {}); + } + if (!binding.arg.effect) + binding.arg.effect = 'none'; + + if (binding.arg.strict) + binding.arg = binding.arg.effect + '-strict'; + else + binding.arg = binding.arg.effect; + } + const allowedTypes = Object.keys(binding.modifiers); allowedTypes.forEach(type => { if (type.substr(-11) == '-collection') { diff --git a/public/js/helpers/DocumentHelper.js b/public/js/helpers/DocumentHelper.js new file mode 100644 index 000000000..c37c632d5 --- /dev/null +++ b/public/js/helpers/DocumentHelper.js @@ -0,0 +1,44 @@ +export function highlightGesamtnote(zeugnisTable, teacherTable) +{ + let zeugnisData = zeugnisTable.getData(); + + let studentsWithNote = new Set(); + + zeugnisData.forEach(row => { + let student = row.uid; + let lv = row.lehrveranstaltung_id; + let note = row.note; + + if (!student || !lv) + return; + + if (note == null || note === "") + return; + + let key = `${student}_${lv}`; + studentsWithNote.add(key); + }); + + teacherTable.deselectRow(); + + teacherTable.getRows().forEach(row => { + let data = row.getData(); + + let student = data.student_uid; + let lv = data.lehrveranstaltung_id; + let note = data.note; + + if (!student || !lv) + return; + + if (note == null || note === "") + return; + + let key = `${student}_${lv}`; + + if (!studentsWithNote.has(key)) + { + row.select(); + } + }); +} \ No newline at end of file diff --git a/public/js/helpers/DragAndDrop.js b/public/js/helpers/DragAndDrop.js index f98697eb2..e08740344 100644 --- a/public/js/helpers/DragAndDrop.js +++ b/public/js/helpers/DragAndDrop.js @@ -30,6 +30,10 @@ const TYPE_DEFINITION = { prestudent: { id: "prestudent_id", dragIcon: "fa-solid fa-user-graduate text-muted" + }, + verband: { + id: "gruppe_id", + dragIcon: "fa-solid fa-user-group text-muted" } // TODO: IMPLEMENT OTHER TYPES }; @@ -116,12 +120,12 @@ function isValidTransferData(event, allowedTypes, strict) { function getTransferData(event, strict) { const result = []; - for (const type of event.dataTransfer.types) { - if (type.substr(0, 16) != 'application/fhc-') { - if (strict) - return null; - continue; - } + const dataTypes = [...event.dataTransfer.types].filter(type => type.startsWith('application/fhc-')); + + if (!dataTypes.length) + return null; + + for (const type of dataTypes) { let base_type = type.substr(16); let collection = false; if (base_type.substr(-11) == '-collection') { @@ -266,18 +270,16 @@ function eventHasTypes(event, allowedTypes, strict) { allowedTypes = VALID_TYPES; allowedTypes = allowedTypes.map(type => 'application/fhc-' + type); - const dataTypes = [...event.dataTransfer.types]; - + const dataTypes = [...event.dataTransfer.types].filter(type => type.startsWith('application/fhc-')); // NOTE(chris): if dragging across browsers the dataTransfer object is // set to a default one without data. Since we do not support dragging // across browsers (yet) we return false which will disallow dropping. if (!dataTypes.length) return false; - + if (!strict) - return allowedTypes.some(type => [...event.dataTransfer.types].includes(type)); - - return [...event.dataTransfer.types].every(type => allowedTypes.includes(type)); + return allowedTypes.some(type => dataTypes.includes(type)); + return dataTypes.every(type => allowedTypes.includes(type)); } function bindDragEnterLeave(el, onEnter, onLeave) { diff --git a/public/js/helpers/EmailHelpers.js b/public/js/helpers/EmailHelpers.js index 4ffdc8122..d4be2dff1 100644 --- a/public/js/helpers/EmailHelpers.js +++ b/public/js/helpers/EmailHelpers.js @@ -1,5 +1,6 @@ -export async function splitMailsHelper(mails, event, subject, body, alertPluginRef, phrasenPluginRef) { +export async function splitMailsHelper(mails, event, subject, body, alertPluginRef, phrasenPluginRef, uidCC) { await phrasenPluginRef.loadCategory('ui'); + let splititem = ","; let maillist = mails.join(splititem); let useBcc = event?.ctrlKey || event?.metaKey; @@ -46,7 +47,10 @@ export async function splitMailsHelper(mails, event, subject, body, alertPluginR } // construct the clean mailLink - let mailLink = useBcc ? `mailto:?bcc=${mailto}` : `mailto:${mailto}`; + let mailadressUid = (uidCC && typeof uidCC === 'string') + ? uidCC + '@' + FHC_JS_DATA_STORAGE_OBJECT.domain + : 'noreply@' + FHC_JS_DATA_STORAGE_OBJECT.domain; + let mailLink = useBcc ? `mailto:${mailadressUid}?cc=${mailadressUid}&bcc=${mailto}` : `mailto:${mailto}`; if (queryString) { // If using BCC, the string already has a '?', so append with '&'. Otherwise, start with '?' mailLink += useBcc ? `&${queryString}` : `?${queryString}`; @@ -63,4 +67,79 @@ export async function splitMailsHelper(mails, event, subject, body, alertPluginR } } } -} \ No newline at end of file +} + + +/** + * just splits the list of mails + * + * @param mails emailadresses + * @param subject subject + * @param useBcc useBcc + * @returns array of links for splitted mails + */ +export function splitMailLinks(mails, subject = "", useBcc = false, uidCC= "", maxLength = 2024) { + if (!Array.isArray(mails) || mails.length === 0) { + return []; + } + + const separator = ","; + const encodedSubject = subject ? encodeURIComponent(subject) : ""; + + // reserve space for the subject parameter + const subjectLength = encodedSubject + ? `?subject=${encodedSubject}`.length + : 0; + + const limit = maxLength - subjectLength; + + const links = []; + let currentRecipients = []; + + for (const mail of mails) { + if (!mail) continue; + + const testRecipients = [...currentRecipients, mail]; + const recipientString = testRecipients.join(separator); + + if (recipientString.length > limit && currentRecipients.length > 0) { + + links.push(createMailto( + currentRecipients.join(separator), + encodedSubject, + useBcc, + uidCC + )); + + currentRecipients = [mail]; + } + else { + currentRecipients.push(mail); + } + } + + if (currentRecipients.length > 0) { + links.push(createMailto( + currentRecipients.join(separator), + encodedSubject, + useBcc, + uidCC + )); + } + + return links; +} + +function createMailto(recipients, encodedSubject, useBcc, uidCC) { + let mailadressUid = uidCC + '@technikum-wien.at'; + + let link = useBcc + ? `mailto:${encodeURIComponent(mailadressUid)}?cc=${encodeURIComponent(mailadressUid)}&bcc=${encodeURIComponent(recipients)}` + : `mailto:${encodeURIComponent(recipients)}`; + + if (encodedSubject) { + link += `${useBcc ? "&" : "?"}subject=${encodedSubject}`; + } + + return link; +} diff --git a/public/js/helpers/TagHelper.js b/public/js/helpers/TagHelper.js index 9282aa167..826e4126a 100644 --- a/public/js/helpers/TagHelper.js +++ b/public/js/helpers/TagHelper.js @@ -15,7 +15,12 @@ export function addTagInTable(addedTag, rows, matchKey, tagsKey = "tags") let tags; try { - tags = JSON.parse(rowData[tagsKey] || "[]"); + let initialTags = rowData[tagsKey] || "[]"; + if (typeof initialTags === 'string') { + tags = JSON.parse(initialTags); + } else { + tags = initialTags; + } } catch (e) { tags = []; } @@ -28,8 +33,14 @@ export function addTagInTable(addedTag, rows, matchKey, tagsKey = "tags") let newTag = { ...addedTag, id: tag.id }; - tags.unshift(newTag); + tags.push(newTag); + tags.sort((a, b) => { + let adone = a.done ? 1 : 0; + let bdone = b.done ? 1 : 0; + if (adone !== bdone) return adone - bdone; + return a.prioritaet - b.prioritaet; + }); rowData[tagsKey] = JSON.stringify(tags); updated = true; }); @@ -53,7 +64,12 @@ export function deleteTagInTable(deletedTag, rows, tagsKeys = ['tags']) let tags; try { - tags = JSON.parse(rowData[key] || "[]"); + let initialTags = rowData[key] || "[]"; + if (typeof initialTags === 'string') { + tags = JSON.parse(initialTags); + } else { + tags = initialTags; + } } catch (e) { tags = []; } @@ -62,7 +78,6 @@ export function deleteTagInTable(deletedTag, rows, tagsKeys = ['tags']) return; let filtered = tags.filter(tag => tag?.id !== deletedTag); - if (filtered.length !== tags.length) { updates[key] = JSON.stringify(filtered); @@ -95,7 +110,12 @@ export function updateTagInTable(updatedTag, rows, fields = ['tags']) let fieldData; try { - fieldData = JSON.parse(rowData[field] || "[]"); + let initialTags = rowData[field] || "[]"; + if (typeof initialTags === 'string') { + fieldData = JSON.parse(initialTags); + } else { + fieldData = initialTags; + } } catch (e) { return; } @@ -108,6 +128,14 @@ export function updateTagInTable(updatedTag, rows, fields = ['tags']) if (index !== -1) { fieldData[index] = { ...updatedTag }; + fieldData.sort((a, b) => { + let adone = a.done ? 1 : 0; + let bdone = b.done ? 1 : 0; + + if (adone !== bdone) return adone - bdone; + return a.prioritaet - b.prioritaet; + }); + let updatedFieldData = JSON.stringify(fieldData); if (updatedFieldData !== rowData[field]) diff --git a/public/js/tabulator/filter/filters.js b/public/js/tabulator/filter/filters.js new file mode 100644 index 000000000..36d48cc8f --- /dev/null +++ b/public/js/tabulator/filter/filters.js @@ -0,0 +1,54 @@ +function dateinput(headerValue, rowValue, rowData, config) { + if (!luxon) + return this.like(headerValue, rowValue, rowData, config); + + let inputFormat = config.inputFormat || 'iso'; + let outputFormat = config.outputFormat || 'dd.MM.yyyy'; + + let value; + if (inputFormat == 'iso') + value = luxon.DateTime.fromISO(rowValue); + else + value = luxon.DateTime.fromFormat(rowValue, inputFormat); + + let range = []; + range = headerValue.split(' - '); + if (range.length == 1) { + range = headerValue.split('-'); + } + if (range.length == 2) { + range = range.map(r => { + r = r.trim(); + if (r) { + let d = luxon.DateTime.fromFormat(r, outputFormat); + if (!d.isValid && inputFormat != 'iso' && inputFormat != outputFormat) + d = luxon.DateTime.fromFormat(r, inputFormat); + if (!d.isValid) + d = luxon.DateTime.fromISO(r); + if (d.isValid) + return d; + } + return null; + }); + if (!range[0] && !range[1]) + return true; + else if (!range[0]) + return this['<='](range[1], value, rowData, config); + else if (!range[1]) + return this['>='](range[0], value, rowData, config); + + let smaller = this['<='](range[1], value, rowData, config); + let bigger = this['>='](range[0], value, rowData, config); + return smaller && bigger; + } + + // fallback + return this.like(headerValue, value.toFormat(outputFormat), rowData, config); +} + +export { + dateinput +}; +export default { + dateinput +}; diff --git a/public/js/tabulator/filters/extendedHeaderFilter.js b/public/js/tabulator/filters/extendedHeaderFilter.js index 35b66dc1c..8626ad4f9 100644 --- a/public/js/tabulator/filters/extendedHeaderFilter.js +++ b/public/js/tabulator/filters/extendedHeaderFilter.js @@ -1,163 +1,617 @@ -function parseFilterExpression(expression) -{ - const collections = []; +let activeTagDropdownCleanup = null; +const TAG_FILTER_CONNECTORS = { + AND: { + key: "AND", + label: "AND", + operator: "&&", + isVisible: false, + }, + OR: { + key: "OR", + label: "OR", + operator: "||", + isVisible: true, + }, + NOT: { + key: "NOT", + label: "NOT", + operator: "!", + isVisible: true, + }, +}; +const TAG_FILTER_CONNECTOR_ORDER = [ + //TAG_FILTER_CONNECTORS.AND, + TAG_FILTER_CONNECTORS.OR, + TAG_FILTER_CONNECTORS.NOT, +]; - try { - const orParts = expression.split('||').map(part => part.trim()); - - orParts.forEach(part => { - - const andParts = part.split('&&').map(p => p.trim()); - - const collection = { positives: [], negatives: [] }; - - andParts.forEach(term => { - - const comparisonMatch = term.match(/^(<=|>=|<|>|=|!=)\s*(-?\d+(?:[.,]\d+)?)$/); - - if (comparisonMatch) - { - const operator = comparisonMatch[1]; - const numberStr = comparisonMatch[2].replace(',', '.'); - const number = parseFloat(numberStr); - collection.positives.push({ type: 'comparison', operator, number }); - } - else if (term.startsWith('!')) - { - const excludeTerm = term.substring(1).trim().replace(/\*/g, '.*'); - collection.negatives.push({ type: 'regex', regex: new RegExp(excludeTerm, 'i') }); - } - else - { - const includeTerm = term.replace(/\*/g, '.*'); - collection.positives.push({ type: 'regex', regex: new RegExp(includeTerm, 'i') }); - } - }); - collections.push(collection); - }); - } catch (e) {} - return collections; +function getConnectorLabel(connector, labels) { + return labels?.connectors?.[connector.key] || connector.label; } -export function extendedHeaderFilter(headerValue, rowValue, rowData, filterParams) -{ - const fields = Array.isArray(filterParams?.field) - ? filterParams.field - : [filterParams?.field]; +export function setTagHeaderFilterValue(value, tabulator, columnField = "tags") { + tabulator?.setHeaderFilterValue(columnField, value); - if (fields.length > 1 && rowData) - { - rowValue = fields - .map(f => rowData[f] ?? '') - .filter(Boolean) - .join(' '); - } - if (typeof headerValue === 'boolean') - { - return rowValue === headerValue; - } + const input = tabulator + ?.getColumn(columnField) + ?.getElement() + ?.querySelector(".tabulator-header-filter input"); - const collections = parseFilterExpression(headerValue); - - function matchValue(value) - { - try { - return collections.some(collection => { - - let positives = collection.positives.length === 0 || collection.positives.every(condition => { - - if (condition.type === 'comparison') - { - let value = parseFloat(rowValue); - if (isNaN(value)) return false; - - switch (condition.operator) { - case '<': - return value < condition.number; - case '>': - return value > condition.number; - case '<=': - return value <= condition.number; - case '>=': - return value >= condition.number; - case '=': - return value === condition.number; - case '!=': - return value !== condition.number; - default: - return false; - } - } - else if (condition.type === 'regex') - { - return condition.regex.test(rowValue); - } - return false; - }); - - let negatives = collection.negatives.every(condition => { - return !condition.regex.test(rowValue); - }); - - return positives && negatives; - }); - } catch (e) { - - } - } - - if (matchValue(rowValue)) - return true; - - if (rowData && filterParams) - { - const childrenField = filterParams?.children || '_children'; - const field = filterParams?.field; - - const children = rowData[childrenField]; - if (Array.isArray(children)) - { - for (let child of children) - { - let childValue = child[field]; - if (extendedHeaderFilter(headerValue, childValue, child, filterParams)) - { - return true; - } - } - } - } - - return false; + if (input) { + input.value = value ?? ""; + } } -export function tagHeaderFilter(headerValue, rowValue, rowData, filterParams) -{ - let data; +export function buildTagHeaderFilterExpression(selectedTagOptions) { + let expression = null; - try { - data = typeof rowValue === 'string' ? JSON.parse(rowValue) : rowValue; - } catch (error) { - return false; - } + selectedTagOptions.forEach(({ value, connector }) => { + const connectorDef = TAG_FILTER_CONNECTORS[connector]; + if (!connectorDef) return; - let combinedText; + if (connectorDef.key === TAG_FILTER_CONNECTORS.NOT.key) { + expression = expression + ? `${expression} ${TAG_FILTER_CONNECTORS.AND.operator} ${connectorDef.operator}${value}` + : `${connectorDef.operator}${value}`; + } else { + expression = expression + ? `${expression} ${connectorDef.operator} ${value}` + : value; + } + }); - if (Array.isArray(data)) - { - combinedText = data - .filter(item => item?.done !== true) - .map(item => `${item?.beschreibung} ${item?.notiz}`) - .join(' '); - } - else if (typeof data === 'object' && data !== null) - { - combinedText = data?.erledigt === false ? `${data?.beschreibung} ${data?.notiz}` : ''; - } - else - { - combinedText = String(data); - } + return expression; +} - return extendedHeaderFilter(headerValue, combinedText, rowData, filterParams) -} \ No newline at end of file +export function buildTagOptionsFromRows(rows, field = "tags") { + let parsedTags = [ + ...new Map( + getRawTagValuesFromRows(rows, field) + .filter((tag) => tag && tag.done !== true) + .sort((a, b) => { + return a.prioritaet - b.prioritaet; + }) + .map((tag) => { + return { + label: tag.beschreibung || tag.typ_kurzbz, + value: tag.beschreibung || tag.typ_kurzbz, + style: tag.style, + automatisiert: tag.automatisiert, + done: tag.done, + }; + }) + .map((obj) => [obj.value, obj]), + ).values(), + ]; + if (!Array.isArray(parsedTags)) return; + + return parsedTags; +} + +export function syncTagHeaderFilterOptions( + rows = [], + initialOptions = [], + selectedOptions = [], + field = "tags" +) { + const options = buildTagOptionsFromRows(rows, field) || []; + + if (Array.isArray(initialOptions)) { + initialOptions.splice(0, initialOptions.length, ...options); + } + + if (Array.isArray(selectedOptions)) { + const availableValues = new Set(options.map((option) => option.value)); + + for (let i = selectedOptions.length - 1; i >= 0; i--) { + if (!availableValues.has(selectedOptions[i].value)) { + selectedOptions.splice(i, 1); + } + } + } + + return options; +} + +export function syncSelectedTagOptionsWithHeaderFilters( + filters = [], + selectedOptions = [], + tagsEnabled = true, + field = "tags", +) { + if ( + !tagsEnabled || + !Array.isArray(selectedOptions) || + !selectedOptions.length + ) { + return false; + } + + const hasActiveTagFilter = filters.some( + (filter) => + filter.field === field && + filter.value !== "" && + filter.value !== null && + filter.value !== undefined, + ); + + if (hasActiveTagFilter) { + return false; + } + + selectedOptions.splice(0, selectedOptions.length); + + return true; +} + +export function getRawTagValuesFromRows(rows = [], field = "tags") { + let parsedTags = []; + + rows.forEach((row) => { + if (!row || (!row[field]?.length && !row._children?.length)) return; + + let newTags = row[field]?.length + ? [row[field]] + : getRawTagValuesFromRows(row._children, field); + + newTags = newTags + .map((newTag) => { + if (typeof newTag === "string") { + try { + newTag = JSON.parse(newTag); + } catch { + return null; + } + } + + return newTag; + }) + .filter((newTag) => newTag !== null); + + parsedTags = parsedTags.concat(newTags); + }); + + return parsedTags.flat(); +} + +export function customTagFilter(cell, onRendered, success, cancel, params) { + const container = document.createElement("div"); + container.style.width = "100%"; + + const input = document.createElement("input"); + input.style.width = "100%"; + input.type = "text"; + let dropdownMenu = null; + let suppressScrollCloseUntil = 0; + + Object.defineProperty(container, "value", { + get: () => input.value, + set: (value) => { + input.value = value ?? ""; + }, + }); + + const positionDropdown = () => { + if (!dropdownMenu) return; + + const rect = input.getBoundingClientRect(); + const width = Math.min(Math.max(rect.width, 360), window.innerWidth - 16); + const left = Math.min( + Math.max(rect.left, 8), + window.innerWidth - width - 8, + ); + + dropdownMenu.style.position = "fixed"; + dropdownMenu.style.left = `${left}px`; + dropdownMenu.style.top = `${rect.bottom + 2}px`; + dropdownMenu.style.width = `${width}px`; + dropdownMenu.style.maxHeight = "50vh"; + dropdownMenu.style.overflowY = "auto"; + dropdownMenu.style.zIndex = "2000"; + }; + + const closeDropdown = () => { + if (dropdownMenu) { + dropdownMenu.remove(); + dropdownMenu = null; + } + + if (activeTagDropdownCleanup === closeDropdown) { + activeTagDropdownCleanup = null; + } + + window.removeEventListener("scroll", handleScroll, true); + window.removeEventListener("resize", positionDropdown); + document.removeEventListener("mousedown", handleOutsideClick, true); + }; + + const suppressScrollClose = () => { + suppressScrollCloseUntil = Date.now() + 300; + }; + + const handleScroll = (event) => { + const target = event.target; + const isDropdownScroll = + target instanceof Node && dropdownMenu?.contains(target); + + if (isDropdownScroll || Date.now() < suppressScrollCloseUntil) { + return; + } + + closeDropdown(); + }; + + const handleOutsideClick = (event) => { + if ( + dropdownMenu?.contains(event.target) || + container.contains(event.target) + ) { + return; + } + + closeDropdown(); + }; + + const clearFilter = ({ closeDropdownAfterClear = true } = {}) => { + input.value = ""; + + if (Array.isArray(params.selectedOptions)) { + params.selectedOptions.splice(0, params.selectedOptions.length); + } + + success(""); + + if (closeDropdownAfterClear) { + closeDropdown(); + } + }; + + const openDropdown = () => { + if (activeTagDropdownCleanup) { + activeTagDropdownCleanup(); + } + + dropdownMenu = getRebuiltDropdown( + params.initialOptions, + params.selectedOptions, + params.labels, + clearFilter, + ); + dropdownMenu.addEventListener("mousedown", (event) => { + event.stopPropagation(); + }); + dropdownMenu.addEventListener("click", suppressScrollClose, true); + + document.body.appendChild(dropdownMenu); + activeTagDropdownCleanup = closeDropdown; + + positionDropdown(); + + window.addEventListener("scroll", handleScroll, true); + window.addEventListener("resize", positionDropdown); + document.addEventListener("mousedown", handleOutsideClick, true); + }; + + const stopHeaderFilterGesture = (event) => { + event.stopPropagation(); + }; + + input.addEventListener("pointerdown", stopHeaderFilterGesture); + input.addEventListener("mousedown", stopHeaderFilterGesture); + input.addEventListener("touchstart", stopHeaderFilterGesture); + input.addEventListener("click", stopHeaderFilterGesture); + input.addEventListener("input", () => { + success(input.value); + }); + input.addEventListener("change", () => { + success(input.value); + }); + input.addEventListener("keydown", (event) => { + if (event.key !== "Escape") return; + + event.preventDefault(); + event.stopPropagation(); + + clearFilter({ closeDropdownAfterClear: false }); + + openDropdown(); + }); + input.addEventListener("focus", (e) => { + e.stopPropagation(); + openDropdown(); + }); + + container.appendChild(input); + + return container; +} + +function getRebuiltDropdown( + initialOptions, + selectedOptions, + labels, + clearFilter, +) { + const dropdownTable = generateDropdownTable( + initialOptions || [], + selectedOptions || [], + labels, + ); + + const menu = document.createElement("div"); + menu.className = "dropdown-menu show py-3 px-1"; + + const clearButton = document.createElement("button"); + clearButton.type = "button"; + clearButton.className = + "btn btn-link btn-sm p-0 position-absolute top-0 end-0 m-1"; + clearButton.title = labels?.clear || "Clear"; + clearButton.setAttribute("aria-label", labels?.clear || "Clear"); + + const clearIcon = document.createElement("i"); + clearIcon.className = "fa-solid fa-filter-circle-xmark"; + clearButton.appendChild(clearIcon); + + clearButton.addEventListener("click", (event) => { + event.preventDefault(); + event.stopPropagation(); + clearFilter({ closeDropdownAfterClear: false }); + menu.querySelectorAll("input[type='checkbox']").forEach((checkbox) => { + checkbox.checked = false; + }); + }); + + menu.appendChild(clearButton); + menu.appendChild(dropdownTable); + + return menu; +} + +function generateDropdownTable(options, selectedTagOptions, labels) { + const table = document.createElement("table"); + table.className = "table table-sm mb-0"; + + // HEADER + const thead = document.createElement("thead"); + const headRow = document.createElement("tr"); + + const headers = [ + labels?.tag || "Tag", + ...TAG_FILTER_CONNECTOR_ORDER.map((connector) => + getConnectorLabel(connector, labels), + ), + ]; + + headers.forEach((h, index) => { + const th = document.createElement("th"); + if (index > 0) { + th.className = "text-center"; + } + + const strong = document.createElement("strong"); + strong.textContent = h; + th.appendChild(strong); + headRow.appendChild(th); + }); + + thead.appendChild(headRow); + table.appendChild(thead); + + // BODY + const tbody = document.createElement("tbody"); + options.forEach((option) => { + const row = document.createElement("tr"); + + // LABEL + const labelTd = document.createElement("td"); + const label = document.createElement("span"); + label.className = "tag " + option.style; + label.innerText = option.label; + + if (option.automatisiert) { + label.innerHTML = " " + option.label; + label.className += " tag_auto"; + } + + labelTd.appendChild(label); + row.appendChild(labelTd); + + function makeCheckbox(connector) { + const td = document.createElement("td"); + const cb = document.createElement("input"); + + td.className = "text-center align-middle"; + + cb.type = "checkbox"; + cb.style.cursor = "pointer"; + + cb.checked = selectedTagOptions.some( + (t) => t.value === option.value && t.connector === connector.key, + ); + + cb.addEventListener("click", (event) => { + event.stopPropagation(); + + if (event.target.checked) { + selectedTagOptions.push({ + label: option.label, + value: option.value, + connector: connector.key, + }); + } else { + const idx = selectedTagOptions.findIndex( + (t) => t.value === option.value && t.connector === connector.key, + ); + + if (idx !== -1) { + selectedTagOptions.splice(idx, 1); + } + } + }); + + td.appendChild(cb); + return td; + } + + TAG_FILTER_CONNECTOR_ORDER.forEach((connector) => { + row.appendChild(makeCheckbox(connector)); + }); + + tbody.appendChild(row); + }); + + table.appendChild(tbody); + + return table; +} + +function parseFilterExpression(expression) { + const collections = []; + + try { + const orParts = expression.split("||").map((part) => part.trim()); + + orParts.forEach((part) => { + const andParts = part.split("&&").map((p) => p.trim()); + + const collection = { positives: [], negatives: [] }; + + andParts.forEach((term) => { + const comparisonMatch = term.match( + /^(<=|>=|<|>|=|!=)\s*(-?\d+(?:[.,]\d+)?)$/, + ); + + if (comparisonMatch) { + const operator = comparisonMatch[1]; + const numberStr = comparisonMatch[2].replace(",", "."); + const number = parseFloat(numberStr); + collection.positives.push({ type: "comparison", operator, number }); + } else if (term.startsWith("!")) { + const excludeTerm = term.substring(1).trim().replace(/\*/g, ".*"); + collection.negatives.push({ + type: "regex", + regex: new RegExp(excludeTerm, "i"), + }); + } else { + const includeTerm = term.replace(/\*/g, ".*"); + collection.positives.push({ + type: "regex", + regex: new RegExp(includeTerm, "i"), + }); + } + }); + collections.push(collection); + }); + } catch (e) {} + return collections; +} + +export function extendedHeaderFilter( + headerValue, + rowValue, + rowData, + filterParams, +) { + const fields = Array.isArray(filterParams?.field) + ? filterParams.field + : [filterParams?.field]; + + if (fields.length > 1 && rowData) { + rowValue = fields + .map((f) => rowData[f] ?? "") + .filter(Boolean) + .join(" "); + } + if (typeof headerValue === "boolean") { + return rowValue === headerValue; + } + + const collections = parseFilterExpression(headerValue); + + function matchValue(value) { + try { + return collections.some((collection) => { + let positives = + collection.positives.length === 0 || + collection.positives.every((condition) => { + if (condition.type === "comparison") { + let value = parseFloat(rowValue); + if (isNaN(value)) return false; + + switch (condition.operator) { + case "<": + return value < condition.number; + case ">": + return value > condition.number; + case "<=": + return value <= condition.number; + case ">=": + return value >= condition.number; + case "=": + return value === condition.number; + case "!=": + return value !== condition.number; + default: + return false; + } + } else if (condition.type === "regex") { + return condition.regex.test(rowValue); + } + return false; + }); + + let negatives = collection.negatives.every((condition) => { + return !condition.regex.test(rowValue); + }); + + return positives && negatives; + }); + } catch (e) {} + } + + if (matchValue(rowValue)) return true; + + if (rowData && filterParams) { + const childrenField = filterParams?.children || "_children"; + const field = filterParams?.field; + + const children = rowData[childrenField]; + if (Array.isArray(children)) { + for (let child of children) { + let childValue = child[field]; + if ( + extendedHeaderFilter(headerValue, childValue, child, filterParams) + ) { + return true; + } + } + } + } + + return false; +} +export function tagHeaderFilter(headerValue, rowValue, rowData, filterParams) { + let data; + + try { + data = typeof rowValue === "string" ? JSON.parse(rowValue) : rowValue; + } catch (error) { + return false; + } + + let combinedText; + + if (Array.isArray(data)) { + combinedText = data + .filter((item) => item?.done !== true) + .map((item) => `${item?.beschreibung} ${item?.notiz}`) + .join(" "); + } else if (typeof data === "object" && data !== null) { + combinedText = + data?.erledigt === false ? `${data?.beschreibung} ${data?.notiz}` : ""; + } else { + combinedText = String(data); + } + + return extendedHeaderFilter(headerValue, combinedText, rowData, filterParams); +} diff --git a/public/js/tabulator/formatter/tags.js b/public/js/tabulator/formatter/tags.js index 0d2f5004c..d78ed2026 100644 --- a/public/js/tabulator/formatter/tags.js +++ b/public/js/tabulator/formatter/tags.js @@ -1,67 +1,128 @@ -export function tagFormatter(cell, tagComponent) -{ - let tags = cell.getValue(); - if (!tags) return; +export function tagFormatter(cell, tagComponent, onRendered, tagKey = "tags") { + const mappedData = tagComponent[tagKey]?.map((tag) => ({ + typ_kurzbz: tag.tag_typ_kurzbz, + automatisiert: tag.automatisiert, + })); - let container = document.createElement('div'); - container.className = "d-flex gap-1"; + let tags = cell.getValue(); + if (!tags) return; - let parsedTags = JSON.parse(tags); - let maxVisibleTags = 2; + let container = document.createElement("div"); + container.className = "d-flex gap-1"; - const rowData = cell.getRow().getData(); - if (rowData._tagExpanded === undefined) { - rowData._tagExpanded = false; - } + let parsedTags = []; + if (typeof tags === "string") { + parsedTags = JSON.parse(tags); + } else if (Array.isArray(tags)) { + parsedTags = tags; + } - const renderTags = () => { - container.innerHTML = ''; - parsedTags = parsedTags.filter(item => item !== null); + let maxVisibleTags = 3; - parsedTags.sort((a, b) => { - let adone = a.done ? 1 : 0; - let bbone = b.done ? 1 : 0; + const rowData = cell.getRow().getData(); + if (rowData._tagExpanded === undefined) { + rowData._tagExpanded = false; + } - if (adone !== bbone) - { - return adone - bbone; - } - return b.id - a.id; - }); - const tagsToShow = rowData._tagExpanded ? parsedTags : parsedTags.slice(0, maxVisibleTags); + const renderTags = () => { + container.innerHTML = ""; + parsedTags = parsedTags.filter((item) => item !== null); - tagsToShow.forEach(tag => { - if (!tag) return; - let tagElement = document.createElement('span'); - tagElement.innerText = tag.beschreibung; - tagElement.title = tag.notiz; - tagElement.className = "tag " + tag.style; - if (tag.done) tagElement.className += " tag_done"; + parsedTags.sort((a, b) => { + let adone = a.done ? 1 : 0; + let bbone = b.done ? 1 : 0; - tagElement.addEventListener('click', (event) => { - event.stopPropagation(); - event.preventDefault(); - tagComponent.editTag(tag.id); - }); + if (adone !== bbone) { + return adone - bbone; + } + return a.prioritaet - b.prioritaet; + }); + const tagsToShow = rowData._tagExpanded + ? parsedTags + : parsedTags.slice(0, maxVisibleTags); - container.appendChild(tagElement); - }); + tagsToShow.forEach((tag) => { + if (!tag) return; + let tagElement = document.createElement("span"); + tagElement.innerText = tag.beschreibung; + tagElement.title = tag.notiz; + tagElement.className = "tag " + tag.style; + if (tag.done) tagElement.className += " tag_done"; - if (parsedTags.length > maxVisibleTags) { - let toggle = document.createElement('button'); - toggle.innerText = (rowData._tagExpanded ? '- ' : '+ ') + (parsedTags.length - maxVisibleTags); - toggle.className = "display_all"; - toggle.title = rowData._tagExpanded ? "Tags ausblenden" : "Tags einblenden"; + const tagDef = mappedData?.find((t) => t.typ_kurzbz === tag.typ_kurzbz); - toggle.addEventListener('click', () => { - rowData._tagExpanded = !rowData._tagExpanded; - renderTags(); - }); + if ( + (!tagDef && tag.typ_kurzbz?.includes("_auto")) || + tagDef?.automatisiert + ) { + tagElement.className += " tag_auto"; + tagElement.innerHTML = + " " + tag.beschreibung; + } - container.appendChild(toggle); - } - }; + tagElement.addEventListener("click", (event) => { + event.stopPropagation(); + event.preventDefault(); + tagComponent.editTag(tag.id); + }); - renderTags(); - return container; -} \ No newline at end of file + container.appendChild(tagElement); + }); + + if (parsedTags.length > maxVisibleTags) { + let toggle = document.createElement("button"); + toggle.innerText = + (rowData._tagExpanded ? "- " : "+ ") + + (parsedTags.length - maxVisibleTags); + toggle.className = "display_all"; + toggle.title = rowData._tagExpanded + ? "Tags ausblenden" + : "Tags einblenden"; + + toggle.addEventListener("click", () => { + rowData._tagExpanded = !rowData._tagExpanded; + renderTags(); + }); + + container.appendChild(toggle); + } + }; + + const fitTags = () => { + if (rowData._tagExpanded) { + renderTags(); + return; + } + + let widthBuffer = 10; + maxVisibleTags = parsedTags.length; + renderTags(); + + while ( + maxVisibleTags > 0 && + container.scrollWidth > (container.clientWidth + widthBuffer) + ) { + maxVisibleTags--; + renderTags(); + } + }; + + let animationFrame = null; + container.fitTags = () => { + if (animationFrame !== null) return; + + animationFrame = requestAnimationFrame(() => { + animationFrame = null; + fitTags(); + }); + }; + + if (onRendered) { + onRendered(() => { + container.fitTags(); + }); + } + + renderTags(); + return container; +} diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index 3c08be752..c57f88984 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -80,7 +80,6 @@ require_once('dbupdate_3.4/61730_Dashboard_Anpassungen.php'); require_once('dbupdate_3.4/40128_search.php'); require_once('dbupdate_3.4/63394_Variablenbeschraenkung.php'); require_once('dbupdate_3.4/63436_cis4_iframe_component.php'); -require_once('dbupdate_3.4/60882_lehrfaecherverteilung_favorites.php'); require_once('dbupdate_3.4/66982_berufsschule.php'); require_once('dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php'); require_once('dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php'); @@ -98,6 +97,8 @@ require_once('dbupdate_3.4/75888_reihungstest_mehrfachdurchfuehrung.php'); require_once('dbupdate_3.4/76150_perm_other_lv_plan.php'); require_once('dbupdate_3.4/68957_dashboard_bookmark_neue_Spalte_sort.php'); require_once('dbupdate_3.4/68530_Dashboard_Cleanup.php'); +require_once('dbupdate_3.4/75959_StudVw_Automatische_Tags.php'); +require_once('dbupdate_3.4/76160_lvv_favorites.php'); require_once('dbupdate_3.4/77080_tabulator_presets_table.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen diff --git a/system/dbupdate_3.4/75959_StudVw_Automatische_Tags.php b/system/dbupdate_3.4/75959_StudVw_Automatische_Tags.php new file mode 100644 index 000000000..1954ae48d --- /dev/null +++ b/system/dbupdate_3.4/75959_StudVw_Automatische_Tags.php @@ -0,0 +1,139 @@ +db_query("SELECT taglib FROM public.tbl_notiz_typ LIMIT 1")) +{ + $qry = 'ALTER TABLE public.tbl_notiz_typ ADD COLUMN taglib character varying(32);'; + + if(!$db->db_query($qry)) + echo ' public.tbl_notiz_typ '.$db->db_last_error().'
'; + else + echo '
public.tbl_notiz_typ: Neue Spalte taglib hinzugefügt'; +} + +//automatic tags anlegen +$tags = [ + [ + 'typ_kurzbz' => 'wh_auto', + 'bezeichnung' => '{WH, WH}', + 'style' => 'tag_dark_grey', + 'prioritaet' => 11, + 'taglib' => 'tags/CoreWiederholerTagLib' + ], + [ + 'typ_kurzbz' => 'dd_auto', + 'bezeichnung' => '{DD, DD}', + 'style' => 'tag_braun', + 'prioritaet' => 10, + 'taglib' => 'tags/CoreDoubleDegreeTagLib' + ], + [ + 'typ_kurzbz' => 'out_auto', + 'bezeichnung' => '{Outgoing, Outgoing}', + 'style' => 'tag_limette', + 'prioritaet' => 12, + 'taglib' => 'tags/CoreOutgoingTagLib' + ], + [ + 'typ_kurzbz' => 'prewh_auto', + 'bezeichnung' => '{Pre-WH, pre-WH}', + 'style' => 'tag_light_grey', + 'prioritaet' => 13, + 'taglib' => 'tags/CorePrewiederholerTagLib' + ], + [ + 'typ_kurzbz' => 'zgv_auto', + 'bezeichnung' => '{ZGV offen, ZGV missing}', + 'style' => 'tag_beige', + 'prioritaet' => 14, + 'taglib' => 'tags/CoreMissingZgvTagLib' + ], + [ + 'typ_kurzbz' => 'unterbrecher_auto', + 'bezeichnung' => '{UB, UB}', + 'style' => 'tag_turquoise', + 'prioritaet' => 15, + 'taglib' => 'tags/CoreUnterbrecherTagLib' + ], + [ + 'typ_kurzbz' => 'stbtr_erh_auto', + 'bezeichnung' => '{erh.Studienbeitrag, Incr. Tuition Fees}', + 'style' => 'tag_bordeaux', + 'prioritaet' => 16, + 'taglib' => 'tags/CoreStbErhoehtTagLib' + ], + [ + 'typ_kurzbz' => 'jgv_auto', + 'bezeichnung' => '{JGV, YGR}', + 'style' => 'tag_olive', + 'prioritaet' => 17, + 'taglib' => 'tags/CoreJgvTagLib' + ], + [ + 'typ_kurzbz' => 'preabbr_auto', + 'bezeichnung' => '{Pre-Abbr, pre-Abbr}', + 'style' => 'tag_blush', + 'prioritaet' => 18, + 'taglib' => 'tags/CorePreabbrecherTagLib' + ], + [ + 'typ_kurzbz' => 'in_auto', + 'bezeichnung' => '{Incoming, Incoming}', + 'style' => 'tag_skyblue', + 'prioritaet' => 19, + 'taglib' => 'tags/CoreIncomingTagLib' + ], + +]; + +foreach ($tags as $tag) { + + $checkQry = " + SELECT 1 + FROM public.tbl_notiz_typ + WHERE typ_kurzbz = '".$tag['typ_kurzbz']."' + "; + + if ($result = $db->db_query($checkQry)) { + + if ($db->db_num_rows($result) == 0) { + + $qry = " + INSERT INTO public.tbl_notiz_typ + ( + typ_kurzbz, + bezeichnung_mehrsprachig, + automatisiert, + aktiv, + tag, + style, + vorrueckung, + prioritaet, + taglib + ) + VALUES + ( + '".$tag['typ_kurzbz']."', + '".$tag['bezeichnung']."', + true, + true, + true, + '".$tag['style']."', + false, + '".$tag['prioritaet']."', + '".$tag['taglib']."' + ) + "; + + if (!$db->db_query($qry)) + { + echo 'public.tbl_notiz_typ: '. $tag['typ_kurzbz']. ' '. $db->db_last_error().'
'; + } + else + { + echo '
public.tbl_notiz_typ: Automatic Tag '.$tag['typ_kurzbz'].' hinzugefuegt'; + } + } + } +} diff --git a/system/dbupdate_3.4/60882_lehrfaecherverteilung_favorites.php b/system/dbupdate_3.4/76160_lvv_favorites.php similarity index 74% rename from system/dbupdate_3.4/60882_lehrfaecherverteilung_favorites.php rename to system/dbupdate_3.4/76160_lvv_favorites.php index 55fb55b80..6d6dfb382 100644 --- a/system/dbupdate_3.4/60882_lehrfaecherverteilung_favorites.php +++ b/system/dbupdate_3.4/76160_lvv_favorites.php @@ -2,15 +2,15 @@ if (!defined('DB_NAME')) exit('No direct script access allowed'); // Add new name type in public.tbl_variablenname -if ($result = @$db->db_query("SELECT 1 FROM public.tbl_variablenname WHERE name = 'lv_favorites';")) +if ($result = @$db->db_query("SELECT 1 FROM public.tbl_variablenname WHERE name = 'lvv_favorites';")) { if ($db->db_num_rows($result) == 0) { - $qry = "INSERT INTO public.tbl_variablenname(name, defaultwert) VALUES('lv_favorites', null);"; + $qry = "INSERT INTO public.tbl_variablenname(name, defaultwert) VALUES('lvv_favorites', null);"; if (!$db->db_query($qry)) echo 'public.tbl_variablenname '.$db->db_last_error().'
'; else - echo 'public.tbl_variablenname: Added name "lv_favorites"
'; + echo 'public.tbl_variablenname: Added name "lvv_favorites"
'; } } diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 197f7d3ed..8630fe31a 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -1036,7 +1036,7 @@ $phrases = array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Bewerber*In möglicherweise vorhanden', + 'text' => 'BewerberIn möglicherweise vorhanden', 'description' => '', 'insertvon' => 'system' ), @@ -3791,7 +3791,7 @@ $phrases = array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Lektor*In', + 'text' => 'LektorIn', 'description' => '', 'insertvon' => 'system' ), @@ -4420,7 +4420,7 @@ $phrases = array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Bewerber*In', + 'text' => 'BewerberIn', 'description' => '', 'insertvon' => 'system' ), @@ -5320,7 +5320,7 @@ $phrases = array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Bewerber*In parken', + 'text' => 'BewerberIn parken', 'description' => '', 'insertvon' => 'system' ), @@ -5340,7 +5340,7 @@ $phrases = array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Bewerber*In ausparken', + 'text' => 'BewerberIn ausparken', 'description' => '', 'insertvon' => 'system' ), @@ -5420,7 +5420,7 @@ $phrases = array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Bewerber*In geparkt bis', + 'text' => 'BewerberIn geparkt bis', 'description' => '', 'insertvon' => 'system' ), @@ -5480,7 +5480,7 @@ $phrases = array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Bewerber*In zurückstellen', + 'text' => 'BewerberIn zurückstellen', 'description' => '', 'insertvon' => 'system' ), @@ -5520,7 +5520,7 @@ $phrases = array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Bewerber*In zurückgestellt bis', + 'text' => 'BewerberIn zurückgestellt bis', 'description' => '', 'insertvon' => 'system' ), @@ -5600,8 +5600,8 @@ $phrases = array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Geparkte und zurückgestellte Bewerber*Innen werden von der Bearbeitung temporär ausgenommen. -Geparkte Bewerber*Innen werden zum angegebenen Datum automatisch entparkt, während zurückgestellte Bewerber*Innen nur manuell durch Drücken des Buttons den Zurückgestellt-Status verlieren. + 'text' => 'Geparkte und zurückgestellte BewerberInnen werden von der Bearbeitung temporär ausgenommen. +Geparkte BewerberInnen werden zum angegebenen Datum automatisch entparkt, während zurückgestellte BewerberInnen nur manuell durch Drücken des Buttons den Zurückgestellt-Status verlieren. Bei einer Zurückstellung dient das Datum nur der Erinnerung.', 'description' => '', 'insertvon' => 'system' @@ -8831,7 +8831,7 @@ The invoice will be sent to you again. The amount is only to be trans 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'ANNEHMEN
(Lektor*In)', + 'text' => 'ANNEHMEN
(LektorIn)', 'description' => '', 'insertvon' => 'system' ), @@ -13702,7 +13702,7 @@ Any unusual occurrences 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Student*In', + 'text' => 'StudentIn', 'description' => '', 'insertvon' => 'system' ), @@ -15878,7 +15878,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => "Alle anzeigen, die auf Empfehlung von Lektor*In warten.", + 'text' => "Alle anzeigen, die auf Empfehlung von LektorIn warten.", 'description' => '', 'insertvon' => 'system' ), @@ -25185,7 +25185,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Student*In ({prestudent_id})', + 'text' => 'StudentIn ({prestudent_id})', 'description' => '', 'insertvon' => 'system' ), @@ -28228,7 +28228,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Mitarbeiter*In', + 'text' => 'MitarbeiterIn', 'description' => '', 'insertvon' => 'system' ), @@ -29231,7 +29231,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Mitarbeiter*In', + 'text' => 'MitarbeiterIn', 'description' => '', 'insertvon' => 'system' ), @@ -29271,7 +29271,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Mitarbeiter*In Information', + 'text' => 'Mitarbeiter Information', 'description' => '', 'insertvon' => 'system' ), @@ -29291,7 +29291,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Student*In', + 'text' => 'StudentIn', 'description' => '', 'insertvon' => 'system' ), @@ -29311,7 +29311,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Student*In Information', + 'text' => 'Student Information', 'description' => '', 'insertvon' => 'system' ), @@ -29763,6 +29763,88 @@ array( ) ), //Profil Phrasen ende + //Studium Phrasen start + array( + 'app' => 'core', + 'category' => 'studium', + 'phrase' => 'studium', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studium', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Study', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studium', + 'phrase' => 'studienordnung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studienordnung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Study regulation', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studium', + 'phrase' => 'lektoren', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lektoren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Lecturers', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studium', + 'phrase' => 'studiensemester', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studiensemester', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Study semester', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + // Studium Phrasen Ende // LvPlan Phrasen start array( 'app' => 'core', @@ -34604,7 +34686,7 @@ array( 'sprache' => 'German', 'text' => 'Sie können eine Entschuldigung für einen Zeitraum von bis zu {0} Tagen in die Vergangenheit und bis zum Ende des aktuellen Semesters hochladen. Die erlaubten Dateitypen für das Dokument sind .pdf, .png und .jpg. - Sobald Sie eine Entschuldigung hochladen wird die zugehörige Studiengangsassistenz informiert und Ihr Anliegen überprüft. Solange Ihre Entschuldigung noch keinen akzeptierten oder abgelehnten Status erhalten hat, steht es Ihnen frei diese inklusive Datei zu löschen. Sobald sie entweder akzeptiert oder abgelehnt wurde können Sie den Eintrag nicht mehr löschen. + Sobald Sie eine Entschuldigung hochladen wird die zugehörige Studiengangsassistenz informiert und Ihr Anliegen überprüft. Solange Ihre Entschuldigung noch keinen akzeptierten oder abgelehnten Status erhalten hat, steht es Ihnen frei diese inklusive Datei zu löschen. Sobald sie entweder akzeptiert oder abgelehnt wurde können Sie den Eintrag nichtmehr löschen. Bei einer akzeptierten Entschuldigung werden sämtliche digitalen Anwesenheiten in diesem Zeitraum als positiv gewertet. Eine abgelehnte Entschuldigung hat keine Auswirkungen auf ihre Anwesenheitsquote.', 'description' => '', @@ -37912,7 +37994,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'In welches Semester sollen diese {count} Prestudent*Innen ({status})verschoben werden?', + 'text' => 'In welches Semester sollen diese {count} PrestudentInnen ({status})verschoben werden?', 'description' => '', 'insertvon' => 'system' ), @@ -39988,6 +40070,447 @@ array( ) ) ), + //**************************** CORE/treemenu + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_interessenten', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Interessenten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Candidates', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_bewerbungnichtabgeschickt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bewerbung nicht abgeschickt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Application not submitted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_bewerbungabgeschickt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bewerbung abgeschickt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Application submitted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_zgv', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'ZGV erfüllt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'ZGV fulfilled', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_statusbestaetigt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Status bestätigt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Status confirmed', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_statusbestaetigtrtnichtangemeldet', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Reihungstest nicht angemeldet', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Ranking test not registered', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_statusbestaetigtrtangemeldet', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Reihungstest angemeldet', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Ranking test registered', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_reihungstestnichtangemeldet', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Reihungstest nicht angemeldet', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Ranking test not registered', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_reihungstestangemeldet', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Reihungstest angemeldet', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Ranking test registered', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_bewerber', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bewerber', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Applicants', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_bewerberrtnichtangemeldet', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Reihungstest nicht angemeldet', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Ranking test not registered', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_bewerberrtangemeldet', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Reihungstest angemeldet', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Ranking test registered', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_bewerberrtangemeldetteilgenommen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Teilgenommen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Participated', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_bewerberrtangemeldetnichtteilgenommen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nicht teilgenommen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Not participated', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_aufgenommen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Aufgenommen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Accepted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_warteliste', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Warteliste', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Waiting list', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_absage', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Absage', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Rejected', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'filter_incoming', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Incoming', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Incoming', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'inout_inout', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'International', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'International', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'inout_incoming', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Incoming', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Incoming', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'inout_outgoing', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Outgoing', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Outgoing', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'treemenu', + 'phrase' => 'inout_shared_studies', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Gemeinsame Studien', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Shared studies', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), //**************************** CORE/stv array( 'app' => 'core', @@ -40157,7 +40680,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Interessent*In', + 'text' => 'InteressentIn', 'description' => '', 'insertvon' => 'system' ), @@ -40337,7 +40860,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'PreStudent*In', + 'text' => 'PreStudentIn', 'description' => '', 'insertvon' => 'system' ), @@ -41301,7 +41824,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Lektor*In', + 'text' => 'LektorIn', 'description' => '', 'insertvon' => 'system' ), @@ -41441,7 +41964,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Student*In hat keinen Status in diesem Semester', + 'text' => 'StudentIn hat keinen Status in diesem Semester', 'description' => '', 'insertvon' => 'system' ), @@ -42201,6 +42724,206 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'neuerTerminBachelorMasterbetreuung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Neuer Termin Bachelor-/Masterarbeitsbetreuung", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "New date for Bachelor/Master thesis supervision", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'neueTerminserie', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Terminserie anlegen", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Create recurring deadline", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4Sprache', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Sprache", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Language", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4fehlerAktualitaetProjektarbeit', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Projektarbeit ist nichtmehr aktuell", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Project work is not current anymore", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'serienTerminEmailSentInfo', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Email über neuen Abgabetermin versendet an: {0}", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Email about new deadline sent to: {0}", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'serienTerminGespeichert', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Serientermin wurde erfolgreich eingetragen!", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Series dates were successfully entered!", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'errorSerienterminSpeichern', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Fehler beim speichern des Serientermins!", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Error saving Series dates!", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4abgabeMitarbeiterbereich', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Dokumentabgabe - MitarbeiterInbereich", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "File submission - Employee area", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4all', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Alle", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'All', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'abgabe', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Abgabe", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Submission', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'ui', @@ -44215,7 +44938,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => "Betreuer*In", + 'text' => "BetreuerIn", 'description' => '', 'insertvon' => 'system' ), @@ -44327,6 +45050,26 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4abgabeStudentenbereich', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Dokumentabgabe - Studierendenbereich", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "File submission - Student area", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'abgabetool', @@ -45355,7 +46098,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => "Projektarbeit Detailansicht Mitarbeiter*In", + 'text' => "Projektarbeit Detailansicht MitarbeiterIn", 'description' => '', 'insertvon' => 'system' ), @@ -45787,6 +46530,30 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4studentAbgabeNotAllowedRegular', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Verspätete Projektabgabe ist bei Terminen, welche von der Studiengangsassistenz für den gesamten Studiengang fixiert wurden nicht erlaubt! + + Um einen Endupload durchführen zu können, müssen Sie ein positiv benotetes Quality Gate 1 & Quality Gate 2 in der relevanten Projektarbeit absolviert haben.", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Late project submissions are not permitted for deadlines set by the program assistant for the entire program! + + To be able to complete a final upload, you must have successfully completed Quality Gate 1 and Quality Gate 2 for the relevant project work.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'abgabetool', @@ -45955,7 +46722,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => "E-Mail Betreuer*In", + 'text' => "E-Mail BetreuerIn", 'description' => '', 'insertvon' => 'system' ), @@ -46015,7 +46782,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => "Erstbetreuer*In Beurteilung herunterladen", + 'text' => "ErstbetreuerIn Beurteilung herunterladen", 'description' => '', 'insertvon' => 'system' ), @@ -46035,7 +46802,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => "Zweitbetreuer*In Beurteilung herunterladen", + 'text' => "ZweitbetreuerIn Beurteilung herunterladen", 'description' => '', 'insertvon' => 'system' ), @@ -46215,7 +46982,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => "Fehler beim Laden der Projektarbeit - Student*In Zuordnung", + 'text' => "Fehler beim Laden der Projektarbeit - StudentIn Zuordnung", 'description' => '', 'insertvon' => 'system' ), @@ -46235,7 +47002,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => "Kein zugeteilter Student*In gefunden für Projektarbeit", + 'text' => "Kein zugeteilter StudentIn gefunden für Projektarbeit", 'description' => '', 'insertvon' => 'system' ), @@ -46510,7 +47277,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Erstbetreuer*In', + 'text' => 'ErstbetreuerIn', 'description' => '', 'insertvon' => 'system' ), @@ -46530,7 +47297,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Zweitbetreuer*In', + 'text' => 'ZweitbetreuerIn', 'description' => '', 'insertvon' => 'system' ), @@ -47235,7 +48002,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Betreuer*In Berufspraktikum oder Projektarbeit', + 'text' => 'BetreuerIn Berufspraktikum oder Projektarbeit', 'description' => '', 'insertvon' => 'system' ), @@ -47516,7 +48283,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Erstbetreuer*In Titel Pre', + 'text' => 'ErstbetreuerIn Titel Pre', 'description' => '', 'insertvon' => 'system' ), @@ -47536,7 +48303,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Erstbetreuer*In Vorname', + 'text' => 'ErstbetreuerIn Vorname', 'description' => '', 'insertvon' => 'system' ), @@ -47556,7 +48323,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Erstbetreuer*In Nachname', + 'text' => 'ErstbetreuerIn Nachname', 'description' => '', 'insertvon' => 'system' ), @@ -47576,7 +48343,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Erstbetreuer*In Titel Post', + 'text' => 'ErstbetreuerIn Titel Post', 'description' => '', 'insertvon' => 'system' ), @@ -47596,7 +48363,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Zweitbetreuer*In Titel Pre', + 'text' => 'ZweitbetreuerIn Titel Pre', 'description' => '', 'insertvon' => 'system' ), @@ -47616,7 +48383,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Zweitbetreuer*In Vorname', + 'text' => 'ZweitbetreuerIn Vorname', 'description' => '', 'insertvon' => 'system' ), @@ -47636,7 +48403,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Zweitbetreuer*In Nachname', + 'text' => 'ZweitbetreuerIn Nachname', 'description' => '', 'insertvon' => 'system' ), @@ -47656,7 +48423,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Zweitbetreuer*In Titel Post', + 'text' => 'ZweitbetreuerIn Titel Post', 'description' => '', 'insertvon' => 'system' ), @@ -47676,21 +48443,13 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Keine Zuordnung oder Berechtigung für die Projektarbeit gefunden! - - Der eingeloggte User "{0}" konnte nicht als StudentIn "{1}" identifiziert werden, hat keine Betreuungszuordnung und keine Administrationsrechte. - - Falls Sie diese Fehlermeldung als StudentIn erhalten loggen Sie sich bitte neu ein, indem Sie den Browser schließen und erneut die CIS Seite aufrufen!', + 'text' => 'Keine Zuordnung oder Berechtigung für die Projektarbeit gefunden!', 'description' => '', 'insertvon' => 'system' ), array( 'sprache' => 'English', - 'text' => 'No assignment or authorization found for the project! - - The logged-in user "{0}" could not be identified as student "{1}", has no reviewer assignment and no administrative rights. - - If you receive this error message as a student, please log in again by closing your browser and reopening the CIS page!', + 'text' => 'No assignment or authorization found for the project!', 'description' => '', 'insertvon' => 'system' ) @@ -48731,7 +49490,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Prüfer*In 1', + 'text' => 'PrüferIn 1', 'description' => '', 'insertvon' => 'system' ), @@ -48751,7 +49510,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Prüfer*In 2', + 'text' => 'PrüferIn 2', 'description' => '', 'insertvon' => 'system' ), @@ -48771,7 +49530,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Prüfer*In 3', + 'text' => 'PrüferIn 3', 'description' => '', 'insertvon' => 'system' ), @@ -49045,7 +49804,7 @@ array( ), array( 'app' => 'core', - 'category' => 'stv', + 'category' => 'ui', 'phrase' => 'weitereEMail', 'insertvon' => 'system', 'phrases' => array( @@ -49875,7 +50634,7 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Prestudent*In ID', + 'text' => 'PrestudentIn ID', 'description' => '', 'insertvon' => 'system' ), @@ -50791,6 +51550,126 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'berichte', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Berichte', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Reports', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'lvplanung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'LV-Planung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Course Planning', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'lehrauftragsliste', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehrauftragsliste', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Teaching Assignments', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'lehrauftraege', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehraufträge', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Lectureships', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'lehrauftragslisteemp', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehrauftrag Lehrende', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Teaching Assignment Lecturers', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'extrakvverwaltung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehrveranstaltung Verwaltung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Course Management', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // feature-55614 end array( 'app' => 'softwarebereitstellung', @@ -52581,7 +53460,7 @@ and represent the current state of research on the topic. The prescribed citatio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Lektor*In hinzufügen', + 'text' => 'LektorIn hinzufügen', 'description' => '', 'insertvon' => 'system' ), @@ -52963,7 +53842,7 @@ and represent the current state of research on the topic. The prescribed citatio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Sender*In', + 'text' => 'SenderIn', 'description' => '', 'insertvon' => 'system' ), @@ -52983,7 +53862,7 @@ and represent the current state of research on the topic. The prescribed citatio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Empfänger*In', + 'text' => 'EmpfängerIn', 'description' => '', 'insertvon' => 'system' ), @@ -53023,7 +53902,7 @@ and represent the current state of research on the topic. The prescribed citatio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Empfänger*In ID', + 'text' => 'EmpfängerIn ID', 'description' => '', 'insertvon' => 'system' ), @@ -55060,7 +55939,7 @@ and represent the current state of research on the topic. The prescribed citatio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Bitte Student*In auswählen!', + 'text' => 'Bitte StudentIn auswählen!', 'description' => '', 'insertvon' => 'system' ), @@ -55194,6 +56073,46 @@ and represent the current state of research on the topic. The prescribed citatio ) ) ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'mailText_profilfoto', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Hallo,\n\nIhr Profilbild wurde entfernt, da es nicht den aktuellen Bildrichtlinen entspricht. Bitte laden Sie unter CIS->Profil ein neues Profilbild hoch!\n\nDanke!", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Hello,\n\nYour profile picture has been removed because it does not comply with the current image guidelines. Please upload a new profile picture under CIS->Profile!\n\nThank you!", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'person', + 'phrase' => 'betreffProfilfoto', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Profilbild', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Profile picture', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // FHC-4 Finetuning END //**************************** CORE/search @@ -55525,7 +56444,7 @@ and represent the current state of research on the topic. The prescribed citatio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Mitarbeiter*In', + 'text' => 'MitarbeiterIn', 'description' => '', 'insertvon' => 'system' ), @@ -56961,7 +57880,7 @@ I have been informed that I am under no obligation to consent to the transmissio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Betreuer*In', + 'text' => 'BetreuerIn', 'description' => '', 'insertvon' => 'system' ), @@ -56981,7 +57900,7 @@ I have been informed that I am under no obligation to consent to the transmissio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Betreuer*In', + 'text' => 'BetreuerIn', 'description' => '', 'insertvon' => 'system' ), @@ -57201,7 +58120,7 @@ I have been informed that I am under no obligation to consent to the transmissio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Löschen nicht möglich, dieser Projektarbeit sind bereits Betreuer*Innen zugewiesen', + 'text' => 'Löschen nicht möglich, dieser Projektarbeit sind bereits BetreuerInnen zugewiesen', 'description' => '', 'insertvon' => 'system' ), @@ -57241,7 +58160,7 @@ I have been informed that I am under no obligation to consent to the transmissio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Projektbetreuer*In ungültig', + 'text' => 'ProjektbetreuerIn ungültig', 'description' => '', 'insertvon' => 'system' ), @@ -57261,7 +58180,7 @@ I have been informed that I am under no obligation to consent to the transmissio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Löschen nicht möglich, Projektbetreuer*In hat bereits einen Vertrag', + 'text' => 'Löschen nicht möglich, ProjektbetreuerIn hat bereits einen Vertrag', 'description' => '', 'insertvon' => 'system' ), @@ -57701,7 +58620,7 @@ I have been informed that I am under no obligation to consent to the transmissio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Betreuer*In bearbeiten', + 'text' => 'BetreuerIn bearbeiten', 'description' => '', 'insertvon' => 'system' ), @@ -57733,6 +58652,26 @@ I have been informed that I am under no obligation to consent to the transmissio ) ) ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'betreuerSpeichern', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'BetreuerIn speichern', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Save reviewer', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'projektarbeit', @@ -58858,7 +59797,7 @@ I have been informed that I am under no obligation to consent to the transmissio ), array( 'sprache' => 'English', - 'text' => 'Saved on {date} by {name}', + 'text' => 'Saved on {date} von {name}', 'description' => '', 'insertvon' => 'system' ) @@ -59578,7 +60517,7 @@ I have been informed that I am under no obligation to consent to the transmissio 'phrases' => array( array( 'sprache' => 'German', - 'text' => 'Student*In ist in diesem Semester keinem Lehrverband zugeteilt', + 'text' => 'StudentIn ist in diesem Semester keinem Lehrverband zugeteilt', 'description' => '', 'insertvon' => 'system' ), @@ -59590,6 +60529,86 @@ I have been informed that I am under no obligation to consent to the transmissio ) ) ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'lvplanung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'LV-Planung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Course-Planning', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'lvplanung_xls', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'LV-Planung EXCEL', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Course-Planning EXCEL', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'lvplanung_html', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'LV-Planung HTML', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Course-Planning HTML', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'RTVerwaltung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Reihungstestverwaltung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Administration Placement Test', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // ### Phrases Dashboard Admin START array( @@ -60360,6 +61379,348 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), // generic tabulator phrases END + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'text_exportXLS', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => '{count} Prestudent(Innen) als Excel exportieren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Export {count} prestudent(s) to Excel', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'alert_chooseStudent', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bitte eine/n Studierende/n markieren!', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Please select a student!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'studienverlauf', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studienverlauf', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Gradelist', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'textNoStatusInSem', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Kein Status im {sem}', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No status in {sem}', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'alert_chooseOnlyOneStudent', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bitte nur eine/n Studierende/n markieren!', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Please select only one student!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'alert_chooseGroupSem', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bitte eine Gruppe auswählen!', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Please select a group!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'errorSavingData', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Speichern der Daten nicht erfolgreich', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' =>'Data saving failed', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'private', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Privat', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'private', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'andCondition', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'und', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'and', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'orCondition', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'oder', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'or', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'notCondition', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'nicht', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'not', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + //Start Finetuning StudVW + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'fenster_schliessen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Sie können nun das Fenster schließen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'This window can be closed now', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tag', + 'phrase' => 'rebuild_tags', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Neuberechnen Tags', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Rebuild Tags', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'selection', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Auswahl', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'selection', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tag', + 'phrase' => 'messageModalWait', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bitte warten Sie, bis alle Tags neu berechnet wurden!', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Please wait until all tags have been recalculated!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tag', + 'phrase' => 'alertSuccessRebuild', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Tags erfolgreich neuberechnet ({count} Prestudent*innnen)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Tags successfully recalculated ({count} prestudents)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'tag', + 'phrase' => 'alertErrorRebuild', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Fehler bei Neuberechnen Tags, PrestudentId(s): ', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Error during Recalculating tags, PrestudentId(s): ', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + //End Finetuning StudVW // tabulator presets section phrases START array( 'app' => 'core', @@ -60762,6 +62123,148 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), // tabulator presets section phrases END + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'openInMailClient', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'In Mailanwendung öffnen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Open in Mailclient', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'kompatibilityMode_WebmailClient', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Kompatibilitätsmodus für Webmail', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Open in Compatibility mode (Webmail)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'webmailclient', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Webmail', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'webmail', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + // StudVw legacy Bis Links begin + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'bis_stud', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'BIS (Studierende)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'BIS (Students)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'bis_stud_meldung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Meldung generieren (Studierende)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Generate Report (Students)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'bis_stud_plausicheck', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Plausibilitätsprüfung (Studierende)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Plausibility Check (Students)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'alert_chooseDegreeProgram', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bitte einen Studiengang auswählen!', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Please select a degree program!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + // StudVw legacy Bis Links ende ); diff --git a/tests/cypress/.gitignore b/tests/cypress/.gitignore new file mode 100644 index 000000000..25e6d917c --- /dev/null +++ b/tests/cypress/.gitignore @@ -0,0 +1,7 @@ +node_modules +.env +.env.* +!.env.example +/cypress/videos +/cypress/screenshots +/results \ No newline at end of file