Compare commits

...
Author SHA1 Message Date
Ivymaster 4219574fa7 Conflicts resolved 2026-09-28 18:00:25 +02:00
Harald Bamberger acedc11be8 adapt styling detailheader employees 2026-09-24 14:39:38 +02:00
Harald Bamberger 15c1da74a2 CisMenu: do not overwrite calculated url if menuitem is multiple times in menu tree 2026-09-23 16:09:42 +02:00
Harald Bamberger d33b45d7cb Merge branch 'bhdev_backport_compat' 2026-09-23 14:58:26 +02:00
Andreas Österreicher b3b6d847a7 Merge branch 'feature-62260/international_skills_groupheader_filter' 2026-09-23 14:36:13 +02:00
Andreas Österreicher 80365de28c Merge branch 'feature-62602/mehrfachnennungen_innerhalb_einer_kategorie' 2026-09-23 14:35:36 +02:00
Andreas Österreicher 2d74522a4f Merge branch 'feature-54225/reihungstest_ablauf_fuer_quereinsteiger' 2026-09-23 12:28:35 +02:00
ma0048 d00aef66e1 master gemerged 2026-09-23 11:32:01 +02:00
ma0048 241eec3ef2 Merge remote-tracking branch 'origin/master' into feature-54225/reihungstest_ablauf_fuer_quereinsteiger
# Conflicts:
#	cis/testtool/menu.php
#	system/dbupdate_3.4.php
2026-09-23 11:29:43 +02:00
Andreas Österreicher 2050f5ae65 Merge branch 'feature-75891/Electronic_Onboarding_Infocenter_Tool' 2026-09-23 11:14:01 +02:00
Andreas Österreicher 587e523990 Merge branch 'feature-67251/infocenter_zum_reihungstest_freigegeben_neue_spalte_eqk' 2026-09-23 11:03:20 +02:00
Andreas Österreicher 5ec1a1c54c Merge branch 'feature-77686/infocentertool_status_setzen_fuer_zgv_pruefung_soll_message_ausloesen' 2026-09-23 10:56:54 +02:00
Andreas Österreicher 2c65ccc8da Merge branch 'bug-77684/reihungstest_auswertung_freischalten_sperren' 2026-09-23 09:40:37 +02:00
Andreas Österreicher 787308aad4 Merge branch 'feature-77356/teilgenommen_setzen_beim_reihungstest_punkte_uebertragen' 2026-09-23 09:37:41 +02:00
Harald Bamberger 8b44e4475b Merge branch 'master' of github.com:FH-Complete/FHC-Core into bhdev_backport_compat 2026-09-22 16:51:34 +02:00
Harald Bamberger d251987850 backport compat controller and component 2026-09-22 16:50:56 +02:00
Harald Bamberger ba92eea7eb studvw: fix lvtermine student_lehrverband 2026-09-22 14:09:51 +02:00
Harald Bamberger 50951dfdcf anrechnungen: add pdf file extension to content disposition filename 2026-09-22 13:00:28 +02:00
Harald Bamberger be7527e994 increase length of column taglib in table public.tbl_notiz_typ 2026-09-22 09:52:09 +02:00
ma0048 224cef7fad bug beim Loeschen von Antworten eines Prueflings in reihungstet_administration.php fixed 2026-09-17 13:29:58 +02:00
Alexei Karpenko 0dadaece08 Merge branch 'master' into feature-75891/Electronic_Onboarding_Infocenter_Tool 2026-09-16 16:59:09 +02:00
Alexei Karpenko 073160dde4 Merge branch 'master' into feature-75891/Electronic_Onboarding_Infocenter_Tool 2026-09-10 20:08:08 +02:00
ma0048 66e400013a bei status setzen fuer zgv pruefung message rausschicken 2026-07-14 13:30:32 +02:00
ma0048 3f95bfd82f boolean auf fhc_boolean geandert 2026-07-14 11:31:23 +02:00
Andreas Österreicher 9629345473 Merge branch 'master' into feature-62260/international_skills_groupheader_filter 2026-07-08 15:28:23 +02:00
Andreas Österreicher 4d72b0047a Merge branch 'master' into feature-62602/mehrfachnennungen_innerhalb_einer_kategorie 2026-07-08 15:26:41 +02:00
ma0048 dc22ac6b06 teilgenommen setzen beim uebetragen der punkte 2026-06-30 17:03:43 +02:00
Ivymaster 69ef6d2707 Minor mod 2026-06-14 21:53:04 +02:00
Ivymaster 69cdb43935 WIP: Second feedback alterations 2026-05-27 15:17:30 +02:00
Ivymaster f3e1208f27 BUG: add fix for weekdays format for time slots db 2026-05-20 13:46:54 +02:00
Ivymaster 13684e638f Add styling changes 2026-05-19 09:52:25 +02:00
Ivymaster 55be235fbe BUG: Add fix for drop higlight of first part body element in selector component 2026-05-18 18:13:33 +02:00
Ivymaster ec782d168c BUG: Fixed issue with org unit filtering in Validity period modal component 2026-05-18 17:59:03 +02:00
Ivymaster ddfcb68d62 BUG: Fixed issues with pointer event listeners on calendar selector 2026-05-18 17:45:08 +02:00
Ivymaster 3cf7543586 Add upper resize element to overlays in "ClassScheduleCalendarSelector" component 2026-05-12 17:59:42 +02:00
Ivymaster c6d438c032 Add minor refactor of "ClasscheduleCalendarSelector" component 2026-05-12 13:45:20 +02:00
Ivymaster 6560b27de9 Add removal of multiple weeks per validation period, and other minor changes 2026-05-11 18:45:44 +02:00
Ivymaster fe5931e6c7 BUG: add fix for wrong lang class schedule type display in ClassSchedule overlay selector component 2026-05-07 14:14:59 +02:00
Ivymaster a7c4571d25 Add removal of console logs in ClassSchedule components 2026-05-07 14:06:34 +02:00
Ivymaster 8d5588de02 Add link to ClassSchedule overview in navigation config 2026-05-06 12:56:44 +02:00
Ivymaster 6a813499d1 Add navigation menu to classSchedule page 2026-05-05 15:56:13 +02:00
Ivymaster 2c75fb7827 BUG: Fix issue with constant refetch upon orgUnit dropdown filtering 2026-04-29 13:24:46 +02:00
Ivymaster 9f8e292f49 Disable deleting of in-use ClassTimeSlotType db entries 2026-04-27 18:26:48 +02:00
Alexei Karpenko 96b2d88d39 Merge branch 'master' into feature-75891/Electronic_Onboarding_Infocenter_Tool 2026-04-27 18:19:42 +02:00
Alexei Karpenko 474fc1fc57 Notiz model: possible to add specific insertvon 2026-04-27 18:17:29 +02:00
Ivymaster 11f93f0c91 Added additional filters to 'classScheduleOverview' component 2026-04-27 18:09:57 +02:00
Alexei Karpenko 3825e2fb38 adresse class: added method to get austrian adress 2026-04-27 17:56:18 +02:00
Ivymaster 7307c1fa44 add prefiltering on validity period form component 2026-04-24 14:08:11 +02:00
Ivymaster 869a7e3335 Create and implement "ClassScheduleCalendarSelector" in "ClassScheduleOverview" component 2026-04-21 14:43:58 +02:00
Ivymaster 010b370914 add class schedule controllers, components, and updated phrases 2026-04-14 16:21:46 +02:00
Ivymaster a29e0aeb62 add frontened calls to endpoints for fetching all study plans and all degree programes 2026-04-14 16:20:13 +02:00
Ivymaster fe68d46831 add endpoint for fetching all study plans 2026-04-14 16:17:38 +02:00
Ivymaster d8f5c412c5 add endpoint for fetching all degree programes 2026-04-14 16:17:17 +02:00
Ivymaster 59d531ed0f add necessary models and DB tables for the class schedule feature 2026-04-14 16:14:54 +02:00
Alexei Karpenko e90e03e0cc infocenter: added info if registered with electronic onboarding, or login with Electronic Onboarding. controller: moved public methods up to public section, hand cursor appear on all links in Stammdaten section 2026-03-18 13:47:24 +01:00
ma0048 c664c14afc phrase hinzugefuegt 2025-12-18 11:10:08 +01:00
ma0048 efc67d14a5 qulikurs spalte hinzugekommen bei infocenter freigegebendata 2025-11-06 12:38:13 +01:00
ma0048 9183c4f04c massnahme einmalig flag hinzugefuegt 2025-10-01 12:03:18 +02:00
ma0048 6495f741b9 added missing phrase 2025-07-09 14:02:41 +02:00
ma0048 d3210f0d3d - foreign keys hinzugefuegt
- beim zuruecksetzen der gebiete, wird auch tbl_pruefling_gebiet zurueckgesetzt
2025-02-18 13:51:20 +01:00
ma0048 a0dce925d4 - rt holt sich zusätzlich alle gebiete aus den semestern davor 2025-01-14 10:56:28 +01:00
61 changed files with 8089 additions and 154 deletions
+1
View File
@@ -3,3 +3,4 @@
if (! defined('BASEPATH')) exit('No direct script access allowed');
$config['infocenter_studiengang_kz'] = '10021, 10027, 10002';
$config['infocenter_qualifikationskurs_kz'] = '10002';
+17
View File
@@ -45,6 +45,14 @@ $config['navigation_header'] = array(
'expand' => true,
'sort' => 30,
'requiredPermissions' => 'admin:w'
),
'classScheduleOverview' => array(
'link' => site_url('lehre/ClassSchedule'),
'icon' => '',
'description' => 'Unterrichtszeiten der Studiengänge',
'expand' => true,
'sort' => 50,
'requiredPermissions' => 'lehre/unterrichtszeiten_gk:r'
)
)
),
@@ -390,3 +398,12 @@ $config['navigation_menu']['apps'] = [
'requiredPermissions' => array('lehre/lehrauftrag_bestellen:r', 'lehre/lehrauftrag_erteilen:r')
]
];
$config['navigation_menu']['lehre/ClassSchedule/index'] = array(
'classScheduleOverview' => array(
'link' => site_url('lehre/ClassSchedule'),
'description' => 'Unterrichtszeiten der Studiengänge',
'icon' => '',
'sort' => 1
)
);
+3
View File
@@ -137,6 +137,9 @@ $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)/stg/(
$route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)/stg/(:any)/.*'] = 'api/frontend/v1/Lehrveranstaltung/getByEmp/$1/$2/$3';
$route['lehre/ClassSchedule/.*'] = 'lehre/ClassSchedule/index';
// load routes from extensions, also look for environment-specific configs
$subdirs = ['application/config/extensions', 'application/config/' . ENVIRONMENT . '/extensions'];
+44
View File
@@ -0,0 +1,44 @@
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
class Compat extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct([
'ci' => ['basis/cis:r'],
'legacy' => ['basis/cis:r'],
]);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
/**
* @access public
* @return void
*/
public function ci()
{
$this->load->view('CisRouterView/CisRouterView.php',['route' => 'Compat']);
}
/**
* @access public
* @return void
*/
public function legacy()
{
$this->load->view('CisRouterView/CisRouterView.php',['route' => 'Compat']);
}
}
@@ -67,8 +67,11 @@ class CisMenu extends FHCAPI_Controller
private function generateUrlForMenuItem($menuItem)
{
$menuItem->url = $this->menuItemUrlHelper($menuItem);
unset($menuItem->content);
if(!isset($menuItem->url))
{
$menuItem->url = $this->menuItemUrlHelper($menuItem);
unset($menuItem->content);
}
if ($menuItem->childs && count($menuItem->childs)) {
$menuItem->childs = $this->generateUrlsForMenuItems($menuItem->childs);
@@ -83,7 +86,7 @@ class CisMenu extends FHCAPI_Controller
return site_url("/CisVue/Cms/content/" . $menuItem->content_id);
}
if (!$menuItem->content || !mb_strlen($menuItem->content)) {
if (!isset($menuItem->content) || !$menuItem->content || !mb_strlen($menuItem->content)) {
return '';
}
@@ -0,0 +1,666 @@
<?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 ClassScheduleApi extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getAllClassTimeValidityPeriods'=> array('lehre/unterrichtszeiten_gk:r'),
'getAllClassTimeValidityPeriodsPerOrganizationalUnit' => array('lehre/unterrichtszeiten_gk:r'),
'getClassTimeValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'),
'createClassTimeSlotValidityPeriod' => array('lehre/unterrichtszeiten_gk:rw'),
'updateClassTimeSlotValidityPeriod' => array('lehre/unterrichtszeiten_gk:rw'),
'deleteClassTimeSlotValidityPeriod' => array('lehre/unterrichtszeiten_gk:rw'),
'getClassTimeSlotsForValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'),
'createClassTimeSlotsForValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'),
'updateClassTimeSlotsForValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'),
'deleteClassTimeSlotsForValidityPeriod' => array('lehre/unterrichtszeiten_gk:r'),
'getAllClassScheduleTypes' => array('lehre/unterrichtszeiten_typ:r', 'lehre/unterrichtszeiten_gk:r'),
'createClassTimeSlotType' => array('lehre/unterrichtszeiten_typ:rw'),
'updateClassTimeSlotType' => array('lehre/unterrichtszeiten_typ:rw'),
'deleteClassTimeSlotType' => array('lehre/unterrichtszeiten_typ:rw'),
]);
$this->load->library('form_validation');
$this->load->model('education/ClassTimeSlotValidityPeriod_model', "ClassTimeSlotValidityPeriodModel");
$this->load->model('education/ClassTimeSlot_model', "ClassTimeSlotModel");
$this->load->model('education/ClassTimeSlotType_model', "ClassTimeSlotTypeModel");
// Loads phrases system
$this->loadPhrases([
'global',
'ui',
]);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getAllClassTimeValidityPeriods()
{
$entitledOrganizationalUnitsShortCodes = $this->permissionlib->getOE_isEntitledFor('lehre/unterrichtszeiten_gk');
$organizationalUnitShortCode = $this->input->get('organizationalUnitShortCode');
$validityPeriodFrom = $this->input->get('validityPeriodFrom');
$validityPeriodTo = $this->input->get('validityPeriodTo');
$this->ClassTimeSlotValidityPeriodModel->db->where_in('lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz', $entitledOrganizationalUnitsShortCodes);
$this->ClassTimeSlotValidityPeriodModel->addSelect(
'lehre.tbl_unterrichtszeiten_gueltigkeit.*,' .
'public.tbl_organisationseinheit.bezeichnung as organisationseinheit_bezeichnung,' .
'public.tbl_organisationseinheit.organisationseinheittyp_kurzbz as organisationseinheit_organisationseinheittyp_kurzbz,' .
'lehre.tbl_studienplan.bezeichnung as studienplan_bezeichnung,' .
'lehre.tbl_unterrichtszeiten_typ.bezeichnung_mehrsprachig as unterrichtszeiten_typ_bezeichnung_mehrsprachig, '
);
$this->ClassTimeSlotValidityPeriodModel->addJoin('lehre.tbl_studienplan', 'lehre.tbl_studienplan.studienplan_id=lehre.tbl_unterrichtszeiten_gueltigkeit.studienplan_id', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addJoin('public.tbl_organisationseinheit', 'public.tbl_organisationseinheit.oe_kurzbz=lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addJoin('lehre.tbl_unterrichtszeiten_typ', 'lehre.tbl_unterrichtszeiten_typ.unterrichtszeitentyp_kurzbz=lehre.tbl_unterrichtszeiten_gueltigkeit.unterrichtszeitentyp_kurzbz', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addOrder('gueltig_von', 'DESC');
if ($organizationalUnitShortCode) {
$this->ClassTimeSlotValidityPeriodModel->db->where('lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz', $organizationalUnitShortCode);
}
if ($validityPeriodFrom) {
$this->ClassTimeSlotValidityPeriodModel->db
->where('lehre.tbl_unterrichtszeiten_gueltigkeit.gueltig_von >=', date('Y-m-d', strtotime($validityPeriodFrom)));
}
if ($validityPeriodTo) {
$this->ClassTimeSlotValidityPeriodModel->db
->where('lehre.tbl_unterrichtszeiten_gueltigkeit.gueltig_bis <=', date('Y-m-d', strtotime($validityPeriodTo)));
}
$class_time_slot_validity_period_res = $this->ClassTimeSlotValidityPeriodModel->load();
$class_time_slot_validity_period_res = $this->getDataOrTerminateWithError($class_time_slot_validity_period_res);
$this->terminateWithSuccess($class_time_slot_validity_period_res);
}
public function getAllClassTimeValidityPeriodsPerOrganizationalUnit($organizationUnitId)
{
if (!$this->isUserEntitledForOrganizationalUnit($organizationUnitId, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->ClassTimeSlotValidityPeriodModel->addSelect(
'lehre.tbl_unterrichtszeiten_gueltigkeit.*,' .
'public.tbl_organisationseinheit.bezeichnung as organisationseinheit_bezeichnung,' .
'public.tbl_organisationseinheit.organisationseinheittyp_kurzbz as organisationseinheit_organisationseinheittyp_kurzbz,' .
'lehre.tbl_studienplan.bezeichnung as studienplan_bezeichnung,' .
'lehre.tbl_unterrichtszeiten_typ.bezeichnung_mehrsprachig as unterrichtszeiten_typ_bezeichnung_mehrsprachig, '
);
$this->ClassTimeSlotValidityPeriodModel->addJoin('lehre.tbl_studienplan', 'lehre.tbl_studienplan.studienplan_id=lehre.tbl_unterrichtszeiten_gueltigkeit.studienplan_id', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addJoin('public.tbl_organisationseinheit', 'public.tbl_organisationseinheit.oe_kurzbz=lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addJoin('lehre.tbl_unterrichtszeiten_typ', 'lehre.tbl_unterrichtszeiten_typ.unterrichtszeitentyp_kurzbz=lehre.tbl_unterrichtszeiten_gueltigkeit.unterrichtszeitentyp_kurzbz', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addOrder('gueltig_von', 'DESC');
$class_time_slot_validity_period_res = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz' => $organizationUnitId]);
$class_time_slot_validity_period_res = $this->getDataOrTerminateWithError($class_time_slot_validity_period_res);
$this->terminateWithSuccess($class_time_slot_validity_period_res);
}
public function getClassTimeValidityPeriod($classTimeSlotValidityPeriodId)
{
$this->ClassTimeSlotValidityPeriodModel->addSelect('
lehre.tbl_unterrichtszeiten_gueltigkeit.*,
public.tbl_organisationseinheit.oe_kurzbz as oe_kurzbz,
public.tbl_organisationseinheit.bezeichnung as oe_bezeichnung,
public.tbl_organisationseinheit.organisationseinheittyp_kurzbz as oe_organisationseinheittyp_kurzbz,
lehre.tbl_studienplan.studienplan_id,
lehre.tbl_studienplan.bezeichnung as studienplan_bezeichnung,
');
$this->ClassTimeSlotValidityPeriodModel->addJoin('public.tbl_organisationseinheit', 'public.tbl_organisationseinheit.oe_kurzbz=lehre.tbl_unterrichtszeiten_gueltigkeit.oe_kurzbz', 'LEFT');
$this->ClassTimeSlotValidityPeriodModel->addJoin('lehre.tbl_studienplan', 'lehre.tbl_studienplan.studienplan_id=lehre.tbl_unterrichtszeiten_gueltigkeit.studienplan_id', 'LEFT');
$class_time_slot_validity_period_res = $this->ClassTimeSlotValidityPeriodModel->load($classTimeSlotValidityPeriodId);
$class_time_slot_validity_period_res = $this->getDataOrTerminateWithError($class_time_slot_validity_period_res);
if (!$class_time_slot_validity_period_res || count($class_time_slot_validity_period_res) === 0) {
$this->terminateWithError($this->p->t('ui', 'classTimeSlotValidityPeriodNotFound'));
}
if (!$this->isUserEntitledForOrganizationalUnit($class_time_slot_validity_period_res[0]->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->terminateWithSuccess($class_time_slot_validity_period_res);
}
public function createClassTimeSlotValidityPeriod()
{
$this->form_validation->set_rules('validityPeriodFrom', 'Validity Period From', 'required|is_valid_date[Y-m-d]', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_validityPeriodFrom')]),
'is_valid_date' => $this->p->t('ui', 'error_fieldInvalidDate', ['field' => $this->p->t('ui', 'field_validityPeriodFrom')])
]);
$this->form_validation->set_rules('validityPeriodTo', 'Validity Period To', 'required|is_valid_date[Y-m-d]|callback_date_greater_equal[validityPeriodFrom]');
$this->form_validation->set_rules('organizationalUnitShortCode', 'Organizational Unit Shortcode', 'required|max_length[32]', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_organizationalUnit')]),
'max_length' => $this->p->t('ui', 'error_fieldMaxLength', ['field' => $this->p->t('ui', 'field_organizationalUnit'), 'max' => 32])
]);
$this->form_validation->set_rules('semester', 'Semester', 'is_natural_no_zero|less_than_equal_to[8]', [
'is_natural_no_zero' => $this->p->t('ui', 'error_fieldInvalid', ['field' => $this->p->t('ui', 'field_semester')]),
'less_than_equal_to' => $this->p->t('ui', 'error_fieldMaxValue', ['field' => $this->p->t('ui', 'field_semester'), 'max' => 8])
]);
$this->form_validation->set_rules('classTimeSlotTypeShortcode', 'Class Time Slot Type Shortcode', 'max_length[32]');
$this->form_validation->set_rules('studyPlanId', 'Study Plan ID', 'is_natural_no_zero');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
if (!$this->isUserEntitledForOrganizationalUnit($this->input->post('organizationalUnitShortCode'), 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$result = $this->ClassTimeSlotValidityPeriodModel->insert([
'gueltig_von' => $this->input->post('validityPeriodFrom'),
'gueltig_bis' => $this->input->post('validityPeriodTo'),
'oe_kurzbz' => $this->input->post('organizationalUnitShortCode'),
'ausbildungssemester' => $this->input->post('semester'),
'anmerkung' => $this->input->post('description'),
'unterrichtszeitentyp_kurzbz' => $this->input->post('classTimeSlotTypeShortcode') ?? null,
'studienplan_id' => $this->input->post('studyPlanId'),
'insertamum' => date('c'),
'insertvon' => getAuthUid(),
'updateamum' => date('c'),
'updatevon' => getAuthUid(),
]);
$data = $this->getDataOrTerminateWithError($result);
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function updateClassTimeSlotValidityPeriod($classTimeSlotValidityPeriodId)
{
$this->form_validation->set_rules('validityPeriodFrom', 'Validity Period From', 'required|is_valid_date[Y-m-d]', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_validityPeriodFrom')]),
'is_valid_date' => $this->p->t('ui', 'error_fieldInvalidDate', ['field' => $this->p->t('ui', 'field_validityPeriodFrom')])
]);
$this->form_validation->set_rules('validityPeriodTo', 'Validity Period To', 'required|is_valid_date[Y-m-d]|callback_date_greater_equal[validityPeriodFrom]');
$this->form_validation->set_rules('organizationalUnitShortCode', 'Organizational Unit Shortcode', 'required|max_length[32]', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_organizationalUnit')]),
'max_length' => $this->p->t('ui', 'error_fieldMaxLength', ['field' => $this->p->t('ui', 'field_organizationalUnit'), 'max' => 32])
]);
$this->form_validation->set_rules('semester', 'Semester', 'is_natural_no_zero|less_than_equal_to[8]', [
'is_natural_no_zero' => $this->p->t('ui', 'error_fieldInvalid', ['field' => $this->p->t('ui', 'field_semester')]),
'less_than_equal_to' => $this->p->t('ui', 'error_fieldMaxValue', ['field' => $this->p->t('ui', 'field_semester'), 'max' => 8])
]);
$this->form_validation->set_rules('classTimeSlotTypeShortcode', 'Class Time Slot Type Shortcode', 'max_length[32]');
$this->form_validation->set_rules('studyPlanId', 'Study Plan ID', 'is_natural_no_zero');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
if (!$this->isUserEntitledForOrganizationalUnit($this->input->post('organizationalUnitShortCode'), 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$result = $this->ClassTimeSlotValidityPeriodModel->update($classTimeSlotValidityPeriodId, [
'gueltig_von' => $this->input->post('validityPeriodFrom'),
'gueltig_bis' => $this->input->post('validityPeriodTo'),
'oe_kurzbz' => $this->input->post('organizationalUnitShortCode'),
'ausbildungssemester' => $this->input->post('semester'),
'anmerkung' => $this->input->post('description'),
'unterrichtszeitentyp_kurzbz' => $this->input->post('classTimeSlotTypeShortcode') ?? null,
'studienplan_id' => $this->input->post('studyPlanId'),
'updateamum' => date('c'),
'updatevon' => getAuthUid(),
]);
$this->db->trans_complete();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(true);
}
public function deleteClassTimeSlotValidityPeriod($classTimeSlotValidityPeriodId)
{
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$validityPeriod = $this->getDataOrTerminateWithError($validityPeriodResult)[0];
if (!$this->isUserEntitledForOrganizationalUnit($validityPeriod->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$result = $this->ClassTimeSlotModel->delete(['unterrichtszeitengueltigkeit_id'=> $classTimeSlotValidityPeriodId]);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$result = $this->ClassTimeSlotValidityPeriodModel->delete($classTimeSlotValidityPeriodId);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function getClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId)
{
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$validityPeriodData = $this->getDataOrTerminateWithError($validityPeriodResult);
if (!$validityPeriodData || count($validityPeriodData) === 0) {
$this->terminateWithError($this->p->t('ui', 'classTimeSlotValidityPeriodNotFound'));
}
$validityPeriod = $validityPeriodData[0];
if (!$this->isUserEntitledForOrganizationalUnit($validityPeriod->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->ClassTimeSlotModel->addOrder('insertamum', 'DESC');
$class_time_slots_res = $this->ClassTimeSlotModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$class_time_slots_res = $this->getDataOrTerminateWithError($class_time_slots_res);
$this->terminateWithSuccess($class_time_slots_res);
}
public function createClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId)
{
$this->form_validation->set_rules('unterrichtszeiten', 'Class Time Slots', 'callback_validate_items_in_class_time_slots');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$validityPeriod = $this->getDataOrTerminateWithError($validityPeriodResult)[0];
if (!$this->isUserEntitledForOrganizationalUnit($validityPeriod->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
foreach ($this->input->post('unterrichtszeiten') as $timeSlot) {
$result = $this->ClassTimeSlotModel->insert([
'wochentag' => $timeSlot['wochentag'],
'uhrzeit_von' => $timeSlot['startTime'],
'uhrzeit_bis' => $timeSlot['endTime'],
'unterrichtszeitentyp_kurzbz' => $timeSlot['classTimeSlotTypeShortcode'],
'unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId,
'insertamum' => date('c'),
'insertvon' => getAuthUid(),
'updateamum' => date('c'),
'updatevon' => getAuthUid(),
]);
$this->getDataOrTerminateWithError($result);
}
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function updateClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId)
{
$this->form_validation->set_rules('unterrichtszeiten', 'Class Time Slots', 'callback_validate_items_in_class_time_slots');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$validityPeriod = $this->getDataOrTerminateWithError($validityPeriodResult)[0];
if (!$this->isUserEntitledForOrganizationalUnit($validityPeriod->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$currentTimeSlotsResult = $this->ClassTimeSlotModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$currentTimeSlots = $this->getDataOrTerminateWithError($currentTimeSlotsResult);
$currentTimeSlotIds = array_column($currentTimeSlots, 'unterrichtszeit_id');
$removedTimeSlotIds = array_values(array_diff($currentTimeSlotIds, array_column($this->input->post('unterrichtszeiten'), 'id')));
if (count($removedTimeSlotIds) > 0) {
$query = 'DELETE FROM lehre.tbl_unterrichtszeiten WHERE unterrichtszeit_id IN ?';
$result = $this->db->query($query, [ $removedTimeSlotIds ]);
}
foreach ($this->input->post('unterrichtszeiten') as $timeSlot) {
$data = [
'wochentag' => $timeSlot['wochentag'],
'uhrzeit_von' => $timeSlot['startTime'],
'uhrzeit_bis' => $timeSlot['endTime'],
'unterrichtszeitentyp_kurzbz' => $timeSlot['classTimeSlotTypeShortcode'],
'unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId,
'updateamum' => date('c'),
'updatevon' => getAuthUid(),
];
if (isset($timeSlot['id'])) {
$result = $this->ClassTimeSlotModel->update($timeSlot['id'], $data);
} else {
$result = $this->ClassTimeSlotModel->insert(array_merge($data, [
'insertvon' => getAuthUid(),
'updateamum' => date('c'),
]));
}
$this->getDataOrTerminateWithError($result);
}
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function deleteClassTimeSlotsForValidityPeriod($classTimeSlotValidityPeriodId)
{
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitengueltigkeit_id' => $classTimeSlotValidityPeriodId]);
$validityPeriod = $this->getDataOrTerminateWithError($validityPeriodResult)[0];
if (!$this->isUserEntitledForOrganizationalUnit($validityPeriod->oe_kurzbz, 'lehre/unterrichtszeiten_gk')) {
$this->terminateWithError($this->p->t('ui', 'keineBerechtigung'));
}
$this->db->trans_start();
$result = $this->ClassTimeSlotModel->delete(['unterrichtszeitengueltigkeit_id'=> $classTimeSlotValidityPeriodId]);
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function getAllClassScheduleTypes()
{
$filter = $this->input->get('filter');
if ($filter) {
if (isset($filter['aktiv'])) {
$this->ClassTimeSlotTypeModel->db->where('aktiv', $filter['aktiv']);
}
}
$class_schedule_types_res = $this->ClassTimeSlotTypeModel->load();
$class_schedule_types_res = $this->getDataOrTerminateWithError($class_schedule_types_res);
$this->terminateWithSuccess($class_schedule_types_res);
}
public function createClassTimeSlotType()
{
$this->form_validation->set_rules('shortCode', 'Short Code', 'required|max_length[32]', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_classTimeSlotTypeShortCode')]),
'max_length' => $this->p->t('ui', 'error_fieldMaxLength', ['field' => $this->p->t('ui', 'field_classTimeSlotTypeShortCode'), 'max' => 32])
]);
$this->form_validation->set_rules('descriptions', 'Descriptions', 'callback_validate_descriptions_array');
$this->form_validation->set_rules('backgroundColor', 'Background Color', 'required|regex_match[/^#([0-9a-fA-F]{3}){1,2}$/]', [
'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_backgroundColor')]),
'regex_match' => $this->p->t('ui', 'error_fieldInvalid', ['field' => $this->p->t('ui', 'field_backgroundColor')]),
]);
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$this->db->trans_start();
$descriptions = $this->input->post('descriptions');
$pgArray = $this->arrayToPgArray($descriptions);
$query = 'INSERT INTO lehre.tbl_unterrichtszeiten_typ (
unterrichtszeitentyp_kurzbz,
bezeichnung_mehrsprachig,
hintergrundfarbe,
aktiv,
insertamum,
insertvon,
updateamum,
updatevon) VALUES (?, ?, ?, ?, ?, ?, ?, ?)';
$result = $this->db->query($query, [
$this->input->post('shortCode'),
$pgArray,
$this->input->post('backgroundColor'),
$this->input->post('isActive'),
date('c'),
getAuthUid(),
date('c'),
getAuthUid(),
]);
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function updateClassTimeSlotType($classTimeSlotTypeId)
{
$this->form_validation->set_rules('descriptions', 'Descriptions', 'callback_validate_descriptions_array');
if($this->form_validation->run() == FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array());
$parsedClassTimeSlotTypeId = urldecode($classTimeSlotTypeId);
$descriptions = $this->input->post('descriptions');
$pgArray = $this->arrayToPgArray($descriptions);
$query = 'UPDATE lehre.tbl_unterrichtszeiten_typ SET bezeichnung_mehrsprachig = ?, hintergrundfarbe = ?, aktiv = ?, updateamum = ?, updatevon = ? WHERE unterrichtszeitentyp_kurzbz = ?';
$result = $this->db->query($query, [
$pgArray,
$this->input->post('backgroundColor'),
$this->input->post('isActive'),
date('c'),
getAuthUid(),
$parsedClassTimeSlotTypeId,
]);
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
public function deleteClassTimeSlotType($classTimeSlotTypeId)
{
$isClassTimeSlotDeletable = true;
$parsedClassTimeSlotTypeId = urldecode($classTimeSlotTypeId);
$validityPeriodResult = $this->ClassTimeSlotValidityPeriodModel->loadWhere(['unterrichtszeitentyp_kurzbz' => $parsedClassTimeSlotTypeId]);
$validityPeriod = $this->getDataOrTerminateWithError($validityPeriodResult);
if ($validityPeriod && count($validityPeriod) > 0) {
$isClassTimeSlotDeletable = false;
}
$classTimeSlotResult = $this->ClassTimeSlotModel->loadWhere(['unterrichtszeitentyp_kurzbz' => $parsedClassTimeSlotTypeId]);
$classTimeSlot = $this->getDataOrTerminateWithError($classTimeSlotResult);
if ($classTimeSlot && count($classTimeSlot) > 0) {
$isClassTimeSlotDeletable = false;
}
$this->db->trans_start();
if (!$isClassTimeSlotDeletable) {
$result = $this->ClassTimeSlotTypeModel->update($parsedClassTimeSlotTypeId, ['aktiv' => false, 'updateamum' => date('c'), 'updatevon' => getAuthUid()]);
} else {
$result = $this->ClassTimeSlotTypeModel->delete(['unterrichtszeitentyp_kurzbz' => $parsedClassTimeSlotTypeId]);
}
if (isError($result)) {
$this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
}
$this->db->trans_complete();
$this->terminateWithSuccess(true);
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
private function arrayToPgArray(array $assoc) {
$flat = [null, null];
foreach ($assoc as $assocItem) {
if ($assocItem['lang'] === 'de') {
$flat[0] = $assocItem['value'];
} else if ($assocItem['lang'] === 'en') {
$flat[1] = $assocItem['value'];
} else {
$flat[] = $assocItem['value'];
}
}
$escaped = array_map(function ($v) {
return '"' . addslashes($v) . '"';
}, $flat);
return '{' . implode(',', $escaped) . '}';
}
public function date_greater_equal($toDate, $fromField)
{
$fromDate = $this->input->post($fromField);
if (!$fromDate) {
$this->form_validation->set_message(
'date_greater_equal',
$this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_validityPeriodTo')])
);
return false;
}
if (!strtotime($toDate) || !strtotime($fromDate)) {
$this->form_validation->set_message(
'date_greater_equal',
$this->p->t('ui', 'error_fieldInvalidDate', ['field' => $this->p->t('ui', 'field_validityPeriodTo')])
);
return false;
}
if (strtotime($toDate) < strtotime($fromDate)) {
$this->form_validation->set_message(
'date_greater_equal',
$this->p->t('ui', 'error_fieldDateGreaterEqual', ['field' => $this->p->t('ui', 'field_validityPeriodTo'), 'otherField' => $this->p->t('ui', 'field_validityPeriodFrom')])
);
return false;
}
return true;
}
public function validate_items_in_class_time_slots($unterrichtszeiten)
{
if (!is_array($this->input->post('unterrichtszeiten')) || count($this->input->post('unterrichtszeiten')) === 0) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
$this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_classTimeSlot')])
);
return false;
}
foreach ($this->input->post('unterrichtszeiten') as $index => $timeSlot) {
if (!isset($timeSlot['wochentag'], $timeSlot['startTime'], $timeSlot['endTime'], $timeSlot['classTimeSlotTypeShortcode'])) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
$this->p->t('ui', 'error_fieldClassTimeSlotContentInvalid', ['field' => $this->p->t('ui', 'field_classTimeSlot')])
);
return false;
}
if (!in_array($timeSlot['wochentag'], [1, 2, 3, 4, 5, 6, 0])) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
$this->p->t('ui', 'error_fieldWeekdayInvalid', ['field' => $this->p->t('ui', 'field_classTimeSlot')])
);
return false;
}
if (!strtotime($timeSlot['startTime']) || !strtotime($timeSlot['endTime'])) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
$this->p->t('ui', 'error_fieldClassTimeSlotTimeInvalid', ['field' => $this->p->t('ui', 'field_classTimeSlot')])
);
return false;
}
if (strtotime($timeSlot['endTime']) <= strtotime($timeSlot['startTime'])) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
$this->p->t('ui', 'error_fieldDateGreaterEqual', ['field' => $this->p->t('ui', 'field_classTimeSlotEndTime'), 'otherField' => $this->p->t('ui', 'field_classTimeSlotStartTime')])
);
return false;
}
}
$slotsByDay = [];
foreach ($this->input->post('unterrichtszeiten') as $timeSlot) {
$slotsByDay[$timeSlot['wochentag']][] = $timeSlot;
}
foreach ($slotsByDay as $day => $slots) {
usort($slots, function ($a, $b) {
return strtotime($a['startTime']) - strtotime($b['startTime']);
});
for ($i = 1; $i < count($slots); $i++) {
if (strtotime($slots[$i]['startTime']) < strtotime($slots[$i - 1]['endTime'])) {
$this->form_validation->set_message(
'validate_items_in_class_time_slots',
$this->p->t('ui', 'error_fieldClassTimeSlotOverlap', ['field' => $this->p->t('ui', 'field_classTimeSlot')])
);
return false;
}
}
}
return true;
}
public function validate_descriptions_array($descriptions)
{
$descriptions = $this->input->post('descriptions');
if (!is_array($descriptions) || count($descriptions) === 0) {
$this->form_validation->set_message(
'validate_descriptions_array',
$this->p->t('ui', 'error_fieldRequired', ['field' => $this->p->t('ui', 'field_descriptions')])
);
return false;
}
foreach ($descriptions as $index => $description) {
if (!isset($description['lang'], $description['value'])) {
$this->form_validation->set_message(
'validate_descriptions_array',
$this->p->t('ui', 'error_fieldDescriptionContentInvalid')
);
return false;
}
if (empty($description['lang']) || empty($description['value'])) {
$this->form_validation->set_message(
'validate_descriptions_array',
$this->p->t('ui', 'error_fieldDescriptionContentInvalid')
);
return false;
}
}
return true;
}
private function isUserEntitledForOrganizationalUnit($organizationalUnitShortCode, $requiredPermission)
{
$entitledOrganizationalUnitsShortCodes = $this->permissionlib->getOE_isEntitledFor($requiredPermission);
return in_array($organizationalUnitShortCode, $entitledOrganizationalUnitsShortCodes);
}
}
@@ -0,0 +1,43 @@
<?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 ClassroomHourApi extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'getAllClassroomHours' => self::PERM_LOGGED,
]);
$this->load->model('ressource/Stunde_model', "ClassroomHourModel");
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getAllClassroomHours()
{
$this->ClassroomHourModel->addOrder('stunde', 'ASC');
$result = $this->ClassroomHourModel->load();
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
}
@@ -26,7 +26,8 @@ class Studgang extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
'getStudiengangInfo'=> self::PERM_LOGGED,
'getAllStudiengange'=> self::PERM_LOGGED,
'getAllDegreePrograms'=> self::PERM_LOGGED,
]);
@@ -43,6 +44,14 @@ class Studgang extends FHCAPI_Controller
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getAllDegreePrograms()
{
$this->load->model('organisation/Studiengang_model', "StudiengangModel");
$class_schedule_res = $this->StudiengangModel->load();
$class_schedule_res = $this->getDataOrTerminateWithError($class_schedule_res);
$this->terminateWithSuccess($class_schedule_res);
}
public function getStudiengangInfo(){
$isMitarbeiter = $this->MitarbeiterModel->isMitarbeiter(getAuthUID());
$isMitarbeiter = $this->getDataOrTerminateWithError($isMitarbeiter);
@@ -0,0 +1,56 @@
<?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 OrganizationalUnitApi extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getAllOrganizationalUnits'=> array('basis/organisationseinheit:r'),
]);
$this->load->library('form_validation');
$this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
// Loads phrases system
$this->loadPhrases([
'global'
]);
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getAllOrganizationalUnits()
{
$entitledOrganizationalUnitsShortCodes = $this->permissionlib->getOE_isEntitledFor('basis/organisationseinheit');
$this->OrganisationseinheitModel->db->where_in('oe_kurzbz', $entitledOrganizationalUnitsShortCodes);
$result = $this->OrganisationseinheitModel->load();
$organization_units_result = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($organization_units_result);
}
}
@@ -20,10 +20,41 @@ class Studienplan extends FHCAPI_Controller
{
// TODO(chris): access!
parent::__construct([
'getBySemester' => self::PERM_LOGGED
'getAllStudyPlans' => self::PERM_LOGGED,
'getStudyPlansByOrganizationalUnitAndSemesterDates' => self::PERM_LOGGED,
'getBySemester' => self::PERM_LOGGED,
'getStudyPlan' => self::PERM_LOGGED,
]);
}
public function getAllStudyPlans()
{
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$result = $this->StudienplanModel->load();
$studien_plan_result = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($studien_plan_result);
}
public function getStudyPlansByOrganizationalUnitAndSemesterDates($organizationalUnitShortCode)
{
$this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$this->load->model('organisation/Studienordnung_model', 'StudienordnungModel');
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$startDate = date('Y-m-d', strtotime($this->input->get('filter[startDate]')));
$endDate = date('Y-m-d', strtotime($this->input->get('filter[endDate]')));
if (!$startDate || !$endDate) {
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Start- oder Enddatum']), self::ERROR_TYPE_GENERAL);
}
$studyPlansResponse = $this->StudienplanModel->getStudyPlansForOrganizationalUnitAndDatesQueryResponse($organizationalUnitShortCode, $startDate, $endDate);
if (isError($studyPlansResponse)) $this->terminateWithError(getError($studyPlansResponse), self::ERROR_TYPE_DB);
if (!hasData($studyPlansResponse)) return $this->terminateWithSuccess(null);
return $this->terminateWithSuccess($this->getDataOrTerminateWithError($studyPlansResponse));
}
public function getBySemester()
{
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
@@ -66,4 +97,15 @@ class Studienplan extends FHCAPI_Controller
$this->terminateWithSuccess(hasData($result) ? getData($result) : []);
}
public function getStudyPlan($id)
{
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$result = $this->StudienplanModel->loadWhere(['studienplan_id' => $id]);
if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_DB);
if (!hasData($result)) return $this->terminateWithSuccess(null);
$this->terminateWithSuccess(getData($result)[0]);
}
}
@@ -27,12 +27,17 @@ class Studiensemester extends FHCAPI_Controller
'getAktNext' => self::PERM_LOGGED,
'getStudienjahrByStudiensemester' => self::PERM_LOGGED,
'getAllStudiensemesterAndAktOrNext' => self::PERM_LOGGED,
'getStudySemestersByOrganizationalUnitAndDates' => self::PERM_LOGGED,
'getStudySemestersByStudyPlanAndDates' => self::PERM_LOGGED,
'current' => self::PERM_LOGGED,
'set' => self::PERM_LOGGED
)
);
// Load model StudiensemesterModel
$this->load->model('organisation/studiensemester_model', 'StudiensemesterModel');
$this->load->model('organisation/Studiengang_model', 'StudiengangModel');
$this->load->model('organisation/Studienordnung_model', 'StudienordnungModel');
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
}
/**
@@ -169,6 +174,49 @@ class Studiensemester extends FHCAPI_Controller
$this->terminateWithSuccess(array($studiensemester, $aktuell));
}
public function getStudySemestersByOrganizationalUnitAndDates($organizationalUnitShortCode)
{
$startDate = date('Y-m-d', strtotime($this->input->get('filter[startDate]')));
$endDate = date('Y-m-d', strtotime($this->input->get('filter[endDate]')));
if (!$startDate || !$endDate) {
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Start- oder Enddatum']), self::ERROR_TYPE_GENERAL);
}
$studyPlansResponse = $this->StudienplanModel->getStudyPlansForOrganizationalUnitQueryResponse($organizationalUnitShortCode);
if (isError($studyPlansResponse)) $this->terminateWithError(getError($studyPlansResponse), self::ERROR_TYPE_DB);
if (!hasData($studyPlansResponse)) return $this->terminateWithSuccess(null);
$studyPlans = $this->getDataOrTerminateWithError($studyPlansResponse);
$studyPlansIds = array_map(function ($studyPlan) { return $studyPlan->studienplan_id; }, $studyPlans);
$studySemestersResponse = $this->StudiensemesterModel->getStudySemestersByStudyPlansAndDatesQueryResponse($studyPlansIds, $startDate, $endDate);
if (isError($studySemestersResponse)) $this->terminateWithError(getError($studySemestersResponse), self::ERROR_TYPE_DB);
if (!hasData($studySemestersResponse)) return $this->terminateWithSuccess([]);
$studySemesters = $this->getDataOrTerminateWithError($studySemestersResponse);
return $this->terminateWithSuccess($studySemesters);
}
public function getStudySemestersByStudyPlanAndDates()
{
$studyPlansId = $this->input->get('filter[studyPlanId]');
$startDate = date('Y-m-d', strtotime($this->input->get('filter[startDate]')));
$endDate = date('Y-m-d', strtotime($this->input->get('filter[endDate]')));
if (!$startDate || !$endDate) {
return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Start- oder Enddatum']), self::ERROR_TYPE_GENERAL);
}
$studySemestersResponse = $this->StudiensemesterModel->getStudySemestersByStudyPlansAndDatesQueryResponse([$studyPlansId], $startDate, $endDate);
if (isError($studySemestersResponse)) $this->terminateWithError(getError($studySemestersResponse), self::ERROR_TYPE_DB);
if (!hasData($studySemestersResponse)) return $this->terminateWithSuccess([]);
$studySemesters = $this->getDataOrTerminateWithError($studySemestersResponse);
return $this->terminateWithSuccess($studySemesters);
}
public function current()
{
$result = $this->StudiensemesterModel->getNearest();
@@ -370,6 +370,7 @@ class LvTermine extends FHCAPI_Controller
$result->semester = $item->semester;
$result->verband = $item->verband;
$result->gruppe = $item->gruppe;
$result->studiensemester_kurzbz = $item->studiensemester_kurzbz;
return $result;
},
$lehrverband_query_result));
@@ -0,0 +1,63 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*/
class ClassSchedule extends Auth_Controller
{
private $_uid; // uid of the logged user
/**
* Constructor
*/
public function __construct()
{
parent::__construct(
array(
'index' => array('lehre/unterrichtszeiten_gk:r'),
)
);
$this->load->library('PermissionLib');
$this->load->library('AuthLib');
$this->loadPhrases(
array(
'ui',
'global',
'person',
'abschlusspruefung',
'password',
'lehre'
)
);
$this->_setAuthUID();
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
public function index()
{
return $this->load->view('lehre/class_schedule/index.php', [
'permissions' => [
'lehre/unterrichtszeiten_typ_r' => $this->permissionlib->isBerechtigt('lehre/unterrichtszeiten_typ', 's'),
'lehre/unterrichtszeiten_typ_w' => $this->permissionlib->isBerechtigt('lehre/unterrichtszeiten_typ', 'suid'),
],
]);
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
/**
* Retrieve the UID of the logged user and checks if it is valid
*/
private function _setAuthUID()
{
$this->_uid = getAuthUID();
if (!$this->_uid) show_error('User authentification failed');
}
}
@@ -34,6 +34,9 @@ class InfoCenter extends Auth_Controller
const PREV_FILTER_ID = 'prev_filter_id';
const KEEP_TABLESORTER_FILTER = 'keepTsFilter';
const ONBOARDING_INSERTVON = 'onboarding';
const ONBOARDING_KENNZEICHENTYP = 'eobRegistrierungsId';
private $_uid; // contains the UID of the logged user
// Used to log with PersonLogLib
@@ -175,6 +178,7 @@ class InfoCenter extends Auth_Controller
$this->load->model('person/Kontakt_model', 'KontaktModel');
$this->load->model('person/Geschlecht_model', 'GeschlechtModel');
$this->load->model('person/adresse_model', 'AdresseModel');
$this->load->model('person/Kennzeichen_model', 'KennzeichenModel');
// Loads libraries
$this->load->library('PersonLogLib');
@@ -1452,6 +1456,100 @@ class InfoCenter extends Auth_Controller
$this->outputJsonSuccess("Done!");
}
public function getAbsageData()
{
$stg_typ = $this->getStudienArtBerechtigung(['b', 'm']);
if (!is_null($stg_typ))
{
$statusgruende = $this->StatusgrundModel->getStatus(self::ABGEWIESENERSTATUS, true)->retval;
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
$studiengaenge = $this->StudiengangModel->getStudiengaengeWithOrgForm(array_column($stg_typ, 'typ'), $studienSemester);
$data = array (
'statusgruende' => $statusgruende,
'studiengaenge' => $studiengaenge->retval
);
$this->outputJsonSuccess($data);
}
else
$this->outputJsonSuccess(null);
}
public function getStudienArtBerechtigung($typ = null)
{
$studiengang_kz_all = $this->permissionlib->getSTG_isEntitledFor('infocenter');
$stg_typ = $this->StudiengangModel->getStudiengangTyp($studiengang_kz_all, $typ);
return getData($stg_typ);
}
public function getStudienartData()
{
$this->outputJsonSuccess($this->getStudienArtBerechtigung(['b', 'm', 'l']));
}
public function saveAbsageForAll()
{
$statusgrund = $this->input->post('statusgrund');
$studiengang = $this->input->post('studiengang');
$abgeschickt = $this->input->post('abgeschickt');
$personen = $this->input->post('personen');
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
if ($statusgrund === 'null' || $studiengang === 'null' || $abgeschickt === 'null' || empty($personen))
$this->terminateWithJsonError("Bitte füllen Sie alle Felder aus");
if ($studiengang === 'all' && $abgeschickt === 'all')
{
foreach($personen as $person)
{
$prestudenten = $this->PrestudentModel->getByPersonWithoutLehrgang($person, $studienSemester);
if (!hasData($prestudenten))
continue;
$prestudentenData = getData($prestudenten);
foreach ($prestudentenData as $prestudent)
{
$this->saveAbsage($prestudent->prestudent_id, $statusgrund);
}
}
}
else
{
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$this->StudienplanModel->addSelect('1');
$this->StudienplanModel->addJoin('lehre.tbl_studienordnung so', 'studienordnung_id');
$escaped = $this->StudienplanModel->db->escape(strtoupper($studiengang));
$this->StudienplanModel->db->where("UPPER(so.studiengangkurzbzlang || ':' || tbl_studienplan.orgform_kurzbz) = $escaped");
$this->StudienplanModel->addLimit(1);
$studiengangResult = $this->StudienplanModel->load();
if (hasData($studiengangResult))
{
foreach($personen as $person)
{
$prestudent = $this->PrestudentModel->getPrestudentByStudiengangAndPerson($studiengang, $person, $studienSemester, $abgeschickt, $abgeschickt === 'all');
if (!hasData($prestudent))
continue;
$prestudentData = getData($prestudent);
$this->saveAbsage($prestudentData[0]->prestudent_id, $statusgrund);
}
}
else
$this->terminateWithJsonError("Falschen Studiengang übergeben!");
}
$this->outputJsonSuccess("Success");
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
@@ -1946,6 +2044,17 @@ class InfoCenter extends Auth_Controller
show_error(getError($user_person));
}
// add info about first electronig onboarding login
$this->KennzeichenModel->addSelect('insertamum');
$onboarding_first_login = $this->KennzeichenModel->loadWhere(
array('person_id' => $person_id, 'kennzeichentyp_kurzbz' => self::ONBOARDING_KENNZEICHENTYP)
);
if (isError($onboarding_first_login))
{
show_error(getError($onboarding_first_login));
}
$data = array (
'lockedby' => $lockedby,
'lockedbyother' => $lockedbyother,
@@ -1955,7 +2064,9 @@ class InfoCenter extends Auth_Controller
'messages' => $messages->retval,
'logs' => $logs,
'notizen' => $notizen->retval,
'notizenbewerbung' => $notizen_bewerbung->retval
'notizenbewerbung' => $notizen_bewerbung->retval,
'created_by_onboarding' => $stammdaten->retval->insertvon == self::ONBOARDING_INSERTVON,
'onboarding_first_login' => hasData($onboarding_first_login) ? getData($onboarding_first_login)[0]->insertamum : null
);
return $data;
@@ -2375,97 +2486,4 @@ class InfoCenter extends Auth_Controller
$this->loglib->logError('Studiengang has no mail for sending Freigabe mail');
}
}
public function getAbsageData()
{
$stg_typ = $this->getStudienArtBerechtigung(['b', 'm']);
if (!is_null($stg_typ))
{
$statusgruende = $this->StatusgrundModel->getStatus(self::ABGEWIESENERSTATUS, true)->retval;
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
$studiengaenge = $this->StudiengangModel->getStudiengaengeWithOrgForm(array_column($stg_typ, 'typ'), $studienSemester);
$data = array (
'statusgruende' => $statusgruende,
'studiengaenge' => $studiengaenge->retval
);
$this->outputJsonSuccess($data);
}
else
$this->outputJsonSuccess(null);
}
public function getStudienArtBerechtigung($typ = null)
{
$studiengang_kz_all = $this->permissionlib->getSTG_isEntitledFor('infocenter');
$stg_typ = $this->StudiengangModel->getStudiengangTyp($studiengang_kz_all, $typ);
return getData($stg_typ);
}
public function getStudienartData()
{
$this->outputJsonSuccess($this->getStudienArtBerechtigung(['b', 'm', 'l']));
}
public function saveAbsageForAll()
{
$statusgrund = $this->input->post('statusgrund');
$studiengang = $this->input->post('studiengang');
$abgeschickt = $this->input->post('abgeschickt');
$personen = $this->input->post('personen');
$studienSemester = $this->variablelib->getVar('infocenter_studiensemester');
if ($statusgrund === 'null' || $studiengang === 'null' || $abgeschickt === 'null' || empty($personen))
$this->terminateWithJsonError("Bitte füllen Sie alle Felder aus");
if ($studiengang === 'all' && $abgeschickt === 'all')
{
foreach($personen as $person)
{
$prestudenten = $this->PrestudentModel->getByPersonWithoutLehrgang($person, $studienSemester);
if (!hasData($prestudenten))
continue;
$prestudentenData = getData($prestudenten);
foreach ($prestudentenData as $prestudent)
{
$this->saveAbsage($prestudent->prestudent_id, $statusgrund);
}
}
}
else
{
$this->load->model('organisation/Studienplan_model', 'StudienplanModel');
$this->StudienplanModel->addSelect('1');
$this->StudienplanModel->addJoin('lehre.tbl_studienordnung so', 'studienordnung_id');
$escaped = $this->StudienplanModel->db->escape(strtoupper($studiengang));
$this->StudienplanModel->db->where("UPPER(so.studiengangkurzbzlang || ':' || tbl_studienplan.orgform_kurzbz) = $escaped");
$this->StudienplanModel->addLimit(1);
$studiengangResult = $this->StudienplanModel->load();
if (hasData($studiengangResult))
{
foreach($personen as $person)
{
$prestudent = $this->PrestudentModel->getPrestudentByStudiengangAndPerson($studiengang, $person, $studienSemester, $abgeschickt, $abgeschickt === 'all');
if (!hasData($prestudent))
continue;
$prestudentData = getData($prestudent);
$this->saveAbsage($prestudentData[0]->prestudent_id, $statusgrund);
}
}
else
$this->terminateWithJsonError("Falschen Studiengang übergeben!");
}
$this->outputJsonSuccess("Success");
}
}
@@ -7,6 +7,8 @@ class Rueckstellung extends Auth_Controller
private $_ci; // Code igniter instance
private $_uid;
const ZGV_UEBERPRUEFUNG_MAIL_VORLAGE = 'ZGVUEP';
public function __construct()
{
parent::__construct(
@@ -23,6 +25,7 @@ class Rueckstellung extends Auth_Controller
$this->load->model('crm/RueckstellungStatus_model', 'RueckstellungStatusModel');
$this->load->model('person/Person_model', 'PersonModel');
$this->load->library('PersonLogLib');
$this->load->library('MessageLib');
$this->_setAuthUID(); // sets property uid
@@ -78,6 +81,8 @@ class Rueckstellung extends Auth_Controller
$this->_log($person_id, $status_kurzbz);
if ($status_kurzbz === 'onhold_zgv')
$this->sendRueckstellungMessage($person_id);
$this->outputJson($result);
}
@@ -104,6 +109,10 @@ class Rueckstellung extends Auth_Controller
if (isError($result))
$this->terminateWithJsonError(getError($result));
$this->_log($person, $status_kurzbz);
if ($status_kurzbz === 'onhold_zgv')
$this->sendRueckstellungMessage($person);
}
$this->outputJsonSuccess("Erfolgreich gespeichert!");
}
@@ -160,4 +169,26 @@ class Rueckstellung extends Auth_Controller
$this->_uid
);
}
private function sendRueckstellungMessage($person_id)
{
$person = $this->_ci->PersonModel->load($person_id);
if (hasData($person))
{
$person = getData($person)[0];
return $this->messagelib->sendMessageUserTemplate(
$person_id,
self::ZGV_UEBERPRUEFUNG_MAIL_VORLAGE,
array(
'anrede' => $person->anrede,
'nachname' => $person->nachname
),
null,
getAuthPersonId(),
'infocenter'
);
}
}
}
+1 -1
View File
@@ -741,7 +741,7 @@ class AnrechnungLib
$fullname = hasData($result) ? $result->retval[0]->vorname. $result->retval[0]->nachname : '';
// Return filename
return 'Anrechnungsantrag'. $orgform_kurzbz .'_LV-'. $lehrveranstaltung_id. '_'. $fullname;
return 'Anrechnungsantrag'. $orgform_kurzbz .'_LV-'. $lehrveranstaltung_id. '_'. $fullname . '.pdf';
}
public function LVhasLector($anrechnung_id)
@@ -0,0 +1,15 @@
<?php
class ClassTimeSlotType_model extends DB_Model
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'lehre.tbl_unterrichtszeiten_typ';
$this->pk = 'unterrichtszeitentyp_kurzbz';
}
}
@@ -0,0 +1,15 @@
<?php
class ClassTimeSlotValidityPeriod_model extends DB_Model
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'lehre.tbl_unterrichtszeiten_gueltigkeit';
$this->pk = 'unterrichtszeitengueltigkeit_id';
}
}
@@ -0,0 +1,15 @@
<?php
class ClassTimeSlot_model extends DB_Model
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'lehre.tbl_unterrichtszeiten';
$this->pk = 'unterrichtszeit_id';
}
}
@@ -168,5 +168,35 @@ class Studienplan_model extends DB_Model
return $this->loadWhere([
'person_id' => $person_id
]);
}
public function getStudyPlansForOrganizationalUnitQueryResponse($organizationalUnitShortCode)
{
$query = "SELECT sp.* FROM lehre.tbl_studienplan sp
JOIN lehre.tbl_studienordnung so ON sp.studienordnung_id = so.studienordnung_id
JOIN public.tbl_studiengang sg ON so.studiengang_kz = sg.studiengang_kz
WHERE sg.oe_kurzbz = ?";
return $this->execReadOnlyQuery($query, array($organizationalUnitShortCode));
}
public function getStudyPlansForOrganizationalUnitAndDatesQueryResponse($organizationalUnitShortCode, $startDate, $endDate)
{
$query = "SELECT DISTINCT sp.* FROM lehre.tbl_studienplan sp
JOIN lehre.tbl_studienordnung so ON sp.studienordnung_id = so.studienordnung_id
JOIN public.tbl_studiengang sg ON so.studiengang_kz = sg.studiengang_kz
AND sg.oe_kurzbz = ? AND sp.studienplan_id IN (
SELECT sps.studienplan_id
FROM lehre.tbl_studienplan_semester sps
JOIN public.tbl_studiensemester ss ON ss.studiensemester_kurzbz = sps.studiensemester_kurzbz
WHERE (
(ss.start >= ? AND ss.ende <= ?) OR
(ss.start <= ? AND ss.ende >= ?) OR
(ss.start <= ? AND ss.ende >= ? AND ss.ende <= ?) OR
(ss.start >= ? AND ss.start <= ? AND ss.ende >= ?)
)
)";
return $this->execReadOnlyQuery($query, array($organizationalUnitShortCode, $startDate, $endDate, $startDate, $endDate, $startDate, $startDate, $endDate, $startDate, $endDate, $endDate));
}
}
@@ -464,4 +464,26 @@ EOSQL;
$result = $this->load($studiensemester_kurzbz);
return hasData($result);
}
public function getStudySemestersByStudyPlansAndDatesQueryResponse($studyPlanIds, $fromDate, $toDate)
{
$subQuery = "(
SELECT ARRAY_AGG(DISTINCT sp.semester)
FROM lehre.tbl_studienplan_semester sp
WHERE sp.studienplan_id IN ?
AND sp.studiensemester_kurzbz = s.studiensemester_kurzbz
) AS semester_numbers";
$query = "SELECT DISTINCT s.*," . $subQuery . "
FROM public.tbl_studiensemester s
JOIN lehre.tbl_studienplan_semester sp ON sp.studiensemester_kurzbz = s.studiensemester_kurzbz
WHERE sp.studienplan_id IN ? AND (
(s.start >= ? AND s.ende <= ?) OR
(s.start <= ? AND s.ende >= ?) OR
(s.start <= ? AND s.ende >= ? AND s.ende <= ?) OR
(s.start >= ? AND s.start <= ? AND s.ende >= ?)
)";
return $this->execQuery($query, array($studyPlanIds, $studyPlanIds, $fromDate, $toDate, $fromDate, $toDate, $fromDate, $fromDate, $toDate, $fromDate, $toDate, $toDate));
}
}
+10 -3
View File
@@ -98,7 +98,7 @@ class Notiz_model extends DB_Model
/**
* Add a Notiz for a given person
*/
public function addNotizForPerson($person_id, $titel, $text, $erledigt, $verfasser_uid)
public function addNotizForPerson($person_id, $titel, $text, $erledigt, $verfasser_uid, $insertvon = null)
{
// Loads model Notizzuordnung_model
$this->load->model('person/Notizzuordnung_model', 'NotizzuordnungModel');
@@ -106,8 +106,15 @@ class Notiz_model extends DB_Model
// Start DB transaction
$this->db->trans_start(false);
$result = $this->insert(array('titel' => $titel, 'text' => $text, 'erledigt' => $erledigt, 'verfasser_uid' => $verfasser_uid,
"insertvon" => $verfasser_uid));
$result = $this->insert(
array(
'titel' => $titel,
'text' => $text,
'erledigt' => $erledigt,
'verfasser_uid' => $verfasser_uid,
'insertvon' => $insertvon ?? $verfasser_uid
)
);
if (isSuccess($result))
{
@@ -0,0 +1,44 @@
<?php
$includesArray = array(
'title' => ucfirst($this->p->t('ui', 'classSchedulePageTitle')),
'vue3' => true,
'axios027' => true,
'bootstrap5' => true,
'tabulator5' => true,
'fontawesome6' => true,
'primevue3' => true,
'navigationcomponent' => true,
'filtercomponent' => true,
'vuedatepicker11' => true,
'momentjs2' => true,
'customJSs' => array(
'vendor/moment/luxonjs/luxon.min.js'
),
'customJSModules' => array(
'public/js/apps/lehre/ClassScheduleApp.js'
),
'customCSSs' => array(
'public/css/components/primevue.css',
'public/css/components/verticalsplit.css',
'public/extensions/FHC-Core-Developer/css/FhcMain.css',
'public/css/components/calendar.css',
'public/css/components/classSchedule.css',
'public/css/components/vue-datepicker.css',
'public/css/classScheduleOverview.css',
)
);
$this->load->view('templates/FHC-Header', $includesArray);
?>
<div id="main">
<core-navigation-cmpt></core-navigation-cmpt>
<router-view
cis-root="<?= CIS_ROOT; ?>"
:permissions="<?= htmlspecialchars(json_encode($permissions)); ?>"
>
</router-view>
</div>
<?php $this->load->view('templates/FHC-Footer', $includesArray); ?>
@@ -8,6 +8,7 @@
$LOGDATA_NAME = '\'Login with code\', \'Login with user\', \'Attempt to register with existing mailadress\', \'Access code sent\', \'Personal data saved\'';
$REJECTED_STATUS = '\'Abgewiesener\'';
$ADDITIONAL_STG = $this->config->item('infocenter_studiengang_kz');
$QUALIFIKATIONSKURS = $this->config->item('infocenter_qualifikationskurs_kz');
$STATUS_KURZBZ = '\'Wartender\', \'Bewerber\', \'Aufgenommener\', \'Student\'';
$STUDIENSEMESTER = '\''.$this->variablelib->getVar('infocenter_studiensemester').'\'';
$ORG_NAME = '\'InfoCenter\'';
@@ -277,7 +278,26 @@ $query = '
WHERE konto.person_id = p.person_id
AND konto.studiensemester_kurzbz = '. $STUDIENSEMESTER .'
AND konto.buchungstyp_kurzbz = '. $KAUTION_DRITT_STAAT .'
) AS "Kaution"
) AS "Kaution",
(
SELECT 1
FROM public.tbl_prestudentstatus pss
JOIN public.tbl_prestudent ps USING(prestudent_id)
JOIN public.tbl_studiengang sg USING(studiengang_kz)
JOIN lehre.tbl_studienplan sp USING(studienplan_id)
JOIN lehre.tbl_studienordnung so USING(studienordnung_id)
WHERE pss.status_kurzbz = '.$INTERESSENT_STATUS.'
AND pss.bewerbung_abgeschicktamum IS NOT NULL
AND ps.person_id = p.person_id
AND sg.studiengang_kz in('.$QUALIFIKATIONSKURS.')
AND NOT EXISTS (
SELECT 1
FROM tbl_prestudentstatus spss
WHERE spss.prestudent_id = ps.prestudent_id
AND spss.status_kurzbz = '.$REJECTED_STATUS.'
)
LIMIT 1
) AS "Qualikurs"
FROM public.tbl_person p
LEFT JOIN (
SELECT tpl.person_id,
@@ -351,7 +371,8 @@ $query = '
'ZGV Nation MA',
'InfoCenter Mitarbeiter',
'Identitätsnachweis',
ucfirst($this->p->t('infocenter', 'kaution'))
ucfirst($this->p->t('infocenter', 'kaution')),
'Qualikurs'
),
'formatRow' => function($datasetRaw) {
@@ -480,6 +501,14 @@ $query = '
$datasetRaw->{'Kaution'} = 'Offen';
}
if ($datasetRaw->{'Qualikurs'} === null)
{
$datasetRaw->{'Qualikurs'} = 'Nein';
}
else
{
$datasetRaw->{'Qualikurs'} = 'Ja';
}
return $datasetRaw;
},
'markRow' => function($datasetRaw) {
@@ -92,6 +92,14 @@
</table>
</div>
<div class="col-lg-6 table-responsive">
<?php if ($onboarding_first_login): ?>
<div class="row" id="onboardingRow">
<?php if ($created_by_onboarding): ?>
<div class="col-xs-5"><strong><?php echo $this->p->t('infocenter','onboardingRegistriert') ?></strong></div>
<?php endif; ?>
<div class="col-xs-7<?php echo $created_by_onboarding ? ' text-right' : '' ?>"><strong><?php echo $this->p->t('infocenter','ersterOnboardingLogin').': '.date_format(date_create($onboarding_first_login), 'd.m.Y') ?></strong></div>
</div>
<?php endif; ?>
<table class="table table-bordered stammdaten_form">
<thead>
<tr>
@@ -191,12 +199,12 @@
</div>
<?php endif; ?>
<div class="col-xs-6">
<a class="editStammdaten">
<a class="editStammdaten" href="javascript:void(0);">
<i class="fa fa-edit"></i>&nbsp;<?php echo $this->p->t('ui','bearbeiten'); ?></a>
<div class="editActionStammdaten" style="display:none">
<a class="cancelStammdaten">
<a class="cancelStammdaten" href="javascript:void(0);">
<i class="fa fa-trash"></i>&nbsp;<?php echo $this->p->t('ui','abbrechen');?></a>
<a class="saveStammdaten">
<a class="saveStammdaten" href="javascript:void(0);">
<i class="fa fa-save"></i>&nbsp;<?php echo $this->p->t('ui','speichern'); ?></a>
</div>
</div>
+1
View File
@@ -632,6 +632,7 @@ elseif (isset($prestudent_id))
$ps_obj->lastStatus = $ps_tmp->status_kurzbz; // letzten Status dem result array hinzufügen
$ps_obj->status_mehrsprachig = $ps_tmp->status_mehrsprachig;
$ps_obj->ausbildungssemester = $ps_tmp->ausbildungssemester;
}
// Falls Status 'Abgewiesene' vorhanden, nach hinten reihen
+33 -15
View File
@@ -29,6 +29,7 @@ require_once('../../include/sprache.class.php');
require_once '../../include/phrasen.class.php';
require_once '../../include/studiengang.class.php';
require_once('../../include/gebiet.class.php');
require_once('../../include/gebiet_pruefling.class.php');
if (!$db = new basis_db())
die('Fehler beim Oeffnen der Datenbankverbindung');
@@ -198,8 +199,8 @@ else if (isset($_SESSION['pruefling_id']))
)
SELECT DISTINCT ON
(gebiet_id, semester, reihung)
SELECT * FROM (SELECT DISTINCT ON
(gebiet_id)
semester,
gebiet_id,
STRING_AGG(studiengang_kz::TEXT, ', ' ORDER BY studiengang_kz) AS studiengang_kz_list,
@@ -226,13 +227,7 @@ else if (isset($_SESSION['pruefling_id']))
JOIN
testtool.tbl_gebiet USING (gebiet_id)
WHERE
(
(prestudent_data.semester= 1 AND tbl_ablauf.semester = 1)
OR
(prestudent_data.semester= 2 AND tbl_ablauf.semester = 2)
OR
(prestudent_data.semester= 3 AND tbl_ablauf.semester IN (1,3))
)
tbl_ablauf.semester <= prestudent_data.semester
AND (
prestudent_data.studienplan_id = tbl_ablauf.studienplan_id
OR
@@ -259,9 +254,7 @@ else if (isset($_SESSION['pruefling_id']))
JOIN
testtool.tbl_gebiet USING (gebiet_id)
WHERE
(prestudent_data.semester= 1 AND tbl_ablauf.semester = 1)
OR
(prestudent_data.semester= 3 AND tbl_ablauf.semester IN (1,3))
tbl_ablauf.semester <= prestudent_data.semester
)
) temp
) temp2
@@ -270,7 +263,7 @@ else if (isset($_SESSION['pruefling_id']))
semester,
gebiet_id,
bezeichnung,
". $bezeichnung_mehrsprachig_sel ."
". $bezeichnung_mehrsprachig_sel ." ) temp3
ORDER BY
semester,
@@ -283,6 +276,8 @@ else if (isset($_SESSION['pruefling_id']))
$lastsemester = '';
$quereinsteiger_stg = '';
$_SESSION['alleGebiete']= [];
$gebiet_pruefling = new gebiet_pruefling();
$quereinsteiger_menu = false;
while($row = $db->db_fetch_object($result))
{
//Jedes Semester in einer eigenen Tabelle anzeigen
@@ -294,8 +289,25 @@ else if (isset($_SESSION['pruefling_id']))
}
$lastsemester = $row->semester;
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
echo '<tr><td class="HeaderTesttool">'. ($row->semester == '1' ? $p->t('testtool/basisgebiete') : $p->t('testtool/quereinstiegsgebiete')).'</td></tr>';
if ($row->semester == '1')
{
// Basisgebiete anzeigen
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
echo '<tr><td class="HeaderTesttool">' . $p->t('testtool/basisgebiete') . '</td></tr>';
}
else
{
if (!$quereinsteiger_menu)
{
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
echo '<tr><td class="HeaderTesttool">' . $p->t('testtool/quereinstiegsgebiete') . '</td></tr>';
$quereinsteiger_menu = true;
}
else
{
echo '<table border="0" cellspacing="0" cellpadding="0" id="Gebiet" style="display: visible; border-collapse: separate; border-spacing: 0 3px; margin-top: 5px;">';
}
}
}
// Bei Quereinstiegsgebieten nach STG clustern und die STG anzeigen
@@ -394,6 +406,12 @@ else if (isset($_SESSION['pruefling_id']))
</td>
<!--<td width="10" class="ItemTesttoolRight" nowrap>&nbsp;</td>-->
</tr>';
if (!$gebiet_pruefling->checkIfExists($_SESSION['pruefling_id'], $row->gebiet_id))
{
$gebiet_pruefling->pruefling_id = $_SESSION['pruefling_id'];
$gebiet_pruefling->gebiet_id = $row->gebiet_id;
$gebiet_pruefling->saveGebietForPruefling();
}
$_SESSION['alleGebiete'][] = $row->gebiet_id;
}
else
+58
View File
@@ -521,5 +521,63 @@ class adresse extends basis_db
}
return true;
}
/**
* Laedt die österreichische Zustell Adresse einer Person
* @param $person_id ID der Person
* @return true wenn ok, false im Fehlerfall
*/
public function loadOesterreichischeZustellAdresse($person_id, $typ = null)
{
//Pruefen ob person_id eine gueltige Zahl ist
if(!is_numeric($person_id) || $person_id == '')
{
$this->errormsg = 'PersonID muss eine Zahl sein';
return false;
}
$typ_clause = '';
if (isset($typ)) $typ_clause = "AND typ=".$this->db_add_param($typ);
//Daten aus der Datenbank lesen
$qry = "SELECT * FROM public.tbl_adresse WHERE person_id=".$this->db_add_param($person_id, FHC_INTEGER, false)."
AND nation = 'A'
{$typ_clause}
ORDER BY zustelladresse DESC, adresse_id DESC LIMIT 1";
if(!$this->db_query($qry))
{
$this->errormsg = 'Fehler bei einer Datenbankabfrage';
return false;
}
if($row = $this->db_fetch_object())
{
$this->adresse_id = $row->adresse_id;
$this->heimatadresse = $this->db_parse_bool($row->heimatadresse);
$this->zustelladresse = $this->db_parse_bool($row->zustelladresse);
$this->gemeinde = $row->gemeinde;
$this->name = $row->name;
$this->nation = $row->nation;
$this->ort = $row->ort;
$this->person_id = $row->person_id;
$this->plz = $row->plz;
$this->strasse = $row->strasse;
$this->typ = $row->typ;
$this->updateamum = $row->updateamum;
$this->updatevon = $row->updatevon;
$this->insertamum = $row->insertamum;
$this->insertvon = $row->insertvon;
$this->firma_id = $row->firma_id;
$this->rechnungsadresse = $this->db_parse_bool($row->rechnungsadresse);
$this->anmerkung = $row->anmerkung;
$this->co_name = $row->co_name;
}
else
{
$this->errormsg = 'Es ist kein Datensatz mit dieser ID vorhanden';
return false;
}
return true;
}
}
?>
+97
View File
@@ -0,0 +1,97 @@
<?php
/* Copyright (C) 2024 Technikum-Wien
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
require_once(dirname(__FILE__).'/basis_db.class.php');
class gebiet_pruefling extends basis_db
{
//Tabellenspalten
public $prueflinggebiet_id;
public $pruefling_id;
public $gebiet_id;
public $insertamum;
public function __construct()
{
parent::__construct();
}
public function loadByPruefling($pruefling_id)
{
$qry = "SELECT *
FROM testtool.tbl_pruefling_gebiet
WHERE pruefling_id = ".$this->db_add_param($pruefling_id, FHC_INTEGER);
if($this->db_query($qry))
{
if($row = $this->db_fetch_object())
{
$this->prueflinggebiet_id = $row->prueflinggebiet_id;
$this->pruefling_id = $row->pruefling_id;
$this->gebiet_id = $row->gebiet_id;
return true;
}
else
{
$this->errormsg = "Pruefling nicht gefunden";
return false;
}
}
else
{
$this->errormsg = "Fehler beim Laden der Gebiete für den Pruefling";
return false;
}
}
public function checkIfExists($pruefling_id, $gebiet_id)
{
$qry = "SELECT 1
FROM testtool.tbl_pruefling_gebiet
WHERE pruefling_id =".$this->db_add_param($pruefling_id, FHC_INTEGER) ." AND gebiet_id =".$this->db_add_param($gebiet_id, FHC_INTEGER);
if ($result = $this->db_query($qry))
{
if ($this->db_num_rows($result) > 0)
return true;
else
return false;
}
}
public function saveGebietForPruefling()
{
$qry = "INSERT INTO testtool.tbl_pruefling_gebiet
(pruefling_id, gebiet_id)
VALUES(".
$this->db_add_param($this->pruefling_id, FHC_INTEGER).','.
$this->db_add_param($this->gebiet_id, FHC_INTEGER).');';
if ($this->db_query($qry))
{
return true;
}
else
{
$this->errormsg = 'Fehler beim Speichern der Daten';
return true;
}
}
}
?>
+41
View File
@@ -0,0 +1,41 @@
html {
font-size: .75em;
}
nav.navbar.navbar-header,
nav.navbar.navbar-left-side {
font-size: 18px
}
nav.navbar.navbar-left-side {
padding-top: 8px;
padding-bottom: 8px;
}
/* Relative sizing inside navbar */
nav.navbar .nav-item {
font-size: 18px
}
nav.navbar .navbar-brand-icon {
font-size: 16px
}
nav.navbar .left-side-menu-link-entry {
font-size: 14px !important;
}
nav.navbar .nav-link {
font-size: 18px;
padding-top: 15px;
padding-bottom: 8px;
}
nav.navbar .dropdown-menu {
padding: 8px 0px;
}
nav.navbar .dropdown-item {
font-size: 16px;
padding: 4px 16px;
}
+80
View File
@@ -0,0 +1,80 @@
.fhc-pointer-events-none {
pointer-events: none;
}
.fhc-pointer-events-all {
pointer-events: all;
}
.fhc-cursor-pointer {
cursor: pointer;
}
.fhc-resize-vertical {
cursor: ns-resize;
}
.fhc-drag-handle:hover {
cursor: grab;
color: #000;
}
.fhc-w-fit {
width: fit-content;
}
.tabulator-group-level-0 {
background-color: #c6c6c6 !important;
display: flex;
align-items: center;
}
.tabulator-group-level-1 {
background-color: #f5f5f5 !important;
display: flex;
align-items: center;
}
.tabulator-cell {
background-color: transparent !important;
}
div[role="row"] {
background-color: white;
}
.v-enter-active {
transition: opacity 0.5s ease;
}
.v-leave-active {
transition: none;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.scrollable {
overflow: auto;
/* Firefox */
scrollbar-width: thin;
scrollbar-color: #888 transparent;
}
/* Chrome / Edge / Safari */
.scrollable::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.scrollable::-webkit-scrollbar-thumb {
background: #888;
border-radius: 999px;
}
.scrollable::-webkit-scrollbar-track {
background: transparent;
}
@@ -107,4 +107,9 @@
.gesperrtoption {
color: gray;
}
#onboardingRow
{
margin-bottom: 15px;
}
+127
View File
@@ -0,0 +1,127 @@
/**
* 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 {
getAllClassTimeValidityPeriods(params) {
return {
method: "get",
url: "/api/frontend/v1/ClassScheduleApi/getAllClassTimeValidityPeriods",
params,
};
},
getAllClassTimeValidityPeriodsPerOrganizationalUnit(organizationUnitId) {
return {
method: "get",
url: `/api/frontend/v1/ClassScheduleApi/getAllClassTimeValidityPeriodsPerOrganizationalUnit/${organizationUnitId}`,
};
},
getClassTimeValidityPeriod(classTimeSlotValidityPeriodId) {
return {
method: "get",
url: `/api/frontend/v1/ClassScheduleApi/getClassTimeValidityPeriod/${classTimeSlotValidityPeriodId}`,
};
},
createClassTimeSlotValidityPeriod(userId, params) {
return {
method: "post",
url: "api/frontend/v1/ClassScheduleApi/createClassTimeSlotValidityPeriod/",
params,
};
},
deleteClassTimeSlotValidityPeriod(userId, classTimeSlotValidityPeriodId) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/deleteClassTimeSlotValidityPeriod/${classTimeSlotValidityPeriodId}`,
};
},
getClassTimeSlotsForValidityPeriod(classTimeSlotValidityPeriodId) {
return {
method: "get",
url: `api/frontend/v1/ClassScheduleApi/getClassTimeSlotsForValidityPeriod/${classTimeSlotValidityPeriodId}`,
};
},
createClassTimeSlotsForValidityPeriod(
userId,
classTimeSlotValidityPeriodId,
params,
) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/createClassTimeSlotsForValidityPeriod/${classTimeSlotValidityPeriodId}`,
params,
};
},
updateClassTimeSlotsForValidityPeriod(
userId,
classTimeSlotValidityPeriodId,
params,
) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/updateClassTimeSlotsForValidityPeriod/${classTimeSlotValidityPeriodId}`,
params,
};
},
updateClassTimeSlotValidityPeriod(
userId,
classTimeSlotValidityPeriodId,
params,
) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/updateClassTimeSlotValidityPeriod/${classTimeSlotValidityPeriodId}`,
params,
};
},
deleteClassTimeSlotsForValidityPeriod(
userId,
classTimeSlotValidityPeriodId,
) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/deleteClassTimeSlotsForValidityPeriod/${classTimeSlotValidityPeriodId}`,
};
},
getAllClassScheduleTypes(queryParams) {
return {
method: "get",
url:
"/api/frontend/v1/ClassScheduleApi/getAllClassScheduleTypes?" +
new URLSearchParams(queryParams).toString(),
};
},
createClassTimeSlotType(params) {
return {
method: "post",
url: "api/frontend/v1/ClassScheduleApi/createClassTimeSlotType/",
params,
};
},
updateClassTimeSlotType(classTimeSlotTypeId, params) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/updateClassTimeSlotType/${encodeURIComponent(classTimeSlotTypeId)}`,
params,
};
},
deleteClassTimeSlotType(userId, classTimeSlotTypeId) {
return {
method: "post",
url: `api/frontend/v1/ClassScheduleApi/deleteClassTimeSlotType/${encodeURIComponent(classTimeSlotTypeId)}`,
};
},
};
+25
View File
@@ -0,0 +1,25 @@
/**
* 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 {
getAllClassroomHours() {
return {
method: "get",
url: "/api/frontend/v1/ClassroomHourApi/getAllClassroomHours",
};
}
};
@@ -0,0 +1,8 @@
export default {
getAllOrganizationalUnits() {
return {
method: "get",
url: "api/frontend/v1/organisation/organizationalUnitApi/getAllOrganizationalUnits",
};
},
}
+6
View File
@@ -16,6 +16,12 @@
*/
export default {
getAllDegreePrograms() {
return {
method: "get",
url: "/api/frontend/v1/Studgang/getAllDegreePrograms",
};
},
studiengangInformation() {
return {
method: 'get',
+36 -10
View File
@@ -1,11 +1,37 @@
export default {
getStudienplaeneBySemester(studiengang_kz, studiensemester_kurzbz, ausbildungssemester, orgform_kurzbz)
{
return {
method: 'get',
url: 'api/frontend/v1/organisation/studienplan/getBySemester',
params: { studiengang_kz, studiensemester_kurzbz, ausbildungssemester, orgform_kurzbz },
};
}
}
getAllStudyPlans() {
return {
method: "get",
url: "api/frontend/v1/organisation/studienplan/getAllStudyPlans",
};
},
getStudyPlansByOrganizationalUnitAndSemesterDates(organizationalUnitShortCode, startDate, endDate) {
return {
method: "get",
url: `api/frontend/v1/organisation/studienplan/getStudyPlansByOrganizationalUnitAndSemesterDates/${organizationalUnitShortCode}?filter[startDate]=${startDate}&filter[endDate]=${endDate}`,
};
},
getStudienplaeneBySemester(
studiengang_kz,
studiensemester_kurzbz,
ausbildungssemester,
orgform_kurzbz,
) {
return {
method: "get",
url: "api/frontend/v1/organisation/studienplan/getBySemester",
params: {
studiengang_kz,
studiensemester_kurzbz,
ausbildungssemester,
orgform_kurzbz,
},
};
},
getStudyPlan(studienplan_id) {
return {
method: "get",
url: `api/frontend/v1/organisation/studienplan/getStudyPlan/${studienplan_id}`,
};
},
};
+12
View File
@@ -30,6 +30,18 @@ export default {
params: { order, start }
};
},
getStudySemestersByOrganizationalUnitAndDates(organizationalUnitShortCode, startDate, endDate) {
return {
method: "get",
url: `api/frontend/v1/organisation/studiensemester/getStudySemestersByOrganizationalUnitAndDates/${organizationalUnitShortCode}?filter[startDate]=${startDate}&filter[endDate]=${endDate}`,
};
},
getStudySemestersByStudyPlanAndDates(studyPlanId, startDate, endDate) {
return {
method: "get",
url: `api/frontend/v1/organisation/studiensemester/getStudySemestersByStudyPlanAndDates?filter[studyPlanId]=${studyPlanId}&filter[startDate]=${startDate}&filter[endDate]=${endDate}`,
};
},
current() {
return {
method: 'get',
+14 -1
View File
@@ -19,6 +19,7 @@ import DeadlineOverview from "../../components/Cis/Abgabetool/DeadlineOverview.j
import Studium from "../../components/Cis/Studium/Studium.js";
import StgOrgLvPlan from "../../components/Cis/LvPlan/StgOrg.js";
import OtherLvPlan from "../../components/Cis/LvPlan/OtherLvPlan.js";
import Compat from "../../components/Cis/Compat.js";
import ApiRouteInfo from '../../api/factory/routeinfo.js';
import {capitalize} from "../../helpers/StringHelpers.js";
@@ -29,6 +30,18 @@ const isMobile = window.matchMedia("(max-width: 767px)").matches;
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(`/${ciPath}`),
routes: [
{
path: `/Cis/Compat/:mode(ci|legacy)/:path(.*)`,
name: 'Compat',
component: Compat,
props: (route) => {
return {
mode: route.params.mode,
path: route.params.path,
query_string: VueRouter.stringifyQuery(route.query)
};
}
},
{
path: `/Cis/Studium`,
name: 'Studium',
@@ -277,7 +290,7 @@ const app = Vue.createApp({
if (target && this.isInternalRoute(target.href)) {
const url = new URL(target.href)
const path = url.pathname
const path = url.pathname + url.search;
const base = this.$router.options.history.base
const route = path.replace(base, '') || '/'
+79
View File
@@ -0,0 +1,79 @@
/**
* Copyright (C) 2023 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import ClassScheduleOverview from "../../components/ClassSchedule/ClassScheduleOverview.js";
import ClassScheduleValidityPeriodOverview from "../../components/ClassSchedule/ClassScheduleValidityPeriodOverview.js";
import ClassScheduleOrgUnitGroupedValidityPeriodsOverview from "../../components/ClassSchedule/ClassScheduleOrgUnitGroupedValidityPeriodsOverview.js";
import FhcAlert from "../../plugins/FhcAlert.js";
import Phrasen from "../../plugins/Phrasen.js";
import FhcApi from "../../plugins/Api.js";
import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js';
import {capitalize} from "../../helpers/StringHelpers.js";
const ciPath =
FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, "") +
FHC_JS_DATA_STORAGE_OBJECT.ci_router;
const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes: [
{
name: "overview",
path: `/${ciPath}/lehre/ClassSchedule`,
component: ClassScheduleOverview,
},
{
name: "validityPeriodsOverviewByOrganizationUnit",
path: `/${ciPath}/lehre/ClassSchedule/validityPeriods/groupedOverviewByOrganizationUnit/:organizationalUnitShortCode`,
component: ClassScheduleOrgUnitGroupedValidityPeriodsOverview,
params: true,
},
{
name: "validityPeriodsOverviewByOrganizationUnitAndStudyPlan",
path: `/${ciPath}/lehre/ClassSchedule/validityPeriods/groupedOverviewByOrganizationUnit/:organizationalUnitShortCode/studyPlan/:studyPlanId`,
component: ClassScheduleOrgUnitGroupedValidityPeriodsOverview,
params: true,
},
{
name: "validityPeriodOverview",
path: `/${ciPath}/lehre/ClassSchedule/validityPeriods/:classTimeSlotValidityPeriodId`,
component: ClassScheduleValidityPeriodOverview,
params: true,
},
],
});
const app = Vue.createApp({
components: {
CoreNavigationCmpt,
ClassScheduleOverview,
ClassScheduleValidityPeriodOverview,
ClassScheduleOrgUnitGroupedValidityPeriodsOverview,
},
});
app.config.globalProperties.$capitalize = capitalize;
app
.use(router)
.use(primevue.config.default, { zIndex: { overlay: 9999 } })
.use(FhcAlert)
.use(Phrasen)
.use(FhcApi)
.mount("#main");
+93
View File
@@ -0,0 +1,93 @@
export default {
name: "Compat",
props: {
mode: { type: String, required: true },
path: { type: String, required: true },
query_string: { type: String, required: true, default: ''}
},
data: function() {
return {
lastLoadediFrameURL: '',
srcUrl: ''
};
},
computed: {
propsWatchHelper: function() {
return this.mode + '#' + this.path + '#' + this.query_string;
}
},
mounted: function() {
this.srcUrl = this.buildSrcUrl();
},
watch: {
propsWatchHelper: function() {
if(this.lastLoadediFrameURL === '') {
return;
}
let currentiFrameURL = this.$refs.compatiframe ? this.$refs.compatiframe.src : '';
let url = this.buildSrcUrl();
if(this.lastLoadediFrameURL !== url) {
this.srcUrl = url;
if(this.srcUrl === url) {
this.$refs.compatiframe.contentWindow.location.href = url;
}
}
}
},
methods: {
buildSrcUrl: function() {
let url = false;
switch(this.mode) {
case 'ci':
url = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/' + this.path;
break;
case 'legacy':
url = FHC_JS_DATA_STORAGE_OBJECT.app_root + this.path;
break;
default:
url = false;
}
if(this.query_string !== '' && url) {
url += '?' + this.query_string;
}
return url;
},
loadHandler: function() {
let iframe_href = this.$refs.compatiframe.contentWindow.location.href;
let ci_urlstart = FHC_JS_DATA_STORAGE_OBJECT.app_root + 'index.ci.php/';
let legacy_urlstart = FHC_JS_DATA_STORAGE_OBJECT.app_root;
let routerpath = null;
this.lastLoadediFrameURL = iframe_href;
if(iframe_href.startsWith(ci_urlstart)) {
routerpath = iframe_href.replace(
ci_urlstart, '/Cis/Compat/ci/');
} else if(iframe_href.startsWith(legacy_urlstart)) {
routerpath = iframe_href.replace(
legacy_urlstart, '/Cis/Compat/legacy/');
} else {
return;
}
if(this.$route.fullPath !== routerpath) {
this.$router.push(routerpath);
}
}
},
template: `
<div class="w-100">
<iframe
ref="compatiframe"
v-if="srcUrl"
:src="srcUrl"
@load="loadHandler"
style="width:100%; height:90vh; border:0; display:block;"
></iframe>
<div v-else class="alert alert-warning">Keine URL gefunden.</div>
</div>
`
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,121 @@
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import ClassScheduleValidityPeriodPreview from "./ClassScheduleValidityPeriodPreview.js";
export default {
name: "ClassScheduleOrgUnitGroupedValidityPeriodsOverview",
components: {
ClassScheduleValidityPeriodPreview,
},
data: () => {
return {
organizationalUnitShortCode: null,
studyPlanId: null,
classScheduleValidityPeriods: [],
classScheduleTimeSlots: [],
classTimeSlotTypes: [],
};
},
computed: {},
methods: {},
async created() {
this.organizationalUnitShortCode =
this.$route.params.organizationalUnitShortCode;
this.studyPlanId = parseInt(this.$route.params.studyPlanId);
let getAllClassTimeValidityPeriodsPerOrganizationalUnitResponse =
await this.$api.call(
ApiClassSchedule.getAllClassTimeValidityPeriodsPerOrganizationalUnit(
this.organizationalUnitShortCode,
),
);
if (
getAllClassTimeValidityPeriodsPerOrganizationalUnitResponse.meta
.status === "success"
) {
this.classScheduleValidityPeriods =
getAllClassTimeValidityPeriodsPerOrganizationalUnitResponse.data.filter(
(period) => {
if (!this.studyPlanId) return true;
return period.studienplan_id === this.studyPlanId;
},
);
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingClassScheduleValidityPeriods"),
);
}
for (let validityPeriod of this.classScheduleValidityPeriods) {
if (
this.studyPlanId &&
validityPeriod.studienplan_id !== this.studyPlanId
) {
continue;
}
let getClassTimeSlotsForValidityPeriodResponse = await this.$api.call(
ApiClassSchedule.getClassTimeSlotsForValidityPeriod(
validityPeriod.unterrichtszeitengueltigkeit_id,
),
);
if (
getClassTimeSlotsForValidityPeriodResponse.meta.status === "success"
) {
this.classScheduleTimeSlots = [
...this.classScheduleTimeSlots,
...getClassTimeSlotsForValidityPeriodResponse.data,
];
} else {
this.$fhcAlert.alertError(
this.$p.t(
"ui",
"errorFetchingClassScheduleTimeSlotForValidityPeriod",
),
);
}
}
let getAllClassTimeSlotTypesResponse = await this.$api.call(
ApiClassSchedule.getAllClassScheduleTypes("filter[aktiv]=true"),
);
if (getAllClassTimeSlotTypesResponse.meta.status === "success") {
this.classTimeSlotTypes = getAllClassTimeSlotTypesResponse.data.map(
(type) => {
let descriptions = [{
lang: "de",
value: type.bezeichnung_mehrsprachig[0] || "",
}, {
lang: "en",
value: type.bezeichnung_mehrsprachig[1] || "",
}];
return {
...type,
bezeichnung_mehrsprachig: descriptions,
};
},
);
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingClassScheduleTimeSlotTypes"),
);
}
},
template: /* html */ `
<div class="container mt-4">
<div class="my-2">
<h2 v-if="!this.studyPlanId">{{ $p.t("ui", "classScheduleOrgUnitGroupedValidityPeriodsOverviewTitle") }}</h2>
<h2 v-else>{{ $p.t("ui", "classScheduleStudyPlanGroupedValidityPeriodsOverviewTitle") }}</h2>
</div>
<class-schedule-validity-period-preview
v-for="validityPeriod in classScheduleValidityPeriods"
:key="validityPeriod.unterrichtszeitengueltigkeit"
:class-schedule-validity-period="validityPeriod"
:class-time-slots="classScheduleTimeSlots.filter(slot => slot.unterrichtszeitengueltigkeit_id === validityPeriod.unterrichtszeitengueltigkeit_id)"
:class-time-slot-types="classTimeSlotTypes"
class="shadow"
/>
</div>
`,
};
@@ -0,0 +1,588 @@
import { CoreFilterCmpt } from "../filter/Filter.js";
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import ApiStudienSemester from "../../../js/api/factory/studiensemester.js";
import ClassScheduleTypeModal from "./ClassScheduleTypeModal.js";
import ClassScheduleValidityPeriodModal from "./ClassScheduleValidityPeriodModal.js";
import CoreForm from "../Form/Form.js";
import FormInput from "../Form/Input.js";
import ApiOrganizationalUnit from "../../../js/api/factory/organizationalUnit.js";
export default {
name: "ClassScheduleOverview",
components: {
CoreFilterCmpt,
ClassScheduleTypeModal,
ClassScheduleValidityPeriodModal,
CoreForm,
FormInput,
},
props: {
permissions: Object,
},
provide() {
return {
cisRoot: this.cisRoot,
hasLehreUnterrichtszeitenTypWPermission:
this.permissions["lehre/unterrichtszeiten_typ_w"] || false,
};
},
watch: {
filterData: {
handler(newValue) {
this.$refs.classTimeSlotValidityPeriodsTable.tabulator.replaceData();
},
deep: true,
},
selectedSemester: {
handler(newValue) {
this.filterData.validityPeriodFrom = this.selectedSemester.start;
this.filterData.validityPeriodTo = this.selectedSemester.ende;
},
},
},
data: () => {
return {
phrasesLoaded: false,
editorParams: null,
classTimeSlotValidityPeriods: [],
classTimeSlotValidityPeriodId: null,
editedClassTimeSlotValidityPeriodId: null,
mondayClassTimeSlots: [],
isClassTimeSlotTypeModalVisible: false,
isClassTimeSlotValidityPeriodModalVisible: false,
organizationalUnits: [],
filteredOrganizationalUnits: [],
allSemesters: [],
filteredSemesters: [],
filterData: {
selectedOrganizationalUnit: null,
validityPeriodFrom: null,
validityPeriodTo: null,
},
selectedSemester: null,
};
},
computed: {
userLanguage() {
return Vue.ref(FHC_JS_DATA_STORAGE_OBJECT.user_language);
},
tabulatorOptions() {
const options = {
ajaxURL: "dummy",
ajaxRequestFunc: async () => {
return await this.getParsedClassTimeSlotValidityPeriodData();
},
ajaxResponse: (url, params, response) => response,
persistenceID: "class_schedule_validity_periods_table",
selectableRows: true,
maxHeight: "65vh",
columns: [
{
title: this.$capitalize(
this.$p.t("ui", "unterrichtszeitenGueltigkeitId"),
),
field: "unterrichtszeitengueltigkeit_id",
visible: false,
},
{
title: this.$capitalize(this.$p.t("ui", "zeitraum")),
formatter: (cell, formatterParams, onRendered) => {
const data = cell.getData();
const validFrom = new Date(data.gueltig_von).toLocaleDateString(
"de-AT",
{
day: "2-digit",
month: "2-digit",
year: "numeric",
},
);
data.gueltig_von;
const validTo = new Date(data.gueltig_bis).toLocaleDateString(
"de-AT",
{
day: "2-digit",
month: "2-digit",
year: "numeric",
},
);
return `${validFrom ? validFrom : "?"} - ${validTo ? validTo : "?"}`;
},
},
{
title: this.$capitalize(this.$p.t("global", "typ")),
field: "unterrichtszeiten_typ_bezeichnung_mehrsprachig",
width: 150,
formatter: (cell, formatterParams, onRendered) => {
if (
!cell.getData().unterrichtszeiten_typ_bezeichnung_mehrsprachig
) {
return "";
}
return this.userLanguage?.value === "English"
? cell.getData()
.unterrichtszeiten_typ_bezeichnung_mehrsprachig[1]
: cell.getData()
.unterrichtszeiten_typ_bezeichnung_mehrsprachig[0];
},
},
{
title: this.$capitalize(this.$p.t("lehre", "sem")),
field: "ausbildungssemester",
width: 150,
},
{
title: this.$capitalize(this.$p.t("global", "beschreibung")),
field: "anmerkung",
width: 150,
},
{
title: this.$capitalize(this.$p.t("global", "actions")),
field: "actions",
minWidth: 150,
maxWidth: 150,
formatter: (cell, formatterParams, onRendered) => {
let container = document.createElement("div");
container.className = "d-flex gap-2";
let button = document.createElement("button");
button = document.createElement("button");
button.className = "btn btn-outline-secondary btn-action";
button.innerHTML = '<i class="fa fa-eye"></i>';
button.title = this.$p.t(
"classSchedule",
"btn_showClassTimeSlotValidityPeriod",
);
button.addEventListener("click", (event) =>
this.$router.push({
name: "validityPeriodOverview",
params: {
classTimeSlotValidityPeriodId:
cell.getData().unterrichtszeitengueltigkeit_id,
},
}),
);
container.append(button);
button = document.createElement("button");
button.className = "btn btn-outline-secondary btn-action";
button.innerHTML = '<i class="fa fa-edit"></i>';
button.title = this.$p.t(
"classSchedule",
"btn_editClassTimeSlotValidityPeriod",
);
button.addEventListener("click", (event) =>
this.editClassTimeSlotValidityPeriod(
cell.getData().unterrichtszeitengueltigkeit_id,
),
);
container.append(button);
button = document.createElement("button");
button.className =
"btn btn-outline-secondary btn-action bg-danger";
button.innerHTML = '<i class="fa fa-xmark text-white"></i>';
button.title = this.$p.t(
"classSchedule",
"btn_deleteClassTimeSlotValidityPeriod",
);
button.addEventListener("click", () => {
let isDeletionConfirmed = confirm(
this.$p.t(
"ui",
"deleteClassTimeSlotValidityPeriodConfirmation",
),
);
if (!isDeletionConfirmed) return;
this.deleteClassTimeSlotValidityPeriod(
cell.getData().unterrichtszeitengueltigkeit_id,
);
});
container.append(button);
return container;
},
frozen: true,
},
],
groupBy: [
"organisationseinheit_bezeichnung_extended",
"studienplan_bezeichnung",
],
groupHeader: [
function (value, count, data) {
let container = document.createElement("span");
container.className =
"d-flex align-items-center justify-content-between";
container.style.display = "inline-block";
container.style.width = "100%";
let label = document.createElement("span");
label.textContent = value;
container.append(label);
let button = document.createElement("button");
button.className =
"btn btn-sm btn-outline-secondary fhc-btn-for-org-unit-grouping";
button.style.marginLeft = "10px";
button.innerHTML = '<i class="fa fa-eye"></i>';
button.dataset.organizationalUnitShortCode = data[0].oe_kurzbz;
container.append(button);
return container;
},
function (value, count, data) {
let container = document.createElement("span");
container.className =
"d-flex align-items-center justify-content-between";
container.style.display = "inline-block";
container.style.width = "100%";
let label = document.createElement("span");
label.textContent = value;
container.append(label);
let button = document.createElement("button");
button.className =
"btn btn-sm btn-outline-secondary fhc-btn-for-org-unit-and-study-plan-grouping";
button.style.marginLeft = "10px";
button.innerHTML = '<i class="fa fa-eye"></i>';
button.dataset.organizationalUnitShortCode = data[0].oe_kurzbz;
button.dataset.studyPlanId = data[0].studienplan_id;
container.append(button);
return container;
},
],
};
return options;
},
tabulatorEvents() {
const events = [
{
event: "renderComplete",
handler: async () => {
document
.querySelectorAll(".fhc-btn-for-org-unit-grouping")
.forEach((button) => {
button.addEventListener("click", (e) => {
let organizationalUnitShortCode =
button.dataset.organizationalUnitShortCode;
this.$router.push({
name: "validityPeriodsOverviewByOrganizationUnit",
params: {
organizationalUnitShortCode,
},
});
});
});
document
.querySelectorAll(".fhc-btn-for-org-unit-and-study-plan-grouping")
.forEach((button) => {
button.addEventListener("click", (e) => {
let organizationalUnitShortCode =
button.dataset.organizationalUnitShortCode;
let studyPlanId = button.dataset.studyPlanId;
this.$router.push({
name: "validityPeriodsOverviewByOrganizationUnitAndStudyPlan",
params: {
organizationalUnitShortCode,
studyPlanId,
},
});
});
});
},
},
];
return events;
},
dropdownParsedOrganizationalUnits() {
return this.organizationalUnits
.filter((unit) => unit.aktiv)
.map((unit) => {
return {
label: `[${unit.organisationseinheittyp_kurzbz}] ${unit.bezeichnung}`,
value: unit.oe_kurzbz,
};
})
.sort((a, b) => a.label.localeCompare(b.label));
},
dropdownParsedSemesters() {
return this.allSemesters.map((semester) => {
return {
label: semester.studiensemester_kurzbz,
value: semester.studiensemester_kurzbz,
start: semester.start,
ende: semester.ende,
};
});
},
hasLehreUnterrichtszeitenTypRPermission() {
return this.permissions["lehre/unterrichtszeiten_typ_r"] || false;
},
},
methods: {
async getParsedClassTimeSlotValidityPeriodData() {
let getAllClassTimeValidityPeriodsResponse = await this.$api.call(
ApiClassSchedule.getAllClassTimeValidityPeriods({
organizationalUnitShortCode:
this.filterData.selectedOrganizationalUnit?.value,
validityPeriodFrom: this.filterData.validityPeriodFrom
? moment(this.filterData.validityPeriodFrom).format("YYYY-MM-DD")
: null,
validityPeriodTo: this.filterData.validityPeriodTo
? moment(this.filterData.validityPeriodTo).format("YYYY-MM-DD")
: null,
}),
);
if (getAllClassTimeValidityPeriodsResponse.meta.status === "success") {
let generalWord = this.$p.t("ui", "general");
return getAllClassTimeValidityPeriodsResponse.data.map(
function (period) {
period.organisationseinheit_bezeichnung_extended =
"[" +
period.organisationseinheit_organisationseinheittyp_kurzbz +
"] " +
period.organisationseinheit_bezeichnung;
if (!period.studienplan_bezeichnung) {
period.studienplan_bezeichnung = generalWord;
}
return {
...period,
};
},
);
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingClassScheduleValidityPeriods"),
);
}
},
showClassTimeSlotValidityPeriodModal() {
this.isClassTimeSlotValidityPeriodModalVisible = true;
},
editClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId) {
this.editedClassTimeSlotValidityPeriodId = classTimeSlotValidityPeriodId;
},
deleteClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId) {
return this.$api
.call(
ApiClassSchedule.deleteClassTimeSlotValidityPeriod(
this.id,
classTimeSlotValidityPeriodId,
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successDelete"));
window.scrollTo(0, 0);
this.$refs.classTimeSlotValidityPeriodsTable.tabulator.replaceData();
})
.catch((error) => {
this.$fhcAlert.handleSystemError(error);
});
},
showClassTimeSlotTypeModal() {
this.isClassTimeSlotTypeModalVisible = true;
},
resetClassTimeSlotTypeModal() {
this.isClassTimeSlotTypeModalVisible = false;
},
resetClassTimeSlotValidityPeriodModal() {
this.isClassTimeSlotValidityPeriodModalVisible = false;
},
filterOrganizationalUnits(event) {
let defaultItem = {
label: this.$p.t("ui", "dropdownEmptyOption"),
value: null,
};
const query = event.query.toLowerCase();
if (!query) {
return (this.filteredOrganizationalUnits = [
defaultItem,
...this.dropdownParsedOrganizationalUnits,
]);
}
return (this.filteredOrganizationalUnits = [defaultItem]
.concat(this.dropdownParsedOrganizationalUnits)
.filter((unit) => {
return unit.label.toLowerCase().includes(query);
}));
},
filterSemesters(event) {
let defaultItem = {
label: this.$p.t("ui", "dropdownEmptyOption"),
value: null,
};
const query = event.query.toLowerCase();
if (!query) {
return (this.filteredSemesters = [
defaultItem,
...this.dropdownParsedSemesters,
]);
}
return (this.filteredSemesters = [defaultItem]
.concat(this.dropdownParsedSemesters)
.filter((semester) => {
return semester.label.toLowerCase().includes(query);
}));
},
},
async created() {
let getAllClassTimeValidityPeriodsResponse = await this.$api.call(
ApiClassSchedule.getAllClassTimeValidityPeriods(),
);
if (getAllClassTimeValidityPeriodsResponse.meta.status === "success") {
this.classTimeSlotValidityPeriods =
getAllClassTimeValidityPeriodsResponse.data;
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingClassScheduleValidityPeriods"),
);
}
let getAllOrganizationalUnitsResponse = await this.$api.call(
ApiOrganizationalUnit.getAllOrganizationalUnits(),
);
if (getAllOrganizationalUnitsResponse.meta.status === "success") {
this.organizationalUnits = getAllOrganizationalUnitsResponse.data.sort(
(a, b) => a.bezeichnung.localeCompare(b.bezeichnung),
);
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingOrganizationalUnits"),
);
}
let getAllSemestersResponse = await this.$api.call(
ApiStudienSemester.getAll("DESC"),
);
if (getAllSemestersResponse.meta.status === "success") {
this.allSemesters = getAllSemestersResponse.data;
} else {
this.$fhcAlert.alertError(this.$p.t("ui", "errorFetchingSemesters"));
}
},
mounted() {
this.$p
.loadCategory(["global", "lehre", "ui", "gruppenmanagement", "core"])
.then(() => {
this.phrasesLoaded = true;
});
},
template: /* html */ `
<div class="container mt-4">
<h1 class='mb-5'>{{ $p.t("ui", "classScheduleOverviewHeading") }}</h1>
<div class="row mb-3">
<div class="col d-flex justify-content-between">
<a class="btn btn-primary mb-3" @click="showClassTimeSlotValidityPeriodModal">{{$p.t('ui', 'addClassTimeSlotValidityPeriodButton')}}</a>
<a v-if="hasLehreUnterrichtszeitenTypRPermission" class="btn btn-secondary mb-3" @click="showClassTimeSlotTypeModal">{{$p.t('ui', 'addClassTimeSlotTypeButton')}}</a>
</div>
</div>
<div class="row mb-3">
<class-schedule-type-modal
v-if="hasLehreUnterrichtszeitenTypRPermission"
:isVisible="isClassTimeSlotTypeModalVisible"
@hideBsModal="resetClassTimeSlotTypeModal"
/>
<class-schedule-validity-period-modal
:isVisible="isClassTimeSlotValidityPeriodModalVisible"
:editedClassTimeSlotValidityPeriodId="editedClassTimeSlotValidityPeriodId"
@hideBsModal="() => { resetClassTimeSlotValidityPeriodModal(); editedClassTimeSlotValidityPeriodId = null; }"
@classTimeSlotValidityPeriodCreated="() => {
$refs.classTimeSlotValidityPeriodsTable.tabulator.replaceData();
resetClassTimeSlotValidityPeriodModal();
this.editedClassTimeSlotValidityPeriodId = null;
}"
@classTimeSlotValidityPeriodUpdated="() => {
$refs.classTimeSlotValidityPeriodsTable.tabulator.replaceData();
resetClassTimeSlotValidityPeriodModal();
this.editedClassTimeSlotValidityPeriodId = null;
}"
/>
<core-filter-cmpt
v-if="phrasesLoaded"
ref="classTimeSlotValidityPeriodsTable"
table-only
:side-menu="false"
:tabulator-options="tabulatorOptions"
:tabulator-events="tabulatorEvents"
>
<template #search>
<slot name="filterzuruecksetzen">
<core-form class="d-flex flex-column flex-md-row align-items-md-end gap-3">
<div>
<form-input
:label="$capitalize($p.t('lehre/organisationseinheit'))"
:suggestions="filteredOrganizationalUnits"
:optionValue="(option) => option.value"
:optionLabel="(option) => option.label"
@complete="filterOrganizationalUnits($event)"
@itemSelect="(option) => { filterData.selectedOrganizationalUnit = option.value; }"
type="autocomplete"
name="organizationalUnitShortCode"
dropdown
forceSelection
>
</form-input>
</div>
<div>
<form-input
v-model="selectedSemester"
:label="$capitalize($p.t('lehre/studiensemester'))"
:suggestions="filteredSemesters"
:optionValue="(option) => option.value"
:optionLabel="(option) => option.label"
@complete="filterSemesters($event)"
type="autocomplete"
name="selectedSemester"
dropdown
forceSelection
>
</form-input>
</div>
<div>
<div class="d-flex align-items-center gap-2">
<form-input
v-model="filterData.validityPeriodFrom"
:label="$p.t('ui', 'validityPeriod') + ' ' + $p.t('ui', 'von')"
:teleport="true"
:enable-time-picker="false"
type="datePicker"
name="validityPeriodFrom"
format="dd.MM.yyyy"
auto-apply
/>
<form-input
v-model="filterData.validityPeriodTo"
:label="$p.t('ui', 'validityPeriod') + ' ' + $p.t('global', 'bis')"
:teleport="true"
:enable-time-picker="false"
type="datePicker"
name="validityPeriodTo"
format="dd.MM.yyyy"
auto-apply
/>
</div>
</div>
</core-form>
</slot>
</template>
</core-filter-cmpt>
</div>
</div>
`,
};
@@ -0,0 +1,273 @@
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import BsModal from "../Bootstrap/Modal.js";
import CoreForm from "../Form/Form.js";
import FormInput from "../Form/Input.js";
export default {
name: "ClassScheduleTypeModal",
components: {
BsModal,
CoreForm,
FormInput,
},
inject: {
hasLehreUnterrichtszeitenTypWPermission:
"hasLehreUnterrichtszeitenTypWPermission",
},
props: {
isVisible: {
type: Boolean,
required: true,
},
},
emits: [
"hideBsModal",
"classTimeSlotTypeCreated",
"classTimeSlotTypeUpdated",
],
watch: {
isVisible(newValue) {
if (newValue) {
this.$refs.classScheduleTypeModal.show();
} else {
this.$refs.classScheduleTypeModal.hide();
}
},
},
data: () => {
return {
isFormVisible: false,
isEditInProgress: false,
editedClassScheduleType: null,
classTimeSlotTypeFormData: {
isActive: true,
shortCode: "",
descriptions: [
{ lang: "de", value: "" },
{ lang: "en", value: "" },
],
backgroundColor: "#ffffff",
},
classScheduleTypes: [],
};
},
methods: {
async getAllClassScheduleTypes() {
let getAllClassScheduleTypeResponse = await this.$api.call(
ApiClassSchedule.getAllClassScheduleTypes(),
);
if (getAllClassScheduleTypeResponse.meta.status === "success") {
this.classScheduleTypes = getAllClassScheduleTypeResponse.data.map(
(type) => {
let descriptions = [{
lang: "de",
value: type.bezeichnung_mehrsprachig[0] || "",
}, {
lang: "en",
value: type.bezeichnung_mehrsprachig[1] || "",
}];
return {
...type,
bezeichnung_mehrsprachig: descriptions,
};
},
);
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingClassScheduleTimeSlotTypes"),
);
}
},
createClassTimeSlotType() {
return this.$refs.classTimeSlotTypeForm
.call(
ApiClassSchedule.createClassTimeSlotType({
isActive: this.classTimeSlotTypeFormData.isActive,
shortCode: this.classTimeSlotTypeFormData.shortCode,
descriptions: this.classTimeSlotTypeFormData.descriptions,
backgroundColor: this.classTimeSlotTypeFormData.backgroundColor,
}),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.$emit("classTimeSlotTypeCreated");
this.getAllClassScheduleTypes();
this.resetClassTimeSlotTypeForm();
});
},
editClassTimeSlotType(classScheduleType) {
this.isEditInProgress = true;
this.isFormVisible = true;
this.editedClassScheduleType = classScheduleType;
this.classTimeSlotTypeFormData = {
isActive: classScheduleType.aktiv,
shortCode: classScheduleType.unterrichtszeitentyp_kurzbz,
descriptions: classScheduleType.bezeichnung_mehrsprachig.map(
(desc) => ({
lang: desc.lang,
value: desc.value,
}),
),
backgroundColor: classScheduleType.hintergrundfarbe || "#ffffff",
};
},
updateClassTimeSlotType() {
return this.$refs.classTimeSlotTypeForm
.call(
ApiClassSchedule.updateClassTimeSlotType(
this.classTimeSlotTypeFormData.shortCode,
{
isActive: this.classTimeSlotTypeFormData.isActive,
descriptions: this.classTimeSlotTypeFormData.descriptions,
backgroundColor: this.classTimeSlotTypeFormData.backgroundColor,
},
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.$emit("classTimeSlotTypeUpdated");
this.getAllClassScheduleTypes();
this.resetClassTimeSlotTypeForm();
});
},
deleteClassTimeSlotType(classScheduleTypeShortCode) {
let isConfirmed = confirm(
this.$p.t("ui", "confirmDeleteClassTimeSlotType"),
);
if (!isConfirmed) {
return Promise.resolve();
}
return this.$api
.call(
ApiClassSchedule.deleteClassTimeSlotType(
this.id,
classScheduleTypeShortCode,
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successDelete"));
window.scrollTo(0, 0);
this.getAllClassScheduleTypes();
})
.catch((error) => {
this.$fhcAlert.alertError(this.$p.t("ui", "errorFetchingClassScheduleTimeSlotTypes"));
});
},
showClassTimeSlotTypeForm() {
this.isFormVisible = true;
},
hideClassTimeSlotTypeForm() {
this.resetClassTimeSlotTypeForm();
},
resetClassTimeSlotTypeForm() {
this.isEditInProgress = false;
this.isFormVisible = false;
this.classTimeSlotTypeFormData = {
isActive: true,
shortCode: "",
descriptions: [
{ lang: "de", value: "" },
{ lang: "en", value: "" },
],
backgroundColor: "#ffffff",
};
},
},
async created() {
await this.getAllClassScheduleTypes();
},
template: /* html */ `
<bs-modal ref="classScheduleTypeModal" size="md" @hideBsModal="() => { $emit('hideBsModal'); resetClassTimeSlotTypeForm(); }">
<template #title>
<p v-if="hasLehreUnterrichtszeitenTypWPermission" class="fw-bold mt-3">{{$p.t('ui', 'editClassTimeSlotTypeModalTitle')}}</p>
<p v-else class="fw-bold mt-3">{{$p.t('ui', 'existingClassScheduleTypesLabel')}}</p>
</template>
<div v-if="!isFormVisible && hasLehreUnterrichtszeitenTypWPermission" class="row mb-3">
<div class="col d-flex justify-content-end">
<button @click="showClassTimeSlotTypeForm" type="button" class="btn btn-primary">{{$p.t('global', 'create')}}</button>
</div>
</div>
<core-form v-else-if="isFormVisible && hasLehreUnterrichtszeitenTypWPermission" ref="classTimeSlotTypeForm" class="row g-3 pb-3">
<div v-if="!isEditInProgress" class="row mb-3">
<form-input
v-model="classTimeSlotTypeFormData.shortCode"
:label="$p.t('ui/shortName')"
type="text"
name="shortCode"
>
</form-input>
</div>
<div v-else class="row mb-3">
<p class="mb-0"><span class="fw-bold">{{$p.t('ui/shortName')}}:</span> {{editedClassScheduleType.unterrichtszeitentyp_kurzbz}}</p>
</div>
<div class="row">
<form-input
v-for="description in classTimeSlotTypeFormData.descriptions"
:key="description.lang"
v-model="description.value"
:name="description.lang"
:label="$p.t('ui/description') + ' (' + description.lang + ')'"
type="textarea"
class="mb-3"
>
</form-input>
</div>
<div class="row mb-3">
<form-input
v-model="classTimeSlotTypeFormData.backgroundColor"
:label="$p.t('ui/backgroundColor')"
type="color"
name="backgroundColor"
>
</form-input>
</div>
<div class="row mb-3">
<div class="col d-flex align-items-center justify-content-end">
<form-input
v-model="classTimeSlotTypeFormData.isActive"
:label="$p.t('ui/isActive')"
type="checkbox"
name="isActive"
>
</form-input>
</div>
</div>
<div class="col d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary" @click="hideClassTimeSlotTypeForm">{{$p.t('ui', 'abbrechen')}}</button>
<button type="button" class="btn btn-primary" @click="isEditInProgress ? updateClassTimeSlotType() : createClassTimeSlotType()">{{$p.t('ui', 'speichern')}}</button>
</div>
</core-form>
<div class="row mb-3">
<p v-if="hasLehreUnterrichtszeitenTypWPermission" class="fw-bold mb-2">{{$p.t('ui', 'existingClassScheduleTypesLabel')}}</p>
<div
>
<div
v-for="classScheduleType in classScheduleTypes"
:key="classScheduleType.unterrichtszeitentyp_kurzbz"
:value="classScheduleType.unterrichtszeitentyp_kurzbz"
:class='{"opacity-50": !classScheduleType.aktiv}'
class=" shadow-sm p-2 mb-2 bg-body rounded"
>
<div class="d-flex justify-content-between align-items-center mb-2">
<span
:style="{ backgroundColor: classScheduleType.hintergrundfarbe }"
class="badge me-2 text-black">
{{classScheduleType.unterrichtszeitentyp_kurzbz}}
</span>
<div v-if="hasLehreUnterrichtszeitenTypWPermission" class="d-flex justify-content-between align-items-center gap-2">
<a href="#" @click.prevent="editClassTimeSlotType(classScheduleType)"><i class="fa fa-edit"></i></a>
<a href="#" @click.prevent="deleteClassTimeSlotType(classScheduleType.unterrichtszeitentyp_kurzbz)"><i class="fa fa-trash text-danger"></i></a>
</div>
</div>
<p>
{{classScheduleType.bezeichnung_mehrsprachig[0].value}} / {{classScheduleType.bezeichnung_mehrsprachig[1].value}}
</p>
</div>
</div>
</div>
</bs-modal>
`,
};
@@ -0,0 +1,170 @@
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import ApiClassroomHour from "../../../js/api/factory/classroomHour.js";
import ClassScheduleCalendarSelector from "./ClassScheduleCalendarSelector.js";
export default {
name: "ClassScheduleValidityPeriodForm",
components: {
ClassScheduleCalendarSelector,
},
props: {
classTimeSlotTypes: {
type: [Array, null],
required: true,
},
classTimeSlotValidityPeriod: {
type: Object,
required: true,
},
editedClassTimeSlots: {
type: Array,
required: false,
default: () => [],
},
classroomHours: {
type: Array,
required: false,
default: () => [],
},
},
emits: ["hideForm", "classTimeSlotsCreated", "classTimeSlotsEdited"],
watch: {
editedClassTimeSlots: {
handler(newVal) {
this.editedOverlays = newVal.map((slot) => {
return {
databaseId: slot.id,
id: slot.identifier,
weekday: slot.wochentag + 1,
type: slot.unterrichtszeitentyp_kurzbz,
startTime: slot.startTime,
endTime: slot.endTime,
};
});
},
immediate: true,
},
},
data: () => {
return {
editedOverlays: [],
classTimeSlots: [],
};
},
computed: {
areClassTimeSlotsEdited() {
return this.editedClassTimeSlots && this.editedClassTimeSlots.length > 0;
},
},
methods: {
hideForm() {
this.$emit("hideForm");
this.classTimeSlots = [];
},
addClassTimeSlotPerDay(day) {
this.classTimeSlots.push({
identifier: Math.random().toString(36).substr(2, 9),
wochentag: this.getWeekdayNumberFromDay(day),
startTime: "08:00",
endTime: "10:00",
classTimeSlotTypeShortcode: null,
});
},
removeClassTimeSlotPerDay(day, identifier) {
this.classTimeSlots = this.classTimeSlots.filter(
(slot) =>
!(
slot.wochentag === this.getWeekdayNumberFromDay(day) &&
slot.identifier === identifier
),
);
},
async createClassTimeSlots() {
let response = await this.$api.call(
ApiClassSchedule.createClassTimeSlotsForValidityPeriod(
this.id,
this.$props.classTimeSlotValidityPeriod
.unterrichtszeitengueltigkeit_id,
{
unterrichtszeiten: this.classTimeSlots.map((slot) => {
slot.wochentag = parseInt(slot.wochentag)- 1;
return slot;
}),
},
),
);
if (response.meta.status === "success") {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.classTimeSlots = [];
this.$emit("classTimeSlotsCreated");
} else {
this.$fhcAlert.handleSystemError(response.meta.message);
}
},
async updateClassTimeSlots() {
let response = await this.$api.call(
ApiClassSchedule.updateClassTimeSlotsForValidityPeriod(
this.id,
this.$props.classTimeSlotValidityPeriod
.unterrichtszeitengueltigkeit_id,
{
unterrichtszeiten: this.classTimeSlots.map((slot) => {
slot.wochentag = parseInt(slot.wochentag)- 1;
return slot;
}),
},
),
);
if (response.meta.status === "success") {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.classTimeSlots = [];
this.$emit("classTimeSlotsEdited");
} else {
this.$fhcAlert.handleSystemError(response.meta.message);
}
},
getWeekdayNumberFromDay(day) {
const days = {
monday: 1,
tuesday: 2,
wednesday: 3,
thursday: 4,
friday: 5,
saturday: 6,
sunday: 7,
};
return days[day.toLowerCase()] || null;
},
handleOverlaysChanged(newOverlays) {
this.classTimeSlots = newOverlays.map((overlay) => {
return {
id: overlay.databaseId || null,
identifier: overlay.id,
wochentag: overlay.weekday,
startTime: overlay.startingTimeSlot.split("-")[0],
endTime: overlay.endingTimeSlot.split("-")[1],
classTimeSlotTypeShortcode: overlay.type,
};
});
},
},
template: `
<div class='row'>
<div class='col-12'>
<class-schedule-calendar-selector
:classroom-hours="this.$props.classroomHours"
:default-class-time-slot-type="this.$props.classTimeSlotTypes.find(type => type.is_default)"
:class-time-slot-types="this.$props.classTimeSlotTypes"
:edited-overlays="this.editedOverlays"
@overlaysChanged="handleOverlaysChanged"
/>
</div>
<div class="col-12 d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary" @click="hideForm">{{$p.t('ui', 'abbrechen')}}</button>
<button v-if="!areClassTimeSlotsEdited" type="button" class="btn btn-primary" @click="createClassTimeSlots">{{$p.t('ui', 'speichern')}}</button>
<button v-else type="button" class="btn btn-primary" @click="updateClassTimeSlots">{{$p.t('ui', 'btnAktualisieren')}}</button>
</div>
</div>
`,
};
@@ -0,0 +1,577 @@
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import ApiStudienPlan from "../../../js/api/factory/studienplan.js";
import ApiStudienSemester from "../../../js/api/factory/studiensemester.js";
import ApiOrganizationalUnit from "../../../js/api/factory/organizationalUnit.js";
import BsModal from "../Bootstrap/Modal.js";
import CoreForm from "../Form/Form.js";
import FormInput from "../Form/Input.js";
import FormValidation from "../Form/Validation.js";
export default {
name: "ClassScheduleValidityPeriodModal",
components: {
BsModal,
CoreForm,
FormInput,
FormValidation,
},
props: {
isVisible: {
type: Boolean,
required: true,
},
editedClassTimeSlotValidityPeriodId: {
type: Number,
default: null,
},
},
emits: [
"hideBsModal",
"classTimeSlotValidityPeriodCreated",
"classTimeSlotValidityPeriodUpdated",
],
watch: {
isVisible(newValue) {
if (newValue) {
this.$refs.classTimeSlotValidityPeriodModal.show();
} else {
this.$refs.classTimeSlotValidityPeriodModal.hide();
}
},
editedClassTimeSlotValidityPeriodId(newValue) {
if (!newValue) return;
return this.$api
.call(ApiClassSchedule.getClassTimeValidityPeriod(newValue))
.then((response) => {
let validityPeriodData = response.data[0];
let parsedOrganizationalUnit = this.dropdownParsedOrganizationalUnits.find(
(unit) => unit.value === validityPeriodData.oe_kurzbz,
);
if (!parsedOrganizationalUnit) {
parsedOrganizationalUnit = {
value: validityPeriodData.oe_kurzbz,
label: validityPeriodData.oe_bezeichnung ,
};
}
this.editedClassTimeSlotValidityPeriod = validityPeriodData;
this.classTimeSlotValidityPeriodFormData = {
id: validityPeriodData.unterrichtszeitengueltigkeit_id,
organizationalUnit: parsedOrganizationalUnit,
studyPlanId: validityPeriodData.studienplan_id,
classTimeSlotTypeShortcode:
validityPeriodData.unterrichtszeitentyp_kurzbz,
validityPeriodFrom: validityPeriodData.gueltig_von,
validityPeriodTo: validityPeriodData.gueltig_bis,
semester: validityPeriodData.ausbildungssemester,
description: validityPeriodData.anmerkung,
};
this.$refs.classTimeSlotValidityPeriodModal.show();
})
.catch((error) => {
this.$fhcAlert.handleSystemError(error);
});
},
"classTimeSlotValidityPeriodFormData.organizationalUnit"() {
this.updateClassTimeSlotValidityPeriodFormDataWatcher();
},
"classTimeSlotValidityPeriodFormData.validityPeriodFrom"() {
this.updateClassTimeSlotValidityPeriodFormDataWatcher();
},
"classTimeSlotValidityPeriodFormData.validityPeriodTo"() {
this.updateClassTimeSlotValidityPeriodFormDataWatcher();
},
},
data: () => {
return {
editedValidityPeriod: null,
isFormVisible: false,
organizationalUnits: [],
filteredOrganizationalUnits: [],
studyPlans: [],
studySemesters: [],
studySemestersByNumber: [],
classTimeSlotTypes: [],
classTimeSlotValidityPeriodFormData: {
id: null,
organizationalUnit: null,
studyPlanId: null,
classTimeSlotTypeShortcode: null,
validityPeriodFrom: null,
validityPeriodTo: null,
semester: null,
description: null,
},
};
},
computed: {
isEditMode() {
return !!this.$props.editedClassTimeSlotValidityPeriodId;
},
isStudyPlanSelectDisabled() {
return (
!this.classTimeSlotValidityPeriodFormData.organizationalUnit
?.value ||
!this.classTimeSlotValidityPeriodFormData.validityPeriodFrom ||
!this.classTimeSlotValidityPeriodFormData.validityPeriodTo
);
},
formattedValidityPeriodFrom() {
if (!this.classTimeSlotValidityPeriodFormData.validityPeriodFrom)
return null;
return moment(this.classTimeSlotValidityPeriodFormData.validityPeriodFrom).format("YYYY-MM-DD");
},
formattedValidityPeriodTo() {
if (!this.classTimeSlotValidityPeriodFormData.validityPeriodTo)
return null;
return moment(this.classTimeSlotValidityPeriodFormData.validityPeriodTo).format("YYYY-MM-DD");
},
userLanguage() {
return Vue.ref(FHC_JS_DATA_STORAGE_OBJECT.user_language);
},
dropdownParsedOrganizationalUnits() {
return this.organizationalUnits
.filter((unit) => unit.aktiv)
.map((unit) => {
return {
label: `[${unit.organisationseinheittyp_kurzbz}] ${unit.bezeichnung}`,
value: unit.oe_kurzbz,
};
})
.sort((a, b) => a.label.localeCompare(b.label));
},
},
methods: {
updateClassTimeSlotValidityPeriodFormDataWatcher() {
if (
!this.classTimeSlotValidityPeriodFormData.organizationalUnit
?.value ||
!this.classTimeSlotValidityPeriodFormData.validityPeriodFrom ||
!this.classTimeSlotValidityPeriodFormData.validityPeriodTo
) {
this.classTimeSlotValidityPeriodFormData.studyPlanId = null;
return;
}
this.refetchFilterableOptions();
},
async refetchFilterableOptions() {
this.studyPlans = await this.getTargetedStudyPlans(
this.classTimeSlotValidityPeriodFormData.organizationalUnit?.value,
this.formattedValidityPeriodFrom,
this.formattedValidityPeriodTo,
);
if (this.isEditMode) {
let isStudyPlanStillValid = this.studyPlans.some(
(plan) =>
plan.studienplan_id ===
this.editedClassTimeSlotValidityPeriod.studienplan_id,
);
if (!isStudyPlanStillValid) {
let editedStudyPlan = await this.getStudyPlan(
this.editedClassTimeSlotValidityPeriod.studienplan_id,
);
if (editedStudyPlan) {
this.studyPlans.push(editedStudyPlan);
}
}
}
let studySemestersByDates =
await this.getStudySemestersByOrganizationalUnitAndDates(
this.classTimeSlotValidityPeriodFormData.organizationalUnit
?.value,
this.formattedValidityPeriodFrom,
this.formattedValidityPeriodTo,
);
let studySemesters = new Array(
...new Set(
studySemestersByDates
.map((s) => s.semester_numbers)
.flat()
.sort((a, b) => a - b),
),
);
this.studySemestersByNumber = studySemesters;
if (this.classTimeSlotValidityPeriodFormData.studyPlanId) {
let studySemestersByStudyProgramId =
await this.getStudySemestersByStudyPlanAndDates(
this.classTimeSlotValidityPeriodFormData.studyPlanId,
this.formattedValidityPeriodFrom,
this.formattedValidityPeriodTo,
);
studySemestersByStudyProgramId = new Array(
...new Set(
studySemestersByStudyProgramId
.map((s) => s.semester_numbers)
.flat()
.sort((a, b) => a - b),
),
);
studySemesters = studySemestersByStudyProgramId;
}
this.studySemestersByNumber = studySemesters;
},
async getTargetedStudyPlans(
organizationalUnitShortCode,
validityPeriodFrom,
validityPeriodTo,
) {
if (
!organizationalUnitShortCode ||
!validityPeriodFrom ||
!validityPeriodTo
) {
return [];
}
let getAllStudyPlansResponse = await this.$api.call(
ApiStudienPlan.getStudyPlansByOrganizationalUnitAndSemesterDates(
organizationalUnitShortCode,
validityPeriodFrom,
validityPeriodTo,
),
);
if (getAllStudyPlansResponse.meta.status !== "success") {
this.$fhcAlert.alertError(this.$p.t("ui", "errorFetchingStudyPlans"));
}
return getAllStudyPlansResponse.data?.length
? getAllStudyPlansResponse.data
: [];
},
async getStudyPlan(studienplan_id) {
if (!studienplan_id) {
return null;
}
let getStudyPlanResponse = await this.$api.call(
ApiStudienPlan.getStudyPlan(studienplan_id),
);
if (getStudyPlanResponse.meta.status !== "success") {
this.$fhcAlert.alertError(this.$p.t("ui", "errorFetchingStudyPlan"));
return null;
}
return getStudyPlanResponse.data ? getStudyPlanResponse.data : null;
},
async getStudySemestersByOrganizationalUnitAndDates(
organizationalUnitShortCode,
validityPeriodFrom,
validityPeriodTo,
) {
if (
!organizationalUnitShortCode ||
!validityPeriodFrom ||
!validityPeriodTo
) {
return [];
}
let getStudySemestersResponse = await this.$api.call(
ApiStudienSemester.getStudySemestersByOrganizationalUnitAndDates(
organizationalUnitShortCode,
validityPeriodFrom,
validityPeriodTo,
),
);
if (getStudySemestersResponse.meta.status !== "success") {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingSemesters"),
);
}
return getStudySemestersResponse.data?.length
? getStudySemestersResponse.data
: [];
},
async getStudySemestersByStudyPlanAndDates(
studyProgramId,
validityPeriodFrom,
validityPeriodTo,
) {
if (!studyProgramId || !validityPeriodFrom || !validityPeriodTo) {
return [];
}
let getStudySemestersResponse = await this.$api.call(
ApiStudienSemester.getStudySemestersByStudyPlanAndDates(
studyProgramId,
validityPeriodFrom,
validityPeriodTo,
),
);
if (getStudySemestersResponse.meta.status !== "success") {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingSemesters"),
);
}
return getStudySemestersResponse.data?.length
? getStudySemestersResponse.data
: [];
},
createClassTimeSlotValidityPeriod() {
return this.$refs.classTimeSlotValidityPeriodData
.call(
ApiClassSchedule.createClassTimeSlotValidityPeriod(this.id, {
...this.classTimeSlotValidityPeriodFormData,
organizationalUnitShortCode:
this.classTimeSlotValidityPeriodFormData.organizationalUnit
?.value,
}),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.$refs.classTimeSlotValidityPeriodModal.hide();
this.resetClassTimeSlotValidityPeriodModal();
window.scrollTo(0, 0);
this.$emit("classTimeSlotValidityPeriodCreated");
})
.catch((error) => {
this.$fhcAlert.handleSystemError(error);
});
},
updateClassTimeSlotValidityPeriod() {
return this.$refs.classTimeSlotValidityPeriodData
.call(
ApiClassSchedule.updateClassTimeSlotValidityPeriod(
this.id,
this.classTimeSlotValidityPeriodFormData.id,
{
...this.classTimeSlotValidityPeriodFormData,
organizationalUnitShortCode:
this.classTimeSlotValidityPeriodFormData.organizationalUnit
?.value,
},
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successSave"));
this.$refs.classTimeSlotValidityPeriodModal.hide();
this.resetClassTimeSlotValidityPeriodModal();
window.scrollTo(0, 0);
this.editedClassTimeSlotValidityPeriod = null;
this.$emit("classTimeSlotValidityPeriodUpdated");
})
.catch((error) => {
this.$fhcAlert.handleSystemError(error);
});
},
resetClassTimeSlotValidityPeriodModal() {
this.$refs.classTimeSlotValidityPeriodData?.clearValidation();
this.classTimeSlotValidityPeriodFormData = {
id: null,
organizationalUnit: null,
studyPlanId: null,
classTimeSlotTypeShortcode: null,
validityPeriodFrom: null,
validityPeriodTo: null,
semester: null,
description: null,
};
},
filterOrganizationalUnits(event) {
const query = event.query.toLowerCase();
if (!query) {
return (this.filteredOrganizationalUnits = [
...this.dropdownParsedOrganizationalUnits,
]);
}
return (this.filteredOrganizationalUnits =
this.dropdownParsedOrganizationalUnits.filter((unit) => {
return unit.label.toLowerCase().includes(query);
}));
},
getClassTimeSlotTypeLabel(classTimeSlotType) {
if (!classTimeSlotType) return "";
return this.userLanguage?.value === "English"
? classTimeSlotType.bezeichnung_mehrsprachig[1].value
: classTimeSlotType.bezeichnung_mehrsprachig[0].value;
},
},
async created() {
let getAllOrganizationalUnitsResponse = await this.$api.call(
ApiOrganizationalUnit.getAllOrganizationalUnits(),
);
if (getAllOrganizationalUnitsResponse.meta.status === "success") {
this.organizationalUnits = getAllOrganizationalUnitsResponse.data.sort(
(a, b) => a.bezeichnung.localeCompare(b.bezeichnung),
);
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingOrganizationalUnits"),
);
}
let getAllStudyPlansResponse = await this.$api.call(
ApiStudienPlan.getAllStudyPlans(),
);
if (getAllStudyPlansResponse.meta.status === "success") {
this.studyPlans = getAllStudyPlansResponse.data;
} else {
this.$fhcAlert.alertError(this.$p.t("ui", "errorFetchingStudyPlans"));
}
let getAllClassTimeSlotTypesResponse = await this.$api.call(
ApiClassSchedule.getAllClassScheduleTypes("filter[aktiv]=true"),
);
if (getAllClassTimeSlotTypesResponse.meta.status === "success") {
this.classTimeSlotTypes = getAllClassTimeSlotTypesResponse.data.map(
(type) => {
let descriptions = [{
lang: "de",
value: type.bezeichnung_mehrsprachig[0] || "",
}, {
lang: "en",
value: type.bezeichnung_mehrsprachig[1] || "",
}];
return {
...type,
bezeichnung_mehrsprachig: descriptions,
};
},
);
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingClassScheduleTimeSlotTypes"),
);
}
},
template: /*html*/ `
<bs-modal ref="classTimeSlotValidityPeriodModal" @hideBsModal="() => { $emit('hideBsModal'); resetClassTimeSlotValidityPeriodModal(); }" size="md">
<template #title>
<p v-if="!classTimeSlotValidityPeriodFormData.id" class="fw-bold mt-3">{{$p.t('ui', 'addClassTimeSlotValidityPeriodModalTitle')}}</p>
<p v-else class="fw-bold mt-3">{{$p.t('ui', 'editClassTimeSlotValidityPeriodModalTitle')}}</p>
</template>
<core-form class="row g-3" ref="classTimeSlotValidityPeriodData">
<form-validation />
<div class="row mb-3">
<form-input
v-model="classTimeSlotValidityPeriodFormData.organizationalUnit"
:label="$capitalize($p.t('lehre/organisationseinheit')) + ' *'"
:suggestions="filteredOrganizationalUnits"
:optionValue="(option) => option.value"
:optionLabel="(option) => option.label"
@complete="filterOrganizationalUnits"
dropdown
forceSelection
type="autocomplete"
name="organizationalUnitShortCode"
>
</form-input>
</div>
<div class="row mb-3">
<div class="col-12 mb-3">
<label>{{$p.t('ui', 'validityPeriod')}}</label>
</div>
<div class="col">
<form-input
v-model="classTimeSlotValidityPeriodFormData.validityPeriodFrom"
:label="$capitalize($p.t('ui/von') + ' *')"
:teleport="true"
:enable-time-picker="false"
type="datePicker"
name="validityPeriodFrom"
format="dd.MM.yyyy"
auto-apply
/>
</div>
<div class="col">
<form-input
v-model="classTimeSlotValidityPeriodFormData.validityPeriodTo"
:label="$capitalize($p.t('global/bis') + ' *')"
:teleport="true"
:enable-time-picker="false"
type="datePicker"
name="validityPeriodTo"
format="dd.MM.yyyy"
auto-apply
/>
</div>
</div>
<div class="row mb-3">
<form-input
v-model="classTimeSlotValidityPeriodFormData.studyPlanId"
:label="$capitalize($p.t('lehre/studienplan'))"
:disabled="isStudyPlanSelectDisabled"
type="select"
name="studyPlan"
>
<option :value="null"> - </option>
<option
v-for="studyPlan in studyPlans"
:key="studyPlan.studienplan_id"
:value="studyPlan.studienplan_id"
>
{{studyPlan.bezeichnung}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="select"
id="ausbildungssemester"
name="semester"
:disabled="isStudyPlanSelectDisabled"
:label="$capitalize($p.t('lehre', 'ausbildungssemester'))"
v-model="classTimeSlotValidityPeriodFormData.semester"
>
<option
v-for="studySemester in studySemestersByNumber"
:key="studySemester"
:value="studySemester"
>
{{studySemester}}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="select"
name="classTimeSlotType"
:label="$capitalize($p.t('ui/classTimeSlotType'))"
v-model="classTimeSlotValidityPeriodFormData.classTimeSlotTypeShortcode"
>
<option :value="null"> - </option>
<option
v-for="classTimeSlotType in classTimeSlotTypes"
:key="classTimeSlotType.unterrichtszeitentyp_kurzbz"
:value="classTimeSlotType.unterrichtszeitentyp_kurzbz"
>
{{ getClassTimeSlotTypeLabel(classTimeSlotType) }}
</option>
</form-input>
</div>
<div class="row mb-3">
<form-input
type="textarea"
name="beschreibung"
:label="$capitalize($p.t('global/beschreibung'))"
v-model="classTimeSlotValidityPeriodFormData.description"
>
</form-input>
</div>
</core-form>
<template #footer>
<button v-if="!classTimeSlotValidityPeriodFormData.id" type="button" class="btn btn-primary" @click="createClassTimeSlotValidityPeriod">{{$p.t('ui', 'speichern')}}</button>
<button v-else type="button" class="btn btn-primary" @click="updateClassTimeSlotValidityPeriod">{{$p.t('ui', 'btnAktualisieren')}}</button>
</template>
</bs-modal>
`,
};
@@ -0,0 +1,317 @@
import ApiClassSchedule from "../../../js/api/factory/classSchedule.js";
import ApiClassroomHour from "../../../js/api/factory/classroomHour.js";
import BsModal from "../Bootstrap/Modal.js";
import ClassScheduleValidityPeriodForm from "./ClassScheduleValidityPeriodForm.js";
import ClassScheduleValidityPeriodModal from "./ClassScheduleValidityPeriodModal.js";
import ClassScheduleCalendarSelector from "./ClassScheduleCalendarSelector.js";
export default {
name: "ClassScheduleValidityPeriodOverview",
components: {
BsModal,
ClassScheduleValidityPeriodForm,
ClassScheduleValidityPeriodModal,
ClassScheduleCalendarSelector,
},
data: () => {
return {
classroomHours: [],
isClassTimeSlotFormVisible: false,
classTimeSlotValidityPeriodId: null,
classTimeSlotValidityPeriod: null,
areClassTimeSlotsLoaded: false,
classTimeSlots: [],
classTimeSlotTypes: [],
editedClassTimeSlots: [],
isClassTimeSlotValidityPeriodModalVisible: false,
editedClassTimeSlotValidityPeriodId: null,
};
},
computed: {
classScheduleValidityPeriodStartDate() {
if (!this.classTimeSlotValidityPeriod) return null;
let dateParts = this.classTimeSlotValidityPeriod.gueltig_von
.split("-")
.reverse();
return dateParts.join(".");
},
classScheduleValidityPeriodEndDate() {
if (!this.classTimeSlotValidityPeriod) return null;
let dateParts = this.classTimeSlotValidityPeriod.gueltig_bis
.split("-")
.reverse();
return dateParts.join(".");
},
},
methods: {
async fetchClassTimeValidityPeriod() {
let getClassTimeValidityPeriodResponse = await this.$api.call(
ApiClassSchedule.getClassTimeValidityPeriod(
this.classTimeSlotValidityPeriodId,
),
);
if (getClassTimeValidityPeriodResponse.meta.status === "success") {
this.classTimeSlotValidityPeriod =
getClassTimeValidityPeriodResponse.data[0];
if (!this.classTimeSlotValidityPeriod) {
this.$fhcAlert.alertError(
this.$p.t("ui", "classTimeSlotValidityPeriodNotFound"),
);
this.$router.push({ name: "overview" });
}
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingClassScheduleValidityPeriod"),
);
}
},
async fetchClassTimeSlots() {
let getClassTimeSlotsForValidityPeriodResponse = await this.$api.call(
ApiClassSchedule.getClassTimeSlotsForValidityPeriod(
this.classTimeSlotValidityPeriodId,
),
);
if (
getClassTimeSlotsForValidityPeriodResponse.meta.status === "success"
) {
this.classTimeSlots = getClassTimeSlotsForValidityPeriodResponse.data;
} else {
this.$fhcAlert.alertError(
this.$p.t(
"ui",
"errorFetchingClassScheduleTimeSlotForValidityPeriod",
),
);
}
this.areClassTimeSlotsLoaded = true;
},
showClassTimeSlotForm() {
this.isClassTimeSlotFormVisible = true;
},
async editClassTimeSlotsForValidityPeriod() {
await this.fetchClassTimeSlots();
this.editedClassTimeSlots =
this.classTimeSlots.map((slot) => {
return {
...slot,
id: slot.unterrichtszeit_id,
startTime: slot.uhrzeit_von,
endTime: slot.uhrzeit_bis,
classTimeSlotTypeShortcode: slot.unterrichtszeitentyp_kurzbz,
};
}) || [];
this.showClassTimeSlotForm();
},
deleteClassTimeSlotsForValidityPeriod() {
let isDeletionConfirmed = confirm(
this.$p.t("ui", "confirmDeleteClassTimeSlotsForGroup"),
);
if (!isDeletionConfirmed) {
return;
}
return this.$api
.call(
ApiClassSchedule.deleteClassTimeSlotsForValidityPeriod(
this.id,
this.classTimeSlotValidityPeriodId,
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successDelete"));
this.classTimeSlots = [];
this.fetchClassTimeSlots();
})
.catch((error) => {
this.$fhcAlert.handleSystemError(error);
});
},
getClassTimeSlotType(classTimeSlot) {
let classTimeSlotType = this.classTimeSlotTypes.find(
(type) =>
type.unterrichtszeitentyp_kurzbz ===
classTimeSlot.unterrichtszeitentyp_kurzbz,
);
return classTimeSlotType;
},
getClassTimeSlotBackgroundColor(classTimeSlot) {
let classTimeSlotType = this.classTimeSlotTypes.find(
(type) =>
type.unterrichtszeitentyp_kurzbz ===
classTimeSlot.unterrichtszeitentyp_kurzbz,
);
return classTimeSlotType ? classTimeSlotType.hintergrundfarbe : "#fff";
},
editClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId) {
this.editedClassTimeSlotValidityPeriodId = classTimeSlotValidityPeriodId;
},
deleteClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId) {
let isDeletionConfirmed = confirm(
this.$p.t("ui", "deleteClassTimeSlotValidityPeriodConfirmation"),
);
if (!isDeletionConfirmed) return;
return this.$api
.call(
ApiClassSchedule.deleteClassTimeSlotValidityPeriod(
this.id,
classTimeSlotValidityPeriodId,
),
)
.then((response) => {
this.$fhcAlert.alertSuccess(this.$p.t("ui", "successDelete"));
this.$router.push({ name: "overview" });
})
.catch((error) => {
this.$fhcAlert.handleSystemError(error);
});
},
resetClassTimeSlotValidityPeriodModal() {
this.isClassTimeSlotValidityPeriodModalVisible = false;
},
},
async created() {
this.classTimeSlotValidityPeriodId =
this.$route.params.classTimeSlotValidityPeriodId;
await this.fetchClassTimeValidityPeriod();
let getAllClassTimeSlotTypesResponse = await this.$api.call(
ApiClassSchedule.getAllClassScheduleTypes("filter[aktiv]=true"),
);
if (getAllClassTimeSlotTypesResponse.meta.status === "success") {
this.classTimeSlotTypes = getAllClassTimeSlotTypesResponse.data.map(
(type) => {
let descriptions = [{
lang: "de",
value: type.bezeichnung_mehrsprachig[0] || "",
}, {
lang: "en",
value: type.bezeichnung_mehrsprachig[1] || "",
}];
return {
...type,
bezeichnung_mehrsprachig: descriptions,
};
},
);
} else {
this.$fhcAlert.alertError(
this.$p.t("ui", "errorFetchingClassScheduleTimeSlotTypes"),
);
}
let getAllClassroomHoursResponse = await this.$api.call(
ApiClassroomHour.getAllClassroomHours(),
);
if (getAllClassroomHoursResponse.meta.status === "success") {
this.classroomHours = getAllClassroomHoursResponse.data.map((hour) => {
return {
...hour,
beginn: hour.beginn.substring(0, 5),
ende: hour.ende.substring(0, 5),
};
});
} else {
this.$fhcAlert.alertError(this.$p.t("ui", "errorFetchingClassroomHours"));
}
this.fetchClassTimeSlots();
},
template: /* html */ `
<div class="container mt-4">
<div class='mb-5'>
<div class="d-flex align-items-center justify-content-between mb-2">
<div class="d-flex align-items-center gap-3">
<h1 class='m-0'>
{{ $p.t("ui", "classScheduleValidityPeriodOverviewHeading") }}
</h1>
<span class="m-0 badge bg-secondary">{{ classTimeSlotValidityPeriod?.orgform_kurzbz }}</span>
</div>
<div class="d-flex align-items-center gap-2">
<a
@click="editClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId)"
class="btn btn-link fs-3 p-0">
<i class="fa fa-edit"></i></a>
<a @click="deleteClassTimeSlotValidityPeriod(classTimeSlotValidityPeriodId)" class="btn btn-link text-danger fs-3 p-0"><i class="fa fa-trash"></i></a>
</div>
</div>
<h2>{{ classScheduleValidityPeriodStartDate }} - {{ classScheduleValidityPeriodEndDate }}</h2>
<h4 class="text-capitalize">{{ $p.t("lehre", "organisationseinheit") }}: {{ classTimeSlotValidityPeriod?.oe_bezeichnung }} ({{ classTimeSlotValidityPeriod?.oe_organisationseinheittyp_kurzbz }})</h4>
<h4 class="text-capitalize">{{ $p.t("lehre", "studienplan") }}: {{ classTimeSlotValidityPeriod?.studienplan_bezeichnung }}</h4>
<h5 class="text-capitalize">{{ $p.t("lehre", "ausbildungssemester") }}:
<span class="fw-normal">{{ classTimeSlotValidityPeriod?.ausbildungssemester }}</span>
</h5>
<h5 class="text-capitalize">{{ $p.t("global", "anmerkung") }}:
<span class="fw-normal">{{ classTimeSlotValidityPeriod?.anmerkung }}</span>
</h5>
</div>
<div>
<div v-if='!isClassTimeSlotFormVisible && !classTimeSlots.length && areClassTimeSlotsLoaded' class="col-12 d-flex justify-content-end">
<button type="button" class="btn btn-primary" @click="showClassTimeSlotForm">{{$p.t('ui', 'addClassTimeSlotButton')}}</button>
</div>
<class-schedule-validity-period-form
v-if="isClassTimeSlotFormVisible"
:class-time-slot-types="this.classTimeSlotTypes"
:classroom-hours="this.classroomHours.map(hour => hour.beginn + '-' + hour.ende)"
:class-time-slot-validity-period="classTimeSlotValidityPeriod"
:edited-class-time-slots="editedClassTimeSlots"
@classTimeSlotsCreated="() => { isClassTimeSlotFormVisible = false; this.areClassTimeSlotsLoaded = false; this.classTimeSlots = []; fetchClassTimeSlots(); this.editedClassTimeSlots = []; }"
@classTimeSlotsEdited="() => { isClassTimeSlotFormVisible = false; this.areClassTimeSlotsLoaded = false; this.classTimeSlots = []; fetchClassTimeSlots(); this.editedClassTimeSlots = []; }"
@hideForm="() => { isClassTimeSlotFormVisible = false; this.areClassTimeSlotsLoaded = false; this.classTimeSlots = []; fetchClassTimeSlots(); this.editedClassTimeSlots = []; }"
class="mb-4"
/>
<div v-if="!isClassTimeSlotFormVisible">
<h4>{{ $p.t("ui", "classScheduleValidityPeriodTimeSlots") }}</h4>
</div>
<transition>
<div v-if="classTimeSlots && Object.keys(classTimeSlots).length > 0 && !isClassTimeSlotFormVisible">
<div class="col-12 d-flex align-items-center justify-content-end gap-2">
<a class="ml-auto" @click="editClassTimeSlotsForValidityPeriod"><i class="fa fa-edit fs-5"></i></a>
<a class="ml-auto" @click="deleteClassTimeSlotsForValidityPeriod"><i class="fa fa-trash text-danger fs-5"></i></a>
</div>
<div class="row border-top rounded p-2 mt-4 mb-2 pt-1 pb-5">
<class-schedule-calendar-selector
:classroom-hours="this.classroomHours.map(hour => hour.beginn + '-' + hour.ende)"
:class-time-slot-types="this.classTimeSlotTypes"
:edited-overlays="classTimeSlots.map((slot) => {
return {
databaseId: slot.id,
id: slot.identifier,
weekday: parseInt(slot.wochentag) + 1,
type: slot.unterrichtszeitentyp_kurzbz,
startTime: slot.uhrzeit_von,
endTime: slot.uhrzeit_bis,
};
})"
:isPreviewMode="true"
/>
</div>
</div>
</transition>
<div v-if="!classTimeSlots || Object.keys(classTimeSlots).length === 0" class="d-flex align-items-center justify-content-center border rounded p-4 mt-4">
<p class="m-0">{{ $p.t("ui", "noClassScheduleValidityPeriodTimeSlotsFound") }}</p>
</div>
</div>
<class-schedule-validity-period-modal
:isVisible="isClassTimeSlotValidityPeriodModalVisible"
:editedClassTimeSlotValidityPeriodId="editedClassTimeSlotValidityPeriodId"
@hideBsModal="() => { resetClassTimeSlotValidityPeriodModal(); editedClassTimeSlotValidityPeriodId = null; }"
@classTimeSlotValidityPeriodUpdated="() => {
resetClassTimeSlotValidityPeriodModal();
this.editedClassTimeSlotValidityPeriodId = null;
fetchClassTimeValidityPeriod();
}"
/>
</div>
`,
};
@@ -0,0 +1,112 @@
import ClassScheduleCalendarSelector from "./ClassScheduleCalendarSelector.js";
import ApiClassroomHour from "../../../js/api/factory/classroomHour.js";
export default {
name: "ClassScheduleValidityPeriodPreview",
components: {
ClassScheduleCalendarSelector,
},
props: {
classScheduleValidityPeriod: {
type: Object,
required: true,
},
classTimeSlots: {
type: Array,
required: true,
},
classTimeSlotTypes: {
type: Array,
required: true,
},
},
data: () => {
return {
classRoomHours: [],
};
},
computed: {
classScheduleValidityPeriodStartDate() {
if (!this.$props.classScheduleValidityPeriod) return null;
let dateParts = this.$props.classScheduleValidityPeriod.gueltig_von
.split("-")
.reverse();
return dateParts.join(".");
},
classScheduleValidityPeriodEndDate() {
if (!this.$props.classScheduleValidityPeriod) return null;
let dateParts = this.$props.classScheduleValidityPeriod.gueltig_bis
.split("-")
.reverse();
return dateParts.join(".");
},
classScheduleValidityPeriodStudyPlan() {
return this.$props.classScheduleValidityPeriod.studienplan_bezeichnung;
},
},
methods: {
showClassScheduleValidityPeriod(classScheduleValidityPeriodId) {
this.$router.push({
name: "validityPeriodOverview",
params: {
classTimeSlotValidityPeriodId: classScheduleValidityPeriodId,
},
});
},
},
async created() {
let getAllClassroomHoursResponse = await this.$api.call(
ApiClassroomHour.getAllClassroomHours(),
);
if (getAllClassroomHoursResponse.meta.status === "success") {
this.classroomHours = getAllClassroomHoursResponse.data.map((hour) => {
return {
...hour,
beginn: hour.beginn.substring(0, 5),
ende: hour.ende.substring(0, 5),
};
});
} else {
this.$fhcAlert.alertError(this.$p.t("ui", "errorFetchingClassroomHours"));
}
},
template: /* html */ `
<div class="container mt-4">
<div class='py-3 d-flex align-items-center justify-content-between'>
<div>
<h2>{{ classScheduleValidityPeriodStartDate }} - {{ classScheduleValidityPeriodEndDate }}</h2>
<h4>{{ classScheduleValidityPeriodStudyPlan }}</h4>
</div>
<a
@click="showClassScheduleValidityPeriod($props.classScheduleValidityPeriod.unterrichtszeitengueltigkeit_id)"
class="ml-auto btn btn-link"
><i class="fa fa-eye fs-5"></i></a>
</div>
<div class='py-3 mb-4'>
<div v-if="$props.classTimeSlots.length > 0 && this.classroomHours?.length > 0">
<div class="row border-top rounded pt-1 pb-5">
<class-schedule-calendar-selector
:classroom-hours="this.classroomHours.map(hour => hour.beginn + '-' + hour.ende)"
:class-time-slot-types="this.classTimeSlotTypes"
:edited-overlays="$props.classTimeSlots.map((slot) => {
return {
databaseId: slot.id,
id: slot.identifier,
weekday: parseInt(slot.wochentag) + 1,
type: slot.unterrichtszeitentyp_kurzbz,
startTime: slot.uhrzeit_von,
endTime: slot.uhrzeit_bis,
};
})"
:isPreviewMode="true"
/>
</div>
</div>
<div v-else class="d-flex align-items-center justify-content-center border rounded p-2">
<p class="m-0">{{ $p.t("ui", "noClassScheduleValidityPeriodTimeSlotsFound") }}</p>
</div>
</div>
</div>
`,
};
@@ -542,10 +542,10 @@ export default {
<template v-if="typeHeader==='mitarbeiter'">
<div class="foto-container col-md-2 d-flex justify-content-start align-items-center w-30 pb-3 gap-3 mt-3 position-relative" style="max-height: 8rem; max-width: 6rem; overflow: hidden;">
<div class="foto-container d-flex flex-column align-items-center h-100 position-relative d-inline-block">
<img
class="d-block w-100 h-100 rounded"
style="height: 84px; object-fit: contain;"
class="d-block rounded"
style="height: 96px;"
alt="Profilbild"
:src="getFotoSrc(headerDataMa.foto)"
/>
@@ -558,15 +558,13 @@ export default {
<template v-if="fotoEditable">
<button
type="button"
class="fotoedit btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute start-0"
style="z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
class="fotoedit buttonleft btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute start-0"
@click="showDeleteModal(person_id)">
<i class="fa fa-xmark"></i>
</button>
<button
type="button"
class="fotoedit btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute end-0"
style="z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
class="fotoedit buttonright btn btn-outline-dark btn-sm d-flex justify-content-center align-items-center position-absolute end-0"
@click="showModal(person_id)">
<i class="fa fa-pen"></i>
</button>
+2 -1
View File
@@ -37,7 +37,8 @@ export default {
function onStart(evt) {
const value = el.dataset.fhcDraggableValue;
if (value) {
setTransferData(evt, JSON.parse(value), true);
let disableImage = binding.modifiers?.noImage === true;
setTransferData(evt, JSON.parse(value), !disableImage);
if (el.dataset.fhcEffectAllowed)
evt.dataTransfer.effectAllowed = el.dataset.fhcEffectAllowed;
+1 -1
View File
@@ -37,7 +37,7 @@ export function formatDate(d) {
// parameter is of type Date
if(d instanceof Date)
{
if (isNaN(date.valueOf())) {
if (isNaN(d.valueOf())) {
return 'N/A';
}
// if the date is an invalid string then creating a date from the string will fail and N/A is returned
+3
View File
@@ -31,6 +31,9 @@ const TYPE_DEFINITION = {
id: "prestudent_id",
dragIcon: "fa-solid fa-user-graduate text-muted"
},
calendar_selector_overlay: {
id: "calendar_selector_overlay_id",
},
verband: {
id: "gruppe_id",
dragIcon: "fa-solid fa-user-group text-muted"
+1
View File
@@ -75,6 +75,7 @@ var Rueckstellung = {
if (FHC_AjaxClient.hasData(data))
{
Rueckstellung.get(personid);
InfocenterDetails._refreshMessages();
InfocenterDetails._refreshLog()
}
else
+5 -1
View File
@@ -93,6 +93,7 @@ require_once('dbupdate_3.4/71399_dashboard_update_widget_paths.php');
require_once('dbupdate_3.4/71645_studvw_messagetab_ladezeit.php');
require_once('dbupdate_3.4/75469_le_optional_4_projektarbeit.php');
require_once('dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php');
require_once('dbupdate_3.4/76031_unterrichtszeiten_der_studiengänge.php');
require_once('dbupdate_3.4/70376_lohnguide.php');
require_once('dbupdate_3.4/75888_reihungstest_mehrfachdurchfuehrung.php');
require_once('dbupdate_3.4/76150_perm_other_lv_plan.php');
@@ -102,6 +103,8 @@ require_once('dbupdate_3.4/75959_StudVw_Automatische_Tags.php');
require_once('dbupdate_3.4/76160_lvv_favorites.php');
require_once('dbupdate_3.4/77080_tabulator_presets_table.php');
require_once('dbupdate_3.4/78292_gehaltsanpassungtyp.php');
require_once('dbupdate_3.4/54225_reihungstest_ablauf_fuer_quereinsteiger.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
@@ -253,7 +256,7 @@ $tabellen=array(
"hr.tbl_lohnguide_modellfunktion" => array("modellfunktion_kurzbz", "bezeichnung", "jobfamilie_kurzbz", "aktiv", "sort", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_lohnguide_modellstelle" => array("modellstelle_kurzbz", "bezeichnung", "grade", "modellfunktion_kurzbz", "aktiv", "sort", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_lohnguide_fachrichtung" => array("fachrichtung_kurzbz", "bezeichnung", "aktiv", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_vertragsbestandteil_lohnguide" => array("vertragsbestandteil_id", "stellenbezeichnung", "vordienstzeit", "fachrichtung_kurzbz", "modellstelle_kurzbz", "kommentar_person", "kommentar_modellstelle"),
"hr.tbl_vertragsbestandteil_lohnguide" => array("vertragsbestandteil_id", "stellenbezeichnung", "vordienstzeit", "fachrichtung_kurzbz", "modellstelle_kurzbz", "kommentar_person", "kommentar_modellstelle"),
"lehre.tbl_abschlussbeurteilung" => array("abschlussbeurteilung_kurzbz","bezeichnung","bezeichnung_english","sort"),
"lehre.tbl_abschlusspruefung" => array("abschlusspruefung_id","student_uid","vorsitz","pruefer1","pruefer2","pruefer3","abschlussbeurteilung_kurzbz","akadgrad_id","pruefungstyp_kurzbz","datum","uhrzeit","sponsion","anmerkung","updateamum","updatevon","insertamum","insertvon","ext_id","note","protokoll","endezeit","pruefungsantritt_kurzbz","freigabedatum"),
"lehre.tbl_abschlusspruefung_antritt" => array("pruefungsantritt_kurzbz","bezeichnung","bezeichnung_english","sort"),
@@ -421,6 +424,7 @@ $tabellen=array(
"testtool.tbl_pruefling_frage" => array("prueflingfrage_id","pruefling_id","frage_id","nummer","begintime","endtime"),
"testtool.tbl_frage_sprache" => array("frage_id","sprache","text","bild","audio","insertamum","insertvon","updateamum","updatevon"),
"testtool.tbl_vorschlag_sprache" => array("vorschlag_id","sprache","text","bild","audio","insertamum","insertvon","updateamum","updatevon"),
"testtool.tbl_pruefling_gebiet" => array("prueflinggebiet_id","pruefling_id","gebiet_id","insertamum"),
"system.tbl_app" => array("app"),
"system.tbl_appdaten" => array("appdaten_id","uid","app","appversion","version","bezeichnung","daten","freigabe","insertamum","insertvon","updateamum","updatevon"),
"system.tbl_cronjob" => array("cronjob_id","server_kurzbz","titel","beschreibung","file","last_execute","aktiv","running","jahr","monat","tag","wochentag","stunde","minute","standalone","reihenfolge","updateamum", "updatevon","insertamum","insertvon","variablen"),
@@ -0,0 +1,41 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
// Creates table public.tbl_notiz_typ if it doesn't exist and grants privileges
if (!$result = @$db->db_query('SELECT 1 FROM testtool.tbl_pruefling_gebiet LIMIT 1'))
{
$qry = 'CREATE TABLE testtool.tbl_pruefling_gebiet
(
prueflinggebiet_id integer NOT NULL,
pruefling_id integer NOT NULL,
gebiet_id integer NOT NULL,
insertamum timestamp DEFAULT now()
);
ALTER TABLE testtool.tbl_pruefling_gebiet OWNER TO fhcomplete;
ALTER TABLE testtool.tbl_pruefling_gebiet ADD CONSTRAINT pk_tbl_pruefling_gebiet PRIMARY KEY (prueflinggebiet_id);
ALTER TABLE testtool.tbl_pruefling_gebiet ADD CONSTRAINT fk_tbl_pruefling_gebiet_pruefling_id FOREIGN KEY (pruefling_id) REFERENCES testtool.tbl_pruefling(pruefling_id) ON UPDATE CASCADE ON DELETE RESTRICT;
ALTER TABLE testtool.tbl_pruefling_gebiet ADD CONSTRAINT fk_tbl_pruefling_gebiet_gebiet_id FOREIGN KEY (gebiet_id) REFERENCES testtool.tbl_gebiet(gebiet_id) ON UPDATE CASCADE ON DELETE RESTRICT;
CREATE SEQUENCE testtool.tbl_pruefling_gebiet_prueflinggebiet_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE testtool.tbl_pruefling_gebiet_prueflinggebiet_id_seq OWNER TO fhcomplete;
ALTER TABLE testtool.tbl_pruefling_gebiet ALTER COLUMN prueflinggebiet_id SET DEFAULT nextval(\'testtool.tbl_pruefling_gebiet_prueflinggebiet_id_seq\');
GRANT SELECT, UPDATE, INSERT, DELETE ON testtool.tbl_pruefling_gebiet TO web;
GRANT SELECT, UPDATE, INSERT, DELETE ON testtool.tbl_pruefling_gebiet TO vilesci;
GRANT SELECT, UPDATE ON testtool.tbl_pruefling_gebiet_prueflinggebiet_id_seq TO vilesci;
GRANT SELECT, UPDATE ON testtool.tbl_pruefling_gebiet_prueflinggebiet_id_seq TO web;
';
if (!$db->db_query($qry))
echo '<strong>public.tbl_pruefling_gebiet: '.$db->db_last_error().'</strong><br>';
else
echo '<br>public.tbl_pruefling_gebiet table created';
}
@@ -4,7 +4,7 @@ if (! defined('DB_NAME')) exit('No direct script access allowed');
//Add column entwicklungs_id to bis.tbl_entwicklungsteam
if(!@$db->db_query("SELECT taglib FROM public.tbl_notiz_typ LIMIT 1"))
{
$qry = 'ALTER TABLE public.tbl_notiz_typ ADD COLUMN taglib character varying(32);';
$qry = 'ALTER TABLE public.tbl_notiz_typ ADD COLUMN taglib character varying(255);';
if(!$db->db_query($qry))
echo '<strong> public.tbl_notiz_typ '.$db->db_last_error().'</strong><br>';
@@ -137,3 +137,30 @@ foreach ($tags as $tag) {
}
}
}
// increase length of taglib column if created with length < 255
$ctaglibsql = <<<EOSQL
select
*
from
information_schema.columns c
where
c.table_schema = 'public'
and c.table_name = 'tbl_notiz_typ'
and c.column_name = 'taglib'
and c.data_type = 'character varying'
and c.character_maximum_length < 255
EOSQL;
if($res = $db->db_query($ctaglibsql))
{
if($db->db_num_rows($res) > 0)
{
$qry = 'ALTER TABLE public.tbl_notiz_typ ALTER COLUMN taglib TYPE varchar(255) USING taglib::varchar(255)';
if(!$db->db_query($qry))
echo '<strong> public.tbl_notiz_typ '.$db->db_last_error().'</strong><br>';
else
echo '<br>public.tbl_notiz_typ: Spalte taglib Datentyp zu varchar(255) geaendert.';
}
}
@@ -0,0 +1,293 @@
<?php
if($result = @$db->db_query("SELECT 1 FROM system.tbl_berechtigung WHERE berechtigung_kurzbz='lehre/unterrichtszeiten_gk' LIMIT 1"))
{
if($db->db_num_rows($result)==0)
{
$qry = "
INSERT INTO system.tbl_berechtigung(berechtigung_kurzbz, beschreibung) VALUES
('lehre/unterrichtszeiten_gk','Unterrichtszeiten Gültigkeit')
";
if(!$db->db_query($qry))
echo '<strong>system.tbl_berechtigung '.$db->db_last_error().'</strong><br>';
else
echo ' system.tbl_berechtigung: lehre/unterrichtszeiten_gk permissions inserted<br>';
}
}
if($result = @$db->db_query("SELECT 1 FROM system.tbl_berechtigung WHERE berechtigung_kurzbz='lehre/unterrichtszeiten_typ' LIMIT 1"))
{
if($db->db_num_rows($result)==0)
{
$qry = "
INSERT INTO system.tbl_berechtigung(berechtigung_kurzbz, beschreibung) VALUES
('lehre/unterrichtszeiten_typ','Unterrichtszeiten Typ')
";
if(!$db->db_query($qry))
echo '<strong>system.tbl_berechtigung '.$db->db_last_error().'</strong><br>';
else
echo ' system.tbl_berechtigung: lehre/unterrichtszeiten_typ permissions inserted<br>';
}
}
if(!$result = @$db->db_query("SELECT 1 FROM lehre.tbl_unterrichtszeiten LIMIT 1"))
{
$qry = '
CREATE TABLE lehre.tbl_unterrichtszeiten (
"unterrichtszeit_id" INTEGER NOT NULL,
"wochentag" INTEGER NOT NULL,
"uhrzeit_von" TIME NOT NULL,
"uhrzeit_bis" TIME NOT NULL,
"unterrichtszeitentyp_kurzbz" VARCHAR(32) NOT NULL,
"unterrichtszeitengueltigkeit_id" INTEGER NOT NULL,
"insertamum" TIMESTAMP WITH TIME ZONE DEFAULT now(),
"insertvon" VARCHAR(32),
"updateamum" TIMESTAMP WITH TIME ZONE DEFAULT now(),
"updatevon" VARCHAR(32),
CONSTRAINT pk_unterrichtszeit_id PRIMARY KEY("unterrichtszeit_id")
);';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten: '.$db->db_last_error().'</strong><br>';
else
echo '<br>lehre.tbl_unterrichtszeiten table created';
$db->db_query('CREATE SEQUENCE IF NOT EXISTS lehre.seq_tbl_unterrichtszeiten_unterrichtszeit_id
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;');
$db->db_query("ALTER TABLE lehre.tbl_unterrichtszeiten ALTER COLUMN unterrichtszeit_id SET DEFAULT nextval('lehre.seq_tbl_unterrichtszeiten_unterrichtszeit_id');");
$qry = 'GRANT SELECT ON TABLE lehre.tbl_unterrichtszeiten TO web;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>web</strong> on lehre.tbl_unterrichtszeiten';
$qry = 'GRANT USAGE ON lehre.seq_tbl_unterrichtszeiten_unterrichtszeit_id TO web;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>web</strong> on lehre.tbl_unterrichtszeiten';
$qry = 'GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE lehre.tbl_unterrichtszeiten TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_unterrichtszeiten';
$qry = 'GRANT USAGE, INSERT ON lehre.seq_tbl_unterrichtszeiten_unterrichtszeit_id TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_unterrichtszeiten';
$qry = 'GRANT USAGE, UPDATE ON lehre.seq_tbl_unterrichtszeiten_unterrichtszeit_id TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_unterrichtszeiten';
}
if(!$result = @$db->db_query("SELECT 1 FROM lehre.tbl_unterrichtszeiten_typ LIMIT 1"))
{
$qry = '
CREATE TABLE lehre.tbl_unterrichtszeiten_typ (
"unterrichtszeitentyp_kurzbz" VARCHAR(32) NOT NULL,
"bezeichnung_mehrsprachig" TEXT[] NOT NULL,
"aktiv" BOOLEAN DEFAULT true,
"hintergrundfarbe" VARCHAR(7),
"is_default" BOOLEAN DEFAULT false,
"insertamum" TIMESTAMP WITH TIME ZONE DEFAULT now(),
"insertvon" VARCHAR(32),
"updateamum" TIMESTAMP WITH TIME ZONE DEFAULT now(),
"updatevon" VARCHAR(32),
CONSTRAINT pk_unterrichtszeitentyp_kurzbz PRIMARY KEY("unterrichtszeitentyp_kurzbz")
);';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_typ: '.$db->db_last_error().'</strong><br>';
else
echo '<br>lehre.tbl_unterrichtszeiten_typ table created';
$db->db_query('CREATE SEQUENCE IF NOT EXISTS lehre.seq_tbl_unterrichtszeiten_typ_unterrichtszeitentyp_kurzbz
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;');
$db->db_query("ALTER TABLE lehre.tbl_unterrichtszeiten_typ ALTER COLUMN unterrichtszeitentyp_kurzbz SET DEFAULT nextval('lehre.seq_tbl_unterrichtszeiten_typ_unterrichtszeitentyp_kurzbz');");
$qry = 'GRANT SELECT ON TABLE lehre.tbl_unterrichtszeiten_typ TO web;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_typ: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>web</strong> on lehre.tbl_unterrichtszeiten_typ';
$qry = 'GRANT USAGE ON lehre.seq_tbl_unterrichtszeiten_typ_unterrichtszeitentyp_kurzbz TO web;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_typ: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>web</strong> on lehre.tbl_unterrichtszeiten_typ';
$qry = 'GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE lehre.tbl_unterrichtszeiten_typ TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_typ: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_unterrichtszeiten_typ';
$qry = 'GRANT USAGE, INSERT ON lehre.seq_tbl_unterrichtszeiten_typ_unterrichtszeitentyp_kurzbz TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_typ: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_unterrichtszeiten_typ';
$qry = 'GRANT USAGE, UPDATE ON lehre.seq_tbl_unterrichtszeiten_typ_unterrichtszeitentyp_kurzbz TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_typ: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_unterrichtszeiten_typ';
$qry = "
INSERT INTO lehre.tbl_unterrichtszeiten_typ (unterrichtszeitentyp_kurzbz, bezeichnung_mehrsprachig, aktiv, hintergrundfarbe, is_default) VALUES
('unterrichtszeiten', ARRAY['Unterrichtszeiten', 'Teaching Times'], 't', '#FFFFFF', 'f'),
('vorlesungen', ARRAY['Vorlesung', 'Lecture'], 't', '#FF8A8A', 't'),
('backuptage', ARRAY['Übung', 'Exercise'], 't', '#8AFF8A', 'f'),
('ausgleichswochen', ARRAY['Ausgleichswochen', 'Compensation Weeks'], 't', '#8A8AFF', 'f'),
('prüfungswochen', ARRAY['Prüfungswochen', 'Exam Weeks'], 't', '#FFFF8A', 'f');
";
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_typ seeders: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Seeded lehre.tbl_unterrichtszeiten_typ with main class time slot types';
}
if(!$result = @$db->db_query("SELECT 1 FROM lehre.tbl_unterrichtszeiten_gueltigkeit LIMIT 1"))
{
$qry = '
CREATE TABLE lehre.tbl_unterrichtszeiten_gueltigkeit (
"unterrichtszeitengueltigkeit_id" BIGINT NOT NULL,
"gueltig_von" DATE NOT NULL,
"gueltig_bis" DATE NOT NULL,
"oe_kurzbz" VARCHAR(32) NOT NULL,
"ausbildungssemester" SMALLINT,
"anmerkung" TEXT,
"unterrichtszeitentyp_kurzbz" VARCHAR(32),
"studienplan_id" INTEGER,
"insertamum" TIMESTAMP WITH TIME ZONE DEFAULT now(),
"insertvon" VARCHAR(32),
"updateamum" TIMESTAMP WITH TIME ZONE DEFAULT now(),
"updatevon" VARCHAR(32),
CONSTRAINT pk_unterrichtszeitengueltigkeit_id PRIMARY KEY("unterrichtszeitengueltigkeit_id")
);';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_gueltigkeit: '.$db->db_last_error().'</strong><br>';
else
echo '<br>lehre.tbl_unterrichtszeiten_gueltigkeit table created';
$db->db_query('CREATE SEQUENCE IF NOT EXISTS lehre.seq_tbl_unterrichtszeiten_gueltigkeit_unterrichtszeitengueltigkeit_id
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;');
$db->db_query("ALTER TABLE lehre.tbl_unterrichtszeiten_gueltigkeit ALTER COLUMN unterrichtszeitengueltigkeit_id SET DEFAULT nextval('lehre.seq_tbl_unterrichtszeiten_gueltigkeit_unterrichtszeitengueltigkeit_id');");
$qry = 'GRANT SELECT ON TABLE lehre.tbl_unterrichtszeiten_gueltigkeit TO web;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_gueltigkeit: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>web</strong> on lehre.tbl_unterrichtszeiten_gueltigkeit';
$qry = 'GRANT USAGE ON lehre.seq_tbl_unterrichtszeiten_gueltigkeit_unterrichtszeitengueltigkeit_id TO web;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_gueltigkeit: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>web</strong> on lehre.tbl_unterrichtszeiten_gueltigkeit';
$qry = 'GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE lehre.tbl_unterrichtszeiten_gueltigkeit TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_gueltigkeit: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_unterrichtszeiten_gueltigkeit';
$qry = 'GRANT USAGE, INSERT ON lehre.seq_tbl_unterrichtszeiten_gueltigkeit_unterrichtszeitengueltigkeit_id TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_gueltigkeit: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_unterrichtszeiten_gueltigkeit';
$qry = 'GRANT USAGE, UPDATE ON lehre.seq_tbl_unterrichtszeiten_gueltigkeit_unterrichtszeitengueltigkeit_id TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_gueltigkeit: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_unterrichtszeiten_gueltigkeit';
}
$result = $db->db_query("SELECT constraint_name FROM information_schema.table_constraints
WHERE table_name='tbl_unterrichtszeiten' AND constraint_type='FOREIGN KEY' AND constraint_name='fk_unterrichtszeitentyp_kurzbz'");
if($db->db_num_rows($result)==0)
{
$qry = "ALTER TABLE lehre.tbl_unterrichtszeiten ADD CONSTRAINT fk_unterrichtszeitentyp_kurzbz FOREIGN KEY(unterrichtszeitentyp_kurzbz) REFERENCES lehre.tbl_unterrichtszeiten_typ(unterrichtszeitentyp_kurzbz);";
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Added foreign key constraint fk_unterrichtszeitentyp_kurzbz to lehre.tbl_unterrichtszeiten';
}
$result = $db->db_query("SELECT constraint_name FROM information_schema.table_constraints
WHERE table_name='tbl_unterrichtszeiten' AND constraint_type='FOREIGN KEY' AND constraint_name='fk_unterrichtszeitengueltigkeit_id'");
if($db->db_num_rows($result)==0)
{
$qry = "ALTER TABLE lehre.tbl_unterrichtszeiten ADD CONSTRAINT fk_unterrichtszeitengueltigkeit_id FOREIGN KEY(unterrichtszeitengueltigkeit_id) REFERENCES lehre.tbl_unterrichtszeiten_gueltigkeit(unterrichtszeitengueltigkeit_id);";
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Added foreign key constraint fk_unterrichtszeitengueltigkeit_id to lehre.tbl_unterrichtszeiten';
}
$result = $db->db_query("SELECT constraint_name FROM information_schema.table_constraints
WHERE table_name='tbl_unterrichtszeiten_gueltigkeit' AND constraint_type='FOREIGN KEY' AND constraint_name='fk_unterrichtszeitentyp_kurzbz'");
if($db->db_num_rows($result)==0)
{
$qry = "ALTER TABLE lehre.tbl_unterrichtszeiten_gueltigkeit ADD CONSTRAINT fk_unterrichtszeitentyp_kurzbz FOREIGN KEY(unterrichtszeitentyp_kurzbz) REFERENCES lehre.tbl_unterrichtszeiten_typ(unterrichtszeitentyp_kurzbz);";
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_gueltigkeit: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Added foreign key constraint fk_unterrichtszeitentyp_kurzbz to lehre.tbl_unterrichtszeiten_gueltigkeit';
}
$result = $db->db_query("SELECT constraint_name FROM information_schema.table_constraints
WHERE table_name='tbl_unterrichtszeiten_gueltigkeit' AND constraint_type='FOREIGN KEY' AND constraint_name='fk_studienplan_id'");
if($db->db_num_rows($result)==0)
{
$qry = "ALTER TABLE lehre.tbl_unterrichtszeiten_gueltigkeit ADD CONSTRAINT fk_studienplan_id FOREIGN KEY(studienplan_id) REFERENCES lehre.tbl_studienplan(studienplan_id);";
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_gueltigkeit: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Added foreign key constraint fk_studienplan_id to lehre.tbl_unterrichtszeiten_gueltigkeit';
}
$result = $db->db_query("SELECT constraint_name FROM information_schema.table_constraints
WHERE table_name='tbl_unterrichtszeiten_gueltigkeit' AND constraint_type='FOREIGN KEY' AND constraint_name='fk_oe_kurzbz'");
if($db->db_num_rows($result)==0)
{
$qry = "ALTER TABLE lehre.tbl_unterrichtszeiten_gueltigkeit ADD CONSTRAINT fk_oe_kurzbz FOREIGN KEY(oe_kurzbz) REFERENCES public.tbl_organisationseinheit(oe_kurzbz);";
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_unterrichtszeiten_gueltigkeit: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Added foreign key constraint fk_oe_kurzbz to lehre.tbl_unterrichtszeiten_gueltigkeit';
}
?>
File diff suppressed because it is too large Load Diff
+64 -6
View File
@@ -251,6 +251,31 @@ if ($deleteSingleResult)
exit();
}
$qry = "SELECT * FROM testtool.tbl_pruefling_gebiet
WHERE pruefling_id=" . $db->db_add_param($pruefling->pruefling_id) . " AND gebiet_id=" . $db->db_add_param($_POST['gebiet_id']) . "";
if ($db->db_query($qry))
{
while ($row = $db->db_fetch_object())
{
$undo .= " INSERT INTO testtool.tbl_pruefling_gebiet(prueflinggebiet_id,pruefling_id,gebiet_id, insertamum) VALUES (" .
$db->db_add_param($row->prueflinggebiet_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->pruefling_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->gebiet_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->insertamum) . ');';
}
}
else
{
$db->db_query('ROLLBACK');
echo json_encode(array(
'status' => 'fehler',
'msg' => 'Fehler beim Erstellen des UNDO Befehls fuer testtool.tbl_pruefling_gebiet'
));
exit();
}
//Antworten loeschen
$qry = "DELETE FROM testtool.tbl_pruefling_frage where pruefling_id=" . $db->db_add_param($pruefling->pruefling_id, FHC_INTEGER) . " AND
frage_id IN (SELECT frage_id FROM testtool.tbl_frage WHERE gebiet_id=" . $db->db_add_param($_POST['gebiet_id']) . ");
@@ -258,7 +283,9 @@ if ($deleteSingleResult)
DELETE FROM testtool.tbl_antwort
WHERE pruefling_id=" . $db->db_add_param($pruefling->pruefling_id) . " AND
vorschlag_id IN (SELECT vorschlag_id FROM testtool.tbl_vorschlag WHERE frage_id IN
(SELECT frage_id FROM testtool.tbl_frage WHERE gebiet_id=" . $db->db_add_param($_POST['gebiet_id']) . "));";
(SELECT frage_id FROM testtool.tbl_frage WHERE gebiet_id=" . $db->db_add_param($_POST['gebiet_id']) . "));
DELETE FROM testtool.tbl_pruefling_gebiet where pruefling_id=" . $db->db_add_param($pruefling->pruefling_id, FHC_INTEGER) . " AND gebiet_id=" . $db->db_add_param($_POST['gebiet_id']) . ";";
if ($result = $db->db_query($qry))
{
@@ -388,9 +415,35 @@ if ($deleteAllResults)
exit();
}
$qry = "SELECT * FROM testtool.tbl_pruefling_gebiet WHERE pruefling_id=" . $db->db_add_param($pruefling->pruefling_id, FHC_INTEGER) . ";
";
if ($db->db_query($qry))
{
while ($row = $db->db_fetch_object())
{
$undo .= " INSERT INTO testtool.tbl_pruefling_gebiet(prueflinggebiet_id,pruefling_id,gebiet_id,insertamum) VALUES (" .
$db->db_add_param($row->prueflinggebiet_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->pruefling_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->gebiet_id, FHC_INTEGER) . ', ' .
$db->db_add_param($row->insertamum) . ');';
}
}
else
{
$db->db_query('ROLLBACK');
echo json_encode(array(
'status' => 'fehler',
'msg' => 'Fehler beim Erstellen des UNDO Befehls fuer testtool.tbl_pruefling_gebiet'
));
exit();
}
//Antworten loeschen
$qry = " DELETE FROM testtool.tbl_pruefling_frage where pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";
DELETE FROM testtool.tbl_antwort WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";";
DELETE FROM testtool.tbl_antwort where pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";
DELETE FROM testtool.tbl_pruefling_gebiet WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";";
if ($result = $db->db_query($qry))
{
@@ -528,8 +581,10 @@ if ($rtprueflingEntSperren)
while ($row = $db->db_fetch_object($result))
$pruefling_ids[] = $row->pruefling_id;
$art = filter_input(INPUT_POST, 'art', FILTER_VALIDATE_BOOLEAN);
$qry = "UPDATE testtool.tbl_pruefling
SET gesperrt =" . $db->db_add_param($_POST['art'], 'BOOLEAN') . "
SET gesperrt =" . $db->db_add_param($art, FHC_BOOLEAN) . "
WHERE pruefling_id IN (" . $db->db_implode4SQL($pruefling_ids) . ")";
$resultSperre = $db->db_query($qry);
@@ -537,7 +592,7 @@ if ($rtprueflingEntSperren)
if ($resultSperre)
{
$msg = $_POST['art'] === 'false' ? 'Pruefling wurde gesperrt' : 'Pruefling wurde freigeschaltet';
$msg = !$art ? 'Pruefling wurde gesperrt' : 'Pruefling wurde freigeschaltet';
echo json_encode(array(
'status' => 'ok',
'msg' => $msg));
@@ -645,12 +700,14 @@ if ($rtFreischalten)
if (isset($_POST['reihungstest_id']) && is_numeric($_POST['reihungstest_id'])
&& isset($_POST['art']))
{
$qry = "UPDATE public.tbl_reihungstest SET freigeschaltet=" . $db->db_add_param($_POST['art'], 'BOOLEAN') . "
$art = filter_input(INPUT_POST, 'art', FILTER_VALIDATE_BOOLEAN);
$qry = "UPDATE public.tbl_reihungstest SET freigeschaltet=" . $db->db_add_param($art, FHC_BOOLEAN) . "
WHERE reihungstest_id=" . $db->db_add_param($_POST['reihungstest_id']) . ";";
if ($result = $db->db_query($qry))
{
$msg = $_POST['art'] === 'false' ? 'Reihungstest wurde gesperrt' : 'Reihungstest wurde freigeschaltet';
$msg = !$art ? 'Reihungstest wurde gesperrt' : 'Reihungstest wurde freigeschaltet';
echo json_encode(array(
'status' => 'ok',
'msg' => $msg));
@@ -993,6 +1050,7 @@ if ($punkteUebertragen)
{
$setRTPunkte->new = false;
$setRTPunkte->punkte = $rtpunkte;
$setRTPunkte->teilgenommen = true;
$setRTPunkte->updateamum = date('Y-m-d H:i:s');
$setRTPunkte->updatevon = $user;
@@ -402,7 +402,10 @@ if(isset($_POST['deleteteilgebiet']))
DELETE FROM testtool.tbl_antwort
WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id)." AND
vorschlag_id IN (SELECT vorschlag_id FROM testtool.tbl_vorschlag WHERE frage_id IN
(SELECT frage_id FROM testtool.tbl_frage WHERE gebiet_id=".$db->db_add_param($_POST['gebiet'])."));";
(SELECT frage_id FROM testtool.tbl_frage WHERE gebiet_id=".$db->db_add_param($_POST['gebiet'])."));
DELETE FROM testtool.tbl_pruefling_gebiet
WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id)." AND gebiet_id=".$db->db_add_param($_POST['gebiet']).";";
if($result = $db->db_query($qry))
{
@@ -497,7 +500,8 @@ if(isset($_POST['delete_all']))
}
//Gebiet loeschen
$qry = "DELETE FROM testtool.tbl_pruefling_frage where pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";
DELETE FROM testtool.tbl_antwort WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";";
DELETE FROM testtool.tbl_antwort WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";
DELETE FROM testtool.tbl_pruefling_gebiet WHERE pruefling_id=".$db->db_add_param($pruefling->pruefling_id).";";
if($result = $db->db_query($qry))
{