diff --git a/application/config/treemenu.php b/application/config/treemenu.php index d7e704222..72a02947b 100644 --- a/application/config/treemenu.php +++ b/application/config/treemenu.php @@ -23,3 +23,7 @@ $config['stv'] = [ "library" => "treemenu/StudVwLib", "permissions" => ["admin:r", "assistenz:r"] ]; +$config['lvv'] = [ + "library" => "treemenu/LvVwLib", + "permissions" => ["admin:r", "assistenz:r"] +]; diff --git a/application/controllers/api/frontend/v1/Lehrveranstaltung.php b/application/controllers/api/frontend/v1/Lehrveranstaltung.php index 935602391..df4dc9cc5 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,8 +77,10 @@ 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); @@ -260,14 +264,31 @@ 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; } diff --git a/application/libraries/treemenu/LvVwLib.php b/application/libraries/treemenu/LvVwLib.php new file mode 100644 index 000000000..bec66a5fb --- /dev/null +++ b/application/libraries/treemenu/LvVwLib.php @@ -0,0 +1,199 @@ +. + */ + +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->config = [ + 'stg' => [ + 'semester' => [ + 'group', + 'verband' => [ + 'group' + ] + ], + 'orgform' + ] + ]; + + // 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, $stg) + { + $stg = $this->getStgFromUrl($stg); + + if ($stg === null) + return []; + + $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/public/js/apps/LVVerwaltung.js b/public/js/apps/LVVerwaltung.js index 18f8a072f..65aca093f 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(.*)*' }, + { name: 'treemenu', path: ':treemenu(.*)*' } + ] }, { path: '/:pathMatch(.*)*', diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index 42c50bdfa..343fec86f 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -1,7 +1,7 @@ import CoreSearchbar from "../searchbar/searchbar.js"; import VerticalSplit from "../verticalsplit/verticalsplit.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"; @@ -19,7 +19,7 @@ export default { CoreSearchbar, VerticalSplit, AppMenu, - StvVerband, + BaseTreemenu, StvStudiensemester, LvTable, LvTabs, @@ -29,12 +29,7 @@ export default { 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), @@ -50,32 +45,12 @@ export default { 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; - } - }, data() { return { selected: [], studiengang: "", - filter: {}, - selectedStudiensemester: this.studiensemester_kurzbz ?? this.defaultSemester, + //filter: {}, + selectedStudiensemester: this.defaultSemester, endpoint: ApiStudiengangTree, dropdowns: { studiensemester_array: [], @@ -83,7 +58,7 @@ export default { lehrform_array: [], raumtyp_array: [], }, - selectedStudiengang: '', + //selectedStudiengang: '', searchbaroptions: { origin: 'lvverwaltung', cssclass: "position-relative", @@ -107,106 +82,119 @@ export default { }, } }, - 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 + computed: { + filter() { + let filter = { + emp: this.$route.params.emp, + studiensemester_kurzbz: this.selectedStudiensemester } - if (this.stg !== undefined) - { - this.selectedStudiengang = this.semester !== '' && this.semester - ? `${this.stg}/${this.semester}` - : this.stg; + 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]; } - else - { - this.selectedStudiengang = ''; - } - this.filter = filter; - }, + + filter.activeFilter = filter.emp ? 'employee' : filter.stg ? 'verband' : null; + + return filter; + } + }, + methods: { handleRowClicked(data) { this.selected = data }, 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 }) { - 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 = []; }, 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 = []; - }, + } }, 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,7 +227,6 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); }, - template: /* html */`
@@ -285,8 +272,15 @@ export default {
- - + + +
@@ -307,7 +301,7 @@ export default {