Compare commits

...

16 Commits

Author SHA1 Message Date
Johann Hoffmann 7fe5a685f1 first attempt at semantically tagging DOM elements for automated testing in stv. cypress is able to navigate from verband tree -> student list -> student details -> tabs and assert various states. WIP writing the test itself more robust and developing more testcases; 2026-03-26 17:45:02 +01:00
Johann Hoffmann b05cd88f14 WIP defining sensible cy-data attributes and robust tests selecting on them 2026-03-24 14:34:43 +01:00
Harald Bamberger 9e6c15a10d Merge branch 'bug-76010/StudVW_Archivieren_Ausbildungsvertrag_Aufgenommene' 2026-03-19 15:41:44 +01:00
ma0068 7603f8f12b Bugfix: use null instead of empty string, provide kuerzel 2026-03-18 14:32:09 +01:00
Andreas Österreicher 96745525f1 Merge branch 'feature-71530/Error_beim_Archivieren_von_Diplomasupplement_STUDVW_Neu' 2026-03-16 11:28:56 +01:00
Andreas Österreicher 8d6e04ea77 Merge branch 'feature-61164/AbgabetoolQualityGates' 2026-03-16 11:09:52 +01:00
Andreas Österreicher d9e5acb52c Merge branch 'master' into feature-61164/AbgabetoolQualityGates 2026-03-16 11:08:44 +01:00
Andreas Österreicher e20ff52f5b Merge branch 'feature-75417/pep_finetuning' 2026-03-16 09:08:10 +01:00
Andreas Österreicher df05af98d2 FHB - Markierung von Outgoing ohne Endedatum 2026-03-16 09:01:33 +01:00
Harald Bamberger 0764a597af do not allow foto editing in contract management 2026-03-12 15:18:26 +01:00
Harald Bamberger 8c36fe585a filter component add missing this 2026-03-12 15:09:46 +01:00
Harald Bamberger d4b81da437 fix filter component also consider tabulator 6 selectableRows option to determine if tabulator has selector 2026-03-12 14:34:42 +01:00
Harald Bamberger b91efb6189 stv notizperson fix permission check to be able to add notes to prestudents that do not have an uid 2026-03-12 13:56:03 +01:00
ma0048 f3986688f2 selfoverview:
nur aktive kategorien anzeigen
uebersetzungen hinzugefuegt
2026-02-26 12:50:37 +01:00
Alexei Karpenko f068b56083 PlausiIssueProducer: removed debugging output and die 2026-02-18 22:30:29 +01:00
Alexei Karpenko fa7a125727 Studierendenverwaltung archivieren: error message when no studentlehrverband found 2026-02-18 21:18:57 +01:00
12 changed files with 260 additions and 50 deletions
@@ -208,7 +208,6 @@ class Documents extends FHCAPI_Controller
$this->load->model('system/Vorlage_model', 'VorlageModel');
$result = $this->VorlageModel->load($xsl);
$this->addMeta("ress", $result);
$vorlage = current($this->getDataOrTerminateWithError($result));
if (!$vorlage)
show_404();
@@ -221,7 +220,7 @@ class Documents extends FHCAPI_Controller
'gedruckt' => true,
'insertamum' => date('c'),
'insertvon' => getAuthUID(),
'uid' => $this->input->post_get('uid') ?: '',
'uid' => $this->input->post_get('uid') ?: null,
'archiv' => true,
'signiert' => !!$sign_user,
'stud_selfservice' => $vorlage->stud_selfservice
@@ -251,6 +250,9 @@ class Documents extends FHCAPI_Controller
'studiensemester_kurzbz' => $ss,
'student_uid' => $akteData['uid']
]);
if (!hasData($result)) $this->terminateWithError($this->p->t("stv", "error_noLehrverbandAssigned"));
$res = current($this->getDataOrTerminateWithError($result));
$studiengang_kz = $res->studiengang_kz;
@@ -332,6 +334,7 @@ class Documents extends FHCAPI_Controller
if ($prestudent_id) {
$this->load->model('crm/prestudent_model', 'PrestudentModel');
$this->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz', 'LEFT');
$this->PrestudentModel->addSelect('tbl_prestudent.*, UPPER(typ || kurzbz) AS kuerzel');
$result = $this->PrestudentModel->load($prestudent_id);
$prestudent = current($this->getDataOrTerminateWithError($result));
@@ -24,6 +24,7 @@ class NotizPerson extends Notiz_Controller
//Load Models
$this->load->model('person/Benutzer_model', 'BenutzerModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
//Permission checks for allowed Oes
if ($this->router->method == 'addNewNotiz')
@@ -38,7 +39,7 @@ class NotizPerson extends Notiz_Controller
{
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Person ID']), self::ERROR_TYPE_GENERAL);
}
$this->_checkIfBerechtigungForOneUidExists($person_id, $allowedStgs);
$this->_checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs);
}
if ( $this->router->method == 'updateNotiz')
@@ -59,7 +60,7 @@ class NotizPerson extends Notiz_Controller
$person_id = current($data)->person_id;
$allowedStgs = $this->permissionlib->getSTG_isEntitledFor('assistenz') ?: [];
$this->_checkIfBerechtigungForOneUidExists($person_id, $allowedStgs);
$this->_checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs);
}
if ($this->router->method == 'deleteNotiz' )
@@ -78,7 +79,7 @@ class NotizPerson extends Notiz_Controller
}
$allowedStgs = $this->permissionlib->getSTG_isEntitledFor('assistenz') ?: [];
$this->_checkIfBerechtigungForOneUidExists($person_id, $allowedStgs);
$this->_checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs);
}
}
@@ -99,44 +100,20 @@ class NotizPerson extends Notiz_Controller
}
//stv: if person has permission of one studiengang of person -> permission to add/update/delete Note
private function _checkIfBerechtigungForOneUidExists($person_id, $allowedStgs)
private function _checkIfBerechtigungForOnePrestudentExists($person_id, $allowedStgs)
{
//get all studentUids of person_id
$result = $this->BenutzerModel->loadWhere(['person_id' => $person_id]);
$result = $this->PrestudentModel->loadWhere(['person_id' => $person_id]);
$data = $this->getDataOrTerminateWithError($result);
$checkarray = [];
foreach ($data as $item)
{
//check if isStudent
$result = $this->StudentModel->isStudent($item->uid);
$isStudent = $this->getDataOrTerminateWithError($result);
if($isStudent)
if(in_array($item->studiengang_kz, $allowedStgs))
{
$checkarray[] = $this->_checkAllowedStgsFromUid($item->uid, $allowedStgs);
return true;
}
}
if (!in_array(1, $checkarray))
return $this->terminateWithError($this->p->t('ui', 'error_keineBerechtigungStg'), self::ERROR_TYPE_GENERAL);
}
private function _checkAllowedStgsFromUid($student_uid, $allowedStgs)
{
$this->load->model('crm/Student_model', 'StudentModel');
$result = $this->StudentModel->loadWhere(['student_uid' => $student_uid]);
$data = $this->getDataOrTerminateWithError($result);
$studiengang_kz = current($data)->studiengang_kz;
if (!in_array($studiengang_kz, $allowedStgs))
{
return 0;
}
else
{
return 1;
}
$this->terminateWithError($this->p->t('ui', 'error_keineBerechtigungStg'), self::ERROR_TYPE_GENERAL);
}
}
+2
View File
@@ -270,6 +270,8 @@ class LehreListHelper
} else if ($row->bisio_id != '' && $row->status != 'Incoming' && ($row->von > $stsemdatumvon || $row->von == '')) {
// if bis datum is not yet known but von is available already
$zusatz .= '(o)(ab '.$datum->formatDatum($row->von, 'd.m.Y').')';
} else if ($row->bisio_id != '' && $row->status != 'Incoming' && ($row->von <= $stsemdatumvon || $row->von == '') && ($row->bis == '' || $row->bis > date('Y-m-d'))){
$zusatz .= '(o)(ab '.$datum->formatDatum($row->von, 'd.m.Y').')';
}
@@ -508,7 +508,7 @@ export default {
this.handlePersonUrl();
},
template: /* html */`
<div class="stv" :class="{ 'sidebar-collapsed': sidebarCollapsed }">
<div class="stv" :class="{ 'sidebar-collapsed': sidebarCollapsed }" data-cy="studentenverwaltung">
<header class="navbar navbar-expand-lg navbar-dark bg-dark flex-md-nowrap p-0 shadow">
<div class="col-md-4 col-lg-3 col-xl-2 d-flex align-items-center">
<button
@@ -632,14 +632,14 @@ export default {
</app-menu>
</div>
</aside>
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100">
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100" data-cy="stv-sidebar">
<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>
</nav>
<main class="col-md-8 ms-sm-auto col-lg-9 col-xl-10">
<main class="col-md-8 ms-sm-auto col-lg-9 col-xl-10" data-cy="stv-main">
<vertical-split>
<template #top>
<stv-list ref="stvList" v-model:selected="selected" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz" @filterActive="handleCustomFilter"></stv-list>
@@ -86,7 +86,7 @@ export default {
this.loadConfig();
},
template: `
<div class="stv-details h-100 d-flex flex-column">
<div class="stv-details h-100 d-flex flex-column" data-cy="stv-details">
<div v-if="!students?.length" class="justify-content-center d-flex h-100 align-items-center">
{{$p.t('ui', 'chooseStudent')}}
</div>
@@ -108,6 +108,7 @@ export default {
<fhc-tabs
v-if="students.length == 1"
ref="tabs"
data-cy="stv-details-tabs"
:useprimevue="true"
:modelValue="students[0]"
:config="config"
@@ -116,7 +117,17 @@ export default {
@changed="reload"
>
</fhc-tabs>
<fhc-tabs v-else ref="tabs" :useprimevue="true" :modelValue="students" :config="config" :default="$route.params.tab" style="flex: 1 1 0%; height: 0%" @changed="reload"></fhc-tabs>
<fhc-tabs
v-else
ref="tabs"
data-cy="stv-details-tabs"
:useprimevue="true"
:modelValue="students"
:config="config"
:default="$route.params.tab"
style="flex: 1 1 0%; height: 0%"
@changed="reload">
</fhc-tabs>
</div>
<div v-else>
Loading...
@@ -13,7 +13,7 @@ export default {
}
},
template: `
<div class="stv-details-multistatus h-100">
<div class="stv-details-multistatus h-100" data-cy="stv-tab-multistatus">
<tbl-multi-status :model-value="modelValue" :config="config"></tbl-multi-status>
</div>
`
@@ -152,6 +152,7 @@ export default{
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-forward"></i>';
button.title = this.$p.t('ui', 'btn_statusVorruecken');
button.setAttribute('data-cy', 'status-btn-advance');
button.addEventListener('click', () =>
this.actionAdvanceStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
);
@@ -164,6 +165,7 @@ export default{
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-check"></i>';
button.title = this.$p.t('ui', 'btn_confirmStatus');
button.setAttribute('data-cy', 'status-btn-confirm');
button.addEventListener('click', () =>
this.actionConfirmStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
);
@@ -175,6 +177,7 @@ export default{
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-edit"></i>';
button.title = this.$p.t('ui', 'btn_editStatus');
button.setAttribute('data-cy', 'status-btn-edit');
button.addEventListener('click', () =>
this.actionEditStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
);
@@ -186,6 +189,7 @@ export default{
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-xmark"></i>';
button.title = this.$p.t('ui', 'btn_deleteStatus');
button.setAttribute('data-cy', 'status-btn-delete');
button.addEventListener('click', () =>
this.actionDeleteStatus(data.status_kurzbz, data.studiensemester_kurzbz, data.ausbildungssemester)
);
@@ -204,6 +208,11 @@ export default{
{
row.getElement().classList.add('text-black','text-opacity-50','fst-italic');
}
// data-cy: unique per status entry for Cypress assertions
const stdsem = rowData.studiensemester_kurzbz ?? 'unknown';
const status = rowData.status_kurzbz ?? 'unknown';
const sem = rowData.ausbildungssemester ?? '0';
row.getElement().setAttribute('data-cy', `status-row-${stdsem}-${status}-${sem}`);
},
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
@@ -393,7 +402,7 @@ export default{
.catch(this.$fhcAlert.handleSystemError);
},
template: `
<div class="stv-multistatus h-100 pt-3">
<div class="stv-multistatus h-100 pt-3" data-cy="stv-multistatus">
<status-modal
ref="test"
:meldestichtag="new Date(dataMeldestichtag)"
@@ -405,6 +414,7 @@ export default{
<core-filter-cmpt
v-if="!this.modelValue.length"
ref="table"
data-cy="stv-multistatus-table"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
table-only
@@ -142,6 +142,10 @@ export default {
row.getElement().classList.add('text-black','text-opacity-50','fst-italic');
}
row.getElement().draggable = true
// data-cy for Cypress e2e: prefer prestudent_id, fall back to uid or person_id
const d = row.getData();
const id = d.prestudent_id ?? d.uid ?? d.person_id ?? 'unknown';
row.getElement().setAttribute('data-cy', 'student-row-' + id);
},
ajaxRequestFunc: (url, config, params) => {
@@ -600,11 +604,12 @@ export default {
// TODO(chris): focusin, focusout, keydown and tabindex should be in the filter component
// TODO(chris): filter component column chooser has no accessibilty features
template: `
<div class="stv-list h-100 pt-3">
<div class="stv-list h-100 pt-3" data-cy="stv-list">
<div
class="tabulator-container d-flex flex-column h-100"
:class="{'has-filter': filter.length}"
tabindex="0"
data-cy="stv-list-table-container"
@focusin="onFocus"
@keydown="onKeydown"
v-draggable:copyLink.capture="selectedDragObject"
@@ -61,6 +61,15 @@ export default {
},
noSemReloadNodes() {
return this.nodes.reduce(this.mapNodesToNoSemReloadNodes, []);
},
colPt() {
return {
rowToggler: ( options ) => {
return {
'data-cy': `tree-toggle-${options?.parent?.props?.node?.key ?? 'unknown'}`
}
}
}
}
},
watch: {
@@ -319,7 +328,7 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
},
template: /* html */`
<div class="overflow-auto" tabindex="-1">
<div class="overflow-auto" tabindex="-1" data-cy="stv-verband-tree">
<pv-treetable
ref="tree"
class="stv-verband p-treetable-sm"
@@ -339,6 +348,7 @@ export default {
field="name"
expander
class="text-break"
:pt="colPt"
>
<template #header>
<div class="text-right">
@@ -349,6 +359,7 @@ export default {
v-model="filters['global']"
class="form-control ps-5"
placeholder="Search"
data-cy="verband-search"
>
</div>
</div>
@@ -358,6 +369,7 @@ export default {
v-if="['semester', 'verband', 'gruppe', 'gruppe_kurzbz'].some(key => node.data.hasOwnProperty(key))"
:data-tree-item-key="node.key"
:title="node.data.studiengang_kz"
:data-cy="'student-collection-'+node.key"
v-drag-click="() => toggleTreeNode(node)"
v-drop:link-strict.student-collection="(evt, students) => dropStudents(node, students)"
>
@@ -367,6 +379,7 @@ export default {
v-else
:data-tree-item-key="node.key"
:title="node.data.studiengang_kz"
:data-cy="'stg-node-label-'+node.key"
v-drag-click="() => toggleTreeNode(node)"
>
{{ node.data.name }}
@@ -383,6 +396,7 @@ export default {
v-if="favorites.on || favorites.list.length"
href="#"
@click.prevent="filterFav"
data-cy="favorite-icon-header"
>
<i
:class="favorites.on ? 'fa-solid' : 'fa-regular'"
@@ -398,6 +412,7 @@ export default {
data-link-fav-add
@click.prevent="markFav(node)"
@keydown.enter.stop.prevent="markFav(node)"
:data-cy="'favorite-icon-'+node.data.studiengang_kz"
>
<i
:class="favorites.list.includes(node.data.link + '') ? 'fa-solid' : 'fa-regular'"
@@ -71,7 +71,6 @@ export default {
:mitarbeiter_uid="this.mitarbeiter_uid"
typeHeader="mitarbeiter"
:domain="config.domain"
fotoEditable
@redirectToLeitung="handleSelection"
>
<template #uid>{{tile_MaUid}}</template>
+2 -2
View File
@@ -280,7 +280,7 @@ export const CoreFilterCmpt = {
});
}
if (tabulatorOptions.selectable || (tabulatorOptions.columns && tabulatorOptions.columns.filter(el => el.formatter == 'rowSelection').length))
if (tabulatorOptions.selectable || tabulatorOptions.selectableRows || (tabulatorOptions.columns && tabulatorOptions.columns.filter(el => el.formatter == 'rowSelection').length))
this.tabulatorHasSelector = true;
if (this.idField) {
@@ -358,7 +358,7 @@ export const CoreFilterCmpt = {
}
},
_updateTabulator() {
this.tabulatorHasSelector = this.tabulatorOptions.selectable || this.filteredColumns.filter(el => el.formatter == 'rowSelection').length;
this.tabulatorHasSelector = this.tabulatorOptions.selectable || this.tabulatorOptions.selectableRows || this.filteredColumns.filter(el => el.formatter == 'rowSelection').length;
this.tabulator.setColumns(this.filteredColumns);
this.tabulator.setData(this.filteredData);
this._setHeaderFilter()
+192 -4
View File
@@ -3903,6 +3903,172 @@ $phrases = array(
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'geplZeitraum',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'geplanter Zeitraum',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'planned Period',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'bitteAuswaehlen',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Bitte auswählen...',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Please select...',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'hinweisLehrende',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Hinweis für Lehrende',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Note for Lecturers',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'lehreinheiten',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Lehreinheiten',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Teaching Units',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'lead',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Leitung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Lead',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'teamlead',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Team / Leitung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Team / Lead',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'ausblick_lvplanung',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ausblick auf Ihre mögliche LV-Planung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Preview of Your Potential Course Planning',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
'phrase' => 'detailselfoverview',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => '<b>Achtung:</b> die vorliegenden Informationen stellen eine Vorabplanung dar und sind als Anfrage an Sie gedacht. <br /><br />
Die Beauftragung der tatsächlichen Lehrveranstaltungen erfolgt durch Ihre Kompetenzfeldleitung. <br /><br />
Ihre aktuell gültigen Lehraufträge und den LV Plan des aktuellen Semesters (Termine) finden Sie wie gewohnt unter „mein CIS“ -> „LV-Plan Hauptmenü“ bzw. „Lehrauftragsverwaltung“.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => '<b>Please note:</b> The information provided represents a preliminary planning and is intended as an inquiry to you.<br /><br />
The official assignment of the actual courses will be carried out by your Competence Field Manager.<br /><br />
Your currently valid teaching assignments and the course schedule for the current semester (dates) can be found as usual under “My CIS” “Schedule Main Menu” or “Teaching Assignment Administration”',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'pep',
'category' => 'ui',
@@ -44726,9 +44892,9 @@ array(
'phrases' => array(
array(
'sprache' => 'German',
'text' => "Für den gesamten Studiengang verbindlicher Termin.
Liegt ein Termin in der Vergangenheit, kann nichts mehr hochgeladen werden. Ist es dennoch erforderlich,
'text' => "Für den gesamten Studiengang verbindlicher Termin.
Liegt ein Termin in der Vergangenheit, kann nichts mehr hochgeladen werden. Ist es dennoch erforderlich,
haben Studierende bei der Studiengangsassistenz um eine Korrektur dieses Termins anzusuchen.",
'description' => '',
'insertvon' => 'system'
@@ -44919,7 +45085,7 @@ 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'
@@ -57008,6 +57174,28 @@ I have been informed that I am under no obligation to consent to the transmissio
)
),
// ### Refactor Messages END
//
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'error_noLehrverbandAssigned',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'StudentIn ist in diesem Semester keinem Lehrverband zugeteilt',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Student has no assignment to any teaching association',
'description' => '',
'insertvon' => 'system'
)
)
),
);