Merge branch 'master' into feature-61232/Studierendenverwaltung_Karteireiter_Projektarbeit_portieren

This commit is contained in:
Alexei Karpenko
2025-09-22 13:39:36 +02:00
135 changed files with 3988 additions and 1819 deletions
+6
View File
@@ -7,3 +7,9 @@ if (! defined('BASEPATH')) exit('No direct script access allowed');
$config['cis_menu_root_content_id'] = 11087;
// send Mails for ProfilUpdate
$config['cis_send_profil_update_mails'] = true;
// Vilesci CI BaseUrl
$config['cis_vilesci_base_url'] = defined('VILESCI_ROOT') ? VILESCI_ROOT : APP_ROOT;
$config['cis_vilesci_index_page'] = 'index.ci.php';
// Cis CI BaseUrl
$config['cis_base_url'] = defined('CIS_ROOT') ? CIS_ROOT : APP_ROOT;
$config['cis_index_page'] = 'cis.php';
+11
View File
@@ -0,0 +1,11 @@
<?php
/*It defines which tags are available in LVVerwaltung and whether they are editable
$config['lvverwaltung_tags'] = [
'tag_1' => ['readonly' => false],
'tag_1' => ['readonly' => true]
];
*/
$config['lvverwaltung_tags'] = [];
+11 -1
View File
@@ -64,7 +64,7 @@ $config['navigation_header'] = array(
'lehrveranstaltungen' => array(
'link' => site_url('lehre/lvplanung/LvTemplateUebersicht'),
'icon' => '',
'description' => 'Lehrveranstaltungen',
'description' => 'Lehrveranstaltungen Templates',
'sort' => 15
),
'reihungstest' => array(
@@ -81,6 +81,16 @@ $config['navigation_header'] = array(
'sort' => 30,
'requiredPermissions' => 'infocenter:r'
),
'lvverwaltung' => array(
'link' => site_url('LVVerwaltung'),
'icon' => '',
'description' => 'LV Verwaltung',
'requiredPermissions' => array(
'admin:r',
'assistenz:r'
),
'sort' => 35
),
'lehrauftrag' => array(
'link' => site_url('lehre/lehrauftrag/Lehrauftrag/Dashboard'),
'description' => 'Lehrauftrag',
+37 -30
View File
@@ -63,6 +63,7 @@ $route['api/v1/system/[S|s]prache/(:any)'] = 'api/v1/system/sprache2/$1';
$route['Cis/LvPlan/.*'] = 'Cis/LvPlan/index/$1';
$route['Cis/MyLvPlan/.*'] = 'Cis/MyLvPlan/index/$1';
$route['Cis/MyLv/.*'] = 'Cis/MyLv/index/$1';
// Studierendenverwaltung List Routes
$route['api/frontend/v1/stv/[sS]tudents/inout'] = 'api/frontend/v1/stv/Students/index';
@@ -75,34 +76,34 @@ $route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/outgoing'] = 'api/
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/inout/gemeinsamestudien'] = 'api/frontend/v1/stv/Students/getGemeinsamestudien';
// (studiengang_kz)/prestudent[/(studiensemester_kurzbz)[/(filter)[/(otherfilter)]]]
$route['api/frontend/v1/stv/[sS]tudents/(:num)/prestudent'] = 'api/frontend/v1/stv/Students/getPrestudents/$1';
$route['api/frontend/v1/stv/[sS]tudents/(:num)/prestudent/([WS]S[0-9]{4})'] = 'api/frontend/v1/stv/Students/getPrestudents/$1/$2';
$route['api/frontend/v1/stv/[sS]tudents/(:num)/prestudent/([WS]S[0-9]{4})/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudents/$1/$2/$3';
$route['api/frontend/v1/stv/[sS]tudents/(:num)/prestudent/([WS]S[0-9]{4})/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudents/$1/$2/$4';
$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/prestudent'] = 'api/frontend/v1/stv/Students/getPrestudents/$1';
$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/prestudent/([WS]S[0-9]{4})'] = 'api/frontend/v1/stv/Students/getPrestudents/$1/$2';
$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/prestudent/([WS]S[0-9]{4})/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudents/$1/$2/$3';
$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/prestudent/([WS]S[0-9]{4})/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudents/$1/$2/$4';
// (studiengang_kz)/(orgform)/prestudent[/(studiensemester_kurzbz)[/(filter)[/(otherfilter)]]]
$route['api/frontend/v1/stv/[sS]tudents/(:num)/([A-Z]{2,3})/prestudent'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2';
$route['api/frontend/v1/stv/[sS]tudents/(:num)/([A-Z]{2,3})/prestudent/([WS]S[0-9]{4})'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2/$3';
$route['api/frontend/v1/stv/[sS]tudents/(:num)/([A-Z]{2,3})/prestudent/([WS]S[0-9]{4})/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2/$3/$4';
$route['api/frontend/v1/stv/[sS]tudents/(:num)/([A-Z]{2,3})/prestudent/([WS]S[0-9]{4})/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2/$3/$5';
$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/([A-Z]{2,3})/prestudent'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2';
$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/([A-Z]{2,3})/prestudent/([WS]S[0-9]{4})'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2/$3';
$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/([A-Z]{2,3})/prestudent/([WS]S[0-9]{4})/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2/$3/$4';
$route['api/frontend/v1/stv/[sS]tudents/(-?[0-9]+)/([A-Z]{2,3})/prestudent/([WS]S[0-9]{4})/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getPrestudentsOrgform/$1/$2/$3/$5';
// (studiensemester_kurzbz)/(studiengang_kz)/(semester)/grp/(gruppe)
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(:num)/(:num)/grp/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsSpezialgruppe/$1/$2/$3/$4';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/(:num)/grp/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsSpezialgruppe/$1/$2/$3/$4';
// (studiensemester_kurzbz)/(studiengang_kz)[/(semester)[/(verband)[/(gruppe)]]]
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(:num)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(:num)/(:num)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2/$3';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(:num)/(:num)/(:any)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2/$3/$4';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(:num)/(:num)/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2/$3/$4/$5';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/(:num)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2/$3';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/(:num)/(:any)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2/$3/$4';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/(:num)/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getStudents/$1/$2/$3/$4/$5';
// (studiensemester_kurzbz)/(studiengang_kz)/(orgform)/(semester)/grp/(gruppe)
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(:num)/([A-Z]{2,3})/(:num)/grp/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsOrgformSpezialgruppe/$1/$2/$3/$4/$5';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/([A-Z]{2,3})/(:num)/grp/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsOrgformSpezialgruppe/$1/$2/$3/$4/$5';
// (studiensemester_kurzbz)/(studiengang_kz)/(orgform)[/(semester)[/(verband)[/(gruppe)]]]
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(:num)/([A-Z]{2,3})'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(:num)/([A-Z]{2,3})/(:num)'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3/$4';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(:num)/([A-Z]{2,3})/(:num)/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3/$4/$5';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(:num)/([A-Z]{2,3})/(:num)/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3/$4/$5/$6';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/([A-Z]{2,3})'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/([A-Z]{2,3})/(:num)'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3/$4';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/([A-Z]{2,3})/(:num)/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3/$4/$5';
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/(-?[0-9]+)/([A-Z]{2,3})/(:num)/(:any)/(:any)'] = 'api/frontend/v1/stv/Students/getStudentsOrgform/$1/$2/$3/$4/$5/$6';
// // (studiensemester_kurzbz)/uid/(uid)
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/uid/(:any)'] = 'api/frontend/v1/stv/Students/getStudent/$1/$2';
@@ -111,24 +112,30 @@ $route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/prestudent/(:num)'] = 'a
// // (studiensemester_kurzbz)/person/(person_id)
$route['api/frontend/v1/stv/[sS]tudents/([WS]S[0-9]{4})/person/(:num)'] = 'api/frontend/v1/stv/Students/getPerson/$1/$2';
// load routes from extensions
$subdir = 'application/config/extensions';
$dirlist = scandir($subdir);
// load routes from extensions, also look for environment-specific configs
$subdirs = ['application/config/extensions', 'application/config/' . ENVIRONMENT . '/extensions'];
if ($dirlist)
foreach($subdirs as $subdir)
{
$files = array_diff($dirlist, array('.','..'));
foreach ($files as &$item)
if(is_dir($subdir))
{
if (is_dir($subdir . DIRECTORY_SEPARATOR . $item))
$dirlist = scandir($subdir);
if ($dirlist)
{
$routes_file = $subdir . DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . 'routes.php';
$files = array_diff($dirlist, array('.','..'));
if (file_exists($routes_file))
foreach ($files as &$item)
{
require($routes_file);
if (is_dir($subdir . DIRECTORY_SEPARATOR . $item))
{
$routes_file = $subdir . DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . 'routes.php';
if (file_exists($routes_file))
{
require($routes_file);
}
}
}
}
}
}
}
+1 -1
View File
@@ -27,7 +27,7 @@ $config['student']['resultfields'] = [
"(p.vorname || ' ' || p.nachname) AS name",
"ARRAY[s.student_uid || '@' || '" . DOMAIN . "'] AS email",
"CASE
WHEN p.foto IS NOT NULL THEN 'data:image/jpeg' || CONVERT_FROM(DECODE('3b','hex'), 'UTF8') || 'base64,' || p.foto
WHEN (p.foto_sperre = false AND p.foto IS NOT NULL) THEN 'data:image/jpeg' || CONVERT_FROM(DECODE('3b','hex'), 'UTF8') || 'base64,' || p.foto
ELSE NULL END
AS photo_url",
"b.aktiv"
+34
View File
@@ -58,6 +58,10 @@ $config['tabs'] =
//if true, Anrechnungen can be added and edited in tab Anrechnungen
'editableAnrechnungen' => false,
],
'notes' => [
//if true, the count of Messages will be shown in the header of the Tab Messages
'showCountNotes' => true
]
];
// List of fields to show when ZGV_DOKTOR_ANZEIGEN is defined
@@ -84,3 +88,33 @@ $config['tabs']['projektarbeit']['defaultProjektbetreuerStunden'] = '4.0';
$config['tabs']['projektarbeit']['defaultProjektbetreuerStundenDiplom'] = '5.0';
$config['tabs']['projektarbeit']['lvLektroinnenzuteilungFixangestelltStundensatz'] = true;
$config['tabs']['projektarbeit']['defaultProjektbetreuerStundensatz'] = '80.0';
$config['student_tab_order'] = [
'details',
'notes',
'messages',
'contact',
'prestudent',
'status',
'documents',
'archive',
'banking',
'grades',
'exam',
'exemptions',
'finalexam',
'mobility',
'jointstudies',
'admissionDates',
'groups',
'functions',
'coursedates',
'resources',
];
$config['students_tab_order'] = [
'banking',
'status',
'groups',
'finalexam',
'archive',
];
+2 -1
View File
@@ -72,6 +72,7 @@ class Auth extends FHC_Controller
{
$this->load->library('AuthLib');
$this->authlib->logout();
redirect('/Cis/Auth/login', 'refresh');
setcookie('fhclogout', 'fhclogout', 0, '/');
redirect(base_url('/cis/private/logout.php'), 'refresh');
}
}
+12 -740
View File
@@ -9,54 +9,21 @@ if (!defined('BASEPATH'))
class ProfilUpdate extends Auth_Controller
{
public static $STATUS_PENDING = NULL;
public static $STATUS_ACCEPTED = NULL;
public static $STATUS_REJECTED = NULL;
public static $TOPICS = [];
public function __construct()
{
parent::__construct([
'index' => ['student/stammdaten:r', 'mitarbeiter/stammdaten:r'],
'id' => ['student/stammdaten:r', 'mitarbeiter/stammdaten:r'],
'getProfilUpdateWithPermission' => ['student/stammdaten:r', 'mitarbeiter/stammdaten:r'],
'acceptProfilRequest' => ['student/stammdaten:rw', 'mitarbeiter/stammdaten:rw'],
'denyProfilRequest' => ['student/stammdaten:rw', 'mitarbeiter/stammdaten:rw'],
'show' => ['basis/cis:r'],
'insertProfilRequest' => ['basis/cis:rw'],
'updateProfilRequest' => ['basis/cis:rw'],
'deleteProfilRequest' => ['basis/cis:rw'],
'selectProfilRequest' => ['basis/cis:r'],
'insertFile' => ['basis/cis:rw'],
'getProfilRequestFiles' => ['basis/cis:r'],
'getStatus' => ['basis/cis:r'],
'getTopic' => ['basis/cis:r'],
'id' => ['student/stammdaten:r', 'mitarbeiter/stammdaten:r']
]);
$this->load->config('cis');
$this->load->model('person/Profil_update_model', 'ProfilUpdateModel');
$this->load->model('person/Kontakt_model', 'KontaktModel');
$this->load->model('person/Adresse_model', 'AdresseModel');
$this->load->model('person/Adressentyp_model', 'AdressenTypModel');
$this->load->model('person/Person_model', 'PersonModel');
$this->load->model('ressource/mitarbeiter_model', 'MitarbeiterModel');
$this->load->model('crm/Student_model', 'StudentModel');
$this->load->model('person/Benutzer_model', 'BenutzerModel');
$this->load->model('system/Sprache_model', 'SpracheModel');
$this->load->model('person/Profil_update_status_model', 'ProfilUpdateStatusModel');
$this->load->model('person/Profil_update_topic_model', 'ProfilUpdateTopicModel');
// Load language phrases
$this->loadPhrases(
array(
'ui',
'global',
'person',
'profil',
'profilUpdate'
)
);
@@ -64,32 +31,10 @@ class ProfilUpdate extends Auth_Controller
$this->load->library('DmsLib');
$this->load->library('PermissionLib');
//? put the uid and pid inside the controller for reusability
$this->uid = getAuthUID();
$this->pid = getAuthPersonID();
// setup the ProfilUpdate states
$this->ProfilUpdateStatusModel->addSelect(['status_kurzbz']);
$status_kurzbz = $this->ProfilUpdateStatusModel->load();
if (hasData($status_kurzbz)) {
list($status_pending, $status_accepted, $status_rejected) = getData($status_kurzbz);
self::$STATUS_PENDING = $status_pending->status_kurzbz;
self::$STATUS_ACCEPTED = $status_accepted->status_kurzbz;
self::$STATUS_REJECTED = $status_rejected->status_kurzbz;
}
// setup the ProfilUpdate topics
$this->ProfilUpdateTopicModel->addSelect(['topic_kurzbz']);
$topic_kurzbz = $this->ProfilUpdateTopicModel->load();
if (hasData($topic_kurzbz)) {
foreach (getData($topic_kurzbz) as $topic) {
self::$TOPICS[$topic->topic_kurzbz] = $topic->topic_kurzbz;
}
}
}
public function index()
{
$this->load->view('Cis/ProfilUpdate');
@@ -100,129 +45,14 @@ class ProfilUpdate extends Auth_Controller
$this->load->view('Cis/ProfilUpdate', ['profil_update_id' => $profil_update_id]);
}
public function getStatus()
{
echo json_encode([self::$STATUS_PENDING => self::$STATUS_PENDING, self::$STATUS_ACCEPTED => self::$STATUS_ACCEPTED, self::$STATUS_REJECTED => self::$STATUS_REJECTED]);
}
public function getTopic()
{
echo json_encode(self::$TOPICS);
}
private function sendEmail_onProfilUpdate_response($uid, $topic, $status)
{
if($this->config->item('cis_send_profil_update_mails') === false)
{
return;
}
$this->load->helper('hlp_sancho_helper');
$email = $uid . "@" . DOMAIN;
function languageQuery($language)
{
return "select index from public.tbl_sprache where sprache = '" + $language + "'";
}
$this->ProfilUpdateStatusModel->addSelect(["bezeichnung_mehrsprachig[(" . languageQuery('German') . ")] as status_de", "bezeichnung_mehrsprachig[(" . languageQuery('English') . ")] as status_en"]);
$status_translation = $this->ProfilUpdateStatusModel->loadWhere(["status_kurzbz" => $status]);
if (isError($status_translation)) {
show_error($this->p->t('profilUpdate', 'ProfilUpdateStatusTranslationError'));
}
$status_translation = hasData($status_translation) ? getData($status_translation)[0] : null;
if (isset($status_translation)) {
$mail_res = sendSanchoMail("profil_update_response", ['topic' => $topic, 'status_de' => $status_translation->status_de, 'status_en' => $status_translation->status_en, 'href' => APP_ROOT . 'Cis/Profil'], $email, ("Profil Änderung " . $this->p->t('profilUpdate', 'pending')));
if (!$mail_res) {
show_error($this->p->t('profilUpdate', 'profilUpdate_email_error'));
}
}
}
private function sendEmail_onProfilUpdate_insertion($uid, $profil_update_id, $topic)
{
if($this->config->item('cis_send_profil_update_mails') === false)
{
return;
}
$this->load->helper('hlp_sancho_helper');
$emails = [];
$isMitarbeiter_res = $this->MitarbeiterModel->isMitarbeiter($uid);
if (isError($isMitarbeiter_res)) {
show_error($this->p->t('profilUpdate', 'profilUpdate_mitarbeiterCheck_error'));
}
$isMitarbeiter_res = getData($isMitarbeiter_res);
//! if the $uid is a mitarbeiter and student, only the hr is notified by email
if ($isMitarbeiter_res) {
//? user is not a student therefore he is a mitarbeiter, send email to Personalverwaltung
//? use constant variable MAIL_GST to mail to the personalverwaltung
$this->MitarbeiterModel->addSelect([TRUE]);
$this->MitarbeiterModel->addJoin("public.tbl_benutzer", "public.tbl_benutzer.uid = public.tbl_mitarbeiter.mitarbeiter_uid");
//? check if the the userID is a mitarbeiter and if the benutzer is active
$res = $this->MitarbeiterModel->loadWhere(["public.tbl_mitarbeiter.mitarbeiter_uid" => $uid, "public.tbl_benutzer.aktiv" => TRUE]);
if (isError($res)) {
show_error("was not able to query the mitarbeiter and benutzer by the uid: " . $uid);
}
if (hasData($res)) {
array_push($emails, MAIL_GST);
} else {
show_error($this->p->t('profilUpdate', 'profilUpdate_mitarbeiterCheck_error'));
}
} else {
//? if it is not a mitarbeiter, check whether it is a student and send email to studiengang
$isStudent_res = $this->StudentModel->isStudent($uid);
if (isError($isStudent_res)) {
show_error($this->p->t('profilUpdate', 'profilUpdate_studentCheck_error'));
}
$isStudent_res = getData($isStudent_res);
if ($isStudent_res) {
//? Send email to the Studiengangsassistentinnen
$this->StudentModel->addSelect(["public.tbl_studiengang.email"]);
$this->StudentModel->addJoin("public.tbl_benutzer", "public.tbl_benutzer.uid = public.tbl_student.student_uid");
$this->StudentModel->addJoin("public.tbl_prestudent", "public.tbl_benutzer.person_id = public.tbl_prestudent.person_id");
$this->StudentModel->addJoin("public.tbl_prestudentstatus", "public.tbl_prestudentstatus.prestudent_id = public.tbl_prestudent.prestudent_id");
$this->StudentModel->addJoin("public.tbl_studiengang", "public.tbl_studiengang.studiengang_kz = public.tbl_prestudent.studiengang_kz");
//* check if the benutzer itself is active
//* check if the student status is Student or Diplomand (active students)
$this->StudentModel->db->where_in("public.tbl_prestudentstatus.status_kurzbz", ['Student', 'Diplomand']);
$res = $this->StudentModel->loadWhere(["public.tbl_benutzer.aktiv" => TRUE, "public.tbl_student.student_uid" => $uid]);
if (isError($res)) {
show_error(getData($res));
} else {
$res = hasData($res) ? getData($res) : [];
foreach ($res as $emailObj) {
array_push($emails, $emailObj->email);
}
}
}
}
$mail_res = [];
//? sending email
foreach ($emails as $email) {
array_push($mail_res, sendSanchoMail("profil_update", ['uid' => $uid, 'topic' => $topic, 'href' => APP_ROOT . 'Cis/ProfilUpdate/id/' . $profil_update_id], $email, ("Profil Änderung von " . $uid)));
}
foreach ($mail_res as $m_res) {
if (!$m_res) {
show_error($this->p->t('profilUpdate', 'profilUpdate_email_error'));
}
}
}
public function show($dms_id)
{
$profil_update = $this->ProfilUpdateModel->loadWhere(['attachment_id' => $dms_id]);
$profil_update = hasData($profil_update) ? getData($profil_update)[0] : null;
//? checks if an profil update exists with the dms_id requested from the user
if ($profil_update) {
if ($profil_update)
{
$is_mitarbeiter_profil_update = getData($this->MitarbeiterModel->isMitarbeiter($profil_update->uid));
$is_student_profil_update = getData($this->StudentModel->isStudent($profil_update->uid));
@@ -230,7 +60,8 @@ class ProfilUpdate extends Auth_Controller
$this->permissionlib->isBerechtigt('student/stammdaten:r') && $is_student_profil_update ||
$this->permissionlib->isBerechtigt('mitarbeiter/stammdaten:r') && $is_mitarbeiter_profil_update ||
$this->uid == $profil_update->uid
) {
)
{
// Get file to be downloaded from DMS
$newFilename = $this->uid . "/document_" . $dms_id;
$download = $this->dmslib->download($dms_id);
@@ -239,576 +70,17 @@ class ProfilUpdate extends Auth_Controller
// Download file
$this->outputFile(getData($download));
} else {
}
else
{
show_error($this->p->t('profilUpdate', 'profilUpdate_permission_error'));
return;
}
} else {
}
else
{
show_error($this->p->t('profilUpdate', 'profilUpdate_dms_error'));
return;
}
}
public function insertFile($replace)
{
$replace = json_decode($replace);
if (!count($_FILES)) {
echo json_encode([]);
return;
}
//? if replace is set it contains the profil_update_id in which the attachment_id has to be replaced
if (isset($replace)) {
$this->ProfilUpdateModel->addSelect(["attachment_id"]);
$profilUpdate = $this->ProfilUpdateModel->load([$replace]);
if (isError($profilUpdate)) {
return json_encode(error($this->p->t('profilUpdate', 'profilUpdate_loading_error')));
}
//? get the attachmentID
$dms_id = hasData($profilUpdate) ? getData($profilUpdate)[0]->attachment_id : null;
//? delete old dms_file of Profil Update
$this->deleteOldVersionFile($dms_id);
}
$files = $_FILES['files'];
$file_count = count($files['name']);
$res = [];
for ($i = 0; $i < $file_count; $i++) {
$_FILES['files']['name'] = $files['name'][$i];
$_FILES['files']['type'] = $files['type'][$i];
$_FILES['files']['tmp_name'] = $files['tmp_name'][$i];
$_FILES['files']['error'] = $files['error'][$i];
$_FILES['files']['size'] = $files['size'][$i];
$dms = [
"kategorie_kurzbz" => "profil_aenderung",
"version" => 0,
"name" => $_FILES['files']['name'],
"mimetype" => $_FILES['files']['type'],
"beschreibung" => $this->uid . " Profil Änderung",
"insertvon" => $this->uid,
"insertamum" => "NOW()",
];
$tmp_res = $this->dmslib->upload($dms, 'files', array("jpg", "png", "pdf"));
$tmp_res = hasData($tmp_res) ? getData($tmp_res) : null;
array_push($res, $tmp_res);
}
echo json_encode($res);
}
public function selectProfilRequest()
{
$_GET = json_decode($this->input->raw_input_stream, true);
$uid = $this->input->get('uid');
$id = $this->input->get('id');
$whereClause = ['uid' => $this->uid];
if (isset($uid))
$whereClause['uid'] = $uid;
if (isset($id))
$whereClause['id'] = $id;
$res = $this->ProfilUpdateModel->getProfilUpdatesWhere($whereClause);
$res = hasData($res) ? getData($res) : null;
echo json_encode($res);
}
public function getProfilRequestFiles()
{
$id = json_decode($this->input->raw_input_stream);
$this->ProfilUpdateModel->addSelect(["attachment_id"]);
$attachmentID = $this->ProfilUpdateModel->load([$id]);
if (isError($attachmentID)) {
return json_encode(error($this->p->t('profilUpdate', 'profilUpdate_loading_error')));
}
//? get the attachmentID
$dms_id = hasData($attachmentID) ? getData($attachmentID)[0]->attachment_id : null;
//? get the name to the file
$this->DmsVersionModel->addSelect(["name", "dms_id"]);
$attachment = $this->DmsVersionModel->load([$dms_id, 0]);
if (isError($attachment)) {
return json_encode(error($this->p->t('profilUpdate', 'profilUpdate_dmsVersion_error')));
}
$attachment = hasData($attachment) ? getData($attachment) : null;
//? returns {name:..., dms_id:...}
echo json_encode($attachment);
}
public function insertProfilRequest()
{
$json = json_decode($this->input->raw_input_stream);
$payload = $json->payload;
$identifier = property_exists($json->payload, "kontakt_id") ? "kontakt_id" : (property_exists($json->payload, "adresse_id") ? "adresse_id" : null);
$data = ["topic" => $json->topic, "uid" => $this->uid, "requested_change" => json_encode($payload), "insertamum" => "NOW()", "insertvon" => $this->uid, "status" => self::$STATUS_PENDING ?: 'Pending'];
//? insert fileID in the dataset if sent with post request
if (isset($json->fileID)) {
$data['attachment_id'] = $json->fileID;
}
//? loops over all updateRequests from a user to validate if the new request is valid
$res = $this->ProfilUpdateModel->getProfilUpdatesWhere(["uid" => $this->uid]);
if (isError($res)) {
show_error($this->p->t('profilUpdate', 'profilUpdate_loading_error'));
}
$res = hasData($res) ? getData($res) : null;
//? the user cannot delete a zustelladresse/kontakt
if (isset($payload->delete) && $payload->{$identifier == "kontakt_id" ? "zustellung" : "zustelladresse"}) {
echo json_encode(error($this->p->t('profilUpdate', 'profilUpdate_deleteZustellung_error')));
return;
}
//? if the user tries to delete a adresse, checks whether the adresse is a heimatadresse, if so an error is raised
if (isset($payload->delete) && $identifier == "adresse_id") {
$adr = $this->AdresseModel->load($payload->$identifier);
$adr = getData($adr)[0];
if ($adr->heimatadresse) {
echo json_encode(error($this->p->t('profilUpdate', 'profilUpdate_deleteZustellung_error')));
return;
}
}
if ($res) {
$pending_changes = array_filter($res, function ($element) {
return $element->status == (self::$STATUS_PENDING ?: "Pending");
});
foreach ($pending_changes as $update_request) {
$existing_change = $update_request->requested_change;
//? the user can add as many new kontakte/adressen as he likes
if (!isset($payload->add) && property_exists($existing_change, $identifier) && property_exists($payload, $identifier) && $existing_change->$identifier == $payload->$identifier) {
//? the kontakt_id / adresse_id of a change has to be unique
echo json_encode(error($this->p->t('profilUpdate', 'profilUpdate_changeTwice_error')));
return;
}
//? if it is not updating any kontakt/adresse, the topic has to be unique
elseif (!$identifier && $update_request->topic == $json->topic) {
echo json_encode(error($this->p->t('profilUpdate', 'profilUpdate_changeTopicTwice_error', ['0' => $update_request->topic])));
return;
}
}
}
$insertID = $this->ProfilUpdateModel->insert($data);
if (isError($insertID)) {
show_error(getData($insertID));
} else {
$insertID = hasData($insertID) ? getData($insertID) : null;
//? sends emails to the correspondents of the $uid
$this->sendEmail_onProfilUpdate_insertion($this->uid, $insertID, $json->topic);
echo json_encode(success($insertID));
}
}
public function updateProfilRequest()
{
$json = json_decode($this->input->raw_input_stream);
$updateData = ["requested_change" => json_encode($json->payload), "updateamum" => "NOW()", "updatevon" => $this->uid];
if (isset($json->fileID)) {
$updateData['attachment_id'] = json_decode($json->fileID);
}
$updateID = $this->ProfilUpdateModel->update([$json->ID], $updateData);
//? insert fileID in the dataset if sent with post request
if (isError($updateID)) {
//catch error
} else {
$updateID = hasData($updateID) ? getData($updateID)[0] : null;
//TODO: should an email be sent to the responsable people when the user changes his profil update
echo json_encode(success($updateID));
}
}
public function deleteProfilRequest()
{
$json = json_decode($this->input->raw_input_stream);
$delete_res = $this->ProfilUpdateModel->delete([$json]);
echo json_encode($delete_res);
}
public function getProfilUpdateWithPermission($status = null)
{
// early return if no status has been passed as argument
if (!isset($status)) {
echo json_encode($this->ProfilUpdateModel->getProfilUpdateWithPermission());
return;
}
// get the sprache of the user
$sprachenIndex = $this->SpracheModel->loadWhere(["sprache" => getUserLanguage()]);
$sprachenIndex = hasData($sprachenIndex) ? getData($sprachenIndex)[0]->index : null;
if (isset($sprachenIndex) && isset($status)) {
// get the corresponding status kurz_bz primary key out of the translation
$status = $this->ProfilUpdateStatusModel->execReadOnlyQuery("select * from public.tbl_profil_update_status where ? = ANY(bezeichnung_mehrsprachig)", [$status]);
$status = hasData($status) ? getData($status)[0]->status_kurzbz : null;
$res = $this->ProfilUpdateModel->getProfilUpdateWithPermission(isset($status) ? ['status' => $status] : null);
echo json_encode($res);
}
}
private function getOE_from_student($student_uid)
{
//? returns the oe_einheit eines Studenten
$query = "SELECT public.tbl_studiengang.oe_kurzbz
FROM public.tbl_student
JOIN public.tbl_studiengang ON tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz
WHERE public.tbl_student.student_uid = ?;";
$res = $this->StudentModel->execReadOnlyQuery($query, [$student_uid]);
if (!isSuccess($res)) {
show_error($this->p->t('profilUpdate', 'profilUpdate_loadingOE_error'));
}
$res = hasData($res) ? getData($res) : [];
$res = array_map(
function ($item) {
return $item->oe_kurzbz;
},
$res
);
return $res;
}
public function acceptProfilRequest()
{
$_POST = json_decode($this->input->raw_input_stream, true);
$id = $this->input->post('profil_update_id', true);
$uid = $this->input->post('uid', true);
//? fetching person_id using UID
$personID = $this->PersonModel->getByUid($uid);
$personID = hasData($personID) ? getData($personID)[0]->person_id : null;
$status_message = $this->input->post('status_message', true);
$topic = $this->input->post('topic', true);
//! somehow the xss check converted boolean false to empty string
$requested_change = $this->input->post('requested_change');
//! check for required information
if (!isset($id) || !isset($uid) || !isset($personID) || !isset($requested_change) || !isset($topic)) {
return json_encode(error($this->p->t('profilUpdate', 'profilUpdate_requiredInformation_error')));
}
$is_mitarbeiter_profil_update = getData($this->MitarbeiterModel->isMitarbeiter($uid));
$is_student_profil_update = getData($this->StudentModel->isStudent($uid));
//? check if the permissions are set correctly
if (
$this->permissionlib->isBerechtigt('student/stammdaten', "suid", $this->getOE_from_student($uid)) && $is_student_profil_update ||
$this->permissionlib->isBerechtigt('mitarbeiter/stammdaten', "suid") && $is_mitarbeiter_profil_update
) {
if (is_array($requested_change) && array_key_exists("adresse_id", $requested_change)) {
$insertID = $this->handleAdresse($requested_change, $personID);
$insertID = hasData($insertID) ? getData($insertID) : null;
if (isset($insertID)) {
$requested_change['adresse_id'] = $insertID;
$update_res = $this->updateRequestedChange($id, $requested_change);
if (isError($update_res)) {
echo json_encode(error($this->p->t('profilUpdate', 'profilUpdate_address_error', [$insertID])));
return;
}
}
} else if (is_array($requested_change) && array_key_exists("kontakt_id", $requested_change)) {
$insertID = $this->handleKontakt($requested_change, $personID);
$insertID = hasData($insertID) ? getData($insertID) : null;
if (isset($insertID)) {
$requested_change['kontakt_id'] = $insertID;
$update_res = $this->updateRequestedChange($id, $requested_change);
if (isError($update_res)) {
echo json_encode(error($this->p->t('profilUpdate', 'profilUpdate_kontakt_error', [$insertID])));
return;
}
}
} else {
switch ($topic) {
// mapping phrasen to database columns to make the update with the correct column names
case self::$TOPICS['Titel']:
$topic = "titelpre";
break;
case self::$TOPICS['Postnomen']:
$topic = "titelpost";
break;
case self::$TOPICS['Vorname']:
$topic = "vorname";
break;
case self::$TOPICS['Nachname']:
$topic = "nachname";
break;
default:
show_error($this->p->t('profilUpdate', 'profilUpdate_topic_error', [$topic]));
return;
}
$result = $this->PersonModel->update($personID, [$topic => $requested_change["value"]]);
if (isError($result)) {
echo json_encode(error($this->p->t('profilUpdate', 'profilUpdate_insert_error')));
return;
}
}
$this->sendEmail_onProfilUpdate_response($uid, $topic, self::$STATUS_ACCEPTED);
echo json_encode($this->setStatusOnUpdateRequest($id, self::$STATUS_ACCEPTED, $status_message, $requested_change));
} else {
show_error($this->p->t('profilUpdate', 'profilUpdate_permission_error'));
}
}
public function denyProfilRequest()
{
$_POST = json_decode($this->input->raw_input_stream, true);
$id = $this->input->post('profil_update_id', true);
$uid = $this->input->post('uid', true);
$topic = $this->input->post('topic', true);
$status_message = $this->input->post('status_message', true);
$is_mitarbeiter_profil_update = getData($this->MitarbeiterModel->isMitarbeiter($uid));
$is_student_profil_update = getData($this->StudentModel->isStudent($uid));
if (
$this->permissionlib->isBerechtigt('student/stammdaten', "suid", $this->getOE_from_student($uid)) && $is_student_profil_update ||
$this->permissionlib->isBerechtigt('mitarbeiter/stammdaten', "suid") && $is_mitarbeiter_profil_update
) {
$this->sendEmail_onProfilUpdate_response($uid, $topic, self::$STATUS_REJECTED);
echo json_encode($this->setStatusOnUpdateRequest($id, self::$STATUS_REJECTED, $status_message));
} else {
show_error($this->p->t('profilUpdate', 'profilUpdate_permission_error'));
}
}
private function updateRequestedChange($id, $requested_change)
{
return $this->ProfilUpdateModel->update([$id], ['requested_change' => json_encode($requested_change)]);
}
private function setStatusOnUpdateRequest($id, $status, $status_message)
{
return $this->ProfilUpdateModel->update([$id], ["status" => $status, "status_timestamp" => "NOW()", "status_message" => $status_message]);
}
private function deleteOldVersionFile($dms_id)
{
if (!isset($dms_id)) {
return;
}
//? collect all the results of the deleted versions in an array
$res = array();
//? delete all the different versions of the dms_file
$dmsVersions = $this->DmsVersionModel->loadWhere(["dms_id" => $dms_id]);
$dmsVersions = hasData($dmsVersions) ? getData($dmsVersions) : null;
if (isset($dmsVersions)) {
$zwischen_res = array_map(function ($item) {
return $item->version;
}, $dmsVersions);
foreach ($zwischen_res as $version) {
array_push($res, $this->DmsVersionModel->delete([$dms_id, $version]));
}
} else {
echo json_encode(error($this->p->t('profilUpdate', 'profilUpdate_dmsVersion_error')));
}
//? returns a result for each deleted dms_file
return $res;
}
private function handleKontakt($requested_change, $personID)
{
$kontakt_id = $requested_change["kontakt_id"];
//? removes the kontakt_id because we don't want to update the kontakt_id in the database
unset($requested_change["kontakt_id"]);
//! ADD
if (array_key_exists('add', $requested_change) && $requested_change['add']) {
//? removes add flag
unset($requested_change['add']);
$requested_change['person_id'] = $personID;
$requested_change['insertamum'] = "NOW()";
$requested_change['insertvon'] = getAuthUID();
$insertID = $this->KontaktModel->insert($requested_change);
$insert_kontakt_id = $insertID;
if (isError($insert_kontakt_id)) {
show_error($this->p->t('profilUpdate', 'profilUpdate_insertKontakt_error'));
}
$insert_kontakt_id = hasData($insert_kontakt_id) ? getData($insert_kontakt_id) : null;
if ($insert_kontakt_id) {
$this->handleDupplicateZustellKontakte($requested_change['zustellung'], $insert_kontakt_id);
}
}
//! DELETE
elseif (array_key_exists('delete', $requested_change) && $requested_change['delete']) {
$this->KontaktModel->delete($kontakt_id);
}
//! UPDATE
else {
$requested_change['updateamum'] = "NOW()";
$requested_change['updatevon'] = getAuthUID();
$update_kontakt_id = $this->KontaktModel->update($kontakt_id, $requested_change);
if (isError($update_kontakt_id)) {
show_error($this->p->t('profilUpdate', 'profilUpdate_updateKontakt_error'));
}
$update_kontakt_id = hasData($update_kontakt_id) ? getData($update_kontakt_id) : null;
if ($update_kontakt_id) {
$this->handleDupplicateZustellKontakte($requested_change['zustellung'], $update_kontakt_id);
}
}
return isset($insertID) ? $insertID : null;
}
private function handleAdresse($requested_change, $personID)
{
$this->AdressenTypModel->addSelect(["adressentyp_kurzbz"]);
$adr_kurzbz = $this->AdressenTypModel->loadWhere(["bezeichnung" => $requested_change['typ']]);
$adr_kurzbz = hasData($adr_kurzbz) ? getData($adr_kurzbz)[0]->adressentyp_kurzbz : null;
//? replace the address_typ with its correct kurzbz foreign key
$requested_change['typ'] = $adr_kurzbz;
$adresse_id = $requested_change["adresse_id"];
//? removes the adresse_id because we don't want to update the kontakt_id in the database
unset($requested_change["adresse_id"]);
//! ADD
if (array_key_exists('add', $requested_change) && $requested_change['add']) {
//? removes add flag
unset($requested_change['add']);
$requested_change['insertamum'] = "NOW()";
$requested_change['insertvon'] = getAuthUID();
$requested_change['person_id'] = $personID;
//TODO: zustelladresse, heimatadresse, rechnungsadresse und nation werden nicht beachtet
$insertID = $this->AdresseModel->insert($requested_change);
$insert_adresse_id = $insertID;
if (isError($insert_adresse_id)) {
show_error($this->p->t('profilUpdate', 'profilUpdate_insertAdresse_error'));
}
$insert_adresse_id = hasData($insert_adresse_id) ? getData($insert_adresse_id) : null;
if ($insert_adresse_id) {
$this->handleDupplicateZustellAdressen($requested_change['zustelladresse'], $insert_adresse_id);
}
}
//! DELETE
elseif (array_key_exists('delete', $requested_change) && $requested_change['delete']) {
$this->AdresseModel->delete($adresse_id);
}
//! UPDATE
else {
$requested_change['updateamum'] = "NOW()";
$requested_change['updatevon'] = getAuthUID();
$update_adresse_id = $this->AdresseModel->update($adresse_id, $requested_change);
if (isError($update_adresse_id)) {
show_error($this->p->t('profilUpdate', 'profilUpdate_updateAdresse_error'));
}
$update_adresse_id = hasData($update_adresse_id) ? getData($update_adresse_id) : null;
if ($update_adresse_id) {
$this->handleDupplicateZustellAdressen($requested_change['zustelladresse'], $update_adresse_id);
}
}
return isset($insertID) ? $insertID : null;
}
private function handleDupplicateZustellKontakte($zustellung, $kontakt_id)
{
if ($zustellung) {
$this->PersonModel->addSelect("public.tbl_kontakt.kontakt_id");
$this->PersonModel->addJoin("public.tbl_kontakt", "public.tbl_kontakt.person_id = public.tbl_person.person_id");
$zustellKontakteArray = $this->PersonModel->loadWhere(["public.tbl_person.person_id" => $this->pid, "zustellung" => TRUE]);
if (!isSuccess($zustellKontakteArray)) {
return error($this->p->t('profilUpdate', 'profilUpdate_loadingZustellkontakte_error'));
}
$zustellKontakteArray = hasData($zustellKontakteArray) ? getData($zustellKontakteArray) : null;
if ($zustellung && count($zustellKontakteArray) > 0) {
$zustellKontakteArray = array_filter($zustellKontakteArray, function ($kontakt) use ($kontakt_id) {
return $kontakt->kontakt_id != $kontakt_id;
});
foreach ($zustellKontakteArray as $kontakt) {
$this->KontaktModel->update($kontakt->kontakt_id, ["zustellung" => FALSE]);
}
}
}
}
private function handleDupplicateZustellAdressen($zustellung, $adresse_id)
{
if ($zustellung) {
$this->PersonModel->addSelect("public.tbl_adresse.adresse_id");
$this->PersonModel->addJoin("public.tbl_adresse", "public.tbl_adresse.person_id = public.tbl_person.person_id");
$zustellAdressenArray = $this->PersonModel->loadWhere(["public.tbl_person.person_id" => $this->pid, "zustelladresse" => TRUE]);
if (!isSuccess($zustellAdressenArray)) {
return error($this->p->t('profilUpdate', 'profilUpdate_loadingZustellAdressen_error'));
}
$zustellAdressenArray = hasData($zustellAdressenArray) ? getData($zustellAdressenArray) : null;
if ($zustellung && count($zustellAdressenArray) > 0) {
$zustellAdressenArray = array_filter($zustellAdressenArray, function ($adresse) use ($adresse_id) {
return $adresse->adresse_id != $adresse_id;
});
foreach ($zustellAdressenArray as $adresse) {
$this->AdresseModel->update($adresse->adresse_id, ["zustelladresse" => FALSE]);
}
}
}
}
}
}
@@ -51,7 +51,7 @@ class LvMenu extends FHCAPI_Controller
$this->load->library("PermissionLib", null, 'PermissionLib');
$this->load->library("PhrasesLib");
$this->load->library("PhrasesLib", null, 'PhrasesLib');
$this->loadPhrases(array('global', 'lehre'));
}
@@ -269,6 +269,8 @@ class LvMenu extends FHCAPI_Controller
'lehrfach_id'=>$lehrfach_id,
'lektor_der_lv'=>$lektor_der_lv,
'lehrfach_oe_kurzbz_arr'=>$lehrfach_oe_kurzbz_arr,
'permissionLib' => &$this->PermissionLib,
'phrasesLib' => &$this->PhrasesLib
];
Events::trigger('lvMenuBuild',
@@ -331,6 +333,7 @@ class LvMenu extends FHCAPI_Controller
'id'=>'core_menu_lvinfo',
'position'=>'10',
'name'=>$this->p->t('lehre', 'lehrveranstaltungsinformation'),
'phrase' => 'lehre/lehrveranstaltungsinformation',
'icon'=>'../../../skin/images/button_lvinfo.png',
'link'=>'',
'c4_icon'=> base_url('skin/images/button_lvinfo.png'),
@@ -349,6 +352,7 @@ class LvMenu extends FHCAPI_Controller
'id'=>'core_menu_feedback',
'position'=>'60',
'name'=>$this->p->t('lehre', 'feedback'),
'phrase' => 'lehre/feedback',
'c4_icon'=> base_url('skin/images/button_feedback.png'),
'c4_link'=> base_url('feedback.php?lvid='.$lvid),
);
@@ -366,6 +370,7 @@ class LvMenu extends FHCAPI_Controller
'id'=>'core_menu_gesamtnote',
'position'=>'80',
'name'=>$this->p->t('lehre', 'gesamtnote'),
'phrase' => 'lehre/gesamtnote',
'c4_icon'=> base_url('skin/images/button_endnote.png'),
'c4_link'=> base_url('cis/private/lehre/benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
//'c4_link'=> base_url('benotungstool/lvgesamtnoteverwalten.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem))
@@ -378,6 +383,7 @@ class LvMenu extends FHCAPI_Controller
'id'=>'core_menu_gesamtnote',
'position'=>'80',
'name'=>$this->p->t('lehre', 'gesamtnote'),
'phrase'=>'lehre/gesamtnote',
'c4_icon'=>base_url('skin/images/button_endnote.png'),
'c4_link'=>'#',
'c4_linkList'=>[[$this->p->t('lehre', 'noteneingabedeaktiviert'),'#']],
@@ -450,6 +456,7 @@ class LvMenu extends FHCAPI_Controller
'id'=>'core_menu_mailanstudierende',
'position'=>'100',
'name'=>$this->p->t('lehre', 'mail'),
'phrase' => 'lehre/mail',
'c4_icon'=>base_url('skin/images/button_feedback.png'),
'c4_icon2' => 'fa-regular fa-envelope',
'c4_link'=>$mailto,
@@ -474,6 +481,7 @@ class LvMenu extends FHCAPI_Controller
'id'=>'core_menu_abmeldung',
'position'=>'120',
'name'=>$this->p->t('lehre', 'abmelden'),
'phrase'=>'lehre/abmelden',
'c4_icon'=>base_url('skin/images/button_studiupload.png'),
'c4_link'=>base_url('abmeldung.php?lvid='.urlencode($lvid).'&stsem='.urlencode($angezeigtes_stsem)),
);
@@ -508,6 +516,7 @@ class LvMenu extends FHCAPI_Controller
'id' => 'core_menu_anerkennungNachgewiesenerKenntnisse',
'position' => '128',
'name' => $this->p->t('lehre', 'anrechnung'),
'phrase' => 'lehre/anrechnung',
'c4_icon' => base_url('skin/images/button_listen.png'),
'c4_icon2' => 'fa-regular fa-folder-open',
'c4_link' => base_url('cis.php/lehre/anrechnung/RequestAnrechnung?studiensemester='.urlencode($angezeigtes_stsem).'&lv_id='.urlencode($lvid))
@@ -525,6 +534,7 @@ class LvMenu extends FHCAPI_Controller
'id' => 'core_menu_anerkennungNachgewiesenerKenntnisse_empfehlen',
'position' => '128',
'name' => $this->p->t('lehre', 'anrechnungen'),
'phrase' => 'lehre/anrechnung',
'c4_icon'=> base_url('skin/images/button_listen.png'),
'c4_icon2' => 'fa-regular fa-folder-open',
'c4_link' => base_url('cis.php/lehre/anrechnung/ReviewAnrechnungUebersicht?studiensemester='.urlencode($angezeigtes_stsem))
@@ -176,6 +176,7 @@ class LvPlan extends FHCAPI_Controller
{
$this->load->model('ressource/Stunde_model', 'StundeModel');
$this->StundeModel->addOrder('stunde', 'ASC');
$stunden = $this->StundeModel->load();
$stunden = $this->getDataOrTerminateWithError($stunden);
@@ -573,8 +573,7 @@ class ProfilUpdate extends FHCAPI_Controller
{
// early return if no status has been passed as argument
if (!isset($status)) {
echo json_encode($this->ProfilUpdateModel->getProfilUpdateWithPermission());
return;
$this->terminateWithSuccess($this->ProfilUpdateModel->getProfilUpdateWithPermission());
}
// get the sprache of the user
@@ -587,7 +586,7 @@ class ProfilUpdate extends FHCAPI_Controller
$status = hasData($status) ? getData($status)[0]->status_kurzbz : null;
$res = $this->ProfilUpdateModel->getProfilUpdateWithPermission(isset($status) ? ['status' => $status] : null);
echo json_encode($res);
$this->terminateWithSuccess($res);
}
}
@@ -641,6 +640,7 @@ class ProfilUpdate extends FHCAPI_Controller
$this->StudentModel->addJoin("public.tbl_prestudent", "public.tbl_benutzer.person_id = public.tbl_prestudent.person_id");
$this->StudentModel->addJoin("public.tbl_prestudentstatus", "public.tbl_prestudentstatus.prestudent_id = public.tbl_prestudent.prestudent_id");
$this->StudentModel->addJoin("public.tbl_studiengang", "public.tbl_studiengang.studiengang_kz = public.tbl_prestudent.studiengang_kz");
$this->StudentModel->addGroupBy(["public.tbl_studiengang.email"]);
//* check if the benutzer itself is active
//* check if the student status is Student or Diplomand (active students)
$this->StudentModel->db->where_in("public.tbl_prestudentstatus.status_kurzbz", ['Student', 'Diplomand']);
@@ -657,8 +657,10 @@ class ProfilUpdate extends FHCAPI_Controller
}
$mail_res = [];
//? sending email
foreach ($emails as $email) {
array_push($mail_res, sendSanchoMail("profil_update", ['uid' => $uid, 'topic' => $topic, 'href' => APP_ROOT . 'Cis/ProfilUpdate/id/' . $profil_update_id], $email, ("Profil Änderung von " . $uid)));
foreach ($emails as $email)
{
$href = $this->config->item('cis_vilesci_base_url') . $this->config->item('cis_vilesci_index_page') . '/Cis/ProfilUpdate/id/' . $profil_update_id;
array_push($mail_res, sendSanchoMail("profil_update", ['uid' => $uid, 'topic' => $topic, 'href' => $href], $email, ("Profil Änderung von " . $uid)));
}
foreach ($mail_res as $m_res) {
if (!$m_res) {
@@ -681,21 +683,21 @@ class ProfilUpdate extends FHCAPI_Controller
function languageQuery($language)
{
return "select index from public.tbl_sprache where sprache = '" + $language + "'";
return "select index from public.tbl_sprache where sprache = '" . $language . "'";
}
$this->ProfilUpdateStatusModel->addSelect(["bezeichnung_mehrsprachig[(" . languageQuery('German') . ")] as status_de", "bezeichnung_mehrsprachig[(" . languageQuery('English') . ")] as status_en"]);
$status_translation = $this->ProfilUpdateStatusModel->loadWhere(["status_kurzbz" => $status]);
if (isError($status_translation)) {
$this->terminateWithError($this->p->t('profilUpdate', 'ProfilUpdateStatusTranslationError'));
}
$status_translation = hasData($status_translation) ? getData($status_translation)[0] : null;
if (isset($status_translation)) {
$mail_res = sendSanchoMail("profil_update_response", ['topic' => $topic, 'status_de' => $status_translation->status_de, 'status_en' => $status_translation->status_en, 'href' => APP_ROOT . 'Cis/Profil'], $email, ("Profil Änderung " . $this->p->t('profilUpdate', 'pending')));
if (isset($status_translation))
{
$href = $this->config->item('cis_base_url') . $this->config->item('cis_index_page') . '/Cis/Profil';
$mail_res = sendSanchoMail("profil_update_response", ['topic' => $topic, 'status_de' => $status_translation->status_de, 'status_en' => $status_translation->status_en, 'href' => $href], $email, ("Profil Änderung " . $status_translation->status_de . ' / Profile Update ' . $status_translation->status_en));
if (!$mail_res) {
$this->addError($this->p->t('profilUpdate', 'profilUpdate_email_error'));
}
@@ -0,0 +1,60 @@
<?php
/**
* Copyright (C) 2024 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class RouteInfo extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'info' => self::PERM_LOGGED,
]);
$this->load->model('system/Webservicelog_model', 'WebservicelogModel');
}
public function info()
{
$payload = json_decode($this->input->raw_input_stream);
if (isset($payload->app) && isset($payload->path) && $this->isValidApp($payload->app) && $this->isValidPath($payload->path))
{
$this->WebservicelogModel->insert(array(
'webservicetyp_kurzbz' => 'content',
'beschreibung' => $payload->app,
'request_data' => $payload->path,
'execute_user' => getAuthUID(),
'execute_time' => 'NOW()'
));
}
$this->terminateWithSuccess(true);
}
protected function isValidApp($app)
{
return preg_match("/^[A-Za-z0-9\-_]+$/", $app);
}
protected function isValidPath($path)
{
return preg_match("/^[\/A-Za-z0-9_.\-~?%=&;]+$/", $path);
}
}
@@ -39,6 +39,8 @@ class Searchbar extends FHCAPI_Controller
'searchCis' => self::PERM_LOGGED,
'searchStv' => self::PERM_LOGGED
]);
$this->load->model('system/Webservicelog_model', 'WebservicelogModel');
}
//------------------------------------------------------------------------------------------------------------------
@@ -103,6 +105,17 @@ class Searchbar extends FHCAPI_Controller
// Convert to json the result from searchlib->search
$result = $this->searchlib->search($this->input->post(self::SEARCHSTR_PARAM), $this->input->post(self::TYPES_PARAM));
$this->WebservicelogModel->insert(array(
'webservicetyp_kurzbz' => 'content',
'beschreibung' => $config['config'],
'request_data' => json_encode(array(
self::SEARCHSTR_PARAM => $this->input->post(self::SEARCHSTR_PARAM),
self::TYPES_PARAM => $this->input->post(self::TYPES_PARAM)
)),
'execute_user' => getAuthUID(),
'execute_time' => 'NOW()'
));
$data = $this->getDataOrTerminateWithError($result);
$this->addMeta('time', $result->meta['time']);
@@ -60,7 +60,11 @@ class BetriebsmittelP extends FHCAPI_Controller
public function getAllBetriebsmittel($type_id, $id)
{
$result = $this->BetriebsmittelpersonModel->getBetriebsmittelData($id, $type_id);
$betriebsmitteltypes = null;
if ($this->input->get('betriebsmitteltypes') !== null && !isEmptyArray($this->input->get('betriebsmitteltypes')))
$betriebsmitteltypes = $this->input->get('betriebsmitteltypes');
$result = $this->BetriebsmittelpersonModel->getBetriebsmittelData($id, $type_id, $betriebsmitteltypes);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
@@ -370,6 +374,12 @@ class BetriebsmittelP extends FHCAPI_Controller
$this->load->model('ressource/Betriebsmitteltyp_model', 'BetriebsmitteltypModel');
$this->BetriebsmitteltypModel->addOrder('beschreibung', 'ASC');
if ($this->input->get('betriebsmitteltypes') !== null && !isEmptyArray($this->input->get('betriebsmitteltypes')))
{
$this->BetriebsmitteltypModel->db->where_in('betriebsmitteltyp', $this->input->get('betriebsmitteltypes'));
}
$result = $this->BetriebsmitteltypModel->load(); // load All
if (isError($result)) {
@@ -116,7 +116,7 @@ class Gruppe extends FHCAPI_Controller
bezeichnung,
gid,
\'false\' as lehrverband');
$gruppen_result = $this->_ci->GruppeModel->loadWhere(array('sichtbar' => true, 'aktiv' => true, 'lehre' => true, 'direktinskription' => false));
$gruppen_result = $this->_ci->GruppeModel->loadWhere(array('sichtbar' => true, 'aktiv' => true, 'lehre' => true, 'direktinskription' => false, 'semester IS NOT NULL' => null));
$gruppen_array = array();
@@ -350,7 +350,7 @@ class Lehreinheit extends FHCAPI_Controller
$this->form_validation->set_rules($field, 'Start KW', 'integer|greater_than[0]|less_than_equal_to[53]');
break;
case 'gewicht':
$this->form_validation->set_rules($field, 'Gewicht', 'numeric');
$this->form_validation->set_rules($field, 'Gewicht', 'numeric|greater_than_equal_to[0]');
break;
case 'raumtyp':
$this->form_validation->set_rules($field, 'Raumtyp', 'required|max_length[16]');
@@ -365,7 +365,7 @@ class Lehreinheit extends FHCAPI_Controller
$this->form_validation->set_rules($field, 'LVNR', 'integer');
break;
case 'unr':
$this->form_validation->set_rules($field, 'UNR', 'integer');
$this->form_validation->set_rules($field, 'UNR', 'integer|greater_than_equal_to[0]');
break;
case 'lehre':
$this->form_validation->set_rules($field, 'Lehre', 'trim');
@@ -107,7 +107,8 @@ class Lektor extends FHCAPI_Controller
$this->form_validation->set_rules($field, 'Planstunden', 'integer|greater_than_equal_to[0]');
break;
case 'stundensatz':
$this->form_validation->set_rules($field, 'Stundensatz', 'numeric|greater_than_equal_to[0]');
$formData['stundensatz'] = str_replace(',', '.', $formData['stundensatz']);
$this->form_validation->set_rules($field, 'Stundensatz', 'callback__check_stundensatz');
break;
case 'faktor':
$this->form_validation->set_rules($field, 'Faktor', 'numeric|greater_than_equal_to[0]');
@@ -119,6 +120,7 @@ class Lektor extends FHCAPI_Controller
$this->form_validation->set_rules($field, 'Bis Melden', 'trim');
break;
case 'semesterstunden':
$formData['semesterstunden'] = str_replace(',', '.', $formData['semesterstunden']);
$this->form_validation->set_rules($field, 'Semesterstunden', 'callback__check_semesterstunden');
break;
case 'mitarbeiter_uid':
@@ -129,7 +131,7 @@ class Lektor extends FHCAPI_Controller
}
if (!$this->form_validation->run())
{
$this->terminateWithError($this->form_validation->error_array());
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
if (isset($formData['semesterstunden']) && (!is_numeric($formData['semesterstunden']) || $formData['semesterstunden'] === ''))
@@ -145,9 +147,26 @@ class Lektor extends FHCAPI_Controller
$result = $this->_ci->lektorlib->updateLektorFromLehreinheit($lehreinheit_id, $mitarbeiter_uid, $formData);
if (isError($result)) $this->terminateWithError(getError($result));
$this->terminateWithSuccess("Erfolgreich geupdated");
$this->terminateWithSuccess($result);
}
public function _check_stundensatz($value)
{
$value = str_replace(',', '.', $value);
if (!is_numeric($value))
{
$this->form_validation->set_message('_check_decimal', 'Das Feld {field} muss eine Zahl sein.');
return false;
}
if ($value < 0 || $value >= 10000) {
$this->form_validation->set_message('_check_decimal', 'Das Feld {field} muss zwischen 0 und 10000 liegen.');
return false;
}
return true;
}
public function _check_semesterstunden($value)
{
if ($value === null || $value === '') {
@@ -171,6 +190,14 @@ class Lektor extends FHCAPI_Controller
);
return false;
}
if ($value > 999.99)
{
$this->form_validation->set_message(
'_check_semesterstunden',
'Das Feld {field} darf maximal 999,99 betragen.'
);
return false;
}
return true;
}
@@ -50,6 +50,11 @@ class Setup extends FHCAPI_Controller
'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Details.js',
'config' => []
);
$tabs['gruppen'] = array (
'title' => 'Gruppen',
'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Gruppen.js',
'config' => []
);
$tabs['lektor'] = array (
'title' => 'LektorInnenzuteilung',
'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Lektor.js',
@@ -20,5 +20,31 @@ class Tags extends Tag_Controller
'doneLehre' => self::BERECHTIGUNG_KURZBZ,
'deleteLehre' => self::BERECHTIGUNG_KURZBZ,
]);
$this->config->load('lvverwaltung');
}
public function getTag($readonly_tags = null)
{
parent::getTag($this->config->item('lvverwaltung_tags'));
}
public function getTags($tags = null)
{
parent::getTags($this->config->item('lvverwaltung_tags'));
}
public function addTag($withZuordnung = true, $updatable_tags = null)
{
parent::addTag(true, $this->config->item('lvverwaltung_tags'));
}
public function updateTag($updatable_tags = null)
{
parent::updateTag($this->config->item('lvverwaltung_tags'));
}
public function deleteTag($withZuordnung = true, $updatable_tags = null)
{
parent::deleteTag(true, $this->config->item('lvverwaltung_tags'));
}
public function doneTag($updatable_tags = null)
{
parent::doneTag($this->config->item('lvverwaltung_tags'));
}
}
@@ -418,6 +418,10 @@ class Messages extends FHCAPI_Controller
}
$data = $this->getDataOrTerminateWithError($result);
if (count($data) < 1)
{
$this->terminateWithError('Error: Messages API no person_id found.');
}
$person = current($data);
return $person->person_id;
@@ -432,8 +436,12 @@ class Messages extends FHCAPI_Controller
);
$data = $this->getDataOrTerminateWithError($result);
if (count($data) < 1)
{
$this->terminateWithError('Error: Messages API no prestudent_id found.');
}
$student = current($data);
// $this->terminateWithError($student->prestudent_id, self::ERROR_TYPE_GENERAL);
return $student->prestudent_id;
}
@@ -18,6 +18,7 @@ class NotizPerson extends Notiz_Controller
'loadDokumente' => ['admin:r', 'assistenz:r'],
'getMitarbeiter' => ['admin:r', 'assistenz:r'],
'isBerechtigt' => ['admin:r', 'assistenz:r'],
'getCountNotes' => ['admin:r', 'assistenz:r'],
]);
}
@@ -63,10 +63,15 @@ class Config extends FHCAPI_Controller
'component' => './Stv/Studentenverwaltung/Details/Details.js',
'config' => $config['details']
];
$result['notes'] = [
'title' => $this->p->t('stv', 'tab_notes'),
'component' => './Stv/Studentenverwaltung/Details/Notizen.js'
'component' => './Stv/Studentenverwaltung/Details/Notizen.js',
'config' => $config['notes'],
'showSuffix' => ($config['notes']['showCountNotes'] ?? false),
'suffixhelper' => APP_ROOT . 'public/js/helpers/Stv/Studentenverwaltung/Details/Notizen/NotizenSuffixHelper.js'
];
$result['contact'] = [
'title' => $this->p->t('stv', 'tab_contact'),
'component' => './Stv/Studentenverwaltung/Details/Kontakt.js',
@@ -190,7 +195,9 @@ class Config extends FHCAPI_Controller
return $result;
});
$this->terminateWithSuccess($result);
$sortConfig = $this->config->item('student_tab_order');
$this->terminateWithSuccess($this->sortTabList($result, $sortConfig));
}
public function students()
@@ -237,7 +244,9 @@ class Config extends FHCAPI_Controller
return $result;
});
$this->terminateWithSuccess($result);
$sortConfig = $this->config->item('students_tab_order');
$this->terminateWithSuccess($this->sortTabList($result, $sortConfig));
}
protected function kontoColumns()
@@ -513,4 +522,34 @@ class Config extends FHCAPI_Controller
return $list;
}
/**
* Sort tab list
*
* @param array $input
* @param array $config
*
* @return array
*/
protected function sortTabList($input, $config)
{
// prepare config
if (!$config || !is_array($config))
$config = [];
else
$config = array_flip($config);
// fill missing items in config
foreach (array_keys($input) as $key) {
if (!isset($config[$key]))
$config[$key] = count($config);
}
// do the sorting
uksort($input, function ($a, $b) use ($config) {
return $config[$a] - $config[$b];
});
return $input;
}
}
@@ -35,8 +35,6 @@ class Favorites extends FHCAPI_Controller
// Load models
$this->load->model('system/Variable_model', 'VariableModel');
// TODO(chris): variable table might be to small to store favorites!
}
public function index()
@@ -62,6 +60,17 @@ class Favorites extends FHCAPI_Controller
$favorites = $this->input->post('favorites');
$removed = [];
while (strlen($favorites) > 64) {
$favObj = json_decode($favorites);
if (!$favObj->list)
break;
$removed[] = array_shift($favObj->list);
$favorites = json_encode($favObj);
}
if ($removed)
$this->addMeta('removed', $removed);
$result = $this->VariableModel->setVariable(getAuthUID(), 'stv_favorites', $favorites);
$this->getDataOrTerminateWithError($result);
@@ -16,7 +16,8 @@ class Notiz extends Notiz_Controller
'updateNotiz' => ['admin:rw', 'assistenz:rw'], // TODO(manu): self::PERM_LOGGED
'deleteNotiz' => ['admin:r', 'assistenz:r'],
'loadDokumente' => ['admin:r', 'assistenz:r'],
'getMitarbeiter' => ['admin:r', 'assistenz:r']
'getMitarbeiter' => ['admin:r', 'assistenz:r'],
'getCountNotes' => ['admin:r', 'assistenz:r'],
]);
//Load Models
@@ -24,7 +24,6 @@ class Status extends FHCAPI_Controller
'updateStatus' => ['admin:rw', 'assistenz:rw'],
'advanceStatus' => ['admin:rw', 'assistenz:rw'],
'confirmStatus' => ['admin:rw', 'assistenz:rw'],
]);
//Load Models
@@ -440,9 +439,10 @@ class Status extends FHCAPI_Controller
]);
if (!$this->form_validation->run())
{
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
$this->load->library('PrestudentLib');
$this->db->trans_start();
@@ -628,8 +628,9 @@ class Status extends FHCAPI_Controller
]);
if (!$this->form_validation->run())
{
$this->terminateWithValidationErrors($this->form_validation->error_array());
}
// Start DB transaction
$this->db->trans_start();
@@ -106,6 +106,7 @@ class Student extends FHCAPI_Controller
$this->PrestudentModel->addSelect('p.staatsbuergerschaft');
$this->PrestudentModel->addSelect('p.matr_nr');
$this->PrestudentModel->addSelect('p.anrede');
$this->PrestudentModel->addSelect('p.zugangscode');
if (defined('ACTIVE_ADDONS') && strpos(ACTIVE_ADDONS, 'bewerbung') !== false) {
$this->PrestudentModel->addSelect(
@@ -253,7 +254,6 @@ class Student extends FHCAPI_Controller
'gebdatum',
'gebort',
'geburtsnation',
'svnr',
'ersatzkennzeichen',
'staatsbuergerschaft',
'matr_nr',
@@ -698,16 +698,15 @@ class Student extends FHCAPI_Controller
//StudentLehrverband anlegen
}
// TODO(chris): DEBUG
/*$result = $this->PrestudentModel->loadWhere([
'pestudent_id' => 1
]);
if (isError($result)) {
return $result;
}*/
}
// TODO(chris): DEBUG
/*$result = $this->PrestudentModel->loadWhere([
'pestudent_id' => 1
]);
if (isError($result)) {
return $result;
}*/
return ['person_id' => $person_id ?? null, 'prestudent_id' => $prestudent_id ?? null];
return success(true);
}
public function requiredIfNotPersonId($value)
@@ -730,4 +729,9 @@ class Student extends FHCAPI_Controller
return true;
return !!$value;
}
public function isValidDate($value)
{
return isValidDate($value);
}
}
@@ -761,7 +761,6 @@ class Students extends FHCAPI_Controller
$this->PrestudentModel->addSelect('wahlname');
$this->PrestudentModel->addSelect('vornamen');
$this->PrestudentModel->addSelect('titelpost');
$this->PrestudentModel->addSelect('svnr');
$this->PrestudentModel->addSelect('ersatzkennzeichen');
$this->PrestudentModel->addSelect('gebdatum');
$this->PrestudentModel->addSelect('geschlecht');
@@ -188,6 +188,7 @@ class Verband extends FHCAPI_Controller
array_unshift($list, [
'name' => 'PreStudent',
'link' => $link . 'prestudent',
'stg_kz' => (int)$studiengang_kz,
'children' => $this->getStdSem($link . 'prestudent/', $studiengang_kz)
]);
+73 -24
View File
@@ -11,6 +11,7 @@ class UHSTAT1 extends FHC_Controller
const CODEX_UNKNOWN_YEAR = 9999;
const CODEX_UNKNOWN_NATION = 'XXX';
const CODEX_UNKNOWN_BILDUNGMAX = 999;
const CODEX_EXCLUDED_NATIONS = ['ZZZ'];
const LOWER_BOUNDARY_YEARS = 160;
const UPPER_BOUNDARY_YEARS = 20;
@@ -32,8 +33,7 @@ class UHSTAT1 extends FHC_Controller
$this->load->library('PermissionLib');
// load models
$this->load->model('codex/Oehbeitrag_model', 'OehbeitragModel');
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->load->model('person/Benutzer_model', 'BenutzerModel');
$this->load->model('system/Sprache_model', 'SpracheModel');
$this->load->model('codex/Abschluss_model', 'AbschlussModel');
$this->load->model('codex/Uhstat1daten_model', 'Uhstat1datenModel');
@@ -104,7 +104,7 @@ class UHSTAT1 extends FHC_Controller
{
$saved = false;
$person_id = $this->_getValidPersonId('sui');
$person_id = $this->_getUHSTATPersonId('sui');
$this->form_validation->set_error_delimiters('<span class="text-danger">', '</span>');
@@ -245,7 +245,7 @@ class UHSTAT1 extends FHC_Controller
// uhstat data can only be deleted with permission
if (!$this->_checkPermission('suid')) show_error('no permission');
$person_id = $this->_getValidPersonId('suid');
$person_id = $this->_getUHSTATPersonId('suid');
$uhstat1datenRes = $this->Uhstat1datenModel->delete(
array('person_id' => $person_id)
@@ -287,13 +287,17 @@ class UHSTAT1 extends FHC_Controller
*/
private function _getFormMetaData()
{
$person_id = $this->_getValidPersonId('s');
$person_id = $this->_getUHSTATPersonId('s');
// read only display param
$readOnly = $this->input->get('readOnly');
// depending on permissions, editing or deleting is possible
$editPermission = $this->_checkPermission('sui');
// checking permissions for form
// saving is possible if there permission or student log in (but not from application tool)
$savePermission = $this->_checkPermission('sui') || ($this->_getUserPersonId() && !$this->_getApplicationToolPersonId());
// deleting only possible with permission
$deletePermission = $this->_checkPermission('suid');
$languageIdx = $this->_getLanguageIndex();
@@ -304,7 +308,7 @@ class UHSTAT1 extends FHC_Controller
'abschluss_nicht_oesterreich' => array(),
'jahre' => array(),
'person_id' => $person_id,
'editPermission' => $editPermission,
'savePermission' => $savePermission,
'deletePermission' => $deletePermission,
'readOnly' => $readOnly
);
@@ -336,15 +340,19 @@ class UHSTAT1 extends FHC_Controller
if (hasData($nationRes))
{
$dropdownNations = [];
$nations = getData($nationRes);
// put austria in beginning of selection
foreach ($nations as $nation)
{
if ($nation->nation_code == self::CODEX_OESTERREICH) array_unshift($nations, $nation);
// put austria in beginning of selection
if ($nation->nation_code == self::CODEX_OESTERREICH)
array_unshift($dropdownNations, $nation);
elseif (!in_array($nation->nation_code, self::CODEX_EXCLUDED_NATIONS)) // add nation if not excluded
$dropdownNations[] = $nation;
}
$formMetaData['nation'] = $nations;
$formMetaData['nation'] = $dropdownNations;
}
// get abschluss list
@@ -386,7 +394,7 @@ class UHSTAT1 extends FHC_Controller
*/
private function _getUHSTAT1Data()
{
$person_id = $this->_getValidPersonId('s');
$person_id = $this->_getUHSTATPersonId('s');
$this->Uhstat1datenModel->addSelect(
implode(', ', array_keys($this->_uhstat1Fields))
@@ -417,29 +425,70 @@ class UHSTAT1 extends FHC_Controller
}
/**
* Gets Id of person having permissions to manage UHSTAT1 data.
* Can be passed as parameter or be in session.
* Gets Id of person, for which UHSTAT1 data is edited.
* Can be passed as parameter, id of logged in person, or be in session.
* @param berechtigungsArt type of permission (suid)
* @return int person_id
*/
private function _getValidPersonId($berechtigungsArt)
private function _getUHSTATPersonId($berechtigungsArt)
{
// if coming from bewerbungstool - person id is in session (person must be logged in bewerbungstool)
$applicationToolPersonId = $this->_getApplicationToolPersonId();
if (isset($applicationToolPersonId) && is_numeric($applicationToolPersonId)) return $applicationToolPersonId;
// if successfully logged in
$loggedInPersonId = $this->_getUserPersonId();
if (isset($loggedInPersonId) && is_numeric($loggedInPersonId))
{
// if person id passed directly...
$person_id = $this->input->post('person_id');
if (!isset($person_id)) $person_id = $this->input->get('person_id');
if (isset($person_id))
{
if (!is_numeric($person_id)) show_error("invalid person id");
// ...check if there is a permission for editing UHSTAT1 data
if ($this->_checkPermission($berechtigungsArt)) return $person_id;
}
// if no id passed, use logged in person id
return $loggedInPersonId;
}
show_error("No permission");
}
/**
* Gets person Id if there is a application tool login.
* @return person Id or null
*/
private function _getApplicationToolPersonId()
{
// if coming from aplication tool - person id is in session (person must be logged in bewerbungstool)
if (isset($_SESSION[self::PERSON_ID_SESSION_INDEX])
&& is_numeric($_SESSION[self::PERSON_ID_SESSION_INDEX])
&& isset($_SESSION[self::LOGIN_SESSION_INDEX])
)
return $_SESSION[self::PERSON_ID_SESSION_INDEX];
// if person id passed directly...
$person_id = $this->input->post('person_id');
if (!isset($person_id)) $person_id = $this->input->get('person_id');
return null;
}
if (!isset($person_id) || !is_numeric($person_id)) show_error("invalid person id");
// ...check if there is a permission for editing UHSTAT1 data
if ($this->_checkPermission($berechtigungsArt)) return $person_id;
show_error("No permission");
/**
* Gets person Id if there is a user login.
* @return person Id or null
*/
private function _getUserPersonId()
{
$loggedInPersonId = getAuthPersonId();
if (isset($loggedInPersonId) && is_numeric($loggedInPersonId))
{
// check if the the user is a student and if the benutzer is active
$this->BenutzerModel->addSelect('1');
$res = $this->BenutzerModel->loadWhere(["public.tbl_benutzer.person_id" => $loggedInPersonId, "public.tbl_benutzer.aktiv" => TRUE]);
if (hasData($res)) return $loggedInPersonId;
}
return null;
}
/**
@@ -1275,7 +1275,6 @@ class InfoCenter extends Auth_Controller
'nachname' => $this->input->post('nachname'),
'titelpost' => isEmptyString($this->input->post('titelpost')) ? null : $this->input->post('titelpost'),
'gebdatum' => isEmptyString($this->input->post('gebdatum')) ? null : date("Y-m-d", strtotime($this->input->post('gebdatum'))),
'svnr' => isEmptyString($this->input->post('svnr')) ? null : $this->input->post('svnr'),
'staatsbuergerschaft' => isEmptyString($this->input->post('buergerschaft')) ? null : $this->input->post('buergerschaft'),
'geschlecht' => $this->input->post('geschlecht'),
'geburtsnation' => isEmptyString($this->input->post('gebnation')) ? null : $this->input->post('gebnation'),
@@ -1816,7 +1815,7 @@ class InfoCenter extends Auth_Controller
}
/**
* Loads all necessary Person data: Stammdaten (name, svnr, contact, ...), Dokumente, Logs and Notizen
* Loads all necessary Person data: Stammdaten (name, contact, ...), Dokumente, Logs and Notizen
* @param $person_id
* @return array
*/
+17
View File
@@ -21,6 +21,7 @@ abstract class Notiz_Controller extends FHCAPI_Controller
'loadDokumente' => self::DEFAULT_PERMISSION_R,
'getMitarbeiter' => self::DEFAULT_PERMISSION_R,
'isBerechtigt' => self::DEFAULT_PERMISSION_R,
'getCountNotes' => self::DEFAULT_PERMISSION_R,
];
if(!is_array($permissions))
@@ -459,4 +460,20 @@ abstract class Notiz_Controller extends FHCAPI_Controller
return $this->terminateWithSuccess($result);
}
public function getCountNotes($person_id)
{
$this->NotizzuordnungModel->addSelect('COUNT(*) AS anzahl', false);
$result = $this->NotizzuordnungModel->loadWhere(
array('person_id' => $person_id)
);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$anzahl = current(getData($result));
return $this->terminateWithSuccess($anzahl->anzahl ?: 0);
}
}
+126 -24
View File
@@ -29,13 +29,36 @@ class Tag_Controller extends FHCAPI_Controller
$this->load->model('person/Notiz_model', 'NotizModel');
$this->load->model('system/Notiztyp_model', 'NotiztypModel');
$this->load->model('person/Notizzuordnung_model', 'NotizzuordnungModel');
$this->loadPhrases([
'ui'
]);
}
public function getTag()
public function getTag($readonly_tags = null)
{
$language = $this->_getLanguageIndex();
$id = $this->input->get('id');
if (is_array($readonly_tags) && !isEmptyArray($readonly_tags))
{
$readonly_tags = $this->_filterTag($readonly_tags, true);
foreach ($readonly_tags as $key => $tag)
{
$readonly_tags[$key] = $this->NotizModel->db->escape($tag);
}
$tags = '(' . implode(',', $readonly_tags) . ')';
$this->NotizModel->addSelect("
CASE
WHEN tbl_notiz_typ.typ_kurzbz IN $tags
THEN TRUE
ELSE FALSE
END as readonly
");
}
$this->NotizModel->addSelect(
"tbl_notiz.titel,
tbl_notiz.text,
@@ -54,7 +77,7 @@ class Tag_Controller extends FHCAPI_Controller
$this->NotizModel->addJoin('public.tbl_benutzer verfasserbenutzer', 'tbl_notiz.verfasser_uid = verfasserbenutzer.uid', 'LEFT');
$this->NotizModel->addJoin('public.tbl_person verfasserperson', 'verfasserbenutzer.person_id = verfasserperson.person_id', 'LEFT');
$this->NotizModel->addJoin('public.tbl_benutzer bearbeiterbenutzer', 'tbl_notiz.verfasser_uid = bearbeiterbenutzer.uid', 'LEFT');
$this->NotizModel->addJoin('public.tbl_benutzer bearbeiterbenutzer', 'tbl_notiz.bearbeiter_uid = bearbeiterbenutzer.uid', 'LEFT');
$this->NotizModel->addJoin('public.tbl_person bearbeiterperson', 'bearbeiterbenutzer.person_id = bearbeiterperson.person_id', 'LEFT');
$notiz = $this->NotizModel->loadWhere(array('notiz_id' => $id));
@@ -62,7 +85,7 @@ class Tag_Controller extends FHCAPI_Controller
$this->terminateWithSuccess(hasData($notiz) ? getData($notiz)[0] : array());
}
public function getTags()
public function getTags($tags = null)
{
$this->NotiztypModel->addSelect(
'typ_kurzbz as tag_typ_kurzbz,
@@ -73,19 +96,36 @@ class Tag_Controller extends FHCAPI_Controller
'
);
$this->NotiztypModel->addOrder('prioritaet');
if (is_array($tags) && !isEmptyArray($tags))
{
$tags = $this->_filterTag($tags, false);
$this->NotiztypModel->db->where_in('typ_kurzbz', $tags);
}
$notiztypen = $this->NotiztypModel->loadWhere(array('aktiv' => true));
$this->terminateWithSuccess(hasData($notiztypen) ? getData($notiztypen) : array());
}
public function addTag($withZuordnung = true)
public function addTag($withZuordnung = true, $updatable_tags = null)
{
$postData = $this->getPostJson();
$checkTyp = $this->NotiztypModel->loadWhere(array('typ_kurzbz' => $postData->tag_typ_kurzbz));
if (!hasData($checkTyp))
$this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL);
if (isError($checkTyp))
$this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen'));
if (!hasData($checkTyp))
$this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen'));
if (is_array($updatable_tags) && !isEmptyArray($updatable_tags))
{
$tags = $this->_filterTag($updatable_tags, false);
if (!in_array($postData->tag_typ_kurzbz, $tags))
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
if ($withZuordnung)
{
@@ -125,48 +165,88 @@ class Tag_Controller extends FHCAPI_Controller
}
}
private function addNotiz($postData)
{
return $this->NotizModel->insert(array(
'titel' => 'TAG', //TODO klären
'text' => $postData->notiz,
'verfasser_uid' => $this->_uid,
'erledigt' => false,
'insertamum' => date('Y-m-d H:i:s'),
'insertvon' => $this->_uid,
'typ' => $postData->tag_typ_kurzbz
));
}
public function updateTag()
public function updateTag($updatable_tags = null)
{
$postData = $this->getPostJson();
$post_tag = $this->NotizModel->loadWhere(array('notiz_id' => $postData->id));
if (isError($post_tag))
$this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen'));
if (!hasData($post_tag))
$this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen'));
if (is_array($updatable_tags) && !isEmptyArray($updatable_tags))
{
$tags = $this->_filterTag($updatable_tags, false);
$post_tag_typ = getData($post_tag)[0]->typ;
if (!in_array($post_tag_typ, $tags))
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$updateData = $this->NotizModel->update(array('notiz_id' => $postData->id),
array('text' => $postData->notiz,
'updateamum' => date('Y-m-d H:i:s'),
'updatevon' => $this->_uid,
'bearbeiter_uid' => $this->_uid,
)
)
);
$this->terminateWithSuccess($updateData);
}
public function doneTag()
public function doneTag($updatable_tags = null)
{
$postData = $this->getPostJson();
$post_tag = $this->NotizModel->loadWhere(array('notiz_id' => $postData->id));
if (isError($post_tag))
$this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen'));
if (!hasData($post_tag))
$this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen'));
if (is_array($updatable_tags) && !isEmptyArray($updatable_tags))
{
$tags = $this->_filterTag($updatable_tags, false);
$post_tag_typ = getData($post_tag)[0]->typ;
if (!in_array($post_tag_typ, $tags))
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$updateData = $this->NotizModel->update(array('notiz_id' => $postData->id),
array('erledigt' => !$postData->done,
'text' => $postData->notiz,
'updateamum' => date('Y-m-d H:i:s'),
'updatevon' => $this->_uid,
'bearbeiter_uid' => $this->_uid,
)
);
$this->terminateWithSuccess($updateData);
}
public function deleteTag($withZuordnung = true)
public function deleteTag($withZuordnung = true, $updatable_tags = null)
{
$postData = $this->getPostJson();
$post_tag = $this->NotizModel->loadWhere(array('notiz_id' => $postData->id));
if (isError($post_tag))
$this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen'));
if (!hasData($post_tag))
$this->terminateWithError($this->p->t('ui', 'fehlerBeimLesen'));
if (is_array($updatable_tags) && !isEmptyArray($updatable_tags))
{
$tags = $this->_filterTag($updatable_tags, false);
$post_tag_typ = getData($post_tag)[0]->typ;
if (!in_array($post_tag_typ, $tags))
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$deleteNotiz = "";
if ($withZuordnung)
@@ -208,5 +288,27 @@ class Tag_Controller extends FHCAPI_Controller
return hasData($result) ? getData($result)[0]->index : 1;
}
private function _filterTag($tags, $readonly = true)
{
$filtered_tags = array_filter($tags, function ($tag) use ($readonly)
{
return isset($tag['readonly']) && $tag['readonly'] === $readonly;
});
return array_keys($filtered_tags);
}
private function addNotiz($postData)
{
return $this->NotizModel->insert(array(
'titel' => 'TAG', //TODO klären
'text' => $postData->notiz,
'verfasser_uid' => $this->_uid,
'erledigt' => false,
'insertamum' => date('Y-m-d H:i:s'),
'insertvon' => $this->_uid,
'typ' => $postData->tag_typ_kurzbz
));
}
}
+1 -1
View File
@@ -670,7 +670,7 @@ class DmsLib
$fileObj = new stdClass();
$fileObj->filename = getData($result)[0]->filename;
$fileObj->file = DMS_PATH.getData($result)[0]->filename;
$fileObj->name = DMS_PATH.getData($result)[0]->name; // original user filename
$fileObj->name = getData($result)[0]->name; // original user filename
$fileObj->mimetype = getData($result)[0]->mimetype;
return success($fileObj);
+7 -7
View File
@@ -176,11 +176,11 @@ class LektorLib
if (!$echter_dv && (!$this->_ci->permissionlib->isBerechtigt('admin')))
{
if (!$this->LehrauftragAufFirma(isset($formData['mitarbeiter_uid']) ? $formData['mitarbeiter_uid'] : $mitarbeiter_uid))
return error("ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\n Daten wurden NICHT gespeichert!\n\n");
return error("ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden NICHT gespeichert!\n\n");
}
else
{
$warning .= "ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\n Daten wurden gespeichert!\n\n";
$warning .= "ACHTUNG: Die maximal erlaubte Semesterstundenanzahl des Lektors von $summe Stunden ($stundengrenze->stunden) wurde ueberschritten!\nDaten wurden gespeichert!\n\n";
}
$stunden_limit_result = $this->getStundenInstitut($mitarbeiter_uid, $lehreinheit->studiensemester_kurzbz, $oe_array);
@@ -190,7 +190,7 @@ class LektorLib
$stunden_limit_array = getData($stunden_limit_result);
foreach ($stunden_limit_array as $stunden_limit)
{
$warning .= $stunden_limit->summe . ' Stunden' . $stunden_limit->bezeichnung;
$warning .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n";
}
}
}
@@ -206,7 +206,7 @@ class LektorLib
$benutzer_aktiv = getData($benutzer_result)[0]->aktiv;
if (!$benutzer_aktiv)
$warning .= "Achtung: Der/Die Benutzer*in ist inaktiv!\nBitte informieren Sie die Personalbteilung.\n\nDaten wurden gespeichert.\n\n";
$warning .= "Achtung: Der/Die Benutzer*in ist inaktiv!\nBitte informieren Sie die Personalbteilung.\nDaten wurden gespeichert.\n\n";
$updatableFields = array(
'semesterstunden',
@@ -236,9 +236,9 @@ class LektorLib
if (isError($result)) return $result;
if ($warning !== '') return error($warning);
if ($warning !== '') return success(['warning' => $warning]);
return success('Successfully updated Lehreinheit');
return success('Erfolgreich geupdated');
}
private function getMaxStunden($mitarbeiter_uid, $studiensemester_kurzbz, $studiengang_kz, $echter_dv)
@@ -280,7 +280,7 @@ class LektorLib
foreach ($stunden_limit_array as $stunden_limit)
{
$error .= $stunden_limit->summe . ' Stunden' . $stunden_limit->bezeichnung;
$error .= $stunden_limit->summe . ' Stunden ' . $stunden_limit->bezeichnung . "\n";
}
}
return error($error);
+1
View File
@@ -122,6 +122,7 @@ class PhrasesLib
$tmpText = substr($tmpText, 0, strlen($tmpText) - 4);
}
}
$tmpText = str_replace(['<span class="caps">', '</span>'], '', $tmpText);
$result->retval[$i]->text = $tmpText;
}
+14 -2
View File
@@ -303,10 +303,22 @@ class ProfilLib{
private function getBenutzerFunktion($uid)
{
$this->ci->load->model("person/Benutzerfunktion_model","BenutzerfunktionModel");
$this->ci->BenutzerfunktionModel->addSelect(["tbl_benutzerfunktion.bezeichnung as Bezeichnung", "tbl_organisationseinheit.bezeichnung as Organisationseinheit", "datum_von as Gültig_von", "datum_bis as Gültig_bis", "wochenstunden as Wochenstunden"]);
$this->ci->BenutzerfunktionModel->addSelect([
"CASE WHEN (tbl_benutzerfunktion.bezeichnung IS NOT NULL AND tbl_benutzerfunktion.bezeichnung <> '' AND tbl_benutzerfunktion.bezeichnung <> tbl_funktion.beschreibung) THEN tbl_funktion.beschreibung || ' - ' || tbl_benutzerfunktion.bezeichnung ELSE tbl_funktion.beschreibung END as \"Bezeichnung\"",
"tbl_organisationseinheit.bezeichnung as Organisationseinheit",
"datum_von as Gültig_von",
"datum_bis as Gültig_bis",
"COALESCE(wochenstunden, '0'::numeric(5,2)) AS \"Wochenstunden\""
]);
$this->ci->BenutzerfunktionModel->addJoin("tbl_funktion", "funktion_kurzbz");
$this->ci->BenutzerfunktionModel->addJoin("tbl_organisationseinheit", "oe_kurzbz");
$benutzer_funktion_res = $this->ci->BenutzerfunktionModel->loadWhere(array('uid' => $uid));
$benutzer_funktion_res = $this->ci->BenutzerfunktionModel->loadWhere(
array(
'uid' => $uid,
'NOW()::date BETWEEN COALESCE(datum_von, \'1970-01-01\'::date) AND COALESCE(datum_bis, \'2170-12-01\'::date)' => null
)
);
if(isError($benutzer_funktion_res)){
return error(getData($benutzer_funktion_res));
}
@@ -24,7 +24,6 @@ class GehaltsbestandteilLib
$this->CI = get_instance();
$this->CI->load->model('vertragsbestandteil/Gehaltsbestandteil_model',
'GehaltsbestandteilModel');
$this->CI->load->library('extensions/FHC-Core-Personalverwaltung/abrechnung/GehaltsLib');
$this->GehaltsbestandteilModel = $this->CI->GehaltsbestandteilModel;
}
@@ -121,10 +120,6 @@ class GehaltsbestandteilLib
{
$this->setUIDtoPGSQL();
// delete Gehaltsabrechnung
$ret = $this->CI->gehaltslib->deleteAbrechnung($gehaltsbestandteil);
//
$ret = $this->GehaltsbestandteilModel->delete($gehaltsbestandteil->getGehaltsbestandteil_id());
if (isError($ret))
@@ -33,6 +33,11 @@ class Lehreinheit_model extends DB_Model
{
$lehreinheiten = array();
$this->addSelect(
'lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, lehrform_kurzbz,
stundenblockung, wochenrythmus, start_kw, raumtyp, raumtypalternativ,
sprache, lehre, unr, lvnr, lehrfach_id, gewicht'
);
$this->addOrder('lehreinheit_id');
$les = $this->loadWhere(
array('lehrveranstaltung_id' => $lehrveranstaltung_id,
@@ -689,10 +694,26 @@ EOSQL;
private function _getTagsCTE()
{
$this->load->config('lvverwaltung');
$tags = $this->config->item('tags');
$whereTags = '';
if (is_array($tags) && !isEmptyArray($tags))
{
$tags = array_keys($tags);
foreach ($tags as $key => $tag)
{
$tags[$key] = $this->db->escape($tag);
}
$whereTags = " AND tbl_notiz_typ.typ_kurzbz IN (" . implode(",", $tags) . ")";
}
return "tag_data_agg AS (
SELECT
lehreinheit_id,
COALESCE(json_agg(tag ORDER BY id), '[]'::json) AS tags
COALESCE(json_agg(tag ORDER BY done), '[]'::json) AS tags
FROM (
SELECT DISTINCT ON (public.tbl_notiz.notiz_id)
tbl_notiz.notiz_id AS id,
@@ -705,8 +726,9 @@ EOSQL;
FROM public.tbl_notizzuordnung
JOIN public.tbl_notiz ON tbl_notizzuordnung.notiz_id = tbl_notiz.notiz_id
JOIN public.tbl_notiz_typ ON tbl_notiz.typ = tbl_notiz_typ.typ_kurzbz
WHERE lehreinheit_id IN (SELECT lehreinheit_id FROM lehreinheiten)
) AS tag
WHERE lehreinheit_id IN (SELECT lehreinheit_id FROM lehreinheiten)"
. $whereTags.
") AS tag
GROUP BY lehreinheit_id
)";
}
@@ -97,7 +97,7 @@ class Betriebsmittelperson_model extends DB_Model
return $this->loadWhere($condition);
}
public function getBetriebsmittelData($id, $type_id)
public function getBetriebsmittelData($id, $type_id, $betriesmitteltypes = null)
{
switch ($type_id) {
case 'person_id':
@@ -113,6 +113,15 @@ class Betriebsmittelperson_model extends DB_Model
return error("ID nicht gültig");
}
$cond .= " = ? ";
$params[] = $id;
if ($betriesmitteltypes && !isEmptyArray($betriesmitteltypes))
{
$cond .= " AND bm.betriebsmitteltyp IN ?";
$params[] = $betriesmitteltypes;
}
$query = "
SELECT
bm.nummer, bmp.person_id, bm.betriebsmitteltyp, bmp.anmerkung as anmerkung,
@@ -126,9 +135,9 @@ class Betriebsmittelperson_model extends DB_Model
JOIN
wawi.tbl_betriebsmittel bm ON (bmp.betriebsmittel_id = bm.betriebsmittel_id)
WHERE
" . $cond . " = ? ";
" . $cond;
return $this->execQuery($query, array($id));
return $this->execQuery($query, $params);
}
/**
+3 -3
View File
@@ -26,7 +26,7 @@ $vater_bildungsstaat = isset($uhstatData->vater_bildungsstaat) ? $uhstatData->va
$vater_bildungmax = isset($uhstatData->vater_bildungmax) ? $uhstatData->vater_bildungmax : set_value('vater_bildungmax');
$readOnly = isset($formMetaData['readOnly']);
$disabled = $readOnly ? ' disabled' : '';
$editPermission = isset($formMetaData['editPermission']) && $formMetaData['editPermission'] === true;
$savePermission = isset($formMetaData['savePermission']) && $formMetaData['savePermission'] === true;
$deletePermission = isset($formMetaData['deletePermission']) && $formMetaData['deletePermission'] === true;
$saved = isset($saved) && $saved === true;
?>
@@ -51,7 +51,7 @@ $saved = isset($saved) && $saved === true;
<?php echo $this->p->t('uhstat', 'uhstat1EinleitungSvnrtext') ?>
</p>
<br>
<?php if ($editPermission): ?>
<?php if ($savePermission): ?>
<?php if (isset($successMessage) && !isEmptyString($successMessage)): ?>
<div class="alert alert-success" id="uhstat_success_alert">
<button type="button" class="close" data-dismiss="alert">x</button>
@@ -288,7 +288,7 @@ $saved = isset($saved) && $saved === true;
</div>
</div>
</fieldset>
<?php if ($editPermission && !$readOnly): ?>
<?php if ($savePermission && !$readOnly): ?>
<br>
<fieldset>
<div class="form-group">
@@ -32,12 +32,6 @@
<div class='stammdaten' id="gebdatum"><?php echo date_format(date_create($stammdaten->gebdatum), 'd.m.Y') ?></div>
</td>
</tr>
<tr>
<td><strong><?php echo ucfirst($this->p->t('person','svnr')) ?></strong></td>
<td>
<div class='stammdaten' id="svnr"><?php echo $stammdaten->svnr ?></div>
</td>
</tr>
<tr>
<td><strong><?php echo ucfirst($this->p->t('person','staatsbuergerschaft')) ?></strong></td>
<td>
-1
View File
@@ -843,7 +843,6 @@ function meine_uid_informationen_detail($db,$uid,$count=0)
$aktiv=$db->db_result($erg,0,"aktiv");
$svnr=$db->db_result($erg,0,"svnr");
$titelpre=$db->db_result($erg,0,"titelpre");
$titelpost=$db->db_result($erg,0,"titelpost");
+34
View File
@@ -0,0 +1,34 @@
<?php
require_once('../../config/cis.config.inc.php');
if (isset($_COOKIE['fhclogout']) && ($_COOKIE['fhclogout'] === 'fhclogout'))
{
setcookie('fhclogout', '', -1, '/');
http_response_code(401);
header('WWW-Authenticate: Basic realm="' . AUTH_NAME . '"');
?>
<!doctype html>
<html>
<head>
<title>FH-Complete logout Basic Auth</title>
<meta http-equiv="refresh" content="2; url=<?php echo APP_ROOT . 'cis/'; ?>"/>
</head>
<body>
<script>
function logout()
{
console.log('FH-Complete logout Basic Auth');
window.location.href = '<?php echo APP_ROOT . 'cis/'; ?>';
}
logout();
</script>
</body>
</html>
<?php
}
else
{
http_response_code(303);
header('Location:' . APP_ROOT . 'cis/');
}
@@ -285,15 +285,16 @@ function showHideBezeichnungDropDown()
if (dd.options[dd.selectedIndex].value == 'DienstV')
{
var str = '<select name="bezeichnung" class="dd_breit">';
str += '<option value="Eheschließung">a) Eigene Eheschließung (3 Tage)</option>';
str += '<option value="Eheschließung">a) Eigene Eheschließung oder Verpartnerung (3 Tage)</option>';
str += '<option value="Geburt eigenes Kind">b) Geburt eines Kindes der Ehefrau/Lebensgefährtin (2 Tage)</option>';
str += '<option value="Heirat Kind/Geschwister">c) Eheschließung eines Kindes/eigener Geschwister (1 Tag)</option>';
str += '<option value="Heirat Kind/Geschwister">c) Eheschließung oder Verpartnerung eines Kindes/eigener Geschwister (1 Tag)</option>';
str += '<option value="Eigene Sponsion/Promotion">d) Teilnahme an eigener Sponsion/Promotion (1 Tag)</option>';
str += '<option value="Lebensbedr. Erkrankung P/K/E">e) Lebensbedrohliche Erkrankung Partner/Kinder/Eltern (3 Tage)</option>';
str += '<option value="Ableben P/K/E">f) Ableben Partner/Kinder/Elternteil (3 Tage)</option>';
str += '<option value="Bestattung G/S/G">g) Teilnahme an Bestattung Geschwister/Schwiegereltern/eigener Großeltern (1 Tag)</option>';
str += '<option value="Wohnungswechsel">h) Wohnungswechsel in eigenen Haushalt (2 Tage)</option>';
str += '<option value="Bundesheer">i) Einberufung Bundesheer</option>';
str += '<option value="Volksschultag">j) erster Volksschultag (1 Tag)</option>';
str += '</select>';
sp.innerHTML = str;
+9 -1
View File
@@ -45,6 +45,7 @@ require_once('../../../include/benutzerberechtigung.class.php');
require_once('../../../include/zeitaufzeichnung_import_csv.class.php');
require_once('../../../include/zeitaufzeichnung_import_post.class.php');
require_once('../../../include/vertragsbestandteil.class.php');
require_once('../../../include/benutzerfunktion.class.php');
$sprache = getSprache();
$p=new phrasen($sprache);
@@ -112,6 +113,13 @@ else
$activities = array('Admin', 'FuE','FuEallg','Lehre', 'Pause', 'Arztbesuch', 'DienstreiseMT', 'Behoerde', 'Ersatzruhe', 'Weiterbildung', 'LVEntwicklung');
}
// Wenn die Funktion Lehrling zugeteilt ist, kann zusaetzlich Berufsschule als Aktivitaet gewaehlt werden
$benutzerfunktion = new benutzerfunktion();
if ($benutzerfunktion->benutzerfunktion_exists($user, 'lehrling', true))
{
$activities[] = 'Berufsschule';
}
$activities_str = "'".implode("','", $activities)."'";
// definiert bis zu welchem Datum die Eintragung nicht mehr möglich ist
@@ -690,7 +698,7 @@ echo '
function checkPausenblock()
{
var sel = $("#aktivitaet").val();
var activities = ["Admin", "Lehre", "FuE", "Operativ", "Betrieb", "Design", "LVEntwicklung", "Weiterbildung", "FuEallg"];
var activities = ["Admin", "Lehre", "FuE", "Operativ", "Betrieb", "Design", "LVEntwicklung", "Weiterbildung", "FuEallg", "Berufsschule"];
if (activities.includes(sel))
showPausenblock();
else
-3
View File
@@ -307,8 +307,6 @@ foreach($prestudent_ids as $pid)
$nation->load($prestudent->zgvnation);
$zgvnation = $nation->kurztext;
$svnr = ($prestudent->svnr == '')?($prestudent->ersatzkennzeichen != ''?'Ersatzkennzeichen: '.$prestudent->ersatzkennzeichen:''):$prestudent->svnr;
foreach($adresse->result as $row_adresse)
{
if($row_adresse->heimatadresse)
@@ -439,7 +437,6 @@ foreach($prestudent_ids as $pid)
'zustell_ort' => $zustellOrt,
'zustell_bundesland' => $zustellBundesland,
'geburtsnation' => $geburtsnation,
'svnr' => $svnr,
'staatsbuergerschaft' => $staatsbuergerschaft,
'geschlecht' => $prestudent->geschlecht,
'telefonnummer' => $telefonnummer,
@@ -125,8 +125,6 @@ $worksheet->write($zeile, ++$i, "PERSONENKENNZEICHEN", $format_bold);
$maxlength[$i] = 19;
$worksheet->write($zeile, ++$i, "STAATSBÜRGERSCHAFT", $format_bold);
$maxlength[$i] = 16;
$worksheet->write($zeile, ++$i, "SVNR", $format_bold);
$maxlength[$i] = 4;
$worksheet->write($zeile, ++$i, "PERSON_ID", $format_bold);
$maxlength[$i] = 6;
$worksheet->write($zeile, ++$i, "ERSATZKENNZEICHEN", $format_bold);
@@ -396,12 +394,7 @@ function draw_content($row)
$worksheet->write($zeile, $i, $row->staatsbuergerschaft);
$i++;
//SVNR
if (mb_strlen($row->svnr) > $maxlength[$i])
$maxlength[$i] = mb_strlen($row->svnr);
$worksheet->write($zeile, $i, $row->svnr);
$i++;
//Person_id
if (mb_strlen($row->person_id) > $maxlength[$i])
$maxlength[$i] = mb_strlen($row->person_id);
-2
View File
@@ -594,7 +594,6 @@ if(!$error)
$student->anmerkungen = $_POST['anmerkung'];
$student->homepage = $_POST['homepage'];
$student->matr_nr = $_POST['matr_nr'];
$student->svnr = $_POST['svnr'];
$student->ersatzkennzeichen = $_POST['ersatzkennzeichen'];
$student->familienstand = $_POST['familienstand'];
$student->geschlecht = $_POST['geschlecht'];
@@ -746,7 +745,6 @@ if(!$error)
$person->gebzeit = $_POST['geburtszeit'];
$person->anmerkungen = $_POST['anmerkung'];
$person->homepage = $_POST['homepage'];
$person->svnr = $_POST['svnr'];
$person->ersatzkennzeichen = $_POST['ersatzkennzeichen'];
$person->familienstand = $_POST['familienstand'];
$person->geschlecht = $_POST['geschlecht'];
+1 -2
View File
@@ -134,8 +134,7 @@ echo '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
</row>
<row>
<label value="SVNR" control="student-detail-textbox-svnr"/>
<hbox><textbox id="student-detail-textbox-svnr" disabled="true" maxlength="16" size="10"/></hbox>
<label value="Ersatzkennzeichen" control="student-detail-textbox-ersatzkennzeichen"/>
<hbox><textbox id="student-detail-textbox-ersatzkennzeichen" disabled="true" maxlength="10" size="15"/></hbox>
<label value="Geburtszeit" control="student-detail-textbox-geburtszeit" hidden="true"/>
-5
View File
@@ -284,10 +284,6 @@ else
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/student/rdf#titelpost" onclick="StudentTreeSort()"/>
<splitter class="tree-splitter"/>
<treecol id="student-treecol-svnr" label="SVNR" flex="1" hidden="false" persist="hidden, width, ordinal"
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/student/rdf#svnr" onclick="StudentTreeSort()"/>
<splitter class="tree-splitter"/>
<treecol id="student-treecol-ersatzkennzeichen" label="Ersatzkennzeichen" flex="1" hidden="false" persist="hidden, width, ordinal"
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/student/rdf#ersatzkennzeichen" onclick="StudentTreeSort()"/>
@@ -445,7 +441,6 @@ else
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/student/rdf#aktiv" label="rdf:http://www.technikum-wien.at/student/rdf#wahlname" />
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/student/rdf#aktiv" label="rdf:http://www.technikum-wien.at/student/rdf#vornamen" />
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/student/rdf#aktiv" label="rdf:http://www.technikum-wien.at/student/rdf#titelpost" />
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/student/rdf#aktiv" label="rdf:http://www.technikum-wien.at/student/rdf#svnr" />
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/student/rdf#aktiv" label="rdf:http://www.technikum-wien.at/student/rdf#ersatzkennzeichen" />
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/student/rdf#aktiv" label="rdf:http://www.technikum-wien.at/student/rdf#geburtsdatum" />
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/student/rdf#aktiv" label="rdf:http://www.technikum-wien.at/student/rdf#geschlecht" />
+10 -7
View File
@@ -786,7 +786,6 @@ function StudentDetailReset()
document.getElementById('student-detail-textbox-geburtszeit').value='';
document.getElementById('student-detail-textbox-anmerkung').value='';
document.getElementById('student-detail-textbox-homepage').value='';
document.getElementById('student-detail-textbox-svnr').value='';
document.getElementById('student-detail-textbox-ersatzkennzeichen').value='';
document.getElementById('student-detail-menulist-familienstand').value='l';
document.getElementById('student-detail-menulist-geschlecht').value='m';
@@ -819,7 +818,6 @@ function StudentDetailDisableFields(val)
document.getElementById('student-detail-textbox-geburtszeit').disabled=val;
document.getElementById('student-detail-textbox-anmerkung').disabled=val;
document.getElementById('student-detail-textbox-homepage').disabled=val;
document.getElementById('student-detail-textbox-svnr').disabled=val;
document.getElementById('student-detail-textbox-ersatzkennzeichen').disabled=val;
document.getElementById('student-detail-menulist-familienstand').disabled=val;
document.getElementById('student-detail-menulist-geschlecht').disabled=val;
@@ -860,7 +858,6 @@ function StudentDetailSave()
geburtszeit = document.getElementById('student-detail-textbox-geburtszeit').value;
anmerkung = document.getElementById('student-detail-textbox-anmerkung').value;
homepage = document.getElementById('student-detail-textbox-homepage').value;
svnr = document.getElementById('student-detail-textbox-svnr').value;
ersatzkennzeichen = document.getElementById('student-detail-textbox-ersatzkennzeichen').value;
familienstand = document.getElementById('student-detail-menulist-familienstand').value;
geschlecht = document.getElementById('student-detail-menulist-geschlecht').value;
@@ -919,7 +916,6 @@ function StudentDetailSave()
req.add('geburtszeit', geburtszeit);
req.add('anmerkung', anmerkung);
req.add('homepage', homepage);
req.add('svnr', svnr);
req.add('ersatzkennzeichen', ersatzkennzeichen);
req.add('familienstand', familienstand);
req.add('geschlecht', geschlecht);
@@ -1167,7 +1163,6 @@ function StudentAuswahl()
geburtszeit=getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#gebzeit" ));
anmerkung=getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#anmerkungen" ));
homepage=getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#homepage" ));
svnr=getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#svnr" ));
ersatzkennzeichen=getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#ersatzkennzeichen" ));
familienstand=getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#familienstand" ));
geschlecht=getTargetHelper(dsource,subject,rdfService.GetResource( predicateNS + "#geschlecht" ));
@@ -1211,7 +1206,6 @@ function StudentAuswahl()
document.getElementById('student-detail-textbox-geburtszeit').value=geburtszeit;
document.getElementById('student-detail-textbox-anmerkung').value=anmerkung;
document.getElementById('student-detail-textbox-homepage').value=homepage;
document.getElementById('student-detail-textbox-svnr').value=svnr;
document.getElementById('student-detail-textbox-ersatzkennzeichen').value=ersatzkennzeichen;
document.getElementById('student-detail-menulist-familienstand').value=familienstand;
@@ -3553,6 +3547,15 @@ function StudentZeugnisDokumentArchivieren()
xml = 'abschlussdokument_lehrgaenge.xml.php';
break;
case 'microcredentialzertifikat_1':
case 'microcredentialzertifikat_2':
case 'microcredentialzertifikat_3':
case 'microcredential_1':
case 'microcredential_2':
case 'microcredential_3':
xml = 'microcredential.xml.php';
break;
default:
alert('Das Archivieren fuer diesen Dokumenttyp wird derzeit nicht unterstuetzt');
return
@@ -4783,7 +4786,7 @@ function StudentNotenMoveFromAntrag()
var uid = document.getElementById('student-detail-textbox-uid').value;
req.add('student_uid', uid);
var txt = "?";
for(var q in req.parms) {
txt = txt+'&'+req.parms[q].name+'='+encodeURIComponent(req.parms[q].value);
-1
View File
@@ -1051,7 +1051,6 @@ class prestudent extends person
//$ps->foto = $row->foto;
$ps->anmerkungen = $row->anmerkungen;
$ps->homepage = $row->homepage;
$ps->svnr = $row->svnr;
$ps->ersatzkennzeichen = $row->ersatzkennzeichen;
$ps->familienstand = $row->familienstand;
$ps->geschlecht = $row->geschlecht;
+1 -3
View File
@@ -294,7 +294,6 @@ class student extends benutzer
$l->gebort=$row->gebort;
$l->gebzeit=$row->gebzeit;
$l->familienstand = $row->familienstand;
$l->svnr=$row->svnr;
$l->foto=$row->foto;
$l->anmerkungen=$row->anmerkung;
$l->aktiv=$this->db_parse_bool($row->aktiv);
@@ -649,7 +648,7 @@ class student extends benutzer
{
$sql_query = "SELECT
person_id, staatsbuergerschaft, geburtsnation, sprache, anrede, titelpost, titelpre,
nachname, vorname, vornamen, gebdatum, gebort, gebzeit, anmerkung, homepage, svnr,
nachname, vorname, vornamen, gebdatum, gebort, gebzeit, anmerkung, homepage,
ersatzkennzeichen, familienstand, geschlecht, anzahlkinder, tbl_person.aktiv, kurzbeschreibung,
tbl_benutzer.aktiv as bnaktiv, tbl_student.studiengang_kz, tbl_student.semester, tbl_student.verband,
tbl_student.gruppe, tbl_student.prestudent_id, tbl_benutzer.uid
@@ -691,7 +690,6 @@ class student extends benutzer
$l->gebzeit = $row->gebzeit;
$l->anmerkungen = $row->anmerkung;
$l->homepage = $row->homepage;
$l->svnr = $row->svnr;
$l->ersatzkennzeichen = $row->ersatzkennzeichen;
$l->familienstand = $row->familienstand;
$l->geschlecht = $row->geschlecht;
+4 -4
View File
@@ -244,10 +244,10 @@ function checkZeilenUmbruch()
if(defined('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN') && CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN && $angemeldet
&& (!defined('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN_STG') || in_array($lv->studiengang_kz, unserialize(CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN_STG)))
&& (!defined('CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN_LVA') || in_array($lv->lehrveranstaltung_id, unserialize(CIS_LEHRVERANSTALTUNG_ANWESENHEIT_ANZEIGEN_LVA)))
&& ($rechte->isBerechtigt('extension/anw_ent_admin')
|| $rechte->isBerechtigt('extension/anwesenheit_lektor')
|| $rechte->isBerechtigt('extension/anwesenheit_student')
|| $rechte->isBerechtigt('extension/anwesenheit_admin')))
&& ($rechte->isBerechtigt('extension/anw_r_ent_assistenz')
|| $rechte->isBerechtigt('extension/anw_r_lektor')
|| $rechte->isBerechtigt('extension/anw_r_student')
|| $rechte->isBerechtigt('extension/anw_r_full_assistenz')))
{
$link='';
$text='';
+10
View File
@@ -28,3 +28,13 @@ textarea[name="anmerkung"] {
content: '\f073';
color: #f3c541;
}
.modal-dialog.modal-xxl {
max-width: 95% !important;
}
.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu:after
{
border-color: black;
}
+153 -1
View File
@@ -1 +1,153 @@
@import '../../vendor/olifolkerd/tabulator6/dist/css/tabulator_simple.min.css';
@import '../../vendor/olifolkerd/tabulator6/dist/css/tabulator_simple.min.css';
.tabulator-row {
border-bottom: none;
}
.tabulator-row .tabulator-frozen,
.tabulator-row .tabulator-cell {
border-bottom: 1px solid #dee2e6;
}
.tabulator-row.tabulator-row-even {
background-color: transparent;
}
.tabulator-headers .tabulator-frozen,
.tabulator-row.tabulator-row-odd .tabulator-frozen,
.tabulator-row.tabulator-row-odd .tabulator-cell {
background-color: #fff;
}
.tabulator-row.tabulator-row-even .tabulator-frozen,
.tabulator-row.tabulator-row-even .tabulator-cell {
background-color: #f2f2f2;
}
.tabulator-row.tabulator-selectable:hover .tabulator-frozen,
.tabulator-row.tabulator-selectable:hover .tabulator-cell {
background-color: #ececec;
}
.tabulator-row.tabulator-selected .tabulator-frozen,
.tabulator-row.tabulator-selected .tabulator-cell {
background-color: #9abcea;
}
.tabulator-row.tabulator-selected:hover .tabulator-frozen,
.tabulator-row.tabulator-selected:hover .tabulator-cell {
background-color: #769bcc;
}
.tabulator .tabulator-col-resize-handle:last-of-type {
z-index: 999999;
}
/* classes for rows that are not selectable in the tabulator, except for rows that are used for calculation */
.tabulator-row.tabulator-unselectable:not(.tabulator-calcs) {
color: #adb5bd !important;
pointer-events: none !important;
}
.tabulator-row.tabulator-unselectable a {
pointer-events: auto !important;
}
/* using bootstrap background classes to style the background color of tabulator rows */
/* bg-warning */
/* odd-rows */
.tabulator-headers .tabulator-frozen.bg-warning,
.tabulator-row.tabulator-row-odd .tabulator-frozen.bg-warning,
.tabulator-row.tabulator-row-odd .tabulator-cell.bg-warning {
background-color: #fcf8e3;
}
/* even-rows */
.tabulator-headers .tabulator-frozen.bg-warning,
.tabulator-row.tabulator-row-even .tabulator-frozen.bg-warning,
.tabulator-row.tabulator-row-even .tabulator-cell.bg-warning {
background-color: #fcf8e3;
}
/* bg-success */
/* odd-rows */
.tabulator-headers .tabulator-frozen.bg-success,
.tabulator-row.tabulator-row-odd .tabulator-frozen.bg-success,
.tabulator-row.tabulator-row-odd .tabulator-cell.bg-success {
background-color: #dff0d8;
}
/* even-rows */
.tabulator-headers .tabulator-frozen.bg-success,
.tabulator-row.tabulator-row-even .tabulator-frozen.bg-success,
.tabulator-row.tabulator-row-even .tabulator-cell.bg-success {
background-color: #dff0d8;
}
/* bg-info */
/* odd-rows */
.tabulator-headers .tabulator-frozen.bg-info,
.tabulator-row.tabulator-row-odd .tabulator-frozen.bg-info,
.tabulator-row.tabulator-row-odd .tabulator-cell.bg-info {
background-color: #d9edf7;
}
/* even-rows */
.tabulator-headers .tabulator-frozen.bg-info,
.tabulator-row.tabulator-row-even .tabulator-frozen.bg-info,
.tabulator-row.tabulator-row-even .tabulator-cell.bg-info {
background-color: #d9edf7;
}
/* special bootstrap5 styling for tableWidget and their accordion-item ::after content */
.accordion-button::after{
content:none !important;
}
.tabulator {
font-size: 1rem;
}
.tabulator-initialfontsize .tabulator {
font-size: 14px;
}
.tabulator-row.tabulator-unselectable .tabulator-cell {
pointer-events: all;
}
.tabulator-tooltip {
color: #fff;
background-color: #000;
}
.tabulator-row.tabulator-unselectable:not(.tabulator-calcs) {
color: #777 !important;
}
.tabulator-cell .btn {
padding: 0 .375rem;
font-size: calc(1rem - 2px / 1.5); /* substract border (2 x 1px) modified by the line-height (1.5) */
border-radius: .2rem;
vertical-align: baseline;
}
.tabulator-row.tabulator-selectable:focus {
box-shadow: 0 0 0 .24rem rgba(13,110,253,.25);
z-index: 1;
outline: 0;
}
.btn-select-col-selected
{
background-color: #e6e6e6;
}
.accordion-item-dark, .accordion-item-dark:focus{
background-color:#CED4DA !important;
border-color:#ADB5BD !important;
box-shadow: none !important;
}
/**
* Make keyboard-focused list items look like the mouse-hovered list items
*/
.tabulator-edit-list .tabulator-edit-list-item.focused {
color: #fff;
background: #1d68cd;
}
+11
View File
@@ -203,3 +203,14 @@
}
}
.hiddenWidget{
background: var(--fhc-disabled-background);
opacity: 40%;
}
.hiddenWidget .card,
.hiddenWidget .card-body,
.hiddenWidget .card-body *{
background: inherit !important;
}
+4
View File
@@ -3973,6 +3973,10 @@
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
}
.p-tabview-panel .btn-close,
.p-tabview-panel .carousel-indicators [data-bs-target] {
box-sizing: content-box;
}
.p-toolbar {
background: #efefef;
+3 -1
View File
@@ -89,7 +89,8 @@
--fhc-link: var(--fhc-blue-10);
/* disabled */
--fhc-disabled: var(--fhc-gray-10);
--fhc-disabled: var(--fhc-gray-90);
--fhc-disabled-background: var(--fhc-gray-30);
/* status colors */
--fhc-danger: var(--fhc-red-10);
@@ -135,6 +136,7 @@
/* disabled */
--fhc-disabled: var(--fhc-gray-10);
--fhc-disabled-background: var(--fhc-gray-120);
/* status colors */
--fhc-danger: var(--fhc-red-10);
+10 -4
View File
@@ -16,10 +16,13 @@
*/
export default {
getAllBetriebsmittel(type, id) {
getAllBetriebsmittel(type, id, betriebsmitteltypes) {
return {
method: 'get',
url: 'api/frontend/v1/betriebsmittel/betriebsmittelP/getAllBetriebsmittel/' + type + '/' + id
url: 'api/frontend/v1/betriebsmittel/betriebsmittelP/getAllBetriebsmittel/' + type + '/' + id,
params: {
betriebsmitteltypes
}
};
},
addNewBetriebsmittel(person_id, params) {
@@ -48,10 +51,13 @@ export default {
url: 'api/frontend/v1/betriebsmittel/betriebsmittelP/deleteBetriebsmittel/' + betriebsmittelperson_id
};
},
getTypenBetriebsmittel() {
getTypenBetriebsmittel(betriebsmitteltypes) {
return {
method: 'get',
url: 'api/frontend/v1/betriebsmittel/betriebsmittelP/getTypenBetriebsmittel/'
url: 'api/frontend/v1/betriebsmittel/betriebsmittelP/getTypenBetriebsmittel/',
params: {
betriebsmitteltypes
}
};
},
loadInventarliste(query) {
+6
View File
@@ -83,5 +83,11 @@ export default {
method: 'get',
url: 'api/frontend/v1/notiz/notizPerson/isBerechtigt/'
};
},
getCountNotes(person_id){
return {
method: 'get',
url: 'api/frontend/v1/notiz/notizPerson/getCountNotes/' + person_id
};
}
};
+14
View File
@@ -22,5 +22,19 @@ export default {
url: '/api/frontend/v1/Ort/ContentID',
params: { ort_kurzbz: ort_kurbz }
};
},
getRooms(datum, von, bis, typ, personenanzahl = 0) {
return {
method: 'get',
url: '/api/frontend/v1/Ort/getRooms',
params: { datum, von, bis, typ, personenanzahl }
};
},
getRoomTypes() {
return {
method: 'get',
url: '/api/frontend/v1/Ort/getTypes',
params: { }
};
}
};
+7
View File
@@ -68,6 +68,13 @@ export default {
params: dms
};
},
getProfilUpdateWithPermission(filter) {
const url_filter = (filter !== '') ? '/' + encodeURIComponent(filter) : '';
return {
method: 'get',
url: '/api/frontend/v1/ProfilUpdate/getProfilUpdateWithPermission' + url_filter
};
},
getProfilRequestFiles(requestID) {
return {
method: 'get',
+29
View File
@@ -0,0 +1,29 @@
/**
* 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 {
info(app, path) {
return {
method: 'post',
url: '/api/frontend/v1/RouteInfo/info',
params: {
app: app,
path: path
}
};
}
};
+8 -2
View File
@@ -18,6 +18,7 @@ import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.j
import Studium from "../../components/Cis/Studium/Studium.js";
import ApiRenderers from '../../api/factory/renderers.js';
import ApiRouteInfo from '../../api/factory/routeinfo.js';
const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router;
@@ -138,10 +139,10 @@ const router = VueRouter.createRouter({
props: true
},
{
path: `/Cis/MyLv`,
path: `/Cis/MyLv/:studiensemester?`,
name: 'MyLv',
component: MylvStudent,
props: true
props: true,
},
{
path: `/Cis/MyLv/Info/:studien_semester/:lehrveranstaltung_id`,
@@ -319,6 +320,7 @@ const app = Vue.createApp({
// kind of a bandaid for bad css on some pages to avoid horizontal scroll
setScrollbarWidth();
app.use(router);
app.use(primevue.config.default, {
zIndex: {
@@ -331,3 +333,7 @@ app.use(PluginsPhrasen);
app.use(Theme);
app.directive('contrast', contrast);
app.mount('#fhccontent');
router.afterEach((to, from, failure) => {
app.config.globalProperties.$api.call(ApiRouteInfo.info('cis4', to.fullPath));
});
@@ -29,14 +29,24 @@ export default {
uid: {
type: [Number, String],
required: true
}
},
/** List of types to allow for creation */
betriebsmittelTypes: {
type: Array,
default: null
},
/**
* If true: only show the types specified in 'betriebsmittelTypes'.
* If false: show all available types.
*/
filterByProvidedTypes: Boolean
},
data() {
return {
tabulatorOptions: {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(
this.endpoint.getAllBetriebsmittel(this.typeId, this.id)
this.endpoint.getAllBetriebsmittel(this.typeId, this.id, (this.filterByProvidedTypes ? this.betriebsmittelTypes : null))
),
ajaxResponse: (url, params, response) => response.data,
columns: [
@@ -294,7 +304,7 @@ export default {
},
created() {
return this.$api
.call(this.endpoint.getTypenBetriebsmittel())
.call(this.endpoint.getTypenBetriebsmittel(this.betriebsmittelTypes))
.then(result => {
this.listBetriebsmitteltyp = result.data;
})
+18 -5
View File
@@ -3,7 +3,8 @@
export default {
name: 'BootstrapModal',
data: () => ({
modal: null
modal: null,
fullscreen: false
}),
props: {
backdrop: {
@@ -34,6 +35,10 @@ export default {
footerClass: {
type: [String,Array,Object],
default: ''
},
allowFullscreenExpand: {
type: Boolean,
default: false
}
},
emits: [
@@ -58,6 +63,9 @@ export default {
},
toggle() {
return this.modal.toggle();
},
toggleFullscreen() {
this.fullscreen = !this.fullscreen
}
},
mounted() {
@@ -122,13 +130,18 @@ export default {
});
});
},
template: `<div ref="modal" class="bootstrap-modal modal" tabindex="-1" @[\`hide.bs.modal\`]="$emit('hideBsModal')" @[\`hidden.bs.modal\`]="$emit('hiddenBsModal')" @[\`hidePrevented.bs.modal\`]="$emit('hidePreventedBsModal')" @[\`show.bs.modal\`]="$emit('showBsModal')" >
<div class="modal-dialog" :class="dialogClass">
template: `<div ref="modal" class="bootstrap-modal modal" tabindex="-1" @[\`hide.bs.modal\`]="$emit('hideBsModal')" @[\`hidden.bs.modal\`]="$emit('hiddenBsModal')" @[\`hidePrevented.bs.modal\`]="$emit('hidePreventedBsModal')" @[\`show.bs.modal\`]="$emit('showBsModal')" @[\`shown.bs.modal\`]="$emit('shownBsModal')">
<div class="modal-dialog" :class="fullscreen ? 'modal-fullscreen' : dialogClass">
<div class="modal-content">
<div v-if="$slots.title" class="modal-header" :class="headerClass">
<h5 class="modal-title"><slot name="title"/></h5>
<slot name="popoutButton"></slot>
<button v-if="!noCloseBtn" type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<div class="d-flex align-items-center ms-auto">
<button type="button" class="btn ms-auto" style="filter: invert(1)" v-if="allowFullscreenExpand" @click="toggleFullscreen">
<i v-if="!fullscreen" class="fa-solid fa-expand"></i>
<i v-else class="fa-solid fa-compress"></i>
</button>
<button v-if="!noCloseBtn" type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<slot name="modal-header-content"></slot>
</div>
<div class="modal-body" :class="bodyClass">
+9 -4
View File
@@ -44,11 +44,16 @@ export default {
},
watch: {
currentDate() {
this.rangeOffset = this.currentDate.startOf('day').diff(this.focusDate.startOf('day'), 'days').days;
if (this.rangeOffset) {
this.$refs.view.$refs.grid.disableAutoScroll();
if (this.currentDate.locale != this.focusDate.locale) {
this.focusDate = this.currentDate;
this.$emit('update:range', this.range);
this.$refs.slider.slidePages(this.rangeOffset).then(this.updatePage);
} else {
this.rangeOffset = this.currentDate.startOf('day').diff(this.focusDate.startOf('day'), 'days').days;
if (this.rangeOffset) {
this.$refs.view.$refs.grid.disableAutoScroll();
this.$emit('update:range', this.range);
this.$refs.slider.slidePages(this.rangeOffset).then(this.updatePage);
}
}
}
},
+8 -3
View File
@@ -47,10 +47,15 @@ export default {
},
watch: {
currentDate() {
this.rangeOffset = this.currentDate.startOf('day').diff(this.focusDate.startOf('day'), 'days').days;
if (this.rangeOffset) {
if (this.currentDate.locale != this.focusDate.locale) {
this.focusDate = this.currentDate;
this.$emit('update:range', this.range);
this.$refs.slider.slidePages(this.rangeOffset).then(this.updatePage);
} else {
this.rangeOffset = this.currentDate.startOf('day').diff(this.focusDate.startOf('day'), 'days').days;
if (this.rangeOffset) {
this.$emit('update:range', this.range);
this.$refs.slider.slidePages(this.rangeOffset).then(this.updatePage);
}
}
}
},
@@ -57,65 +57,70 @@ export default {
p.style.setProperty('white-space', 'normal');
p.style.setProperty('max-width', '400px');
})
},
prepareContent() {
// replaces the tablesorter with the tabulator
let tables = Array.from(document.getElementsByClassName("tablesorter"));
tables.forEach((table, index) => {
this.sanitizeLegacyTables(table)
new Tabulator(table, {
index: index,
layout: "fitDataFill",
columnDefaults: {
formatter: "html",
resizable: true,
minWidth: "100px"
}
})
})
document.querySelectorAll("#cms [data-confirm]").forEach((el) => {
el.addEventListener("click", (evt) => {
evt.preventDefault();
BsConfirm.popup(el.dataset.confirm)
.then(() => {
Axios.get(el.href)
.then((res) => {
// TODO(chris): check for success then show message and/or reload
location = location;
})
.catch((err) => console.error("ERROR:", err));
})
.catch(() => {});
});
});
document.querySelectorAll("#cms [data-href]").forEach((el) => {
el.href = el.dataset.href.replace(
/^ROOT\//,
FHC_JS_DATA_STORAGE_OBJECT.app_root
);
});
document.querySelectorAll("[href]").forEach((element) => {
let orignal_href = element.getAttribute("href");
let new_href = replaceRelativeLegacyLink(orignal_href);
element.href = new_href;
});
document.querySelectorAll("[style*=background-color]").forEach((element) => {
if (element.style.backgroundColor == "rgb(255, 255, 255)"){
element.style.backgroundColor = "var(--fhc-background)";
}
if(element.querySelector("*[style*=background-color]")){
element.style.backgroundColor = "var(--fhc-tertiary)";
}
});
}
},
mounted(){
// replaces the tablesorter with the tabulator
let tables = Array.from(document.getElementsByClassName("tablesorter"));
tables.forEach((table, index) => {
this.sanitizeLegacyTables(table)
new Tabulator(table, {
index: index,
layout: "fitDataFill",
columnDefaults: {
formatter: "html",
resizable: true,
minWidth: "100px"
}
})
})
document.querySelectorAll("#cms [data-confirm]").forEach((el) => {
el.addEventListener("click", (evt) => {
evt.preventDefault();
BsConfirm.popup(el.dataset.confirm)
.then(() => {
Axios.get(el.href)
.then((res) => {
// TODO(chris): check for success then show message and/or reload
location = location;
})
.catch((err) => console.error("ERROR:", err));
})
.catch(() => {});
});
});
document.querySelectorAll("#cms [data-href]").forEach((el) => {
el.href = el.dataset.href.replace(
/^ROOT\//,
FHC_JS_DATA_STORAGE_OBJECT.app_root
);
});
document.querySelectorAll("[href]").forEach((element) => {
let orignal_href = element.getAttribute("href");
let new_href = replaceRelativeLegacyLink(orignal_href);
element.href = new_href;
});
document.querySelectorAll("[style*=background-color]").forEach((element) => {
if (element.style.backgroundColor == "rgb(255, 255, 255)"){
element.style.backgroundColor = "var(--fhc-background)";
}
if(element.querySelector("*[style*=background-color]")){
element.style.backgroundColor = "var(--fhc-tertiary)";
}
});
},
updated() {
this.prepareContent();
},
mounted(){
this.prepareContent();
},
template: /*html*/ `
<!-- div that contains the content -->
<div v-if="content" class="container" style="max-width: 100%;"><div class="row"><div class="col">
@@ -1,6 +1,6 @@
import { replaceRelativeLegacyLink } from "../../../../helpers/LegacyLinkReplaceHelper.js"
export default {
name: "GeneralComponent",
name: "NewsContentType",
props:{
content:{
type:String,
@@ -12,7 +12,7 @@ export default {
required:true,
},
content_id:{
type:Number,
type: [Number, String],
}
},
methods: {
@@ -129,7 +129,7 @@ export default {
template: /*html*/ `
<!-- div that contains the content -->
<!-- TODO: test with more img content from cms-->
<div v-if="imgContent"><img v-bind="imgContent"></img></div>
<div v-if="imgContent"><img v-bind="imgContent"/></div>
<div v-html="content" v-else-if="content" ></div>
<p v-else>Content was not found</p>
`,
+8 -1
View File
@@ -43,6 +43,13 @@ export default {
return menuItem.c4_link ?? null;
}
},
getMenuName(menuItem) {
if(menuItem.phrase) {
return this.$p.t(menuItem.phrase)
} else {
return menuItem.name
}
}
},
template:/*html*/`
<div v-if="!menu">{{$p.t('lehre','lehrveranstaltungsUnavailable')}}</div>
@@ -54,7 +61,7 @@ export default {
:disabled="c4_disabled(menuItem)" :data-bs-toggle="menuItem.c4_moodle_links?.length?'dropdown':null"
class="menu-entry p-2 w-100 text-wrap border border-1 rounded-3 d-flex flex-column align-items-center justify-content-center text-center text-decoration-none link h-100">
<img :src="menuItem.c4_icon" :alt="menuItem.name" />
<p class="w-100 mt-2 mb-0">{{menuItem.name}}</p>
<p class="w-100 mt-2 mb-0">{{ getMenuName(menuItem) }}</p>
<a v-for="([text,link],index) in menuItem.c4_linkList" target="_blank" :href="link" class="my-1 w-100 submenu text-decoration-none" :index="index">{{text}}</a>
</a>
<ul v-if="menuItem.c4_moodle_links?.length" class="dropdown-menu p-0" :aria-labelledby="menuItem.name">
@@ -19,6 +19,7 @@ export default {
props: {
lehrveranstaltung_id: Number,
bezeichnung: String,
bezeichnung_eng: String,
module: String,
farbe: String,
lvinfo: Boolean,
+12 -7
View File
@@ -75,21 +75,26 @@ export default {
this.$refs.studiensemester.selectedIndex++;
this.$refs.studiensemester.dispatchEvent(new Event('change', { bubbles: true }));
},
setHash(val) {
// TODO: make this a router param to enable history
location.hash = val;
updateRouter(val) {
this.$router.push(`/Cis/MyLv/${val}`);
}
},
created() {
axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Cis/Mylv/Studiensemester').then(res => {
this.studiensemester = res.data.retval || [];
const hash = location.hash.substring(1);
if (hash && this.studiensemester.filter(s => s.studiensemester_kurzbz == hash).length)
this.currentSemester = hash;
const routerStudiensemester = this.$route.params.studiensemester;
if (routerStudiensemester && this.studiensemester.filter(s => s.studiensemester_kurzbz == routerStudiensemester).length)
this.currentSemester = routerStudiensemester;
else
this.currentSemester = this.nearestSem;
});
},
beforeRouteUpdate(to, from, next){
if (to.params.studiensemester && this.studiensemester.filter(s => s.studiensemester_kurzbz == to.params.studiensemester).length && to.params.studiensemester != this.currentSemester)
this.currentSemester = to.params.studiensemester;
next();
},
template: `
<h2>{{$p.t('lehre/myLV')}}</h2>
@@ -104,7 +109,7 @@ export default {
<button :aria-label="$p.t('lehre','previousStudSemester')" v-tooltip.top="{showDelay:1000, value:$p.t('lehre','previousStudSemester')}" class="btn btn-outline-secondary" type="button" :disabled="currentIsFirst" @click="prevSem">
<i class="fa fa-caret-left" aria-hidden="true"></i>
</button>
<select ref="studiensemester" v-model="currentSemester" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setHash($event.target.value)">
<select ref="studiensemester" v-model="currentSemester" class="form-select" :aria-label="$p.t('global/studiensemester_auswaehlen')" @change="updateRouter($event.target.value)">
<option v-for="semester in studiensemester" :key="semester.studiensemester_kurzbz">{{semester.studiensemester_kurzbz}}</option>
</select>
<button class="btn btn-outline-secondary" :aria-label="$p.t('lehre','nextStudSemester')" v-tooltip.top="{showDelay:1000, value:$p.t('lehre','nextStudSemester')}" type="button" :disabled="currentIsLast" @click="nextSem">
@@ -11,6 +11,7 @@ import RoleInformation from "./ProfilComponents/RoleInformation.js";
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
import { dateFilter } from '../../../tabulator/filters/Dates.js';
export default {
components: {
@@ -40,7 +41,7 @@ export default {
persistence: {
columns: false
},
height: 300,
minHeight: 300,
layout: "fitColumns",
responsiveLayout: "collapse",
responsiveLayoutCollapseUseFormatters: false,
@@ -74,18 +75,24 @@ export default {
{
title: Vue.computed(() => this.preloadedPhrasen.gueltigVonPhrase),
field: "Gültig_von",
headerFilter: true,
headerFilterFunc: 'dates',
headerFilter: dateFilter,
resizable: true,
minWidth: 200,
visible: true
visible: true,
formatter:"datetime",
formatterParams: this.datetimeFormatterParams()
},
{
title: Vue.computed(() => this.preloadedPhrasen.gueltigBisPhrase),
field: "Gültig_bis",
headerFilter: true,
headerFilterFunc: 'dates',
headerFilter: dateFilter,
resizable: true,
minWidth: 200,
visible: true
visible: true,
formatter:"datetime",
formatterParams: this.datetimeFormatterParams()
},
{
title: Vue.computed(() => this.preloadedPhrasen.wochenstundenPhrase),
@@ -102,7 +109,7 @@ export default {
persistence: {
columns: false
},
height: 300,
minHeight: 300,
layout: "fitColumns",
responsiveLayout: "collapse",
responsiveLayoutCollapseUseFormatters: false,
@@ -138,9 +145,12 @@ export default {
{
title: Vue.computed(() => this.preloadedPhrasen.ausgabedatumPhrase),
field: "Ausgegeben_am",
headerFilter: true,
headerFilterFunc: 'dates',
headerFilter: dateFilter,
minWidth: 200,
visible: true
visible: true,
formatter:"datetime",
formatterParams: this.datetimeFormatterParams()
},
],
}
@@ -167,11 +177,11 @@ export default {
this.$api
.call(ApiProfilUpdate.selectProfilRequest())
.then((request) => {
if (!request.error && request) {
if (!request.error && request.data) {
this.data.profilUpdates = request.data;
this.data.profilUpdates.sort(this.sortProfilUpdates);
} else {
console.error("Error when fetching profile updates: " + res.data);
console.error("Error when fetching profile updates: " + request);
}
})
.catch((err) => {
@@ -211,6 +221,15 @@ export default {
setTableColumnTitles() { // reevaluates computed phrasen
if(this.$refs.betriebsmittelTable) this.$refs.betriebsmittelTable.tabulator.setColumns(this.betriebsmittel_table_options.columns)
if(this.$refs.funktionenTable) this.$refs.funktionenTable.tabulator.setColumns(this.funktionen_table_options.columns)
},
datetimeFormatterParams: function() {
const params = {
inputFormat:"yyyy-MM-dd",
outputFormat:"dd.MM.yyyy",
invalidPlaceholder:"(invalid date)",
timezone:FHC_JS_DATA_STORAGE_OBJECT.timezone
};
return params;
}
},
@@ -5,6 +5,8 @@ import RoleInformation from "./ProfilComponents/RoleInformation.js";
import ProfilEmails from "./ProfilComponents/ProfilEmails.js";
import ProfilInformation from "./ProfilComponents/ProfilInformation.js";
import { dateFilter } from '../../../tabulator/filters/Dates.js';
export default {
components: {
CoreFilterCmpt,
@@ -24,7 +26,7 @@ export default {
persistence: {
columns: false
},
height: 300,
minHeight: 300,
layout: "fitColumns",
responsiveLayout: "collapse",
responsiveLayoutCollapseUseFormatters: false,
@@ -60,18 +62,24 @@ export default {
{
title: Vue.computed(() => this.$p.t('global/gueltigVon')),
field: "Gültig_von",
headerFilter: true,
headerFilterFunc: 'dates',
headerFilter: dateFilter,
resizable: true,
minWidth: 200,
visible: true
visible: true,
formatter:"datetime",
formatterParams: this.datetimeFormatterParams()
},
{
title: Vue.computed(() => this.$p.t('global/gueltigBis')),
field: "Gültig_bis",
headerFilter: true,
headerFilterFunc: 'dates',
headerFilter: dateFilter,
resizable: true,
minWidth: 200,
visible: true
visible: true,
formatter:"datetime",
formatterParams: this.datetimeFormatterParams()
},
{
title: Vue.computed(() => this.$p.t('profil/wochenstunden')),
@@ -91,6 +99,15 @@ export default {
funktionenTableBuilt: function () {
this.$refs.funktionenTable.tabulator.setData(this.data.funktionen);
},
datetimeFormatterParams: function() {
const params = {
inputFormat:"yyyy-MM-dd",
outputFormat:"dd.MM.yyyy",
invalidPlaceholder:"(invalid date)",
timezone:FHC_JS_DATA_STORAGE_OBJECT.timezone
};
return params;
}
},
watch: {
'data.funktionen'(newVal) {
@@ -88,6 +88,18 @@ export default {
if (isMitarbeiter) {
content["isMitarbeiter"] = isMitarbeiter;
}
const filesFromDatabase =
await this.$api
.call(ApiProfilUpdate.getProfilRequestFiles(
updateRequest.profil_update_id
))
.then((res) => {
return res.data;
});
files = filesFromDatabase;
content["files"] = files;
}
//? adds the status information if the profil update request was rejected or accepted
@@ -10,21 +10,34 @@ export default {
type: String,
}
},
inject: [
'studiengang_kz', // inject info that should not be displayed
],
inject: {
// inject info that should not be displayed
'studiengang_kz': {
from: 'studiengang_kz',
default: false
},
},
computed: {
getLinkGruppeListe() {
if(this.studiengang_kz === false) {
return '';
}
return this.data.gruppe?.value && this.data.verband?.value && this.data.semester?.value ? FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'cis/private/stud_in_grp.php?kz='+this.studiengang_kz+'&sem=' + this.data.semester.value
+ '&verband=' + this.data.verband.value + '&grp=' + this.data.gruppe.value : ''
},
getLinkVerbandListe() {
if(this.studiengang_kz === false) {
return '';
}
return this.data.verband?.value && this.data.semester?.value ? FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'cis/private/stud_in_grp.php?kz='+this.studiengang_kz+'&sem=' + this.data.semester.value
+ '&verband=' + this.data.verband.value : ''
},
getLinkSemesterListe() {
if(this.studiengang_kz === false) {
return '';
}
return this.data.semester?.value ? FHC_JS_DATA_STORAGE_OBJECT.app_root
+ 'cis/private/stud_in_grp.php?kz='+this.studiengang_kz+'&sem=' + this.data.semester.value : ''
}
@@ -1,8 +1,11 @@
import Dms from "../../../../Form/Upload/Dms.js";
import ApiProfil from '../../../../../api/factory/profil.js';
export default {
components: {
AutoComplete: primevue.autocomplete,
Dms: Dms
},
props: {
@@ -11,9 +14,13 @@ export default {
type: Boolean,
default: false,
},
files: {
type: Array,
default: []
},
},
inject: ["getZustelladressenCount"],
inject: ["getZustelladressenCount", "updateFileID"],
data() {
return {
@@ -23,6 +30,8 @@ export default {
nationenList: [],
originalValue: null,
zustellAdressenCount: null,
dmsData: [],
fileschanged: false
};
},
@@ -83,6 +92,11 @@ export default {
//? sets the value of a property to null when an empty string is entered to keep the isChanged function valid
if (bind === "zustelladresse") {
this.data[bind] = event.target.checked;
} else if(bind === 'files') {
if(this.dmsData.length > 0 && this.dmsData[0].type !== 'application/x.fhc-dms+json') {
this.fileschanged = true;
}
this.updateFileID(this.dmsData);
} else {
this.data[bind] = event.target.value === "" ? null : event.target.value;
}
@@ -91,6 +105,11 @@ export default {
// update the zustellAdressen count
this.zustellAdressenCount = this.getZustelladressenCount();
},
deleteDmsData: function() {
this.dmsData = [];
this.updateValue(null, 'files');
}
},
computed: {
@@ -111,12 +130,14 @@ export default {
!this.data.strasse ||
!this.data.plz ||
!this.data.ort ||
!this.data.typ
!this.data.typ ||
this.dmsData.length === 0
) {
return false;
}
return this.originalValue !== JSON.stringify(this.data);
const datachanged = this.originalValue !== JSON.stringify(this.data);
return datachanged || this.fileschanged;
},
},
@@ -133,6 +154,12 @@ export default {
this.zustellAdressenCount = this.getZustelladressenCount();
},
mounted() {
if (this.files) {
this.dmsData = this.files;
}
},
template: /*html*/ `
<div class="gy-3 row justify-content-center align-items-center">
<!-- warning message for too many zustellungs Adressen -->
@@ -212,9 +239,28 @@ export default {
</div>
</div>
<div class="row g-2">
<div class="col">
<div class="form-underline-titel">{{$p.t('profilUpdate','meldebestaetigung')}}*</div>
<dms
ref="update"
id="files"
name="files"
:multiple="false"
v-model="dmsData"
@update:model-value="updateValue($event,'files')"
></dms>
</div>
<div class="col-auto">
<div>&nbsp;</div>
<button
@click="deleteDmsData"
class="btn btn-danger"
:aria-label="$p.t('profilUpdate','deleteAttachment')"
:title="$p.t('profilUpdate','deleteAttachment')"
><i style="color:white" class="fa fa-trash" aria-hidden="true"></i></button>
</div>
</div>
</div>
`,
@@ -16,7 +16,7 @@ export default {
mixins: [BsModal],
props: {
titel: {
type: Object,
type: String,
},
files: {
type: Array,
@@ -57,7 +57,7 @@ export default {
},
template: /*html*/`
<bs-modal v-show="!loading" ref="modalContainer" v-bind="$props" body-class="" dialog-class="modal-lg" class="bootstrap-alert" :backdrop="false">
<bs-modal ref="modalContainer" v-bind="$props" body-class="" dialog-class="modal-lg" class="bootstrap-alert" :backdrop="false">
<template #title>
<p style="opacity:0.8" class="ms-2" v-if="!updateID">{{$p.t('profilUpdate','profilBildUpdateMessage',[titel])}}</p>
</template>
@@ -112,14 +112,17 @@ export default {
<div class="form-underline-titel">{{topic}}</div>
<span class="form-underline-content">{{data.value}} </span>
</div>
<div v-if="files?.length" class="ms-2">
<a target="_blank" :href="getDocumentLink(file.dms_id)" v-for="file in files">{{file.name}}</a>
</div>
</template>
<component v-else :is="getComponentView" :data="data"></component>
</div>
</div>
<div v-if="files?.length" class="card mt-4">
<div class="card-header">{{$p.t('profilUpdate','nachweisdokumente')}}</div>
<div class="card-body">
<a target="_blank" :href="getDocumentLink(file.dms_id)" v-for="file in files">{{file.name}}</a>
</div>
</div>
`,
};
@@ -11,6 +11,7 @@ import FetchProfilUpdates from "./ProfilComponents/FetchProfilUpdates.js";
import EditProfil from "./ProfilModal/EditProfil.js";
import ApiProfilUpdate from '../../../api/factory/profilUpdate.js';
import { dateFilter } from '../../../tabulator/filters/Dates.js';
export default {
components: {
@@ -39,7 +40,7 @@ export default {
persistence: {
columns: false
},
height: 200,
minHeight: 200,
layout: "fitColumns",
columns: [{
title: Vue.computed(() => this.preloadedPhrasen.zutrittsGruppenPhrase),
@@ -51,7 +52,7 @@ export default {
persistence: {
columns: false
},
height: 300,
minHeight: 300,
layout: "fitColumns",
responsiveLayout: "collapse",
responsiveLayoutCollapseUseFormatters: false,
@@ -85,9 +86,12 @@ export default {
{
title: Vue.computed(() =>this.preloadedPhrasen.ausgabedatum) ,
field: "Ausgegeben_am",
headerFilter: true,
headerFilterFunc: 'dates',
headerFilter: dateFilter,
minWidth: 200,
visible: true
visible: true,
formatter:"datetime",
formatterParams: this.datetimeFormatterParams()
},
],
},
@@ -133,11 +137,11 @@ export default {
this.$api
.call(ApiProfilUpdate.selectProfilRequest())
.then((request) => {
if (!request.error && res) {
if (!request.error && request.data) {
this.data.profilUpdates = request.data;
this.data.profilUpdates.sort(this.sortProfilUpdates);
} else {
console.error("Error when fetching profile updates: " + res.data);
console.error("Error when fetching profile updates: " + request);
}
})
.catch((err) => {
@@ -160,6 +164,15 @@ export default {
this.$refs.editModal.show();
});
},
datetimeFormatterParams: function() {
const params = {
inputFormat:"yyyy-MM-dd",
outputFormat:"dd.MM.yyyy",
invalidPlaceholder:"(invalid date)",
timezone:FHC_JS_DATA_STORAGE_OBJECT.timezone
};
return params;
}
},
computed: {
@@ -188,16 +188,21 @@ export default {
<span class="form-underline-content" >{{data.requested_change.value}}</span>
</div>
<div v-if="files?.length" class="ms-2">
<a v-for="file in files" target="_blank" :href="getDocumentLink(file.dms_id)" >{{file.name}}</a>
</div>
</template>
<component v-else :is="getComponentView" :withZustelladresse="getComponentView==='adresse'?true:false" :data="data.requested_change"></component>
</div>
</div>
<div v-if="files?.length" class="card mt-3">
<div class="card-header">{{$p.t('profilUpdate','nachweisdokumente')}}</div>
<div class="card-body">
<a v-for="file in files" target="_blank" :href="getDocumentLink(file.dms_id)" >{{file.name}}</a>
</div>
</div>
</div>
</div>
@@ -31,10 +31,10 @@ export default {
Loading,
AcceptDenyUpdate,
},
inject: ["profilUpdateTopic", "profilUpdateStates"],
inject: ["profilUpdateStates"],
props: {
id: {
type: Number,
type: String,
},
},
data() {
@@ -44,51 +44,53 @@ export default {
modalData: null,
loading: false,
filter: "Pending",
events: [],
profil_update_id: Number(this.id),
};
},
computed:{
profilUpdateOptions: function(){
return {
ajaxURL:
FHC_JS_DATA_STORAGE_OBJECT.app_root +
FHC_JS_DATA_STORAGE_OBJECT.ci_router +
`/Cis/ProfilUpdate/`,
ajaxURLGenerator: (url, config, params) => {
//? this function needs to be an array function in order to access the this properties of the Vue component
switch (this.filter) {
case this.profilUpdateStates["Pending"]:
return (
url +
`getProfilUpdateWithPermission/${this.profilUpdateStates["Pending"]}`
);
case this.profilUpdateStates["Accepted"]:
return (
url +
`getProfilUpdateWithPermission/${this.profilUpdateStates["Accepted"]}`
);
case this.profilUpdateStates["Rejected"]:
return (
url +
`getProfilUpdateWithPermission/${this.profilUpdateStates["Rejected"]}`
);
default:
return url + `getProfilUpdateWithPermission`;
}
},
computed: {
profilUpdateEvents: function () {
return [
{
"event": "dataProcessed",
"handler": this.handleDataProcessed
}
];
},
profilUpdateOptions: function () {
return {
ajaxURL: 'dummy',
ajaxRequestFunc: (url, config, params) => {
return this.$api.call(ApiProfilUpdate.getProfilUpdateWithPermission(params.filter));
},
ajaxParams: () => {
let filter = '';
switch (this.filter) {
case this.profilUpdateStates["Pending"]:
filter = this.profilUpdateStates["Pending"];
break;
case this.profilUpdateStates["Accepted"]:
filter = this.profilUpdateStates["Accepted"];
break;
case this.profilUpdateStates["Rejected"]:
filter = this.profilUpdateStates["Rejected"];
break;
default:
filter = '';
}
return {
"filter": filter
};
},
ajaxResponse: (url, params, response) => {
//url - the URL of the request
//params - the parameters passed with the request
//response - the JSON object returned in the body of the response.
//? sorts the response data from the backend
if (response)
response.sort((ele1, ele2) => sortProfilUpdates(ele1, ele2, this));
if (response?.data)
response.data.sort((ele1, ele2) => sortProfilUpdates(ele1, ele2, this));
return response;
return response.data;
},
//? adds tooltip with the status message of a profil update request if its status is not pending
columnDefaults: {
@@ -356,7 +358,19 @@ export default {
this.$refs.UpdatesTable.tabulator.setData();
//? store the selected view in the session storage of the browser
sessionStorage.setItem("filter", event.target.value);
},
},
handleDataProcessed: function () {
if (this.profil_update_id) {
const arrayRowData = this.$refs.UpdatesTable.tabulator
.getData()
.filter((row) => {
return row.profil_update_id === this.profil_update_id;
});
if (arrayRowData.length) {
this.showAcceptDenyModal(arrayRowData[0]);
}
}
}
},
watch: {
loading: function (newValue, oldValue) {
@@ -374,20 +388,7 @@ export default {
},
mounted() {
//? opens the AcceptDenyUpdate Modal if a preselected profil_update_id was passed to the component (used for email links)
if (this.profil_update_id) {
this.$refs.UpdatesTable.tabulator.on("dataProcessed", () => {
const arrayRowData = this.$refs.UpdatesTable.tabulator
.getData()
.filter((row) => {
return row.profil_update_id === this.profil_update_id;
});
if (arrayRowData.length) {
this.showAcceptDenyModal(arrayRowData[0]);
}
});
}
//? opens the AcceptDenyUpdate Modal if a preselected profil_update_id was passed to the component (used for email links)
if (sessionStorage.getItem("filter")) {
this.filter = sessionStorage.getItem("filter");
}
@@ -409,7 +410,7 @@ export default {
</div>
<loading ref="loadingModalRef" :timeout="0"></loading>
<core-filter-cmpt v-if="profilUpdateStates && categoryLoaded" :title="$p.t('profilUpdate','profilUpdateRequests')" ref="UpdatesTable" :tabulatorEvents="events" :tabulator-options="profilUpdateOptions" tableOnly :sideMenu="false" />
<core-filter-cmpt v-if="profilUpdateStates && categoryLoaded" :title="$p.t('profilUpdate','profilUpdateRequests')" ref="UpdatesTable" :tabulatorEvents="profilUpdateEvents" :tabulator-options="profilUpdateOptions" tableOnly :sideMenu="false" />
</div>`,
};
+21 -22
View File
@@ -1,6 +1,6 @@
import {CoreFilterCmpt} from "../../../components/filter/Filter.js";
import VueDatePicker from '../../vueDatepicker.js.php';
import ApiOrt from '../../../api/factory/ort.js'
export const Raumsuche = {
name: "Raumsuche",
props: {
@@ -13,6 +13,8 @@ export const Raumsuche = {
},
data() {
return {
phrasenPromise: null,
phrasenResolved: false,
tabulatorUuid: Vue.ref(0),
tableBuiltResolve: null,
tableBuiltPromise: null,
@@ -54,8 +56,8 @@ export const Raumsuche = {
columns: [
{title: Vue.computed(() => this.$p.t('rauminfo/raum_kurzbz')), field: 'ort_kurzbz', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('global/bezeichnung')), field: 'bezeichnung', widthGrow: 2},
{title: Vue.computed(() => this.$p.t('global/nummer')), field: 'nummer', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('global/personen')), field: 'personen', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('rauminfo/raumnummer')), field: 'nummer', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('rauminfo/personcap')), field: 'personen', widthGrow: 1},
{title: Vue.computed(() => this.$p.t('rauminfo/rauminfo')),
field: 'linkInfo', formatter: this.linkFormatter, widthGrow: 1},
{title: Vue.computed(() => this.$p.t('rauminfo/roomReservations')),
@@ -68,17 +70,6 @@ export const Raumsuche = {
handler: async () => {
this.tableBuiltResolve()
}
},
{
event: "cellClick",
handler: async (e, cell) => {
if((cell.column.field === 'linkInfo' || cell.column.field === 'linkRes') && cell.value){
window.open(cell.value, '_blank');
e.stopPropagation();
}
}
}
]};
},
@@ -132,7 +123,8 @@ export const Raumsuche = {
this.$refs.raumsucheTable.tabulator.setData(d);
},
loadRoomTypes() {
this.$fhcApi.factory.ort.getRoomTypes().then(res => {
this.$api.call(ApiOrt.getRoomTypes())
.then(res => {
res?.data?.forEach(type => {
type.beschreibung = type.beschreibung.replace('&amp;', '&')
})
@@ -141,7 +133,7 @@ export const Raumsuche = {
})
},
loadRooms() {
this.$fhcApi.factory.ort.getRooms(this.datum.toISOString(), this.getTimeString(this.von), this.getTimeString(this.bis), this.selectedType?.raumtyp_kurzbz ?? '', this.anzahl)
this.$api.call(ApiOrt.getRooms(this.datum.toISOString(), this.getTimeString(this.von), this.getTimeString(this.bis), this.selectedType?.raumtyp_kurzbz ?? '', this.anzahl))
.then(res => {
if(res?.data?.retval) this.setupData(res.data.retval)
})
@@ -167,6 +159,7 @@ export const Raumsuche = {
return `${hours}:${minutes}`;
},
async setupMounted() {
this.tableBuiltPromise = new Promise(this.tableResolve)
await this.tableBuiltPromise
@@ -182,7 +175,7 @@ export const Raumsuche = {
if(this.$refs.raumsucheTable) {
this.$refs.raumsucheTable.$refs.table.style.setProperty('height', h+'px')
}
}
},
computed: {
@@ -191,7 +184,8 @@ export const Raumsuche = {
}
},
created() {
this.phrasenPromise = this.$p.loadCategory(['rauminfo', 'global'])
this.phrasenPromise.then(()=> {this.phrasenResolved = true})
},
mounted() {
this.setupMounted()
@@ -239,7 +233,7 @@ export const Raumsuche = {
</VueDatePicker>
</div>
<div class="col-lg-auto">
<div class="col-12 col-lg-3">
<select ref="raumtyp" id="raumtypSelect" v-model="selectedType" class="form-select"
:aria-label="$p.t('global/studiensemester_auswaehlen')" @change="setRoute($event.target.value)">
<option :key="defaultType" selected :value="defaultType">{{defaultType.beschreibung}}</option>
@@ -248,16 +242,21 @@ export const Raumsuche = {
</div>
<div class="col-4 col-lg-2">
<InputNumber v-model="anzahl" :prefix="$p.t('rauminfo/anzahlPersonen') + ': '" inputId="anzahlInput" :min="1" :max="100" />
<div class="col-12 col-lg-3">
<InputNumber v-model="anzahl"
:prefix="$p.t('rauminfo/minCapacity') + ': '"
inputId="anzahlInput" :min="1" :max="1000"
:style="{'width': '100%'}"
/>
</div>
<div class="col-8 col-lg-2 d-flex justify-content-center align-items-center">
<div class="col-12 col-lg-2">
<button class="btn btn-primary border-0" @click="search">{{ $p.t('rauminfo/roomSearch') }} <i class="fa fa-magnifying-glass"></i></button>
</div>
</div>
<core-filter-cmpt
v-if="phrasenResolved"
@uuidDefined="handleUuidDefined"
:title="''"
ref="raumsucheTable"
@@ -23,14 +23,6 @@ export default {
this.$p.t('person/ort'),
this.event.ort_kurzbz
].join(": "));
this.event.lektor = [
this.event.lektor[0],
this.event.lektor[0],
this.event.lektor[0],
this.event.lektor[0],
this.event.lektor[0],
];
if (Array.isArray(this.event.lektor) && this.event.lektor.length > 0) {
if (this.event.lektor.length > 3) {
+1 -1
View File
@@ -156,7 +156,7 @@ export default {
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
</div>
<div v-else-if="!hidden || editMode" :id="widgetID" class="dashboard-item card overflow-hidden h-100 position-relative" :class="{'draggedItem':dragstate, 'dashboard-item-overlay':resizeOverlay, [arguments?.className]:arguments && arguments.className}">
<div v-else-if="!hidden || editMode" :id="widgetID" class="dashboard-item card overflow-hidden h-100 position-relative" :class="{'hiddenWidget':hidden, 'draggedItem':dragstate, 'dashboard-item-overlay':resizeOverlay, [arguments?.className]:arguments && arguments.className}">
<div v-show="!dragstate" class="h-100 card border-0">
<div v-if="widget" class="card-header d-flex ps-0 pe-2 align-items-center">
<Transition>
+5 -5
View File
@@ -106,7 +106,7 @@ export default {
setPrev() {
const thisIndex = this.allNewsList.findIndex(n=>n.news_id == this.selected.news_id)
const prevIndex = thisIndex ? thisIndex - 1 : this.allNewsList.length - 1
this.setSelected(this.allNewsList[prevIndex])
this.updateNewsContentClasses();
},
@@ -118,14 +118,14 @@ export default {
return classString
},
async setSelected(news) {
let clickedElement = document.getElementById('card-'+news.news_id);
let clickedElementIndex = this.allNewsList.indexOf(news);
let oldElementIndex = this.allNewsList.indexOf(this.selected);
const clickedElement = document.getElementById('card-'+news.news_id);
const clickedElementIndex = this.allNewsList.indexOf(news);
const oldElementIndex = this.allNewsList.indexOf(this.selected);
//if the clicked element is already active, do nothing
if(clickedElementIndex === oldElementIndex) return;
//add prev/next class to the clicked element
if(clickedElementIndex > oldElementIndex){
if(clickedElementIndex > oldElementIndex) {
clickedElement.classList.add('carousel-item-next');
}else{
clickedElement.classList.add('carousel-item-prev');
+1 -1
View File
@@ -159,7 +159,7 @@ export default {
// this.$emit('setConfig', true); -> use this to enable widget config mode if needed
},
template: /*html*/ `
<div class="widgets-url w-100 h-100 overflow-scroll" style="padding: 1rem 1rem;">
<div class="widgets-url w-100 h-100 overflow-auto" style="padding: 1rem 1rem;">
<div class="d-flex flex-column justify-content-between">
<button class="btn btn-outline-secondary btn-sm w-100 mt-2 card" @click="openCreateModal" type="button">{{$p.t('bookmark','newLink')}}</button>
+46 -20
View File
@@ -274,8 +274,23 @@ export default {
dragging(event){
if(this.mode == MODE_MOVE){
this.toggleDraggedItemOverlay(true);
this.clonedWidget.style.top = `${this.clientY-20}px`;
this.clonedWidget.style.left = `${this.clientX-15}px`;
const containerRect = this.$refs.container.getBoundingClientRect();
const clonedWidgetRect = this.clonedWidget.getBoundingClientRect();
let desiredTop = this.clientY - 20;
let desiredLeft = this.clientX - 15;
const minTop = 0;
const maxTop = containerRect.height - clonedWidgetRect.height;
const minLeft = 0;
const maxLeft = containerRect.width - clonedWidgetRect.width;
const constrainedTop = Math.max(minTop, Math.min(maxTop, desiredTop));
const constrainedLeft = Math.max(minLeft, Math.min(maxLeft, desiredLeft));
this.clonedWidget.style.top = `${constrainedTop}px`;
this.clonedWidget.style.left = `${constrainedLeft}px`;
}
},
createNewGrid(items) {
@@ -413,11 +428,14 @@ export default {
setTimeout(() => {
this.draggedNode = evt.target.closest(".drop-grid-item");
//clones the widget for the drag Image
let clone = evt.target.closest(".drop-grid-item")?.cloneNode(true);
clone.style.zIndex = 5;
clone.classList.add("widgetClone");
this.$refs.container.appendChild(clone);
const hiddenWidget = clone.querySelector("[style='display: none;']");
hiddenWidget.style.removeProperty("display");
this.clonedWidget = clone;
}, 0);
@@ -434,12 +452,9 @@ export default {
},
dragOver(evt) {
if ((this.y + 1) > this.rows && (this.mode == MODE_MOVE || this.mode == MODE_RESIZE)) {
this.positionUpdates = this.positionUpdates?.filter(item => {
return item.widgetid == this.draggedItem.data.widgetid;
})
this.dragEnd();
this.dragCancel();
}
}
if (!this.active)
return this.dragCancel();
this.checkPinnedWidgetAnimation();
@@ -454,17 +469,17 @@ export default {
let x = this.x + this.draggedOffset[0];
let y = this.y + this.draggedOffset[1];
if (x < 0) {
this.draggedOffset[0] -= x;
this.draggedOffset[0] += x;
x = 0;
} else if (x + this.draggedItem.w > this.cols) {
this.draggedOffset[0] += this.cols - this.draggedItem.w - x;
x = this.cols - this.draggedItem.w;
}
if (y < 0) {
this.draggedOffset[1] -= y;
this.draggedOffset[1] += y;
y = 0;
}
this.positionUpdates = this.dragGrid.move(this.draggedItem, x, y);
this.positionUpdates= this.dragGrid.move(this.draggedItem, x, y);
break;
}
case MODE_RESIZE: {
@@ -481,6 +496,7 @@ export default {
}
},
dragCancel() {
this.removeWidgetClones();
this.additionalRowComputed = false;
this.toggleDraggedItemOverlay(false);
this.mode = MODE_IDLE;
@@ -492,8 +508,12 @@ export default {
},
dragEnd() {
if (this.mode == MODE_IDLE)
this.removeWidgetClones();
this.toggleDraggedItemOverlay(false);
if (this.mode == MODE_IDLE){
return;
}
// clean up unused classes
let draggedItemNode = document.getElementById(this.draggedItem.data.widgetid);
draggedItemNode.classList.remove("border-danger");
@@ -501,19 +521,19 @@ export default {
ele.classList.remove("denied-dragging-animation");
})
let widgetClones = document.getElementsByClassName("widgetClone");
for (let i=0; i <widgetClones.length; i++){
this.$refs.container.removeChild(widgetClones[i]);
}
if (!this.active || this.x < 0 || this.y < 0 || this.x >= this.cols)
return this.dragCancel();
//if (!this.active || this.x < 0 || this.y < 0 || this.x >= this.cols)
//return this.dragCancel();
this.mode = MODE_IDLE;
let updated = [];
this.convertGridResultToUpdate(this.positionUpdates, updated);
updated = this._updateFixedPositions(updated);
if (updated.length)
this.$emit('rearrangeItems', updated.filter(v => v));
this.draggedItem = null;
this.draggedNode = null;
this.$emit('draggedItem', null);
},
_updateFixedPositions(updated) {
updated.forEach((item, index) => {
@@ -597,6 +617,12 @@ export default {
draggedItemNode.classList.remove("border-danger");
}
},
removeWidgetClones(){
let widgetClones = Array.from(document.getElementsByClassName("widgetClone"));
for (let i = 0; i < widgetClones.length; i++) {
this.$refs.container.removeChild(widgetClones[i]);
}
},
mouseDown(){
this.mode = MODE_MOUSE_DOWN;
},
@@ -612,7 +638,7 @@ export default {
@touchmove="dragOver"
@touchend="dragCancel"
@dragover.prevent="dragOver"
@drop="dragEnd"
@drop="dragEnd($event)"
@mousemove="updateCursorOnMouseMove"
@mouseleave="mouseLeave">
<TransitionGroup tag="div">
@@ -626,7 +652,7 @@ export default {
@mouse-up="mouseUp"
@start-resize="startResize"
@dragging="dragging"
@end-drag="dragCancel"
@end-drag="dragEnd"
@touch-end="dragEnd();mouseUp();"
@touch-start="updateCursorOnMouseMove($event); mouseDown();"
class="position-absolute"
+2 -1
View File
@@ -3,6 +3,7 @@ import FhcFragment from "../Fragment.js";
let _uuid = {};
export default {
name: "FormInput",
inheritAttrs: false,
components: {
FhcFragment
@@ -220,7 +221,7 @@ export default {
if (this.tag == 'VueDatePicker' && !this._.components.VueDatePicker) {
this._.components.VueDatePicker = Vue.defineAsyncComponent(() => import("../vueDatepicker.js.php"));
} else if (this.tag == 'PvAutocomplete' && !this._.components.PvAutocomplete) {
this._.components.PvAutocomplete = Vue.defineAsyncComponent(() => import(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/public/js/components/primevue/autocomplete/autocomplete.esm.min.js"));
this._.components.PvAutocomplete = primevue.autocomplete;
} else if (this.tag == 'UploadImage' && !this._.components.UploadImage) {
this._.components.UploadImage = Vue.defineAsyncComponent(() => import("./Upload/Image.js"));
} else if (this.tag == 'UploadDms' && !this._.components.UploadDms) {
+2 -1
View File
@@ -23,7 +23,8 @@ export default {
if (!Array.isArray(feedback))
feedback = [feedback];
const ts = Date.now();
this.feedback[valid ? 'success' : 'danger'] = feedback.map(msg => [msg, ts]);
this.feedback[valid ? 'success' : 'danger']
.push(...feedback.map(msg => [msg, ts]));
}
},
mounted() {
@@ -38,33 +38,6 @@ export default {
},
template: `
<div>
<div class="row align-items-start mb-3">
<form-input
v-if="showLVID"
:label="$p.t('lehre', 'lehrveranstaltung_id')"
type="text"
container-class="col-3"
v-model="data.lehrveranstaltung_id"
name="lehrveranstaltung_id"
/>
<form-input
v-if="showGewichtung"
:label="$p.t('lehre', 'gewicht')"
type="text"
container-class="col-3"
v-model="data.gewicht"
name="gewicht"
/>
<form-input
:label="$p.t('lehre', 'detailanmerkung')"
type="textarea"
container-class="col-3"
v-model="formattedAnmerkung"
name="anmerkung"
id="anmerkung"
rows="4"
/>
</div>
<div class="row mb-3">
<form-input
:label="$p.t('lehre', 'lehrfach')"
@@ -96,55 +69,15 @@ export default {
{{ lehrform.bez_kurz }} {{ lehrform.bez }}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
:label="$p.t('global', 'sprache')"
type="select"
:label="$p.t('lehre', 'detailanmerkung')"
type="textarea"
container-class="col-3"
v-model="data.sprache"
name="sprache"
>
<option
v-for="sprache in dropdowns.sprachen_array"
:key="sprache.sprache"
:value="sprache.sprache"
>
{{ sprache.sprache }}
</option>
</form-input>
<form-input
:label="$p.t('lehre', 'unr')"
type="text"
container-class="col-3"
v-model="data.unr"
name="unr"
/>
</div>
<div class="row mb-3">
<form-input
:label="$p.t('lehre', 'studiensemester')"
type="select"
container-class="col-3"
v-model="data.studiensemester_kurzbz"
name="studiensemester_kurzbz"
>
<option
v-for="semester in dropdowns.studiensemester_array"
:key="semester.studiensemester_kurzbz"
:value="semester.studiensemester_kurzbz"
>
{{ semester.studiensemester_kurzbz }}
</option>
</form-input>
<form-input
:label="$p.t('lehre', 'lehre')"
type="checkbox"
container-class="col-3"
v-model="data.lehre"
name="lehre"
v-model="formattedAnmerkung"
name="anmerkung"
id="anmerkung"
rows="10"
/>
</div>
@@ -179,30 +112,109 @@ export default {
{{ raumtyp.raumtyp_kurzbz }} {{ raumtyp.beschreibung }}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
:label="$p.t('global', 'sprache')"
type="select"
container-class="col-3"
v-model="data.sprache"
name="sprache"
>
<option
v-for="sprache in dropdowns.sprachen_array"
:key="sprache.sprache"
:value="sprache.sprache"
>
{{ sprache.sprache }}
</option>
</form-input>
<form-input
:label="$p.t('lehre', 'studiensemester')"
type="select"
container-class="col-3"
v-model="data.studiensemester_kurzbz"
name="studiensemester_kurzbz"
>
<option
v-for="semester in dropdowns.studiensemester_array"
:key="semester.studiensemester_kurzbz"
:value="semester.studiensemester_kurzbz"
>
{{ semester.studiensemester_kurzbz }}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
:label="$p.t('lehre', 'startkw')"
type="number"
container-class="col-2"
min="0"
container-class="col-1"
v-model="data.start_kw"
name="start_kw"
/>
<form-input
:label="$p.t('lehre', 'stundenblockung')"
type="number"
container-class="col-2"
min="0"
container-class="col-1"
v-model="data.stundenblockung"
name="stundenblockung"
/>
<form-input
:label="$p.t('lehre', 'wochenrhythmus')"
type="number"
container-class="col-2"
min="0"
container-class="col-1"
v-model="data.wochenrythmus"
name="wochenrythmus"
/>
<div class="col-3 d-flex align-items-end">
<form-input
:label="$p.t('lehre', 'lehre')"
type="checkbox"
v-model="data.lehre"
name="lehre"
/>
</div>
</div>
<div class="row mb-3">
<form-input
v-if="showLVID"
:label="$p.t('lehre', 'lehrveranstaltung_id')"
type="text"
container-class="col-2"
v-model="data.lehrveranstaltung_id"
name="lehrveranstaltung_id"
/>
<form-input
:label="$p.t('lehre', 'unr')"
type="text"
container-class="col-1"
v-model="data.unr"
name="unr"
/>
<form-input
v-if="showGewichtung"
:label="$p.t('lehre', 'gewicht')"
type="text"
container-class="col-1"
v-model="data.gewicht"
name="gewicht"
/>
</div>
</div>
`
@@ -50,7 +50,7 @@ export default{
let button = document.createElement('button');
container.className = "d-flex gap-1";
button.className = 'btn btn-outline-secondary btn-action';
button.className = 'btn btn-outline-secondary';
button.innerHTML = '<i class="fa fa-xmark"></i>';
button.title = this.$p.t('ui', 'loeschen');
button.addEventListener('click', (event) => {
@@ -63,7 +63,7 @@ export default{
{
button = document.createElement('button');
container.className = "d-flex gap-2";
button.className = 'btn btn-outline-secondary btn-action';
button.className = 'btn btn-outline-secondary';
button.innerHTML = '<i class="fa fa-calendar-xmark"></i>';
button.title = this.$p.t('lehre', 'auslvplanentfernen');
button.disabled = !cell.getData().verplant;
@@ -32,13 +32,7 @@ export default {
studiensemester_kurzbz: { type: String, required: false, default: null },
emp: { type: String, required: false, default: null }
},
computed: {
selectedStudiensemester() {
return this.studiensemester_kurzbz != null
? this.studiensemester_kurzbz
: this.defaultSemester;
}
},
provide() {
return {
currentSemester: Vue.computed(() => this.selectedStudiensemester),
@@ -70,12 +64,16 @@ export default {
emp() {
this.updateFilter();
},
studiensemester_kurzbz(newVal) {
this.selectedStudiensemester = newVal ?? this.defaultSemester;
}
},
data() {
return {
selected: [],
studiengang: "",
filter: {},
selectedStudiensemester: this.studiensemester_kurzbz ?? this.defaultSemester,
endpoint: ApiStudiengangTree,
dropdowns: {
studiensemester_array: [],
@@ -88,10 +86,12 @@ export default {
},
selectedStudiengang: '',
searchbaroptions: {
origin: 'lvverwaltung',
cssclass: "position-relative",
calcheightonly: true,
types: [
"mitarbeiter"
"mitarbeiter",
"mitarbeiter_ohne_zuordnung"
],
actions: {
employee: {
@@ -121,9 +121,9 @@ export default{
}
this.$refs.form.call(ApiLektor.update(updatedData))
.then(result => {
let error = result.data?.error;
if (error)
this.$fhcAlert.alertWarning(error)
let warning = result.data?.retval?.warning;
if (warning)
this.$fhcAlert.alertWarning(warning)
this.original = {...this.data};
if (this.changed.mitarbeiter_uid)
@@ -192,12 +192,13 @@ export default{
container-class="col-3"
dropdown
@complete="searchLektor"
name="lektorautocomplete"
></form-input>
<form-input
:label="$p.t('lehre', 'anmerkung')"
type="text"
container-class="col-3"
container-class="col-6"
v-model="data.anmerkung"
name="anmerkung"
>
@@ -207,7 +208,9 @@ export default{
<div class="row mb-3">
<form-input
:label="$p.t('lehre', 'las')"
type="text"
type="number"
min="0"
step="0.01"
container-class="col-3"
:disabled="data.vertrag?.vertragsstatus_kurzbz === 'akzeptiert'"
v-model="data.semesterstunden"
@@ -217,7 +220,9 @@ export default{
<form-input
:label="$p.t('lehre', 'planstunden')"
type="text"
type="number"
min="0"
step="0.01"
container-class="col-3"
v-model="data.planstunden"
name="planstunden"
@@ -225,13 +230,15 @@ export default{
</form-input>
</div>
<div class="row mb-3">
<div class="row mb-3 d-flex align-items-end">
<form-input
:label="data.default_stundensatz !== null
? $p.t('lehre', 'stundensatz') + ' (' + $p.t('lehre', 'default') + ': ' + data.default_stundensatz + ')'
: $p.t('lehre', 'stundensatz')"
type="text"
type="number"
min="0"
step="0.01"
container-class="col-3"
v-model="data.stundensatz"
:disabled="data.vertrag?.vertragsstatus_kurzbz === 'akzeptiert'"
@@ -85,7 +85,7 @@ export default{
button.className = 'btn btn-outline-secondary btn-action';
button.innerHTML = '<i class="fa fa-calendar-xmark"></i>';
button.disabled = !cell.getData().verplant;
button.title = this.$p.t('ui', 'auslvplanentfernen');
button.title = this.$p.t('lehre', 'auslvplanentfernen');
button.addEventListener('click', (event) => {
event.stopPropagation();
this.deleteLVPlan(cell.getData().mitarbeiter_uid, cell.getData().lehreinheit_id)
@@ -199,7 +199,7 @@ export default{
table-only
:side-menu="false"
reload
:new-btn-label="$p.t('lehre', 'addlektor')"
:new-btn-label="$p.t('lehre', 'addLektor')"
new-btn-show
@click:new="showAutocomplete = !showAutocomplete"
>
@@ -88,8 +88,14 @@ export default{
.catch(this.$fhcAlert.handleSystemError);
},
cancelVertrag()
async cancelVertrag()
{
if (await this.$fhcAlert.confirm({
message: this.$p.t('lehre', 'vertragConfirm'),
acceptLabel: this.$p.t('ui', 'ja').charAt(0).toUpperCase() + this.$p.t('ui', 'ja').slice(1),
acceptClass: 'btn btn-danger'}) === false)
return;
let needUpdate = {
vertrag_id: this.data.vertrag.vertrag_id,
mitarbeiter_uid: this.mitarbeiter_uid,
@@ -103,7 +109,6 @@ export default{
.catch(this.$fhcAlert.handleSystemError);
},
},
// language=HTML
template: `
<core-form ref="form">
<fieldset class="overflow-hidden" v-if="showVertragsdetails">
@@ -126,6 +131,7 @@ export default{
type="button"
class="btn btn-outline-secondary w-100"
@click="cancelVertrag"
:title="$p.t('lehre', 'cancelvertrag')"
>
<i class="fa-solid fa-ban"></i>
</button>
@@ -56,7 +56,13 @@ export default {
}
},
deep: true,
},
currentSemester: {
handler(newVal)
{
this.lv_info_default.studiensemester_kurzbz = newVal
this.lv_info = false;
}
}
},
data() {
@@ -137,6 +143,8 @@ export default {
selectableRows: true,
rowContextMenu: (component, e) => {
if (e.getData()?.lehreinheit_id === undefined)
return;
return [
{
label: "LV-Teil kopieren",
@@ -159,7 +167,7 @@ export default {
label: "Nur mit Gruppen",
action: (e, row) =>
{
this.copyLehreinheit(row, "halb");
this.copyLehreinheit(row, "gruppen");
},
},
{
@@ -233,6 +241,17 @@ export default {
const renderTags = () => {
container.innerHTML = '';
parsedTags = parsedTags.filter(item => item !== null);
parsedTags.sort((a, b) => {
let adone = a.done ? 1 : 0;
let bbone = b.done ? 1 : 0;
if (adone !== bbone)
{
return adone - bbone;
}
return b.id - a.id;
});
const tagsToShow = rowData._tagExpanded ? parsedTags : parsedTags.slice(0, maxVisibleTags);
tagsToShow.forEach(tag => {
@@ -445,7 +464,7 @@ export default {
if (!tagExists)
{
addedTag.id = tag.id;
tags.push({ ...addedTag });
tags.unshift({ ...addedTag });
targetRow.update({ tags: JSON.stringify(tags) });
targetRow.reformat();
}
@@ -659,19 +678,19 @@ export default {
<slot name="filterzuruecksetzen"></slot>
</template>
</core-filter-cmpt>
<bs-modal ref="lehreinheitModal" dialogClass="modal-lg">
<template #title>
<p class="fw-bold mt-3">{{$p.t('lehre', 'newlehreinheit')}}</p>
</template>
<bs-modal ref="lehreinheitModal" dialogClass="modal-xxl">
<template #title>
<p class="fw-bold mt-3">{{$p.t('lehre', 'newlehreinheit')}}</p>
</template>
<template v-if="lv_info">
<details-form :data="lv_info"/>
</template>
<template v-if="lv_info">
<details-form :data="lv_info"/>
</template>
<template #footer>
<button type="button" class="btn btn-primary" @click="addNewLehreinheit">{{$p.t('ui', 'speichern')}}</button>
</template>
</bs-modal>
<template #footer>
<button type="button" class="btn btn-primary" @click="addNewLehreinheit">{{$p.t('ui', 'speichern')}}</button>
</template>
</bs-modal>
`
};

Some files were not shown because too many files have changed in this diff Show More