Validation

This commit is contained in:
cgfhtw
2023-10-02 16:38:11 +02:00
parent 2a0a0a81f2
commit fa74438f66
6 changed files with 258 additions and 92 deletions
@@ -19,7 +19,8 @@ class Studentenverwaltung extends FHC_Controller
$this->load->view('Studentenverwaltung', [
'permissions' => [
'student/bpk' => $this->permissionlib->isBerechtigt('student/bpk')
'student/bpk' => $this->permissionlib->isBerechtigt('student/bpk'),
'student/alias' => $this->permissionlib->isBerechtigt('student/alias')
]
]);
}
@@ -2,12 +2,19 @@
if (! defined('BASEPATH')) exit('No direct script access allowed');
use \DateTime as DateTime;
class Student extends FHC_Controller
{
public function __construct()
{
// TODO(chris): access!
parent::__construct();
// Load language phrases
$this->loadPhrases([
'ui'
]);
}
public function get($prestudent_id)
@@ -94,6 +101,7 @@ class Student extends FHC_Controller
$this->outputJson(getData($result) ?: []);
}
}
public function save($prestudent_id)
{
// TODO(chris): stdSem from Variable
@@ -104,7 +112,18 @@ class Student extends FHC_Controller
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
$this->load->model('education/Studentlehrverband_model', 'StudentlehrverbandModel');
$data = json_decode(utf8_encode($this->input->raw_input_stream), true);
$this->load->library('form_validation');
$_POST = json_decode(utf8_encode($this->input->raw_input_stream), true);
$this->form_validation->set_rules('gebdatum', 'Geburtsdatum', 'callback_isValidDate', [
'isValidDate' => $this->p->t('ui', 'error_invalid_date')
]);
// TODO(chris): other validations?
if ($this->form_validation->run() == false) {
return $this->outputJsonError($this->form_validation->error_array());
}
$result = $this->StudentModel->loadWhere(['prestudent_id' =>$prestudent_id]);
if (isError($result)) {
@@ -128,30 +147,17 @@ class Student extends FHC_Controller
$person_id = $person->person_id;
}
$array_allowed_props = ['verband','semester','gruppe'];
$update = array();
foreach ($array_allowed_props as $prop)
$array_allowed_props_lehrverband = ['verband', 'semester', 'gruppe'];
$update_lehrverband = array();
foreach ($array_allowed_props_lehrverband as $prop)
{
if(isset($data[$prop])){
$update[$prop] = $data[$prop];
$val = $this->input->post($prop);
if ($val !== null) {
$update_lehrverband[$prop] = $val;
}
}
//TODO(chris): form validation verband
if (count($update))
{
if($uid === null)
{
$this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST);
return $this->outputJson("Kein/e StudentIn vorhanden!");
}
$this->StudentlehrverbandModel->update([
'studiensemester_kurzbz' => $studiensemester_kurzbz,
'student_uid' => $uid],
$update
);
}
$array_allowed_props = [
$array_allowed_props_person = [
'anrede',
'bpk',
'titelpre',
@@ -160,7 +166,7 @@ class Student extends FHC_Controller
'vorname',
'vornamen',
'wahlname',
'geburtsdatum',
'gebdatum',
'gebort',
'geburtsnation',
'svnr',
@@ -174,52 +180,83 @@ class Student extends FHC_Controller
'anmerkung',
'homepage'
];
$update = array();
foreach ($array_allowed_props as $prop)
$update_person = array();
foreach ($array_allowed_props_person as $prop)
{
if(isset($data[$prop])){
$update[$prop] = $data[$prop];
$val = $this->input->post($prop);
if ($val !== null) {
$update_person[$prop] = $val;
}
}
if (count($update))
$array_allowed_props_student = ['matrikelnr'];
$update_student = array();
foreach ($array_allowed_props_student as $prop)
{
if($person_id === null)
{
$this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST);
return $this->outputJson("Keine Person vorhanden!");
$val = $this->input->post($prop);
if ($val !== null) {
$update_student[$prop] = $val;
}
$this->PersonModel->update(
}
if (count($update_lehrverband) + count($update_student) && $uid === null) {
$this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST);
// TODO(chris): phrase
return $this->outputJson("Kein/e StudentIn vorhanden!");
}
if (count($update_person) && $person_id === null) {
$this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST);
// TODO(chris): phrase
return $this->outputJson("Keine Person vorhanden!");
}
if (count($update_lehrverband))
{
$result = $this->StudentlehrverbandModel->update([
'studiensemester_kurzbz' => $studiensemester_kurzbz,
'student_uid' => $uid
], $update_lehrverband);
if (isError($result)) {
$this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR);
return $this->outputJson(getError($result));
}
}
if (count($update_person))
{
$result = $this->PersonModel->update(
$person_id,
$update
$update_person
);
if (isError($result)) {
$this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR);
return $this->outputJson(getError($result));
}
}
$array_allowed_props = ['matrikelnr'];
$update = array();
foreach ($array_allowed_props as $prop)
if (count($update_student))
{
if(isset($data[$prop])){
$update[$prop] = $data[$prop];
}
}
if (count($update))
{
if ($uid === null) {
$this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST);
return $this->outputJson("Kein/e StudentIn vorhanden!");
}
$this->StudentModel->update(
$result = $this->StudentModel->update(
[$uid],
$update
$update_student
);
if (isError($result)) {
$this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR);
return $this->outputJson(getError($result));
}
}
$this->outputJsonSuccess(true);
}
public function isValidDate($date)
{
try {
new DateTime($date);
} catch (Exception $e) {
return false;
}
return true;
}
}
+10 -1
View File
@@ -13,6 +13,9 @@
'public/css/Studentenverwaltung.css',
'public/css/components/vue-datepicker.css'
],
'customJSs' => [
'vendor/npm-asset/primevue/toast/toast.min.js'
],
'customJSModules' => [
'public/js/apps/Studentenverwaltung.js'
]
@@ -22,7 +25,13 @@
?>
<div id="main">
<router-view active-addons="<?= defined('ACTIVE_ADDONS') ? ACTIVE_ADDONS : ''; ?>" cis-root="<?= CIS_ROOT; ?>" :permissions="<?= htmlspecialchars(json_encode($permissions)); ?>"></router-view>
<router-view
active-addons="<?= defined('ACTIVE_ADDONS') ? ACTIVE_ADDONS : ''; ?>"
cis-root="<?= CIS_ROOT; ?>"
:permissions="<?= htmlspecialchars(json_encode($permissions)); ?>"
:config="<?= htmlspecialchars(json_encode(['generateAlias' => !defined('GENERATE_ALIAS_STUDENT') ? true : GENERATE_ALIAS_STUDENT])); ?>"
>
</router-view>
</div>
<?php $this->load->view('templates/FHC-Footer', $includesArray); ?>
+12 -1
View File
@@ -57,4 +57,15 @@
}
.stv-list > #filterTableDataset {
flex: 1 1 auto;
}
}
.toast.toast-success {
color: #0f5132;
background-color: #d1e7dd!important;
border-color: #badbcc!important;
}
.toast.toast-danger {
color: #842029;
background-color: #f8d7da!important;
border-color: #f5c2c7!important;
}
@@ -31,15 +31,18 @@ export default {
VerticalSplit
},
props: {
config: Object,
permissions: Object,
cisRoot: String,
activeAddons: String // semicolon separated list of active addons
},
provide() {
return {
hasBpkPermission: this.permissions['student/bpk'],
cisRoot: this.cisRoot,
activeAddonBewerbung: this.activeAddons.split(';').includes('bewerbung')
activeAddonBewerbung: this.activeAddons.split(';').includes('bewerbung'),
configGenerateAlias: this.config.generateAlias,
hasBpkPermission: this.permissions['student/bpk'],
hasAliasPermission: this.permissions['student/alias']
}
},
data() {
@@ -5,7 +5,8 @@ import {CoreRESTClient} from '../../../../RESTClient.js';
export default {
components: {
VueDatePicker,
FormUploadImage
FormUploadImage,
PvToast: primevue.toast
},
inject: {
showBpk: {
@@ -18,6 +19,14 @@ export default {
},
cisRoot: {
from: 'cisRoot'
},
generateAlias: {
from: 'configGenerateAlias',
default: false
},
hasAliasPermission: {
from: 'hasAliasPermission',
default: false
}
},
props: {
@@ -35,13 +44,42 @@ export default {
"v": "verheiratet",
"w": "verwitwet"
},
original: null,
data: null,
studentIn: null
changed: {},
studentIn: null,
gebDatumIsValid: false,
gebDatumIsInvalid: false
}
},
computed: {
aliasNotAllowed() {
return this.generateAlias === false && !this.hasAliasPermission;
},
changedLength() {
return Object.keys(this.changed).length;
}
},
watch: {
student(n) {
this.updateStudent(n);
},
data: {
handler(n) {
let res = {};
for (var k in this.original) {
if (k == 'gebdatum') {
if (new Date(this.original[k]).toString() != new Date(n[k]).toString())
res[k] = n[k];
} else {
if (this.original[k] !== n[k])
res[k] = n[k];
}
}
this.changed = res;
this.resetErrors();
},
deep: true
}
},
methods: {
@@ -53,6 +91,7 @@ export default {
this.data = result;
if (!this.data.familienstand)
this.data.familienstand = '';
this.original = {...this.data};
})
.catch(err => {
console.error(err.response.data || err.message);
@@ -60,7 +99,67 @@ export default {
},
save() {
CoreRESTClient
.post('components/stv/Student/save/' + this.student.prestudent_id, this.data)
.post('components/stv/Student/save/' + this.student.prestudent_id, this.changed)
.then(result => result.data)
.then(result => {
this.resetErrors();
if (CoreRESTClient.isError(result)) {
let errors = CoreRESTClient.getError(result);
if (errors === "Generic error")
console.error(errors, result);
else {
for (var k in errors)
this.addError(k, errors[k]);
}
} else {
for (var node of document.querySelectorAll(Object.keys(this.changed).map(el => '#stv-details-' + el).join(',')))
node.classList.add('is-valid');
if (this.changed.gebdatum !== undefined)
this.gebDatumIsValid = true;
// TODO(chris): phrase
this.addToast('Gespeichert', '', 'success');
this.original = {...this.data};
this.changed = {};
}
})
.catch(err => {
// TODO(chris): phrase
this.addToast('Error', err?.response?.data || err?.message, 'error');
})
},
resetErrors() {
Array.from(this.$refs.form.getElementsByClassName('is-valid')).forEach(el => el.classList.remove('is-valid'));
Array.from(this.$refs.form.getElementsByClassName('is-invalid')).forEach(el => el.classList.remove('is-invalid'));
Array.from(this.$refs.form.getElementsByClassName('invalid-feedback')).forEach(el => el.remove());
this.gebDatumIsValid = false;
this.gebDatumIsInvalid = false;
},
addError(field, msg) {
let id = 'stv-details-' + field;
let input = document.getElementById(id);
if (field === 'gebdatum') {
this.gebDatumIsInvalid = true;
input = document.getElementById('dp-input-' + id).parentNode;
}
input.classList.add('is-invalid');
let feedback = document.createElement('div');
feedback.classList.add('invalid-feedback');
feedback.innerHTML = msg;
input.after(feedback);
},
addToast(header, msg, severity) {
this.$refs.responseToast.add({
severity: severity,
summary: header,
detail: msg,
life: 3000
})
}
},
created() {
@@ -90,68 +189,73 @@ export default {
});
this.updateStudent(this.student);
},
//TODO(chris): Felder student_uid, person_id sperren, Personenkz
mounted() {
console.log();
},
//TODO(chris): Geburtszeit? Anzahl der Kinder?
template: `
<div class="stv-details-details h-100 pb-3">
<div ref="form" class="stv-details-details h-100 pb-3">
<fieldset>
<legend>Person</legend>
<template v-if="data">
<div class="row mb-3">
<div class="row mb-3 align-items-center">
<label for="stv-details-person_id" class="col-sm-1 col-form-label">Person ID</label>
<div class="col-sm-3">
<input id="stv-details-person_id" type="text" class="form-control" v-model="data.person_id" disabled>
</div>
<label v-if="showZugangscode" for="stv-details-zugangscode" class="col-sm-1 col-form-label">Zugangscode</label>
<div v-if="showZugangscode" class="col-sm-3">
<a :href="cisRoot + 'addons/bewerbung/cis/registration.php?code=' + data.zugangscode + '&emailAdresse=' + data.email_privat" target="_blank">{{data.zugangscode}}</a>
<span class="form-text">
<a :href="cisRoot + 'addons/bewerbung/cis/registration.php?code=' + data.zugangscode + '&emailAdresse=' + data.email_privat" target="_blank">{{data.zugangscode}}</a>
</span>
</div>
<label v-if="showBpk" for="stv-details-bpk" class="col-sm-1 col-form-label">BPK</label>
<div v-if="showBpk" class="col-sm-3">
<input id="stv-details-bpk" type="text" class="form-control" v-model="data.bpk">
<input id="stv-details-bpk" type="text" class="form-control" v-model="data.bpk" maxlength="28">
</div>
</div>
<div class="row mb-3">
<div class="row mb-3 align-items-center">
<label for="stv-details-anrede" class="col-sm-1 col-form-label">Anrede</label>
<div class="col-sm-3">
<input id="stv-details-anrede" type="text" class="form-control" v-model="data.anrede">
<input id="stv-details-anrede" type="text" class="form-control" v-model="data.anrede" maxlength="16">
</div>
<label for="stv-details-titelpre" class="col-sm-1 col-form-label">Titel Pre</label>
<div class="col-sm-3">
<input id="stv-details-titelpre" type="text" class="form-control" v-model="data.titelpre">
<input id="stv-details-titelpre" type="text" class="form-control" v-model="data.titelpre" maxlength="64">
</div>
<label for="stv-details-titelpost" class="col-sm-1 col-form-label">Titel Post</label>
<div class="col-sm-3">
<input id="stv-details-titelpost" type="text" class="form-control" v-model="data.titelpost">
<input id="stv-details-titelpost" type="text" class="form-control" v-model="data.titelpost" maxlength="32">
</div>
</div>
<div class="row mb-3">
<div class="row mb-3 align-items-center">
<label for="stv-details-nachname" class="col-sm-1 col-form-label">Nachname</label>
<div class="col-sm-3">
<input id="stv-details-nachname" type="text" class="form-control" v-model="data.nachname">
<input id="stv-details-nachname" type="text" class="form-control" v-model="data.nachname" maxlength="64">
</div>
<label for="stv-details-vorname" class="col-sm-1 col-form-label">Vorname</label>
<div class="col-sm-3">
<input id="stv-details-vorname" type="text" class="form-control" v-model="data.vorname">
<input id="stv-details-vorname" type="text" class="form-control" v-model="data.vorname" maxlength="32">
</div>
<label for="stv-details-vornamen" class="col-sm-1 col-form-label">Vornamen</label>
<div class="col-sm-3">
<input id="stv-details-vornamen" type="text" class="form-control" v-model="data.vornamen">
<input id="stv-details-vornamen" type="text" class="form-control" v-model="data.vornamen" maxlength="128">
</div>
</div>
<div class="row mb-3">
<div class="row mb-3 align-items-center">
<label for="stv-details-wahlname" class="col-sm-1 col-form-label">Wahlname</label>
<div class="col-sm-3">
<input id="stv-details-wahlname" type="text" class="form-control" v-model="data.wahlname">
<input id="stv-details-wahlname" type="text" class="form-control" v-model="data.wahlname" maxlength="128">
</div>
</div>
<div class="row mb-3">
<div class="row mb-3 align-items-center">
<label for="dp-input-stv-details-gebdatum" class="col-sm-1 col-form-label">Geburtsdatum</label>
<div class="col-sm-3">
<vue-date-picker uid="stv-details-gebdatum" v-model="data.gebdatum" :clearable="false" no-today auto-apply :enable-time-picker="false" format="dd.MM.yyyy" preview-format="dd.MM.yyyy"></vue-date-picker>
<vue-date-picker id="stv-details-gebdatum" :input-class-name="gebDatumIsInvalid ? 'form-control is-invalid' : (gebDatumIsValid ? 'form-control is-valid' : 'form-control')" uid="stv-details-gebdatum" v-model="data.gebdatum" :clearable="false" no-today auto-apply :enable-time-picker="false" format="dd.MM.yyyy" preview-format="dd.MM.yyyy"></vue-date-picker>
</div>
<label for="stv-details-gebort" class="col-sm-1 col-form-label">Geburtsort</label>
<div class="col-sm-3">
<input id="stv-details-gebort" type="text" class="form-control" v-model="data.gebort">
<input id="stv-details-gebort" type="text" class="form-control" v-model="data.gebort" maxlength="128">
</div>
<label for="stv-details-geburtsnation" class="col-sm-1 col-form-label">Geburtsnation</label>
<div class="col-sm-3">
@@ -162,17 +266,17 @@ export default {
</select>
</div>
</div>
<div class="row mb-3">
<div class="row mb-3 align-items-center">
<label for="stv-details-svnr" class="col-sm-1 col-form-label">SVNR</label>
<div class="col-sm-3">
<input id="stv-details-svnr" type="text" class="form-control" v-model="data.svnr">
<input id="stv-details-svnr" type="text" class="form-control" v-model="data.svnr" maxlength="16">
</div>
<label for="stv-details-ersatzkennzeichen" class="col-sm-1 col-form-label">Ersatzkennzeichen</label>
<div class="col-sm-3">
<input id="stv-details-ersatzkennzeichen" type="text" class="form-control" v-model="data.ersatzkennzeichen">
<input id="stv-details-ersatzkennzeichen" type="text" class="form-control" v-model="data.ersatzkennzeichen" maxlength="10">
</div>
</div>
<div class="row mb-3">
<div class="row mb-3 align-items-center">
<label for="stv-details-staatsbuergerschaft" class="col-sm-1 col-form-label">Staatsbürgerschaft</label>
<div class="col-sm-3">
<select id="stv-details-staatsbuergerschaft" class="form-control" v-model="data.staatsbuergerschaft">
@@ -183,7 +287,7 @@ export default {
</div>
<label for="stv-details-matr_nr" class="col-sm-1 col-form-label">Matrikelnummer</label>
<div class="col-sm-3">
<input id="stv-details-matr_nr" type="text" class="form-control" v-model="data.matr_nr">
<input id="stv-details-matr_nr" type="text" class="form-control" v-model="data.matr_nr" maxlength="32">
</div>
<label for="stv-details-sprache" class="col-sm-1 col-form-label">Sprache</label>
<div class="col-sm-3">
@@ -192,7 +296,7 @@ export default {
</select>
</div>
</div>
<div class="row mb-3">
<div class="row mb-3 align-items-center">
<label for="stv-details-geschlecht" class="col-sm-1 col-form-label">Geschlecht</label>
<div class="col-sm-3">
<select id="stv-details-geschlecht" class="form-control" v-model="data.geschlecht">
@@ -217,7 +321,7 @@ export default {
</div>
<label for="stv-details-homepage" class="col-sm-1 col-form-label">Homepage</label>
<div class="col-sm-3">
<input id="stv-details-homepage" type="text" class="form-control" v-model="data.homepage">
<input id="stv-details-homepage" type="text" class="form-control" v-model="data.homepage" maxlength="256">
</div>
</div>
</template>
@@ -231,11 +335,11 @@ export default {
<div class="row mb-3 align-items-center">
<label for="stv-details-student_uid" class="col-sm-1 col-form-label">UID</label>
<div class="col-sm-3">
<input id="stv-details-student_uid" type="text" class="form-control" v-model="data.student_uid">
<input id="stv-details-student_uid" type="text" class="form-control" v-model="data.student_uid" disabled>
</div>
<label for="stv-details-personenkennzeichen" class="col-sm-1 col-form-label">Personenkennzeichen</label>
<div class="col-sm-3">
<input id="stv-details-personenkennzeichen" type="text" class="form-control" v-model="data.matrikelnr">
<input id="stv-details-personenkennzeichen" type="text" class="form-control" v-model="data.matrikelnr" disabled>
</div>
<label for="stv-details-aktiv" class="col-sm-1 col-form-label">Aktiv</label>
<div class="col-sm-3">
@@ -247,30 +351,31 @@ export default {
<div class="row mb-3 align-items-center">
<label for="stv-details-semester" class="col-sm-1 col-form-label">Semester</label>
<div class="col-sm-3">
<input id="stv-details-semester" type="text" class="form-control" v-model="data.semester">
<input id="stv-details-semester" type="text" class="form-control" v-model="data.semester" maxlength="2">
</div>
<label for="stv-details-verband" class="col-sm-1 col-form-label">Verband</label>
<div class="col-sm-3">
<input id="stv-details-verband" type="text" class="form-control" v-model="data.verband">
<input id="stv-details-verband" type="text" class="form-control" v-model="data.verband" maxlength="1">
</div>
<label for="stv-details-gruppe" class="col-sm-1 col-form-label">Gruppe</label>
<div class="col-sm-3">
<input id="stv-details-gruppe" type="text" class="form-control" v-model="data.gruppe">
<input id="stv-details-gruppe" type="text" class="form-control" v-model="data.gruppe" maxlength="1">
</div>
</div>
<div class="row mb-3 align-items-center">
<label for="stv-details-alias" class="col-sm-1 col-form-label">Alias</label>
<div class="col-sm-3">
<input id="stv-details-alias" type="text" class="form-control" v-model="data.alias">
<input id="stv-details-alias" type="text" class="form-control" v-model="data.alias" :disabled="aliasNotAllowed">
</div>
</div>
<div>
<button type="button" class="btn btn-primary" @click="save">Speichern</button>
<button type="button" class="btn btn-primary" @click="save" :disabled="!changedLength">Speichern</button>
</div>
</template>
<div v-else>
Loading...
</div>
</fieldset>
<pv-toast ref="responseToast" style="z-index:9999"></pv-toast>
</div>`
};