App urls for studierendenverwaltung (neu: /studvw)

This commit is contained in:
chfhtw
2026-07-03 12:16:30 +02:00
parent 0b70153da0
commit c59b24ab4a
6 changed files with 161 additions and 328 deletions
+11
View File
@@ -72,6 +72,17 @@ $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/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';
+44
View File
@@ -0,0 +1,44 @@
<?php
/**
* Copyright (C) 2026 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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);
}
}
+24 -160
View File
@@ -24,167 +24,26 @@ 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',
children: [
{ name: 'search', path: 'search/:query' },
{ name: 'searchtypes', path: 'search/:types/:query' },
{ name: 'prestudent', path: 'prestudent/:prestudent_id' },
{ name: 'student', path: 'student/:student_uid' },
{ name: 'person', path: 'person/:person_id' },
{ name: 'treemenu', path: ':treemenu(.*)*' }
]
}
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 +56,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;
});
+5 -6
View File
@@ -23,7 +23,7 @@ export default {
required: true,
},
preselectedKey: {
type: String,
type: Array,
default: null
}
},
@@ -156,18 +156,17 @@ export default {
},
async setPreselection()
{
if (!this.preselectedKey)
if (!this.preselectedKey || !this.preselectedKey.length)
{
this.selectedKey = null;
return;
}
let rawKey = this.preselectedKey
if (!rawKey || typeof rawKey !== 'string')
if (!this.preselectedKey || !Array.isArray(this.preselectedKey))
return;
const parts = this.preselectedKey.split('/');
// [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);
+76 -161
View File
@@ -53,12 +53,7 @@ export default {
cisRoot: String,
avatarUrl: String,
logoutUrl: String,
activeAddons: String, // semicolon separated list of active addons
url_studiensemester_kurzbz: String,
url_mode: String,
url_prestudent_id: String,
url_tab: String,
url_studiengang: String
activeAddons: String // semicolon separated list of active addons
},
provide() {
return {
@@ -141,7 +136,6 @@ export default {
mergeResults: 'person'
},
studiengangKz: undefined,
studiengangKuerzel: '',
studiensemesterKurzbz: this.defaultSemester,
selected_semester: undefined,
selected_orgform: undefined,
@@ -193,34 +187,52 @@ export default {
}
return extraItems;
},
listEndpoint() {
if (['index', 'stdsem'].includes(this.$route.name))
return null;
if (this.$route.params.query) {
return ApiStv.students.search({
searchstr: this.$route.params.query,
types: this.$route.params.types?.split('+') || []
}, this.$route.params.stdsem);
}
let url = 'stdsem/' + this.$route.params.stdsem + '/';
if (this.$route.params.treemenu) {
url += this.$route.params.treemenu.join('/');
return ApiTreemenu.data('stv', url);
}
const paramName = Object.keys(this.$route.params).find(k => k != 'stdsem');
if (paramName) {
url += this.$route.name + '/' + this.$route.params[paramName];
return ApiTreemenu.data('stv', url);
}
return null;
},
studiengangKuerzel() {
if (!this.lists?.stgs)
return '';
const stg = this.lists.stgs
.find(stg => stg.studiengang_kz == this.studiengangKz);
if (stg)
return (stg.typ + stg.kurzbz).toUpperCase();
return '';
}
},
watch: {
'url_studiensemester_kurzbz': function (newVal, oldVal) {
if (newVal !== oldVal) {
this.studiensemesterKurzbz = newVal;
if(this.$route.name === 'search')
{
this.handleSearchUrl();
}
else
{
this.$refs.stvList.updateUrl();
this.$refs.details.reload();
}
listEndpoint(n, o) {
if (n != o) {
this.$refs.stvList.updateUrl(n || []);
}
},
'url_studiengang': function (newVal, oldVal) {
if (newVal !== oldVal) {
this.checkUrlStudiengang();
}
},
'url_mode': function () {
this.handlePersonUrl();
},
url_prestudent_id() {
this.handlePersonUrl();
},
'appconfig.font_size'() {
// add to html class
const classList = Object.keys(this.$refs.config.setup.font_size.options);
@@ -273,161 +285,66 @@ export default {
);
},
onSelectVerband({ link, studiengang_kz, semester, orgform_kurzbz }) {
let urlpath = String(link);
/*if (!urlpath.match(/\/prestudent/))
{
urlpath = 'CURRENT_SEMESTER' + '/' + urlpath;
}*/
urlpath = 'stdsem/' + this.studiensemesterKurzbz + '/' + urlpath;
this.$refs.stvList.updateUrl(ApiTreemenu.data('stv', urlpath));
this.studiengangKz = studiengang_kz;
this.selected_semester = semester;
this.selected_orgform = orgform_kurzbz;
const stg = this.lists.stgs.find((element) => {
return (element.studiengang_kz === this.studiengangKz);
});
if (stg)
{
this.studiengangKuerzel = (stg.typ + stg.kurzbz).toUpperCase()
this.$router.push({
name: 'studiengang',
params: {
studiensemester_kurzbz: this.studiensemesterKurzbz,
studiengang: this.studiengangKuerzel
}
});
} else
{
this.studiengangKuerzel = '';
this.$router.push({
name: 'studiensemester',
params: {
studiensemester_kurzbz: this.studiensemesterKurzbz
}
});
}
},
studiensemesterChanged(v) {
this.studiensemesterKurzbz = v;
this.$router.push({
name: 'treemenu',
params: {
studiensemester_kurzbz: v
treemenu: link.split('/')
}
});
},
studiensemesterChanged(stdsem) {
if (!stdsem) {
// no valid studiensemester in url
this.$router.replace({
params: {
stdsem: this.defaultSemester.toLowerCase()
}
});
return;
}
if (stdsem.toLowerCase() != this.studiensemesterKurzbz?.toLowerCase()) {
this.$router.push({
params: {
stdsem: stdsem.toLowerCase()
}
});
}
this.studiensemesterKurzbz = stdsem;
},
reloadList() {
this.$refs.stvList.reload();
},
searchfunction(params, config) {
return this.$api.call(ApiSearchbar.searchStv(params), config);
},
handlePersonUrl() {
if (this.$route.params.id) {
this.$refs.stvList.updateUrl(
ApiStv.students.uid(this.$route.params.id, 'CURRENT_SEMESTER'),
true
);
} else if (this.$route.params.prestudent_id) {
this.$refs.stvList.updateUrl(
ApiStv.students.prestudent(this.$route.params.prestudent_id, 'CURRENT_SEMESTER'),
true
);
} else if (this.$route.params.person_id) {
this.$refs.stvList.updateUrl(
ApiStv.students.person(this.$route.params.person_id, 'CURRENT_SEMESTER'),
true
);
} else if (this.$route.params.searchstr) {
this.handleSearchUrl();
}
else
{
this.clearTabulator();
}
},
handleSearchUrl() {
const searchsettings = {
searchstr: this.$route.params.searchstr,
types: this.$route.params.types?.split('+') || []
};
// init into student list
this.$refs.stvList.updateUrl(
ApiStv.students.search(searchsettings, this.studiensemesterKurzbz)
);
// init into searchbar
this.$refs.searchbar.searchsettings.searchstr = searchsettings.searchstr;
this.$refs.searchbar.searchsettings.types = searchsettings.types;
this.$nextTick(this.blurSearchbar);
},
clearTabulator() {
if(['index', 'studiensemester'].includes(this.$route.name))
{
if(this.$refs?.stvList?.$refs?.table?.tabulator)
{
this.$refs.stvList.$refs.table.tabulator.setData([]);
}
}
},
checkUrlStudiengang() {
if (this.url_studiengang) {
const stg = this.lists.stgs.find((element) => {
const kuerzel = (element.typ + element.kurzbz).toUpperCase();
return (this.url_studiengang === kuerzel);
});
if (stg) {
this.studiengangKz = stg.studiengang_kz;
this.studiengangKuerzel = (stg.typ + stg.kurzbz).toUpperCase();
} else {
this.$router.replace({
name: 'studiensemester',
params: {
studiensemester_kurzbz: this.studiensemesterKurzbz
}
});
}
}
else
{
this.studiengangKz = undefined;
this.studiengangKuerzel = '';
this.clearTabulator();
}
},
onSearch(e) {
this.deleteCustomFilter();
const searchsettings = { ...this.$refs.searchbar.searchsettings };
if (searchsettings.searchstr.length >= 2) {
this.blurSearchbar();
this.deleteCustomFilter();
if (!searchsettings.types.length || searchsettings.types.length == this.$refs.searchbar.types.length) {
this.$router.push({
name: 'search',
params: {
studiensemester_kurzbz: this.studiensemesterKurzbz,
searchstr: searchsettings.searchstr
query: searchsettings.searchstr
}
});
} else {
this.$router.push({
name: 'search_w_types',
name: 'searchtypes',
params: {
studiensemester_kurzbz: this.studiensemesterKurzbz,
searchstr: searchsettings.searchstr,
query: searchsettings.searchstr,
types: searchsettings.types.join('+')
}
});
}
}
},
blurSearchbar() {
this.$refs.searchbar.$refs.input.blur();
this.$refs.searchbar.abort();
this.$refs.searchbar.hideresult();
},
handleCustomFilter(filter){
this.filter = filter;
},
@@ -436,15 +353,13 @@ export default {
}
},
created() {
if (!this.url_studiensemester_kurzbz) {
if (!this.$route.params.stdsem) {
this.$router.replace({
name: 'studiensemester',
name: 'stdsem',
params: {
studiensemester_kurzbz: this.defaultSemester
stdsem: this.defaultSemester.toLowerCase()
}
});
} else {
this.studiensemesterKurzbz = this.url_studiensemester_kurzbz;
}
this.$api
@@ -480,7 +395,6 @@ export default {
.then(result => {
this.lists.stgs = result.data;
this.lists.active_stgs = this.lists.stgs.filter(stg => stg.aktiv);
this.checkUrlStudiengang();
})
.catch(this.$fhcAlert.handleSystemError);
@@ -507,7 +421,8 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
},
mounted() {
this.handlePersonUrl();
if (this.listEndpoint)
this.$refs.stvList.updateUrl(this.listEndpoint);
},
template: /* html */`
<div class="stv" :class="{ 'sidebar-collapsed': sidebarCollapsed }">
@@ -638,13 +553,13 @@ export default {
<div class="offcanvas-header justify-content-end px-1 d-md-none">
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
</div>
<stv-verband :preselectedKey="studiengangKz ? '' + studiengangKz : null" :endpoint="verbandEndpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
<stv-studiensemester v-model:studiensemester-kurzbz="studiensemesterKurzbz" @update:studiensemester-kurzbz="studiensemesterChanged"></stv-studiensemester>
<stv-verband :preselected-key="$route.params.treemenu" :endpoint="verbandEndpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
<stv-studiensemester :studiensemester-kurzbz="$route.params.stdsem || defaultSemester" @update:studiensemester-kurzbz="studiensemesterChanged"></stv-studiensemester>
</nav>
<main class="col-md-8 ms-sm-auto col-lg-9 col-xl-10">
<vertical-split>
<template #top>
<stv-list ref="stvList" v-model:selected="selected" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz" @filterActive="handleCustomFilter"></stv-list>
<stv-list ref="stvList" v-model:selected="selected" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz" @filter-active="handleCustomFilter"></stv-list>
</template>
<template #bottom>
<stv-details ref="details" :students="selected" @reload="reloadList"></stv-details>
@@ -28,7 +28,7 @@ export default {
],
props: {
preselectedKey: {
type: String,
type: Array,
default: null
}
},