Merge branch 'master' into feature-61164/AbgabetoolQualityGates

# Conflicts:
#	public/css/Cis4/Cis.css
#	system/dbupdate_3.4.php
#	system/phrasesupdate.php
This commit is contained in:
Johann Hoffmann
2025-12-09 17:03:18 +01:00
57 changed files with 1969 additions and 389 deletions
+6 -1
View File
@@ -61,7 +61,11 @@ $config['tabs'] =
'notes' => [
//if true, the count of Messages will be shown in the header of the Tab Messages
'showCountNotes' => true
]
],
'combinePeople' => [
//multitab should only be shown with this length of selection
'validCountMulti' => 2,
],
];
// List of fields to show when ZGV_DOKTOR_ANZEIGEN is defined
@@ -117,5 +121,6 @@ $config['students_tab_order'] = [
'status',
'groups',
'finalexam',
'combinePeople',
'archive',
];
+1 -1
View File
@@ -14,7 +14,7 @@ class Pub extends Auth_Controller
{
parent::__construct(
array(
'bild' => ['basis/cis:r']
'bild' => ['basis/cis:r', 'assistenz:r']
)
);
}
@@ -298,7 +298,7 @@ class Aufnahmetermine extends FHCAPI_Controller
$reihungstestangetreten =
(isset($formData['reihungstestangetreten']) && !empty($formData['reihungstestangetreten']))
? $formData['reihungstestangetreten']
: null;
: false;
$aufnahmegruppe_kurzbz =
(isset($formData['aufnahmegruppe_kurzbz']) && !empty($formData['aufnahmegruppe_kurzbz']))
? $formData['aufnahmegruppe_kurzbz']
@@ -33,6 +33,8 @@ class Config extends FHCAPI_Controller
{
// TODO(chris): permissions
parent::__construct([
'get' => ['admin:r', 'assistenz:r'],
'set' => ['admin:r', 'assistenz:r'],
'filter' => ['admin:r', 'assistenz:r'],
'student' => ['admin:r', 'assistenz:r'],
'students' => ['admin:r', 'assistenz:r']
@@ -55,6 +57,95 @@ class Config extends FHCAPI_Controller
}
/**
* get App config
*/
public function get()
{
$this->load->model('system/Variable_model', 'VariableModel');
$this->load->config('stv');
$config = [];
#number_displayed_past_studiensemester
$result = $this->VariableModel->getVariables(getAuthUID(), ['number_displayed_past_studiensemester']);
$data = $this->getDataOrTerminateWithError($result);
$number_displayed_past_studiensemester_default = $this->config->item('number_displayed_past_studiensemester_default');
$config['number_displayed_past_studiensemester'] = [
"type" => "number",
"label" => $this->p->t('stv', 'settings_no_displayed_past_sem'),
"value" => $data['number_displayed_past_studiensemester']
?? $number_displayed_past_studiensemester_default
];
#font_size
$result = $this->VariableModel->getVariables(getAuthUID(), ['stv_font_size']);
$data = $this->getDataOrTerminateWithError($result);
$config['font_size'] = [
"type" => "select",
"label" => $this->p->t('stv', 'settings_fontsize'),
"value" => $data['stv_font_size'] ?? "fs_normal",
"options" => [
"fs_xx-small" => $this->p->t('stv', 'settings_fontsize_xx-small'),
"fs_x-small" => $this->p->t('stv', 'settings_fontsize_x-small'),
"fs_small" => $this->p->t('stv', 'settings_fontsize_small'),
"fs_normal" => $this->p->t('stv', 'settings_fontsize_normal'),
"fs_big" => $this->p->t('stv', 'settings_fontsize_big'),
"fs_huge" => $this->p->t('stv', 'settings_fontsize_huge')
]
];
#others
Events::trigger('stv_config_get', function & () use (&$config) {
return $config;
});
$this->terminateWithSuccess($config);
}
/**
* set App config
*/
public function set()
{
$this->load->model('system/Variable_model', 'VariableModel');
$this->load->library('form_validation');
$this->form_validation->set_rules(
'number_displayed_past_studiensemester',
$this->p->t('stv', 'settings_no_displayed_past_sem'),
'required|integer'
);
$this->form_validation->set_rules(
'font_size',
$this->p->t('stv', 'settings_fontsize'),
'required|in_list[fs_xx-small,fs_x-small,fs_small,fs_normal,fs_big,fs_huge]'
);
Events::trigger('stv_config_validation', $this->form_validation);
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$this->VariableModel->setVariable(
getAuthUID(),
'number_displayed_past_studiensemester',
$this->input->post('number_displayed_past_studiensemester')
);
$this->VariableModel->setVariable(
getAuthUID(),
'stv_font_size',
$this->input->post('font_size')
);
Events::trigger('stv_config_set', $this->input);
$this->terminateWithSuccess();
}
/*
* Get the config for the student filters
*
* @return void
@@ -407,6 +498,15 @@ class Config extends FHCAPI_Controller
]
];
if($this->permissionlib->isBerechtigt('basis/person'))
{
$result['combinePeople'] = [
'title' => $this->p->t('stv', 'tab_combine_people'),
'component' => './Stv/Studentenverwaltung/Details/CombinePeople.js',
'config' => $config['combinePeople']
];
}
$result['kontaktieren'] = [
'title' => $this->p->t('stv', 'tab_kontaktieren'),
'component' => absoluteJsImportUrl('public/js/components/Stv/Studentenverwaltung/Details/Kontaktieren.js'),
@@ -83,7 +83,7 @@ class GemeinsameStudien extends FHCAPI_Controller
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->StudiensemesterModel->addOrder('studienjahr_kurzbz', 'DESC');
$this->StudiensemesterModel->addOrder('start', 'DESC');
$result = $this->StudiensemesterModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
@@ -147,7 +147,8 @@ class Gruppen extends FHCAPI_Controller
'lehre' => true,
'sichtbar' => true,
'aktiv' => true,
'direktinskription' => false
'direktinskription' => false,
'generiert' => false
]);
$data = $this->getDataOrTerminateWithError($result);
@@ -268,10 +268,6 @@ class Projektarbeit extends FHCAPI_Controller
{
$this->form_validation->set_data($formData);
$this->form_validation->set_rules('titel', 'Titel', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Titel'])
]);
$this->form_validation->set_rules('projekttyp_kurzbz', 'Projekttyp', 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Projekttyp'])
]);
@@ -18,6 +18,8 @@
if (! defined('BASEPATH')) exit('No direct script access allowed');
use \DateTime as DateTime;
/**
* This controller operates between (interface) the JS (GUI) and the back-end
* Provides data to the ajax get calls about addresses
@@ -111,7 +113,7 @@ class Pruefung extends FHCAPI_Controller
// Load language phrases
$this->loadPhrases([
'global', 'ui','lehre'
'global', 'ui', 'lehre', 'exam'
]);
}
@@ -172,174 +174,11 @@ class Pruefung extends FHCAPI_Controller
*
* @param lehrveranstaltung_id, student_uid, lehreinheit_id
*
* @return values on success
* retval 0: pruefung inserted
* reval 1: pruefung and zeugnisnote inserted
* retval 2: pruefung inserted, no insert Zeugnisnote
* (change after date of examination)
* retval 3: pruefung of type Termin2 inserted
* and pruefung of type Termin1 as well
* retval 5: prueufungen Termin 2 and 1 inserted
* and no insert Zeugnisnote (change after date of examination)
* @return void
*/
public function insertPruefung()
{
$authUID = getAuthUID();
$this->load->library('form_validation');
$this->form_validation->set_rules('lehrveranstaltung_id', $this->p->t('lehre', 'lehrveranstaltung'), 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'lehrveranstaltung')]),
]);
$this->form_validation->set_rules('lehreinheit_id', $this->p->t('lehre', 'lehreinheit'), 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'lehreinheit')]),
]);
$this->form_validation->set_rules('pruefungstyp_kurzbz', $this->p->t('lehre', 'pruefung'), 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('global', 'typ')]),
]);
$this->form_validation->set_rules(
'datum',
$this->p->t('global', 'datum'),
['is_valid_date']
);
if ($this->form_validation->run() == false)
{
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
//calculate studiensemester_kurzbz this from lehreinheit (case newPruefung)
$studiensemester_kurzbz = $this->input->post('studiensemester_kurzbz');
if (!$studiensemester_kurzbz)
{
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$result = $this->LehreinheitModel->load($this->input->post('lehreinheit_id'));
$lehreinheit = $this->getDataOrTerminateWithError($result);
$studiensemester_kurzbz = current($lehreinheit)->studiensemester_kurzbz;
if (isError($result))
{
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
}
$result = $this->PruefungModel->insert([
'lehreinheit_id' => $this->input->post('lehreinheit_id'),
'student_uid' => $this->input->post('student_uid'),
'mitarbeiter_uid' => $this->input->post('mitarbeiter_uid'),
'datum' => $this->input->post('datum'),
'pruefungstyp_kurzbz' => $this->input->post('pruefungstyp_kurzbz'),
'note' => $this->input->post('note'),
'anmerkung' => $this->input->post('anmerkung'),
'insertamum' => date('c'),
'insertvon' => $authUID,
]);
$this->getDataOrTerminateWithError($result);
//check if existing zeugnisnote
$this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel');
$result = $this->ZeugnisnoteModel->loadWhere(array(
'lehrveranstaltung_id' => $this->input->post('lehrveranstaltung_id'),
'student_uid' => $this->input->post('student_uid'),
'studiensemester_kurzbz' => $studiensemester_kurzbz));
if (isError($result))
{
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
if (!hasData($result))
{
//insert zeugnisnote, if not existing
$result = $this->ZeugnisnoteModel->insert(array(
'lehrveranstaltung_id' => $this->input->post('lehrveranstaltung_id'),
'student_uid' => $this->input->post('student_uid'),
'studiensemester_kurzbz' => $studiensemester_kurzbz,
'note' => $this->input->post('note'),
'uebernahmedatum' => date('c'),
'benotungsdatum' => $this->input->post('datum'),
'insertamum' => date('c'),
'insertvon' => $authUID
));
if (isError($result))
{
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->terminateWithSuccess(1);
}
$return_code = 0;
//handling Termin1 if not existing
if($this->input->post('pruefungstyp_kurzbz') == "Termin2")
{
$resultP = $this->PruefungModel->loadWhere(array(
'lehreinheit_id' => $this->input->post('lehreinheit_id'),
'student_uid' => $this->input->post('student_uid'),
'pruefungstyp_kurzbz' => 'Termin1'));
if (isError($resultP))
{
$this->terminateWithError(getError($resultP), self::ERROR_TYPE_GENERAL);
}
if(!hasData($resultP))
{
//check if existing Zeugnisnote
$this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel');
$this->ZeugnisnoteModel->addJoin('lehre.tbl_lehreinheit', 'lehrveranstaltung_id');
$resultP = $this->ZeugnisnoteModel->loadWhere(array(
'lehrveranstaltung_id' => $this->input->post('lehrveranstaltung_id'),
'student_uid' => $this->input->input->post('student_uid'),
'lehre.tbl_zeugnisnote.studiensemester_kurzbz' => $studiensemester_kurzbz));
if (isError($resultP))
{
$this->terminateWithError(getError($resultP), self::ERROR_TYPE_GENERAL);
}
if (!hasData($resultP))
{
$this->terminateWithError("Zeugnisnote existiert nicht", self::ERROR_TYPE_GENERAL);
}
$dataNote = current(getData($resultP));
$resultN = $this->PruefungModel->insert([
'lehreinheit_id' => $this->input->post('lehreinheit_id'),
'student_uid' => $this->input->post('student_uid'),
'mitarbeiter_uid' => $this->input->post('mitarbeiter_uid'),
'datum' => $dataNote->benotungsdatum,
'pruefungstyp_kurzbz' => 'Termin1',
'note' => $dataNote->note,
'punkte' => $dataNote->punkte,
'anmerkung' => 'automatisiert aus Zeugnisnote erstellt',
'insertamum' => date('c'),
'insertvon' => $authUID,
]);
if (isError($resultN)) {
$this->terminateWithError(getError($resultN), self::ERROR_TYPE_GENERAL);
}
$return_code = 3;
}
}
$note = current(getData($result));
$uebernahmedatum = new DateTime($note->uebernahmedatum);
$benotungsdatum = new DateTime($note->benotungsdatum);
$checkDate = $uebernahmedatum === '' || $benotungsdatum > $uebernahmedatum
? $benotungsdatum
: $uebernahmedatum;
if ($checkDate >= $this->input->post('datum') && $note !== $note->note)
{
$this->terminateWithSuccess($return_code + 2);
}
$this->terminateWithSuccess($return_code + 2);
$this->insertOrUpdatePruefung();
}
/**
@@ -348,8 +187,6 @@ class Pruefung extends FHCAPI_Controller
* @param pruefung_id
*
* @return success or error
*
* no impact on lehre.tbl_zeugnisnote
*/
public function updatePruefung($pruefung_id)
{
@@ -359,48 +196,7 @@ class Pruefung extends FHCAPI_Controller
if (!$oldpruefung)
show_404(); // Pruefung that should be updated does not exist
$authUID = getAuthUID();
$this->load->library('form_validation');
$this->form_validation->set_rules('lehrveranstaltung_id', $this->p->t('lehre', 'lehrveranstaltung'), 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'lehrveranstaltung')]),
]);
$this->form_validation->set_rules('lehreinheit_id', $this->p->t('lehre', 'lehreinheit'), 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'lehreinheit')]),
]);
$this->form_validation->set_rules('pruefungstyp_kurzbz', $this->p->t('lehre', 'pruefung'), 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('global', 'typ')]),
]);
$this->form_validation->set_rules(
'datum',
$this->p->t('global', 'datum'),
['is_valid_date']
);
if ($this->form_validation->run() == false)
{
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
$result = $this->PruefungModel->update(
[
'pruefung_id' => $pruefung_id
],
[ 'lehreinheit_id' => $this->input->post('lehreinheit_id'),
'student_uid' => $this->input->post('student_uid'),
'mitarbeiter_uid' => $this->input->post('mitarbeiter_uid'),
'note' => $this->input->post('note'),
'pruefungstyp_kurzbz' => $this->input->post('pruefungstyp_kurzbz'),
'datum' => $this->input->post('datum'),
'anmerkung' => $this->input->post('anmerkung'),
'updatevon' => $authUID,
'updateamum' => date('c'),
]
);
$this->getDataOrTerminateWithError($result);
return $this->outputJsonSuccess(true);
$this->insertOrUpdatePruefung($pruefung_id);
}
/**
@@ -574,4 +370,198 @@ class Pruefung extends FHCAPI_Controller
return $this->terminateWithSuccess($data);
}
protected function insertOrUpdatePruefung($pruefung_id=null)
{
$authUID = getAuthUID();
$this->load->library('form_validation');
$this->form_validation->set_rules('lehreinheit_id', $this->p->t('lehre', 'lehreinheit'), 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('lehre', 'lehreinheit')]),
]);
$this->form_validation->set_rules('pruefungstyp_kurzbz', $this->p->t('lehre', 'pruefung'), 'required', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('global', 'typ')]),
]);
$this->form_validation->set_rules(
'datum',
$this->p->t('global', 'datum'),
['is_valid_date']
);
if ($this->form_validation->run() == false)
{
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
$this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel');
$this->PruefungModel->db->trans_start();
if ($this->input->post('pruefungstyp_kurzbz') == "Termin2")
{
//Wenn ein 2. Termin angelegt wird, und kein 1. Termin vorhanden ist,
//dann wird auch ein 1. Termin angelegt mit der derzeitigen Zeugnisnote
$resultP = $this->PruefungModel->loadWhere(array(
'lehreinheit_id' => $this->input->post('lehreinheit_id'),
'student_uid' => $this->input->post('student_uid'),
'pruefungstyp_kurzbz' => 'Termin1'));
$termin1 = $this->getDataOrTerminateWithError($resultP);
if (!$termin1)
{
//check if existing Zeugnisnote
$this->ZeugnisnoteModel->addJoin('lehre.tbl_lehreinheit', 'lehrveranstaltung_id');
$this->ZeugnisnoteModel->db->where(
'lehre.tbl_zeugnisnote.studiensemester_kurzbz',
'lehre.tbl_lehreinheit.studiensemester_kurzbz',
false
);
$resultP = $this->ZeugnisnoteModel->loadWhere(array(
'lehrveranstaltung_id' => $this->input->post('lehrveranstaltung_id'),
'student_uid' => $this->input->post('student_uid')
));
$zeugnisnoten = $this->getDataOrTerminateWithError($resultP);
if ($zeugnisnoten)
{
$zeugnisnote = current($zeugnisnoten);
$resultN = $this->PruefungModel->insert([
'lehreinheit_id' => $this->input->post('lehreinheit_id'),
'student_uid' => $this->input->post('student_uid'),
'mitarbeiter_uid' => $this->input->post('mitarbeiter_uid'),
'datum' => $zeugnisnote->benotungsdatum,
'pruefungstyp_kurzbz' => 'Termin1',
'note' => $zeugnisnote->note,
'punkte' => $zeugnisnote->punkte,
'anmerkung' => 'automatisiert aus Zeugnisnote erstellt',
'insertamum' => date('c'),
'insertvon' => $authUID,
]);
$this->getDataOrTerminateWithError($resultN);
}
//Wenn keine Zeugnisnote vorhanden ist, dann wird kein
//1.Termin angelegt
}
}
if(intval($pruefung_id) > 0)
{
$result = $this->PruefungModel->update(
[
'pruefung_id' => $pruefung_id
],
[ 'lehreinheit_id' => $this->input->post('lehreinheit_id'),
'student_uid' => $this->input->post('student_uid'),
'mitarbeiter_uid' => $this->input->post('mitarbeiter_uid'),
'note' => $this->input->post('note'),
'pruefungstyp_kurzbz' => $this->input->post('pruefungstyp_kurzbz'),
'datum' => $this->input->post('datum'),
'anmerkung' => $this->input->post('anmerkung'),
'updatevon' => $authUID,
'updateamum' => date('c'),
]
);
}
else
{
$result = $this->PruefungModel->insert([
'lehreinheit_id' => $this->input->post('lehreinheit_id'),
'student_uid' => $this->input->post('student_uid'),
'mitarbeiter_uid' => $this->input->post('mitarbeiter_uid'),
'datum' => $this->input->post('datum'),
'pruefungstyp_kurzbz' => $this->input->post('pruefungstyp_kurzbz'),
'note' => $this->input->post('note'),
'anmerkung' => $this->input->post('anmerkung'),
'insertamum' => date('c'),
'insertvon' => $authUID,
'punkte' => $this->input->post('punkte') ? str_replace(',', '.', $this->input->post('punkte')) : null
]);
}
$this->getDataOrTerminateWithError($result);
//get studiensemester_kurzbz and lehreveranstaltung_id from lehreinheit
$this->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$result = $this->LehreinheitModel->load($this->input->post('lehreinheit_id'));
$lehreinheiten = $this->getDataOrTerminateWithError($result);
if (!$lehreinheiten) {
$this->terminateWithValidationErrors([
'lehreinheit_id' => $this->p->t('ui', 'error_fieldNotFound', [
'field' => $this->p->t('lehre', 'lehreinheit')
])
]);
}
$lehreinheit = current($lehreinheiten);
$studiensemester_kurzbz = $lehreinheit->studiensemester_kurzbz;
$lehrveranstaltung_id = $lehreinheit->lehrveranstaltung_id;
//check if existing zeugnisnote
$result = $this->ZeugnisnoteModel->loadWhere(array(
'lehrveranstaltung_id' => $lehrveranstaltung_id,
'student_uid' => $this->input->post('student_uid'),
'studiensemester_kurzbz' => $studiensemester_kurzbz
));
$zeugnisnoten = $this->getDataOrTerminateWithError($result);
if (!$zeugnisnoten)
{
//insert zeugnisnote, if not existing
$result = $this->ZeugnisnoteModel->insert(array(
'lehrveranstaltung_id' => $lehrveranstaltung_id,
'student_uid' => $this->input->post('student_uid'),
'studiensemester_kurzbz' => $studiensemester_kurzbz,
'note' => $this->input->post('note'),
'uebernahmedatum' => date('c'),
'benotungsdatum' => $this->input->post('datum'),
'insertamum' => date('c'),
'insertvon' => $authUID,
'punkte' => $this->input->post('punkte') ? str_replace(',', '.', $this->input->post('punkte')) : null
));
$this->getDataOrTerminateWithError($result);
$this->PruefungModel->db->trans_complete();
$this->terminateWithSuccess();
}
$note = current($zeugnisnoten);
$uebernahmedatum = new DateTime($note->uebernahmedatum);
$benotungsdatum = new DateTime($note->benotungsdatum);
$pruefungsdatum = new DateTime($this->input->post('datum'));
$checkDate = $note->uebernahmedatum === '' || $benotungsdatum > $uebernahmedatum
? $benotungsdatum
: $uebernahmedatum;
if ($checkDate > $pruefungsdatum && $this->input->post('note') !== $note->note)
{
$this->PruefungModel->db->trans_complete();
$this->terminateWithSuccess($this->p->t('exam', 'hinweis_changeAfterExamDate'));
}
//update zeugnisnote, if existing and valid datum
$result = $this->ZeugnisnoteModel->update([
'lehrveranstaltung_id' => $lehrveranstaltung_id,
'student_uid' => $this->input->post('student_uid'),
'studiensemester_kurzbz' => $studiensemester_kurzbz
], [
'note' => $this->input->post('note'),
'uebernahmedatum' => date('c'),
'benotungsdatum' => $this->input->post('datum'),
'updateamum' => date('c'),
'updatevon' => $authUID,
'punkte' => $this->input->post('punkte') ? str_replace(',', '.', $this->input->post('punkte')) : null
]);
$this->PruefungModel->db->trans_complete();
$this->terminateWithSuccess();
}
}
@@ -765,6 +765,86 @@ class Students extends FHCAPI_Controller
$this->terminateWithSuccess($data);
}
/**
* @param string $studiensemester_kurzbz
*
* @return void
*/
public function search($studiensemester_kurzbz)
{
$this->addMeta('ci_method', __FUNCTION__);
$this->addMeta('ci_params', array(
'studiensemester_kurzbz' => $studiensemester_kurzbz
));
$this->load->library('SearchLib', [ 'config' => 'searchstv' ]);
$this->load->library('form_validation');
$this->form_validation->set_rules('searchstr', 'searchstr', 'required');
$this->form_validation->set_rules('types[]', 'types', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$result = $this->searchlib->search($this->input->post('searchstr'), $this->input->post('types'));
$data = $this->getDataOrTerminateWithError($result);
$this->load->model('crm/Prestudent_model', 'PrestudentModel');
$this->prepareQuery($studiensemester_kurzbz);
$this->PrestudentModel->addSelect("COALESCE(v.semester::text, CASE WHEN public.get_rolle_prestudent(tbl_prestudent.prestudent_id, NULL) IN ('Aufgenommener', 'Bewerber', 'Wartender', 'interessent') THEN public.get_absem_prestudent(tbl_prestudent.prestudent_id, NULL)::text ELSE ''::text END) AS semester", false);
$this->PrestudentModel->addSelect('v.verband');
$this->PrestudentModel->addSelect('v.gruppe');
//add status per semester
$this->PrestudentModel->addSelect(
"(
SELECT status_kurzbz
FROM public.tbl_prestudentstatus pss
WHERE pss.prestudent_id = public.tbl_prestudent.prestudent_id
AND pss.studiensemester_kurzbz = " . $this->PrestudentModel->escape($studiensemester_kurzbz) . "
ORDER BY GREATEST(pss.datum, '0001-01-01') DESC
LIMIT 1
) AS statusofsemester"
);
$this->addSelectPrioRel();
$this->addFilter($studiensemester_kurzbz);
$prestudent_ids = [];
$student_uids = [];
$this->addMeta('data', $data);
foreach ($data as $row) {
$dataset = json_decode($row->data);
if ($row->type == 'prestudent') {
$prestudent_ids[] = $dataset->prestudent_id;
} elseif ($row->type == 'student') {
$student_uids[] = $dataset->uid;
}
}
if ($prestudent_ids && $student_uids) {
$this->PrestudentModel->db->where_in('tbl_prestudent.prestudent_id', $prestudent_ids);
$this->PrestudentModel->db->or_where_in('s.student_uid', $student_uids);
} elseif ($prestudent_ids) {
$this->PrestudentModel->db->where_in('tbl_prestudent.prestudent_id', $prestudent_ids);
} elseif ($student_uids) {
$this->PrestudentModel->db->where_in('s.student_uid', $student_uids);
} else {
$this->terminateWithSuccess([]);
}
$result = $this->PrestudentModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
/**
* @param string|null $studiensemester_kurzbz
* @param string $type
@@ -165,7 +165,17 @@ class Verband extends FHCAPI_Controller
$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("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);
@@ -173,6 +183,7 @@ class Verband extends FHCAPI_Controller
$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);
@@ -188,6 +199,7 @@ class Verband extends FHCAPI_Controller
array_unshift($list, [
'name' => 'PreStudent',
'link' => $link . 'prestudent',
'no_sem_reload' => true,
'stg_kz' => (int)$studiengang_kz,
'children' => $this->getStdSem($link . 'prestudent/', $studiengang_kz)
]);
@@ -216,7 +228,6 @@ class Verband extends FHCAPI_Controller
$list = array_merge($list, $result);
}
}
}
$this->terminateWithSuccess($list);
}
@@ -233,10 +233,10 @@ class Person extends API_Controller
//Quersumme bilden
for ($i = 0; $i < 10; $i++)
{
$erg += $gewichtung[$i] * $tmpSvnr{$i};
$erg += $gewichtung[$i] * $tmpSvnr[$i];
}
if ($tmpSvnr{3} != ($erg % 11)) //Vergleichen der Pruefziffer mit Quersumme Modulo 11
if ($tmpSvnr[3] != ($erg % 11)) //Vergleichen der Pruefziffer mit Quersumme Modulo 11
{
return error('SVNR ist ungueltig');
}
@@ -244,7 +244,7 @@ class Person extends API_Controller
if (mb_strlen($person['svnr']) == 12)
{
$last = substr($person['svnr'], 10, 12);
if ($last{0} != 'v' || !is_numeric($last{1}))
if ($last[0] != 'v' || !is_numeric($last[1]))
{
return error('SVNR ist ungueltig');
}
@@ -22,6 +22,7 @@ class InfoCenter extends Auth_Controller
const REIHUNGSTESTABSOLVIERT_PAGE = 'reihungstestAbsolviert';
const ABGEWIESEN_PAGE = 'abgewiesen';
const AUFGENOMMEN_PAGE = 'aufgenommen';
const ONBOARDING_PAGE = 'onboarding';
const SHOW_DETAILS_PAGE = 'showDetails';
const SHOW_ZGV_DETAILS_PAGE = 'showZGVDetails';
const ZGV_UBERPRUEFUNG_PAGE = 'ZGVUeberpruefung';
@@ -116,6 +117,7 @@ class InfoCenter extends Auth_Controller
'index' => 'infocenter:r',
'freigegeben' => 'infocenter:r',
'abgewiesen' => 'infocenter:r',
'onboarding' => 'infocenter:r',
'aufgenommen' => 'infocenter:r',
'reihungstestAbsolviert' => 'infocenter:r',
'showDetails' => 'infocenter:r',
@@ -230,6 +232,13 @@ class InfoCenter extends Auth_Controller
$this->load->view('system/infocenter/infocenterAbgewiesen.php');
}
public function onboarding()
{
$this->_setNavigationMenu(self::ONBOARDING_PAGE); // define the navigation menu for this page
$this->load->view('system/infocenter/onboarding.php');
}
/**
* Aufgenommene page of the InfoCenter tool
@@ -1553,6 +1562,7 @@ class InfoCenter extends Auth_Controller
$reihungstestAbsolviertLink = site_url(self::INFOCENTER_URI.'/'.self::REIHUNGSTESTABSOLVIERT_PAGE);
$abgewiesenLink = site_url(self::INFOCENTER_URI.'/'.self::ABGEWIESEN_PAGE);
$aufgenommenLink = site_url(self::INFOCENTER_URI.'/'.self::AUFGENOMMEN_PAGE);
$onboardingLink = site_url(self::INFOCENTER_URI.'/'.self::ONBOARDING_PAGE);
$currentFilterId = $this->input->get(self::FILTER_ID);
if (isset($currentFilterId))
@@ -1561,6 +1571,7 @@ class InfoCenter extends Auth_Controller
$reihungstestAbsolviertLink .= '?'.self::PREV_FILTER_ID.'='.$currentFilterId;
$abgewiesenLink .= '?'.self::PREV_FILTER_ID.'='.$currentFilterId;
$aufgenommenLink .= '?'.self::PREV_FILTER_ID.'='.$currentFilterId;
$onboardingLink .= '?'.self::PREV_FILTER_ID.'='.$currentFilterId;
}
$this->navigationlib->setSessionMenu(
@@ -1624,6 +1635,18 @@ class InfoCenter extends Auth_Controller
'', // target
40 // sort
),
'ohnePrestudent' => $this->navigationlib->oneLevel(
'Electronic Onboarding', // description
$onboardingLink, // link
null, // children
'users', // icon
null, // subscriptDescription
false, // expand
null, // subscriptLinkClass
null, // subscriptLinkValue
'', // target
50 // sort
),
)
);
}
@@ -1650,6 +1673,8 @@ class InfoCenter extends Auth_Controller
$link = site_url(self::ZGV_UEBERPRUEFUNG_URI);
if ($origin_page === self::ABGEWIESEN_PAGE)
$link = site_url(self::INFOCENTER_URI.'/'.self::ABGEWIESEN_PAGE);
if ($origin_page === self::ONBOARDING_PAGE)
$link = site_url(self::INFOCENTER_URI.'/'.self::ONBOARDING_PAGE);
if ($origin_page === self::AUFGENOMMEN_PAGE)
$link = site_url(self::INFOCENTER_URI.'/'.self::AUFGENOMMEN_PAGE);
@@ -1691,6 +1716,7 @@ class InfoCenter extends Auth_Controller
$freigegebenLink = site_url(self::INFOCENTER_URI.'/'.self::FREIGEGEBEN_PAGE);
$absolviertLink = site_url(self::INFOCENTER_URI.'/'.self::REIHUNGSTESTABSOLVIERT_PAGE);
$abgewiesenLink = site_url(self::INFOCENTER_URI.'/'.self::ABGEWIESEN_PAGE);
$onboardingLink = site_url(self::INFOCENTER_URI.'/'.self::ONBOARDING_PAGE);
$prevFilterId = $this->input->get(self::PREV_FILTER_ID);
if (isset($prevFilterId))
{
@@ -1767,6 +1793,24 @@ class InfoCenter extends Auth_Controller
)
);
}
if($page == self::ONBOARDING_PAGE)
{
$this->navigationlib->setSessionElementMenu(
'onboarding',
$this->navigationlib->oneLevel(
'Electronic Onboarding', // description
$onboardingLink, // link
null, // children
'users', // icon
null, // subscriptDescription
false, // expand
null, // subscriptLinkClass
null, // subscriptLinkValue
'', // target
50 // sort
)
);
}
}
/**
+4 -1
View File
@@ -22,7 +22,8 @@
'public/css/components/function.css'
],
'customJSs' => [
'vendor/vuejs/vuedatepicker_js/vue-datepicker.iife.js'
'vendor/vuejs/vuedatepicker_js/vue-datepicker.iife.js',
'vendor/moment/luxonjs/luxon.min.js'
#'vendor/npm-asset/primevue/tree/tree.min.js',
#'vendor/npm-asset/primevue/toast/toast.min.js'
],
@@ -53,6 +54,8 @@ $configArray = [
active-addons="<?= defined('ACTIVE_ADDONS') ? ACTIVE_ADDONS : ''; ?>"
stv-root="<?= site_url('Studentenverwaltung'); ?>"
cis-root="<?= CIS_ROOT; ?>"
avatar-url="<?= site_url('Cis/Pub/bild/person/' . getAuthPersonId()); ?>"
logout-url="<?= site_url('Cis/Auth/logout'); ?>"
:permissions="<?= htmlspecialchars(json_encode($permissions)); ?>"
:config="<?= htmlspecialchars(json_encode($configArray)); ?>"
>
@@ -0,0 +1,47 @@
<?php
$this->load->view(
'templates/FHC-Header',
array(
'title' => 'Info Center',
'jquery3' => true,
'jqueryui1' => true,
'jquerycheckboxes1' => true,
'bootstrap3' => true,
'fontawesome4' => true,
'sbadmintemplate3' => true,
'tablesorter2' => true,
'ajaxlib' => true,
'filterwidget' => true,
'navigationwidget' => true,
'dialoglib' => true,
'phrases' => array(
'person' => array('vorname', 'nachname'),
'ui' => array('bitteEintragWaehlen')
),
'customCSSs' => array('public/css/sbadmin2/tablesort_bootstrap.css', 'public/css/infocenter/infocenterPersonDataset.css'),
'customJSs' => array('public/js/bootstrapper.js', 'public/js/infocenter/rueckstellung.js', 'public/js/infocenter/infocenterPersonDataset.js')
)
);
?>
<div id="wrapper">
<?php echo $this->widgetlib->widget('NavigationWidget'); ?>
<div id="page-wrapper">
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<h3 class="page-header">
Electronic Onboarding
</h3>
</div>
</div>
<div>
<?php $this->load->view('system/infocenter/onboardingData.php'); ?>
</div>
</div>
</div>
</div>
<?php $this->load->view('templates/FHC-Footer'); ?>
@@ -0,0 +1,116 @@
<?php
$APP = '\'infocenter\'';
$KENNZEICHEN = '\'eobRegistrierungsId\'';
$query = '
SELECT
p.person_id AS "PersonId",
p.vorname AS "Vorname",
p.nachname AS "Nachname",
pl.zeitpunkt AS "LockDate",
pl.lockuser AS "LockUser",
rueck.datum_bis AS "HoldDate",
rueck.bezeichnung AS "Rueckstellgrund"
FROM public.tbl_person p
JOIN tbl_kennzeichen ON p.person_id = tbl_kennzeichen.person_id AND kennzeichentyp_kurzbz = '. $KENNZEICHEN .'
LEFT JOIN (
SELECT tpl.person_id,
tpl.zeitpunkt,
sp.nachname AS lockuser
FROM system.tbl_person_lock tpl
JOIN public.tbl_benutzer sb USING (uid)
JOIN public.tbl_person sp ON sb.person_id = sp.person_id
WHERE tpl.app = '.$APP.'
) pl ON p.person_id = pl.person_id
LEFT JOIN (
SELECT
tbl_rueckstellung.person_id,
tbl_rueckstellung.datum_bis,
tbl_rueckstellung.status_kurzbz,
array_to_json(bezeichnung_mehrsprachig::varchar[])->>0 as bezeichnung
FROM public.tbl_rueckstellung
JOIN public.tbl_rueckstellung_status USING(status_kurzbz)
JOIN public.tbl_person sp ON tbl_rueckstellung.person_id = sp.person_id
WHERE tbl_rueckstellung.rueckstellung_id =
(
SELECT srueck.rueckstellung_id
FROM public.tbl_rueckstellung srueck
WHERE srueck.person_id = tbl_rueckstellung.person_id
AND datum_bis >= NOW()
ORDER BY srueck.datum_bis DESC LIMIT 1
)
) rueck ON rueck.person_id = p.person_id
WHERE p.person_id NOT IN (SELECT person_id FROM public.tbl_prestudent)';
$filterWidgetArray = array(
'query' => $query,
'app' => InfoCenter::APP,
'datasetName' => 'onboarding',
'filter_id' => $this->input->get('filter_id'),
'requiredPermissions' => 'infocenter',
'datasetRepresentation' => 'tablesorter',
'checkboxes' => 'PersonId',
'additionalColumns' => array('Details'),
'columnsAliases' => array(
'PersonId',
ucfirst($this->p->t('person', 'vorname')) ,
ucfirst($this->p->t('person', 'nachname')),
ucfirst($this->p->t('global', 'sperrdatum')),
ucfirst($this->p->t('global', 'gesperrtVon')),
ucfirst($this->p->t('infocenter', 'rueckstelldatum')),
ucfirst($this->p->t('infocenter', 'rueckstellgrund')),
),
'formatRow' => function($datasetRaw) {
/* NOTE: Dont use $this here for PHP Version compatibility */
$datasetRaw->{'Details'} = sprintf(
'<a href="%s?person_id=%s&origin_page=%s&fhc_controller_id=%s&prev_filter_id=%s">Details</a>',
site_url('system/infocenter/InfoCenter/showDetails'),
$datasetRaw->{'PersonId'},
'onboarding',
(isset($_GET['fhc_controller_id']) ? $_GET['fhc_controller_id'] : ''),
(isset($_GET['filter_id']) ? $_GET['filter_id'] : '')
);
if ($datasetRaw->{'LockDate'} == null)
{
$datasetRaw->{'LockDate'} = '-';
}
if ($datasetRaw->{'LockUser'} == null)
{
$datasetRaw->{'LockUser'} = '-';
}
if ($datasetRaw->{'HoldDate'} == null)
{
$datasetRaw->{'HoldDate'} = '-';
}
else
{
$datasetRaw->{'HoldDate'} = date_format(date_create($datasetRaw->{'HoldDate'}), 'Y-m-d H:i');
}
if ($datasetRaw->{'Rueckstellgrund'} === null)
{
$datasetRaw->{'Rueckstellgrund'} = '-';
}
return $datasetRaw;
},
'markRow' => function($datasetRaw) {
if ($datasetRaw->LockDate != null)
{
return FilterWidget::DEFAULT_MARK_ROW_CLASS;
}
}
);
echo $this->widgetlib->widget('FilterWidget', $filterWidgetArray);
?>
+5
View File
@@ -62,6 +62,11 @@ if(!$rechte->isBerechtigt('student/noten',$studiengang_kz, 's'))
$semester = isset($_GET['semester'])?$_GET['semester']:'';
$typ = isset($_GET['typ'])?$_GET['typ']:'';
if(isset($_GET['studiensemester']) && preg_match('/[WS]S[0-9]{4}/', $_GET['studiensemester']))
{
$semester_aktuell = $_GET['studiensemester'];
}
if($semester=='')
die('Bitte ein Semester auswaehlen');
@@ -64,6 +64,11 @@ if (!$rechte->isBerechtigt('student/noten', $studiengang_kz, 's'))
$semester = isset($_GET['semester']) ? $_GET['semester'] : '';
$typ = isset($_GET['typ']) ? $_GET['typ'] : '';
if(isset($_GET['studiensemester']) && preg_match('/[WS]S[0-9]{4}/', $_GET['studiensemester']))
{
$semester_aktuell = $_GET['studiensemester'];
}
if ($semester == '')
die('Bitte ein Semester auswaehlen');
+6 -6
View File
@@ -178,7 +178,7 @@ class File_CSV
return $field;
}
if ($quote && $field{0} == $quote && $field{strlen($field)-1} == $quote) {
if ($quote && $field[0] == $quote && $field[strlen($field) - 1] == $quote) {
return substr($field, 1, -1);
}
return $field;
@@ -230,7 +230,7 @@ class File_CSV
} elseif ($c == "\n" || $c == "\r") {
$sub = ($prev == "\r") ? 2 : 1;
if ((strlen($buff) >= $sub) &&
($buff{strlen($buff) - $sub} == $quote))
($buff[strlen($buff) - $sub] == $quote))
{
$in_quote = false;
}
@@ -312,9 +312,9 @@ class File_CSV
$last =& $fields[count($fields) - 1];
// Fallback to read the line with readQuoted when guess
// that the simple explode won't work right
if (($last{strlen($last) - 1} == "\n"
&& $last{0} == $conf['quote']
&& $last{strlen(rtrim($last)) - 1} != $conf['quote'])
if (($last[strlen($last) - 1] == "\n"
&& $last[0] == $conf['quote']
&& $last[strlen(rtrim($last)) - 1] != $conf['quote'])
||
(count($fields) != $conf['fields'])
// XXX perhaps there is a separator inside a quoted field
@@ -511,4 +511,4 @@ class File_CSV
return true;
}
}
?>
?>
+5 -5
View File
@@ -96,7 +96,7 @@ class File_Util
{
if (File_Util::isAbsolute($path)) {
if (FILE_WIN32) {
return substr($path, $path{3} == '\\' ? 4 : 3);
return substr($path, $path[3] == '\\' ? 4 : 3);
}
return ltrim($path, '/');
}
@@ -182,7 +182,7 @@ class File_Util
if (FILE_WIN32) {
return preg_match('/^[a-zA-Z]:(\\\|\/)/', $path);
}
return ($path{0} == '/') || ($path{0} == '~');
return ($path[0] == '/') || ($path[0] == '~');
}
/**
@@ -244,11 +244,11 @@ class File_Util
} else {
$cwd = getcwd();
$drive = substr($cwd, 0, 2);
if ($path{0} !== $separator{0}) {
if ($path[0] !== $separator[0]) {
$path = substr($cwd, 3) . $separator . $path;
}
}
} elseif ($path{0} !== $separator) {
} elseif ($path[0] !== $separator) {
$path = getcwd() . $separator . $path;
}
@@ -323,7 +323,7 @@ class File_Util
$entries = array();
for ($dir = dir($path); false !== $entry = $dir->read(); ) {
if ($list & FILE_LIST_DOTS || $entry{0} !== '.') {
if ($list & FILE_LIST_DOTS || $entry[0] !== '.') {
$isRef = ($entry === '.' || $entry === '..');
$isDir = $isRef || is_dir($path .'/'. $entry);
if ( ((!$isDir && $list & FILE_LIST_FILES) ||
+1 -1
View File
@@ -240,7 +240,7 @@ class filter extends basis_db
{
//$value->value = preg_replace('/(.*?)selected=.selected./', '$1', $value->value);
//$value->value = preg_replace('/(.*?)\s*[selected=?selected?]/', '$1', $value->value);
//$value->value = preg_replace('/(.*?)\s*selected="selected"\s*/', '${1}', $value->value);
//$value->value = preg_replace('/(.*?)\s*selected="selected"\s*/', '$[1]', $value->value);
//$value->value = preg_replace('/^\s*selected=.*?selected.*?\s*/', ' ', $value->value);
//$value->value = str_replace('selected=', '', $value->value);
+1 -1
View File
@@ -1532,7 +1532,7 @@ class WavFile
} else {
// replace
for ($i = 0; $i < $sampleBytes; ++$i) {
$this->_samples{$offset + $i} = $sampleBinary{$i};
$this->_samples[$offset + $i] = $sampleBinary[$i];
}
}
+1 -1
View File
@@ -1435,7 +1435,7 @@ class Securimage
$length = strlen($code['display']);
for($i = 0; $i < $length; ++$i) {
$letter = $code['display']{$i};
$letter = $code['display'][$i];
$letters[] = $letter;
}
}
+1 -1
View File
@@ -844,7 +844,7 @@ html {
#cis-main .modal-header{
background-color: var(--fhc-primary);
color: var(--fhc-white-50);
color: var(--fhc-light);
}
#cis-main .modal-header .btn-close{
+25 -1
View File
@@ -11,6 +11,24 @@
html {
font-size: .875em;
}
html.fs_xx-small {
font-size: .5em;
}
html.fs_x-small {
font-size: .625em;
}
html.fs_small {
font-size: .75em;
}
html.fs_normal {
font-size: .875em;
}
html.fs_big {
font-size: 1em;
}
html.fs_huge {
font-size: 1.125em;
}
#appMenu {
width: 300px;
@@ -43,6 +61,12 @@ html {
flex: 1 1 auto;
}
#nav-user-btn img {
object-fit: contain;
height: 2.5rem;
width: 2.5rem;
}
.tabulator-row.disabled.tabulator-row-odd .tabulator-cell {
color: var(--gray-400);
}
@@ -160,4 +184,4 @@ html {
.tiny-90 div.tox.tox-tinymce {
height: 90% !important;
}
}
+4
View File
@@ -16,11 +16,15 @@
padding: .5rem 1rem;
text-decoration: none;
}
.fhc-app-menu li a.disabled {
--bs-link-opacity: .5;
}
.fhc-app-menu li a.active,
.fhc-app-menu li a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
background: var(--surface-hover);
}
.fhc-app-menu li a.disabled,
.fhc-app-menu li a.active {
pointer-events: none;
}
+32
View File
@@ -0,0 +1,32 @@
/**
* Copyright (C) 2025 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/>.
*/
export default {
get() {
return {
method: 'get',
url: 'api/frontend/v1/stv/config/get'
};
},
set(params) {
return {
method: 'post',
url: 'api/frontend/v1/stv/config/set',
params
};
}
};
+8
View File
@@ -46,6 +46,14 @@ export default {
url: url
};
},
search(params, studiensemester_kurzbz) {
return {
method: 'post',
url: 'api/frontend/v1/stv/students/search/'
+ encodeURIComponent(studiensemester_kurzbz),
params
};
},
verband(relative_path) {
return {
method: 'get',
+38
View File
@@ -148,6 +148,44 @@ const router = VueRouter.createRouter({
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(.*)*',
redirect: {
+135
View File
@@ -0,0 +1,135 @@
/**
* Copyright (C) 2025 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/>.
*/
import BsModal from "./Bootstrap/Modal.js";
import FhcForm from "./Form/Form.js";
import FormInput from "./Form/Input.js";
export default {
name: 'AppConfig',
components: {
BsModal,
FhcForm,
FormInput
},
emits: [
'update:modelValue'
],
props: {
modelValue: {
type: Object,
required: true
},
endpoints: {
type: Object,
required: true
}
},
data() {
return {
setup: {},
tempValues: {}
};
},
watch: {
'$p.user_language.value'(n, o) {
if (n !== o && o !== undefined && Object.keys(this.setup).length) {
this.$api
.call(this.endpoints.get())
.then(res => {
this.setup = {};
Object.keys(res.data).forEach(key => {
const binding = { ...res.data[key] };
delete binding.value;
delete binding.options;
const options = res.data[key].options;
this.setup[key] = {
binding,
options
};
});
})
.catch(this.$fhcAlert.handleSystemErrors);
}
}
},
methods: {
update() {
this.$refs.form
.call(this.endpoints.set(this.tempValues))
.then(() => {
this.$emit('update:modelValue', { ...this.tempValues });
this.$refs.modal.hide();
this.$fhcAlert.alertSuccess(this.$p.t('ui/settings_saved'));
})
.catch(this.$fhcAlert.handleSystemErrors);
}
},
created() {
this.$api
.call(this.endpoints.get())
.then(res => {
Object.keys(res.data).forEach(key => {
const binding = { ...res.data[key] };
delete binding.value;
delete binding.options;
const options = res.data[key].options;
this.tempValues[key] = res.data[key].value;
this.setup[key] = {
binding,
options
};
});
this.$emit('update:modelValue', { ...this.tempValues });
})
.catch(this.$fhcAlert.handleSystemErrors);
},
template: /* html */`
<fhc-form class="stv-config" ref="form" @submit.prevent="update">
<bs-modal
ref="modal"
class="fade"
id="configModal"
dialog-class="modal-lg"
@hidden-bs-modal="tempValues = { ...modelValue }"
>
<template #title>{{ $p.t('ui/settings') }}</template>
<template #default>
<div class="d-flex flex-column gap-5">
<form-input
v-for="(value, key) in setup"
v-model="tempValues[key]"
v-bind="value.binding"
>
<option
v-for="(label, val) in value.options"
:key="val"
:value="val"
>{{ label }}</option>
</form-input>
</div>
</template>
<template #footer>
<button class="btn btn-primary" type="submit">
{{ $p.t('ui/speichern') }}
</button>
</template>
</bs-modal>
</fhc-form>`
};
+1
View File
@@ -64,5 +64,6 @@ export default {
{{ menu.description }}
</a>
</li>
<slot />
</ul>`
};
@@ -124,6 +124,10 @@ export default {
? `${this.stg}/${this.semester}`
: this.stg;
}
else
{
this.selectedStudiengang = '';
}
this.filter = filter;
},
handleRowClicked(data)
@@ -163,6 +167,7 @@ export default {
const routeName = this.filter.emp ? 'byEmp' : 'byStg';
const params = { stg };
params.semester = '';
if (semester !== null)
params.semester = semester;
if (studiensemester_kurzbz)
@@ -132,9 +132,10 @@ export default {
frozen: true
}
],
layout: 'fitDataFill',
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: '400',
selectable: 1,
selectableRangeMode: 'click',
index: 'message_id',
pagination: true,
@@ -147,7 +148,7 @@ export default {
dataTreeCollapseElement:"<i class='fas fa-minus-square'></i>",
dataTreeChildIndent: 15,
dataTreeStartExpanded: false,
persistenceID: 'core-message',
persistenceID: 'core-message-2025112401',
locale: 'de',
"langs": {
"de":{ //German language definition
+231 -12
View File
@@ -16,8 +16,10 @@
*/
import CoreSearchbar from "../searchbar/searchbar.js";
import NavLanguage from "../navigation/Language.js";
import VerticalSplit from "../verticalsplit/verticalsplit.js";
import AppMenu from "../AppMenu.js";
import AppConfig from "../AppConfig.js";
import StvVerband from "./Studentenverwaltung/Verband.js";
import StvList from "./Studentenverwaltung/List.js";
import StvDetails from "./Studentenverwaltung/Details.js";
@@ -26,14 +28,17 @@ import StvStudiensemester from "./Studentenverwaltung/Studiensemester.js";
import ApiSearchbar from "../../api/factory/searchbar.js";
import ApiStv from "../../api/factory/stv.js";
import ApiStvVerband from '../../api/factory/stv/verband.js';
import ApiStvConfig from '../../api/factory/stv/config.js';
export default {
name: 'Studentenverwaltung',
components: {
CoreSearchbar,
NavLanguage,
VerticalSplit,
AppMenu,
AppConfig,
StvVerband,
StvList,
StvDetails,
@@ -45,6 +50,8 @@ export default {
permissions: Object,
stvRoot: String,
cisRoot: String,
avatarUrl: String,
logoutUrl: String,
activeAddons: String, // semicolon separated list of active addons
url_studiensemester_kurzbz: String,
url_mode: String,
@@ -76,15 +83,19 @@ export default {
},
configShowAufnahmegruppen: this.config.showAufnahmegruppen,
configAllowUebernahmePunkte: this.config.allowUebernahmePunkte,
configUseReihungstestPunkte: this.config.useReihungstestPunkte
configUseReihungstestPunkte: this.config.useReihungstestPunkte,
appConfig: Vue.computed(() => this.appconfig)
}
},
data() {
return {
appconfig: {},
configEndpoints: ApiStvConfig,
selected: [],
searchbaroptions: {
origin: 'stv',
calcheightonly: true,
nolivesearch: true,
types: {
student: Vue.computed(() => this.$p.t('search/type_student')),
prestudent: Vue.computed(() => this.$p.t('search/type_prestudent'))
@@ -123,6 +134,8 @@ export default {
studiengangKz: undefined,
studiengangKuerzel: '',
studiensemesterKurzbz: this.defaultSemester,
selected_semester: undefined,
selected_orgform: undefined,
lists: {
nations: [],
sprachen: [],
@@ -131,12 +144,60 @@ export default {
verbandEndpoint: ApiStvVerband
}
},
computed: {
appMenuExtraItems() {
const extraItems = [];
if (this.studiengangKz !== undefined && this.selected_semester !== undefined) {
const studiengang_kz = String(this.studiengangKz);
const semester = String(this.selected_semester);
const orgform = this.selected_orgform || '';
extraItems.push({
link: FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'content/statistik/notenspiegel.php?typ=xls'
+ '&studiengang_kz=' + studiengang_kz
+ '&semester=' + semester
+ '&studiensemester=' + this.studiensemesterKurzbz
+ '&orgform=' + orgform,
description: 'stv/grade_report_xls'
});
extraItems.push({
link: FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'content/statistik/notenspiegel_erweitert.php?typ=xls'
+ '&studiengang_kz=' + studiengang_kz
+ '&semester=' + semester
+ '&studiensemester=' + this.studiensemesterKurzbz
+ '&orgform=' + orgform,
description: 'stv/grade_report_xls_extended'
});
extraItems.push({
link: FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'content/statistik/notenspiegel.php?typ=html'
+ '&studiengang_kz=' + studiengang_kz
+ '&semester=' + semester
+ '&studiensemester=' + this.studiensemesterKurzbz
+ '&orgform=' + orgform,
description: 'stv/grade_report_html'
});
}
return extraItems;
}
},
watch: {
'url_studiensemester_kurzbz': function (newVal, oldVal) {
if (newVal !== oldVal) {
this.studiensemesterKurzbz = newVal;
this.$refs.stvList.updateUrl();
this.$refs.details.reload();
if(this.$route.name === 'search')
{
this.handleSearchUrl();
}
else
{
this.$refs.stvList.updateUrl();
this.$refs.details.reload();
}
}
},
'url_studiengang': function (newVal, oldVal) {
@@ -146,6 +207,25 @@ export default {
},
'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);
classList.forEach(cn => document.documentElement.classList.remove(cn));
document.documentElement.classList.add(this.appconfig.font_size);
// recalc Tabulator heights
if (this.$el) {
const tabulatorEls = this.$el.querySelectorAll('.tabulator');
for (const el of tabulatorEls) {
const tabulators = Tabulator.findTable(el);
if (tabulators) {
tabulators[0].searchRows().forEach(row => row.normalizeHeight());
}
}
}
}
},
methods: {
@@ -159,7 +239,7 @@ export default {
}
},
buildPrestudentSearchResultLink(data) {
return this.$fhcApi.getUri(
return this.$api.getUri(
'/studentenverwaltung'
+ '/' + this.studiensemesterKurzbz
+ '/prestudent/'
@@ -167,7 +247,7 @@ export default {
);
},
buildStudentSearchResultLink(data) {
return this.$fhcApi.getUri(
return this.$api.getUri(
'/studentenverwaltung'
+ '/' + this.studiensemesterKurzbz
+ '/student/'
@@ -175,14 +255,14 @@ export default {
);
},
buildPersonSearchResultLink(data) {
return this.$fhcApi.getUri(
return this.$api.getUri(
'/studentenverwaltung'
+ '/' + this.studiensemesterKurzbz
+ '/person/'
+ data.person_id
);
},
onSelectVerband( {link, studiengang_kz}) {
onSelectVerband({ link, studiengang_kz, semester, orgform_kurzbz }) {
let urlpath = String(link);
if (!urlpath.match(/\/prestudent/))
{
@@ -191,6 +271,8 @@ export default {
this.$refs.stvList.updateUrl(ApiStv.students.verband(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);
});
@@ -223,9 +305,6 @@ export default {
studiensemester_kurzbz: v
}
});
this.$refs.stvList.updateUrl();
this.$refs.details.reload();
},
reloadList() {
this.$refs.stvList.reload();
@@ -249,6 +328,37 @@ export default {
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() {
@@ -269,6 +379,42 @@ export default {
});
}
}
else
{
this.studiengangKz = undefined;
this.studiengangKuerzel = '';
this.clearTabulator();
}
},
onSearch(e) {
const searchsettings = { ...this.$refs.searchbar.searchsettings };
if (searchsettings.searchstr.length >= 2) {
this.blurSearchbar();
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
}
});
} else {
this.$router.push({
name: 'search_w_types',
params: {
studiensemester_kurzbz: this.studiensemesterKurzbz,
searchstr: searchsettings.searchstr,
types: searchsettings.types.join('+')
}
});
}
}
},
blurSearchbar() {
this.$refs.searchbar.$refs.input.blur();
this.$refs.searchbar.abort();
this.$refs.searchbar.hideresult();
}
},
created() {
@@ -376,10 +522,58 @@ export default {
<span class="fa-solid fa-table-list"></span>
</button>
<core-searchbar
ref="searchbar"
:searchoptions="searchbaroptions"
:searchfunction="searchfunction"
class="searchbar position-relative w-100"
show-btn-submit
@submit.prevent="onSearch"
></core-searchbar>
<div id="nav-user" class="dropdown">
<button
id="nav-user-btn"
class="btn btn-link rounded-0 py-0"
type="button"
data-bs-toggle="dropdown"
data-bs-target="#nav-user-menu"
aria-expanded="false"
aria-controls="nav-user-menu"
>
<img
:src="avatarUrl"
:alt="$p.t('profilUpdate/profilBild')"
class="bg-light avatar rounded-circle border border-light"
/>
</button>
<ul
ref="navUserDropdown"
class="dropdown-menu dropdown-menu-dark dropdown-menu-end rounded-0 text-center m-0"
aria-labelledby="nav-user-btn"
>
<li>
<button
type="button"
class="dropdown-item"
data-bs-toggle="modal"
data-bs-target="#configModal"
>
{{ $p.t('ui/settings') }}
</button>
</li>
<li><hr class="dropdown-divider m-0"/></li>
<li>
<nav-language
item-class="dropdown-item border-left-dark"
/>
</li>
<li><hr class="dropdown-divider m-0"/></li>
<li>
<a class="dropdown-item" :href="logoutUrl">
{{ $p.t('ui/logout') }}
</a>
</li>
</ul>
</div>
</header>
<div class="container-fluid overflow-hidden">
<div class="row h-100">
@@ -389,14 +583,38 @@ export default {
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" :aria-label="$p.t('ui/schliessen')"></button>
</div>
<div class="offcanvas-body">
<app-menu app-identifier="stv" />
<app-menu app-identifier="stv">
<li class="dropend">
<a
class="dropdown-toggle"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
:class="{ disabled: !appMenuExtraItems.length }"
data-bs-popper-config='{"strategy":"fixed"}'
>
{{ $p.t('stv/grade_report') }}
</a>
<ul class="dropdown-menu p-0">
<li
v-for="(item, key) in appMenuExtraItems"
:key="key"
>
<a class="dropdown-item" :href="item.link" target="_blank">
{{ $p.t(item.description) }}
</a>
</li>
</ul>
</li>
</app-menu>
</div>
</aside>
<nav id="sidebarMenu" class="bg-light offcanvas offcanvas-start col-md p-md-0 h-100">
<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" :endpoint="verbandEndpoint" @select-verband="onSelectVerband" class="col" style="height:0%"></stv-verband>
<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">
@@ -411,5 +629,6 @@ export default {
</main>
</div>
</div>
<app-config ref="config" v-model="appconfig" :endpoints="configEndpoints"></app-config>
</div>`
};
@@ -41,25 +41,34 @@ export default {
return Object.fromEntries(Object.entries(this.configStudents).filter(([ , value ]) => !value.showOnlyWithUid && !value.showOnlyWithUid));
}
},
watch: {
'$p.user_language.value'(n, o) {
if (n !== o && o !== undefined)
this.loadConfig();
}
},
methods: {
loadConfig() {
this.$api
.call(ApiStvApp.configStudent())
.then(result => {
this.configStudent = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvApp.configStudents())
.then(result => {
this.configStudents = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
},
reload() {
if (this.$refs.tabs?.$refs?.current?.reload)
this.$refs.tabs.$refs.current.reload();
}
},
created() {
this.$api
.call(ApiStvApp.configStudent())
.then(result => {
this.configStudent = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiStvApp.configStudents())
.then(result => {
this.configStudents = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.loadConfig();
},
template: `
<div class="stv-details h-100 d-flex flex-column">
@@ -163,12 +163,12 @@ export default {
frozen: true
},
],
layout: 'fitDataFill',
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: '200',
index: 'abschlusspruefung_id',
persistenceID: 'stv-details-finalexam'
persistenceID: 'stv-details-finalexam-2025112401'
},
tabulatorEvents: [
{
@@ -107,10 +107,10 @@ export default {
frozen: true
},
],
layout: 'fitDataFill',
layout: 'fitDataStretchFrozen',
height: '500',
index: 'anrechnung_id',
persistenceID: 'stv-details-anrechnungen'
persistenceID: 'stv-details-anrechnungen-2025112401'
},
tabulatorEvents: [
{
@@ -120,12 +120,12 @@ export default {
frozen: true
}
],
layout: 'fitDataFill',
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: 200,
index: 'aufnahmetermin_id',
persistenceID: 'stv-details-table_admission-dates'
persistenceID: 'stv-details-table_admission-dates-2025112401'
},
tabulatorEvents: [
{
@@ -0,0 +1,83 @@
export default {
name: "TabCombinePeople",
inject: {
cisRoot: {
from: 'cisRoot'
},
},
props: {
modelValue: Object,
},
data(){
return {
iframeUrl: null,
viewLoaded: false
}
},
computed: {
personIds() {
return Array.isArray(this.modelValue)
? this.modelValue.map(e => e.person_id)
: [this.modelValue.person_id];
},
detailStringPerson1(){
let person1 = this.modelValue[0];
return person1.vorname + " " + person1.nachname + "(" + person1.person_id + ")";
},
detailStringPerson2(){
let person2 = this.modelValue[1];
return person2.vorname + " " + person2.nachname + "(" + person2.person_id+ ")";
},
},
methods: {
combinePeople(){
this.viewLoaded = true;
let person1_id = this.personIds[0];
let person2_id = this.personIds[1];
if(person1_id == person2_id) {
return this.$fhcAlert.alertError(this.$p.t('stv', 'error_combinePeople_samePerson'));
}
let linkCombinePeople = this.cisRoot + 'vilesci/stammdaten/personen_wartung.php?person_id_1=' + person1_id + '&person_id_2='+ person2_id;
this.openLink(linkCombinePeople);
},
openLink(url) {
this.iframeUrl = url;
},
goBack(){
this.viewLoaded = false;
this.iframeUrl = null;
}
},
template: /*html*/ `
<div class="stv-details-combine-people h-100 pb-3">
<div v-if="!this.viewLoaded">
<h4>Personen zusammenlegen</h4>
<div v-if="this.modelValue.length">
<div v-if="this.modelValue.length == 2">
<p>{{$p.t('stv', 'question_combine_people', { person1: detailStringPerson1, person2: detailStringPerson2 })}}</p>
<button class="btn btn-primary" @click="combinePeople">{{$p.t('ui', 'ok')}}</button>
</div>
<div v-else>
ungültige Anzahl: {{this.modelValue.length}} <!-- should not be seen anymore-->
</div>
</div>
</div>
<div v-else>
<button class="btn btn-secondary" @click="goBack">{{$p.t('ui', 'cancel')}}</button>
</div>
<!-- Iframe-Section -->
<iframe
v-if="iframeUrl"
:src="iframeUrl"
class="w-100 mt-4 border-0"
style="height: 600px;"
></iframe>
</div>
`
};
@@ -71,10 +71,10 @@ export default {
frozen: true
},
],
layout: 'fitColumns',
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 200,
persistenceID: 'core-mobility-purpose'
persistenceID: 'core-mobility-purpose-2025112401'
},
tabulatorEvents: [
{
@@ -69,10 +69,10 @@ export default {
frozen: true
},
],
layout: 'fitColumns',
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 200,
persistenceID: 'core-mobility-support'
persistenceID: 'core-mobility-support-2025112401'
},
tabulatorEvents: [
{
@@ -109,12 +109,12 @@ export default {
frozen: true
},
],
layout: 'fitDataFill',
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: 200,
index: 'bisio_id',
persistenceID: 'stv-details-table_mobiliy'
persistenceID: 'stv-details-table_mobiliy-2025112401'
},
tabulatorEvents: [
{
@@ -217,9 +217,10 @@ export default {
},
columns,
height: '100%',
layout: 'fitDataStretchFrozen',
selectable: 1,
selectableRangeMode: 'click',
persistenceID: 'stv-details-noten-zeugnis-2025111801',
persistenceID: 'stv-details-noten-zeugnis-2025112401',
persistence:{
columns: ["width", "visible", "frozen"]
}
@@ -227,15 +227,15 @@ export default{
const rowData = row.getData();
if (this.dataMeldestichtag && this.dataMeldestichtag > rowData.datum)
{
row.getElement().classList.add('disabled');
row.getElement().classList.add('text-black','text-opacity-50','fst-italic');
}
},
layout: 'fitDataFill',
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
selectable: false,
index: 'statusId',
persistenceID: 'stv-multistatus'
persistenceID: 'stv-multistatus-2025112401'
},
tabulatorEvents: [
{
@@ -40,6 +40,9 @@ export default {
}
return lehreinheiten;
},
firmenverwaltungLink(){
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'vilesci/stammdaten/firma_frameset.html';
}
},
props: {
@@ -255,18 +258,25 @@ export default {
<div class="row mb-3">
<form-input
container-class="stv-details-projektarbeit-firma"
:label="$p.t('projektarbeit', 'firma')"
type="autocomplete"
optionLabel="name"
v-model="formData.firma"
name="firma"
:suggestions="filteredFirmen"
@complete="searchFirma"
:min-length="3"
>
</form-input>
<div class="col-10">
<form-input
container-class="stv-details-projektarbeit-firma"
:label="$p.t('projektarbeit', 'firma')"
type="autocomplete"
optionLabel="name"
v-model="formData.firma"
name="firma"
:suggestions="filteredFirmen"
@complete="searchFirma"
:min-length="3"
>
</form-input>
</div>
<div class="col-2 align-content-center">
<a :href="firmenverwaltungLink" target="_blank">
{{ $p.t('projektarbeit', 'zurFirmenverwaltung') }}
</a>
</div>
</div>
<div class="row mb-3">
@@ -240,7 +240,7 @@ export default {
frozen: true
},
],
//layout: 'fitDataFill',
layout: 'fitDataStretchFrozen',
height: 'auto',
minHeight: '200',
selectable: 1,
@@ -248,7 +248,7 @@ export default {
persistence:{
columns: true, //persist column layout
},
persistenceID: 'stv-details-projektarbeit'
persistenceID: 'stv-details-projektarbeit-2025112401'
}
return options;
}
@@ -396,8 +396,8 @@ export default {
<template #footer>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{$p.t('ui', 'abbrechen')}}</button>
<button v-if="statusNew" class="btn btn-primary" @click="addNewProjektarbeit()"> {{$p.t('ui', 'speichern')}}</button>
<button v-if="!statusNew && activeTab == 'details'" class="btn btn-primary" @click="updateProjektarbeit()"> {{$p.t('ui', 'speichern')}}</button>
<button v-if="statusNew" class="btn btn-primary" @click="addNewProjektarbeit()"> {{$p.t('ui', 'speichern')}}</button>
<button v-if="!statusNew && activeTab == 'details'" class="btn btn-primary" @click="updateProjektarbeit()"> {{$p.t('ui', 'speichern')}}</button>
</template>
</bs-modal>
@@ -115,7 +115,7 @@ export default {
frozen: true
},
],
//layout: 'fitDataFill',
layout: 'fitDataStretchFrozen',
layoutColumnsOnNewData: false,
height: 'auto',
minHeight: '100',
@@ -125,7 +125,7 @@ export default {
persistence:{
columns: true, //persist column layout
},
persistenceID: 'stv-details-projektbetreuer'
persistenceID: 'stv-details-projektbetreuer-2025112401'
},
tabulatorEvents: [
{
@@ -46,8 +46,6 @@ export default{
{title: "Punkte", field: "punkte", visible: false},
{
title: 'Aktionen', field: 'actions',
minWidth: 150,
maxWidth: 150,
formatter: (cell, formatterParams, onRendered) => {
let container = document.createElement('div');
container.className = "d-flex gap-2";
@@ -93,7 +91,7 @@ export default{
layoutColumnsOnNewData: false,
height: 'auto',
index: 'pruefung_id',
persistenceID: 'stv-details-pruefung-list'
persistenceID: 'stv-details-pruefung-list-2025112402'
},
tabulatorEvents: [
{
@@ -148,7 +146,6 @@ export default{
listMas: [],
listMarks: [],
zeugnisData: [],
checkData:[],
filter: false,
statusNew: true,
isStartDropDown: false,
@@ -181,7 +178,7 @@ export default{
this.pruefungData.student_uid = this.uid;
this.pruefungData.note = 9;
this.pruefungData.datum = new Date();
this.pruefungData.datum = luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
this.pruefungData.pruefungstyp_kurzbz = null;
if(lv_id){
this.pruefungData.lehrveranstaltung_id = lv_id;
@@ -193,7 +190,7 @@ export default{
this.isStartDropDown = false;
this.loadPruefung(pruefung_id).then(() => {
this.pruefungData.note = 9;
this.pruefungData.datum = new Date();
this.pruefungData.datum = luxon.DateTime.now().setZone(FHC_JS_DATA_STORAGE_OBJECT.timezone).toISODate();
this.pruefungData.pruefungstyp_kurzbz = null;
this.pruefungData.anmerkung = null;
this.prepareDropdowns();
@@ -229,9 +226,8 @@ export default{
return this.$refs.examData
.call(ApiStvExam.addPruefung(this.pruefungData))
.then(response => {
this.checkData = response.data;
if (this.checkData === 2 || this.checkData === 5)
this.$fhcAlert.alertInfo(this.$p.t('exam', 'hinweis_changeAfterExamDate'));
if (response.data)
this.$fhcAlert.alertDefault('info', 'Info', response.data, true);
else
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.hideModal('pruefungModal');
@@ -243,12 +239,13 @@ export default{
});
},
updatePruefung(pruefung_id){
this.checkChangeAfterExamDate();
return this.$refs.examData
.call(ApiStvExam.updatePruefung(pruefung_id, this.pruefungData))
.then(response => {
this.checkData = response.data;
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
if (response.data)
this.$fhcAlert.alertDefault('info', 'Info', response.data, true);
else
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
this.hideModal('pruefungModal');
this.resetModal();
}).catch(this.$fhcAlert.handleSystemError)
@@ -266,27 +263,6 @@ export default{
else
this.showHint = false;
},
checkChangeAfterExamDate() {
const data = {
student_uid: this.pruefungData.student_uid,
studiensemester_kurzbz: this.pruefungData.studiensemester_kurzbz,
lehrveranstaltung_id: this.pruefungData.lehrveranstaltung_id
};
return this.$api
.call(ApiStvExam.checkZeugnisnoteLv(data))
.then(result => {
this.zeugnisData = result.data;
let checkDate = this.zeugnisData[0].uebernahmedatum === '' ||
this.zeugnisData[0].benotungsdatum > this.zeugnisData[0].uebernahmedatum
? this.zeugnisData[0].benotungsdatum
: this.zeugnisData[0].uebernahmedatum;
if (checkDate >= this.pruefungData.datum
&& this.pruefungData.note !== this.zeugnisData[0].note) {
this.$fhcAlert.alertInfo(this.$p.t('exam', 'hinweis_changeAfterExamDate'));
}
})
.catch(this.$fhcAlert.handleSystemError);
},
deletePruefung(pruefung_id) {
return this.$api
.call(ApiStvExam.deletePruefung(pruefung_id))
@@ -534,6 +510,7 @@ export default{
container-class="mb-3"
type="DatePicker"
v-model="pruefungData.datum"
model-type="yyyy-MM-dd"
name="datum"
:label="$p.t('global/datum')"
auto-apply
@@ -2,6 +2,8 @@ import {CoreFilterCmpt} from "../../filter/Filter.js";
import ListNew from './List/New.js';
import ListFilter from './List/Filter.js';
import { capitalize } from '../../../helpers/StringHelpers.js';
import draggable from '../../../directives/draggable.js';
export default {
@@ -133,7 +135,17 @@ export default {
{
return Promise.resolve({ data: []});
}
return this.$api.call({method: 'post', url, params});
/**
* 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;
@@ -175,7 +187,7 @@ export default {
count: 0,
filteredcount: 0,
selectedcount: 0,
currentEndpointRawUrl: ''
currentEndpoint: null
}
},
computed: {
@@ -228,7 +240,84 @@ export default {
return "StudentList_" + today + ".csv";
}
},
watch: {
'$p.user_language.value'(n, o) {
if (n !== o && o !== undefined && this.$refs.table.tableBuilt) {
this.translateTabulator();
}
}
},
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();
},
@@ -273,16 +362,20 @@ export default {
updateUrl(endpoint, first) {
this.lastSelected = first ? undefined : this.selected;
if( endpoint === undefined )
console.log('function param endpoint: ' + JSON.stringify(endpoint));
console.log('current endpoint: ' + JSON.stringify(this.currentEndpoint));
if( endpoint === undefined && this.currentEndpoint === null)
{
endpoint = {url: this.currentEndpointRawUrl};
}
else if( endpoint.url === undefined )
endpoint = { url: '' };
}
else if( endpoint === undefined )
{
endpoint.url = this.currentEndpointRawUrl;
} else
endpoint = JSON.parse(JSON.stringify(this.currentEndpoint));
}
else
{
this.currentEndpointRawUrl = endpoint.url;
this.currentEndpoint = JSON.parse(JSON.stringify(endpoint));
}
endpoint.url = endpoint.url.replace(
@@ -290,20 +383,25 @@ export default {
encodeURIComponent(this.currentSemester)
);
const params = {};
if (this.filter.length)
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.tabulatorOptions.ajaxURL = endpoint.url;
this.tabulatorOptions.ajaxParams = params;
} else
if (this.$refs.table.tabulator) {
this.$refs.table.tabulator.on("tableBuilt", () => {
this.$refs.table.tabulator.setData(endpoint.url, params);
this.$refs.table.tabulator.setData(endpoint.url, params, method);
});
}
} else
this.$refs.table.tabulator.setData(endpoint.url, params);
this.$refs.table.tabulator.setData(endpoint.url, params, method);
},
dragCleanup(evt) {
if (evt.dataTransfer.dropEffect == 'none')
@@ -402,6 +500,7 @@ export default {
*/`
:new-btn-label="$p.t('stv/action_new')"
@click:new="actionNewPrestudent"
@table-built="translateTabulator"
>
<template #filter>
<div class="card">
@@ -16,11 +16,17 @@ export default {
inject: {
$reloadList: {
from: '$reloadList',
required: true
default: () => {}
},
currentSemester: {
from: 'currentSemester',
required: true
},
appConfig: {
from: 'appConfig',
default: {
number_displayed_past_studiensemester: 5
}
}
},
emits: [
@@ -52,6 +58,9 @@ export default {
return this.nodes.filter(node => this.favorites.list.includes(node.key));
return this.nodes;
},
noSemReloadNodes() {
return this.nodes.reduce(this.mapNodesToNoSemReloadNodes, []);
}
},
watch: {
@@ -59,6 +68,14 @@ export default {
if (newVal !== oldVal) {
this.setPreselection();
}
},
'appConfig.number_displayed_past_studiensemester'(newVal, oldVal) {
if (oldVal !== undefined) {
this.noSemReloadNodes.forEach(node => {
delete node.children;
this.onExpandTreeNode(node);
});
}
}
},
methods: {
@@ -114,7 +131,14 @@ export default {
},
onSelectTreeNode(node) {
if (node.data.link)
this.$emit('selectVerband', {link: node.data.link, studiengang_kz: node.data.stg_kz});
this.$emit('selectVerband', {link: node.data.link, studiengang_kz: node.data.stg_kz, semester: node.data.semester, orgform_kurzbz: node.data.orgform_kurzbz});
},
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 = {
@@ -187,22 +211,25 @@ export default {
if (!currentNode)
return;
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;
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++)
+59 -3
View File
@@ -33,7 +33,8 @@ export default {
data() {
return {
current: null,
tabs: {}
tabs: {},
count: null
}
},
computed: {
@@ -113,10 +114,12 @@ export default {
};
}
if (Array.isArray(config))
if (Array.isArray(config)) {
config.forEach((item, key) => _addToTabs(key, item));
else
}
else {
Object.entries(config).forEach(([key, item]) => _addToTabs(key, item));
}
if (this.current === null || !tabs[this.current]) {
if (tabs[this.default])
@@ -129,6 +132,57 @@ export default {
updateSuffix() {
this.getTabSuffix(this.currentTab);
},
removeInvalidCountTabs(){
if(this.modelValue.length)
{
let countIst = this.modelValue.length;
const tabsToDelete = [];
Object.entries(this.config).forEach(([key, item]) => {
const target = item?.config ? item : item?.value || item;
// check config for validCountMulti
if (target.config?.validCountMulti !== undefined) {
let tab;
let countSoll;
tab = key;
countSoll = target.config.validCountMulti;
//check if tab is existing
if (countSoll !== undefined && countSoll == countIst) {
//add tab if it was removed before
if (tab in this.tabs == false) {
const value = Vue.reactive({
suffix: '',
showSuffix: item.showSuffix || false
});
this.tabs[tab] = {
component: Vue.markRaw(Vue.defineAsyncComponent(() => import(item.component))),
title: Vue.computed(() => item.title || tab),
config: item.config,
tab,
value,
suffixhelper: item.suffixhelper ?? null
};
}
}
//add to toDeleteArray if count is not allowed
if (countSoll !== undefined && countSoll !== countIst) {
tabsToDelete.push(tab);
}
}
});
// Delete all tabs with count not allowed
tabsToDelete.forEach(k => {
delete this.tabs[k];
});
}
},
async getTabSuffix(tab) {
if (!tab.value.showSuffix) {
return;
@@ -151,9 +205,11 @@ export default {
},
mounted() {
this.getTabSuffixes();
this.removeInvalidCountTabs();
},
updated() {
this.getTabSuffixes();
this.removeInvalidCountTabs();
},
template: `
<template v-if="useprimevue">
@@ -0,0 +1,62 @@
/**
* Copyright (C) 2025 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/>.
*/
export default {
emits: [
'changed'
],
props: {
activeClass: {
type: String,
default: 'active'
},
itemClass: {
type: [String, Array, Object],
default: ''
}
},
data() {
return {
languages: FHC_JS_DATA_STORAGE_OBJECT.server_languages
};
},
methods:{
onChange(lang) {
if (this.languages.some(l => l.sprache === lang)) {
this.$p
.setLanguage(lang)
.then(() => {
if (document.querySelector('[cis4Reload]'))
window.location.reload();
else
this.$emit('changed', lang);
});
}
}
},
template: /*html*/`
<div class="navigation-language d-flex justify-content-center align-items-center flex-nowrap overflow-hidden">
<button
v-for="lang in languages"
:class="[itemClass, {[activeClass]: $p.user_language.value == lang.sprache}]"
:selected="$p.user_language.value == lang.sprache"
@click.prevent="onChange(lang.sprache)"
>
{{ lang.bezeichnung }}
</button>
</div>`
};
+43 -14
View File
@@ -23,7 +23,17 @@ export default {
mergedStudent,
mergedPerson
},
props: [ "searchoptions", "searchfunction" ],
props: {
searchoptions: {
type: Object,
required: true
},
searchfunction: {
type: Function,
required: true
},
showBtnSubmit: Boolean
},
provide() {
return {
query: Vue.computed(() => this.lastQuery)
@@ -102,11 +112,22 @@ export default {
>
<button
v-if="searchsettings.searchstr"
type="button"
class="searchbar_input_clear btn btn-outline-secondary"
@click="clearInput"
@focusin.stop
>
<i class="fas fa-close"></i>
</button>
<button
v-if="showBtnSubmit"
type="submit"
class="btn btn-primary"
:title="$p.t('search/submit')"
:aria-label="$p.t('search/submit')"
>
<i class="fas fa-search"></i>
</button>
<button
data-bs-toggle="collapse"
data-bs-target="#searchSettings"
@@ -219,12 +240,12 @@ export default {
});
}
},
methods: {
clearInput() {
this.searchsettings.searchstr = "";
this.hideresult();
this.$refs.input.focus()
},
methods: {
clearInput() {
this.searchsettings.searchstr = "";
this.hideresult();
this.$refs.input.focus();
},
getInitiallySelectedTypes() {
let result = false;
if (this.searchoptions.origin) {
@@ -283,13 +304,9 @@ export default {
this.calcSearchResultHeight();
},
search: function() {
if( this.searchtimer !== null ) {
clearTimeout(this.searchtimer);
}
if (this.abortController) {
this.abortController.abort();
this.abortController = null;
}
if(this.searchoptions?.nolivesearch === true) return;
this.abort();
if( this.searchsettings.searchstr.length >= 2 ) {
this.calcSearchResultExtent();
this.searchtimer = setTimeout(
@@ -300,6 +317,16 @@ export default {
this.showresult = false;
}
},
abort() {
if (this.searchtimer !== null) {
clearTimeout(this.searchtimer);
}
if (this.abortController) {
this.abortController.abort();
this.abortController = null;
}
this.searchresult = [];
},
callsearchapi: function() {
this.error = null;
this.searchresult.splice(0, this.searchresult.length);
@@ -392,6 +419,8 @@ export default {
window.removeEventListener('resize', this.calcSearchResultExtent);
},
showsearchresult: function() {
if(this.searchoptions?.nolivesearch === true) return;
if( this.searchsettings.searchstr.length >= 2 ) {
this.showresult = true;
window.addEventListener('resize', this.calcSearchResultExtent);
+1
View File
@@ -86,6 +86,7 @@ require_once('dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php');
require_once('dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php');
require_once('dbupdate_3.4/67490_studstatus_suche_abort_controller_haengt.php');
require_once('dbupdate_3.4/61164_abgabetool_quality_gates.php');
require_once('dbupdate_3.4/68744_StV_settings.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
@@ -0,0 +1,16 @@
<?php
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 = 'stv_font_size';"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO public.tbl_variablenname(name, defaultwert) VALUES('stv_font_size', null);";
if (!$db->db_query($qry))
echo '<strong>public.tbl_variablenname '.$db->db_last_error().'</strong><br>';
else
echo 'public.tbl_variablenname: Added name "stv_font_size"<br>';
}
}
+23
View File
@@ -529,6 +529,29 @@ $filters = array(
',
'oe_kurzbz' => null,
),
array(
'app' => 'infocenter',
'dataset_name' => 'onboarding',
'filter_kurzbz' => 'InfoCenterOnboarding',
'description' => '{Alle}',
'sort' => 1,
'default_filter' => true,
'filter' => '
{
"name": "Electronic Onboarding - Alle",
"columns": [
{"name": "PersonId"},
{"name": "Vorname"},
{"name": "Nachname"},
{"name": "LockUser"},
{"name": "HoldDate"},
{"name": "Rueckstellgrund"}
],
"filters": []
}
',
'oe_kurzbz' => null,
),
array(
'app' => 'infocenter',
'dataset_name' => 'aufgenommen',
-3
View File
@@ -1,3 +0,0 @@
<?php
phpinfo();
?>
+320
View File
@@ -38719,6 +38719,166 @@ array(
)
),
//**************************** CORE/stv
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'settings_no_displayed_past_sem',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Anzahl angezeigter vergangener Studiensemester',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Number of past semesters of study displayed',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'settings_fontsize',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Zoom',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Zoom',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'settings_fontsize_xx-small',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sehr Klein',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Very Small',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'settings_fontsize_x-small',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Kleiner',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Smaller',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'settings_fontsize_small',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Klein',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Small',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'settings_fontsize_normal',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Normal',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Normal',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'settings_fontsize_big',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Groß',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Big',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'settings_fontsize_huge',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Sehr groß',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Very big',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
@@ -38759,6 +38919,86 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'notes_person',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Anmerkungen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'notes',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'notes_prestudent',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Anmerkung Pre',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'note Pre',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'aufnahmegruppe_kurzbz',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Aufnahmegruppe',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'admission group',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'mentor',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Mentor',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'mentor',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
@@ -38939,6 +39179,86 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'grade_report',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Notenspiegel',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Grade report',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'grade_report_xls',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Notenspiegel EXCEL',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Grade report EXCEL',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'grade_report_xls_extended',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Notenspiegel erweitert EXCEL',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Grade report extended EXCEL',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',
'phrase' => 'grade_report_html',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Notenspiegel HTML',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Grade report HTML',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'stv',