diff --git a/application/controllers/Tempus.php b/application/controllers/Tempus.php
index 139f0b690..175a9ffa4 100644
--- a/application/controllers/Tempus.php
+++ b/application/controllers/Tempus.php
@@ -16,25 +16,45 @@ class Tempus extends Auth_Controller
// Load Libraries
$this->load->library('VariableLib', ['uid' => getAuthUID()]);
-
+
// Load Config
$this->load->config('calendar');
}
- /**
- * @return void
- */
- public function _remap()
+ public function index()
{
+ $this->_loadView();
+ }
+ public function sync()
+ {
+ $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
+ $this->load->model('organisation/Studienplan_model', 'StudienplanModel');
+ $this->load->model('organisation/Studiengang_model', 'StudiengangModel');
+ $this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
+ $this->load->model('ressource/Kalenderstatus_model', 'KalenderStatusModel');
+
+ $this->StudiengangModel->addOrder('typ');
+ $this->StudiengangModel->addOrder('kurzbz');
+ $this->StudiengangModel->addJoin('tbl_organisationseinheit', 'oe_kurzbz', 'LEFT');
+ $organisationen = getData($this->StudiengangModel->loadWhere(array('tbl_studiengang.aktiv' => 'true')));
+
+ $this->StudiensemesterModel->addOrder('start', 'DESC');
+ $studiensemestern = getData($this->StudiensemesterModel->load());
+
+ $this->_loadView(array('organisationen' => $organisationen, 'studiensemestern' => $studiensemestern));
+ }
+
+ private function _loadView($extraVariables = [])
+ {
$this->load->view('Tempus', [
'permissions' => [
'admin' => $this->permissionlib->isBerechtigt('admin')
],
- 'variables' => [
+ 'variables' => array_merge([
'semester_aktuell' => $this->variablelib->getVar('semester_aktuell'),
'timezone' => $this->config->item('timezone')
- ]
+ ], $extraVariables)
]);
}
-}
+}
\ No newline at end of file
diff --git a/application/controllers/api/frontend/v1/tempus/Coursepicker.php b/application/controllers/api/frontend/v1/tempus/Coursepicker.php
index 686b39023..b8f9b9269 100644
--- a/application/controllers/api/frontend/v1/tempus/Coursepicker.php
+++ b/application/controllers/api/frontend/v1/tempus/Coursepicker.php
@@ -8,7 +8,6 @@ class Coursepicker extends FHCAPI_Controller
public function __construct()
{
parent::__construct([
- 'search' => self::PERM_LOGGED,
'getByStg' => self::PERM_LOGGED
]);
@@ -21,77 +20,6 @@ class Coursepicker extends FHCAPI_Controller
$this->loadPhrases(['ui']);
}
-
- public function search()
- {
- $query = $this->input->get('query');
- if (is_null($query))
- $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL);
-
- $query_words = explode(' ', $query);
-
- //TODO Where weiter anpassen z.B. Fachbereich
- $this->_ci->LehreinheitModel->addSelect('tbl_lehreinheit.lehreinheit_id,
- tbl_lehreinheit.lvnr,
- tbl_lehreinheit.lehrfach_id,
- lehrfach.kurzbz AS lehrfach,
- lehrfach.bezeichnung AS lehrfach_bez,
- lehrfach.farbe AS lehrfach_farbe,
- tbl_lehreinheit.lehrform_kurzbz AS lehrform,
- lema.mitarbeiter_uid AS lektor_uid,
- tbl_mitarbeiter.kurzbz AS lektor,
- tbl_studiengang.studiengang_kz,
- upper(tbl_studiengang.typ::character varying::text || tbl_studiengang.kurzbz::text) AS studiengang,
- lvb.semester,
- lvb.verband,
- lvb.gruppe,
- lvb.gruppe_kurzbz,
- tbl_lehreinheit.raumtyp,
- tbl_lehreinheit.raumtypalternativ,
- tbl_lehreinheit.stundenblockung,
- tbl_lehreinheit.wochenrythmus,
- lema.semesterstunden,
- lema.planstunden,
- tbl_lehreinheit.start_kw,
- tbl_lehreinheit.anmerkung,
- tbl_lehreinheit.studiensemester_kurzbz');
-
- $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehreinheitmitarbeiter lema', 'tbl_lehreinheit.lehreinheit_id = lema.lehreinheit_id');
- $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehreinheitgruppe lvb', 'tbl_lehreinheit.lehreinheit_id = lvb.lehreinheit_id');
- $this->_ci->LehreinheitModel->addJoin('public.tbl_studiengang', 'lvb.studiengang_kz = tbl_studiengang.studiengang_kz');
- $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrveranstaltung lehrfach', 'tbl_lehreinheit.lehrfach_id = lehrfach.lehrveranstaltung_id');
- $this->_ci->LehreinheitModel->addJoin('public.tbl_mitarbeiter', 'lema.mitarbeiter_uid = tbl_mitarbeiter.mitarbeiter_uid');
- $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrform', 'tbl_lehrform.lehrform_kurzbz = tbl_lehreinheit.lehrform_kurzbz');
-
- $this->_ci->MitarbeiterModel->db->group_start();
-
- foreach ($query_words as $word)
- {
- $this->_ci->LehreinheitModel->db->group_start();
- $this->_ci->LehreinheitModel->db->where('lema.mitarbeiter_uid ILIKE', "%" . $word . "%");
- $this->_ci->LehreinheitModel->db->or_where('lvb.gruppe_kurzbz ILIKE', "%" . $word . "%");
- $this->_ci->LehreinheitModel->db->or_where('tbl_studiengang.kurzbzlang ILIKE', "%" . $word . "%");
- $this->_ci->LehreinheitModel->db->or_where('lvb.verband ILIKE', "%" . $word . "%");
- $this->_ci->LehreinheitModel->db->or_where('lvb.gruppe ILIKE', "%" . $word . "%");
- $this->_ci->LehreinheitModel->db->or_where('lehrfach.bezeichnung ILIKE', "%" . $word . "%");
-
- if (is_numeric($word))
- {
- $this->_ci->LehreinheitModel->db->or_where('tbl_studiengang.studiengang_kz', $word);
- $this->_ci->LehreinheitModel->db->or_where('lvb.semester', $word);
- }
- $this->_ci->LehreinheitModel->db->group_end();
-
- }
- $this->_ci->LehreinheitModel->db->group_end();
- $this->_ci->LehreinheitModel->db->where('tbl_lehreinheit.studiensemester_kurzbz = \'SS2025\'');
- $this->_ci->LehreinheitModel->db->where(array('tbl_lehrform.verplanen' => true));
-
- $result = $this->_ci->LehreinheitModel->load();
-
- $this->terminateWithSuccess(hasData($result) ? getData($result) : array());
- }
-
public function getByStg()
{
$this->_ci->form_validation->set_data($_POST);
diff --git a/application/controllers/api/frontend/v1/tempus/Kalender.php b/application/controllers/api/frontend/v1/tempus/Kalender.php
index 75b8b02f7..833d0c0aa 100644
--- a/application/controllers/api/frontend/v1/tempus/Kalender.php
+++ b/application/controllers/api/frontend/v1/tempus/Kalender.php
@@ -41,6 +41,7 @@ class Kalender extends FHCAPI_Controller
$this->_ci->load->library('LogLib');
$this->_ci->load->library('form_validation');
$this->_ci->load->library('KalenderLib');
+ $this->_ci->load->library('KalenderSyncLib');
$this->_ci->load->library('RaumvorschlagLib');
$this->loadPhrases([
'ui'
@@ -273,7 +274,7 @@ class Kalender extends FHCAPI_Controller
public function sync()
{
- $result = $this->_ci->kalenderlib->sync();
+ $result = $this->_ci->kalendersynclib->sync();
$this->terminateWithSuccess(getData($result));
}
public function syncToLecturer()
diff --git a/application/controllers/api/frontend/v1/tempus/KalenderSync.php b/application/controllers/api/frontend/v1/tempus/KalenderSync.php
new file mode 100644
index 000000000..ed5b7b145
--- /dev/null
+++ b/application/controllers/api/frontend/v1/tempus/KalenderSync.php
@@ -0,0 +1,298 @@
+ ['admin:r', 'assistenz:r'],
+ 'loadSync' => ['admin:r', 'assistenz:r'],
+ 'add' => ['admin:r', 'assistenz:r'],
+ 'start' => ['admin:r', 'assistenz:r'],
+ 'delete' => ['admin:r', 'assistenz:r'],
+ 'updateSync' => ['admin:r', 'assistenz:r'],
+ 'getStudienplan' => ['admin:r', 'assistenz:r'],
+ 'getSyncStatus' => ['admin:r', 'assistenz:r'],
+ ]);
+
+ $this->loadPhrases([
+ 'lehre',
+ ]);
+
+ $this->_ci = &get_instance();
+ $this->_ci->load->library('form_validation');
+ $this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel');
+ $this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
+ $this->_ci->load->model('ressource/Kalendersyncstatus_model', 'KalendersyncstatusModel');
+ $this->_ci->load->model('ressource/Kalenderstatus_model', 'KalenderStatusModel');
+ $this->_ci->load->library('KalenderSyncLib');
+ }
+
+ public function getSyncs()
+ {
+ $this->_ci->form_validation->set_data($_GET);
+ $this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required");
+
+ if($this->_ci->form_validation->run() === FALSE)
+ $this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
+ $studiensemester_kurzbz = $this->_ci->input->get('studiensemester_kurzbz', TRUE);
+
+ $language = getUserLanguage() == 'German' ? 0 : 1;
+
+ $this->_ci->KalenderStatusModel->addSelect('tbl_kalender_syncstatus.*, array_to_json(bezeichnung_mehrsprachig::varchar[])->>' . $language .' AS sync_status_kurzbz, tbl_organisationseinheit.bezeichnung as oebezeichnung, tbl_studienplan.bezeichnung as studienplanbezeichnung');
+ $this->_ci->KalenderStatusModel->addJoin('lehre.tbl_kalender_status', 'tbl_kalender_syncstatus.sync_status_kurzbz = tbl_kalender_status.status_kurzbz');
+ $this->_ci->KalenderStatusModel->addJoin('public.tbl_organisationseinheit', 'tbl_kalender_syncstatus.oe_kurzbz = tbl_organisationseinheit.oe_kurzbz');
+ $this->_ci->KalenderStatusModel->addJoin('lehre.tbl_studienplan', 'tbl_kalender_syncstatus.studienplan_id = tbl_studienplan.studienplan_id', 'LEFT');
+
+ $data = $this->_ci->KalendersyncstatusModel->loadWhere(array('studiensemester_kurzbz' => $studiensemester_kurzbz));
+
+ if (isError($data))
+ $this->terminateWithError(getError($data));
+
+ $this->terminateWithSuccess(getData($data));
+ }
+
+ public function delete()
+ {
+ $this->load->library('form_validation');
+
+ $this->form_validation->set_rules('kalender_syncstatus_id', 'kalender_syncstatus_id', 'required');
+
+ if (!$this->form_validation->run())
+ $this->terminateWithValidationErrors($this->form_validation->error_array());
+
+ $kalender_syncstatus_id = $this->input->post('kalender_syncstatus_id');
+
+ $result = $this->_ci->KalendersyncstatusModel->load($kalender_syncstatus_id);
+
+ if (isError($result))
+ $this->terminateWithError(getError($result));
+
+ if (!hasData($result))
+ $this->terminateWithError($this->p->t('lehre', 'errorDeleting'));
+
+ $del_result = $this->_ci->KalendersyncstatusModel->delete(getData($result)[0]->kalender_syncstatus_id);
+
+ if (isError($del_result))
+ $this->terminateWithError(getError($del_result));
+ $this->terminateWithSuccess($del_result);
+ }
+
+ public function loadSync()
+ {
+ $this->_ci->form_validation->set_data($_GET);
+ $this->_ci->form_validation->set_rules('kalender_syncstatus_id',"kalender_syncstatus_id","required");
+
+ if($this->_ci->form_validation->run() === FALSE)
+ $this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
+ $kalender_syncstatus_id = $this->_ci->input->get('kalender_syncstatus_id', TRUE);
+ $data = $this->_ci->KalendersyncstatusModel->loadWhere(array('kalender_syncstatus_id' => $kalender_syncstatus_id));
+ if (isError($data))
+ $this->terminateWithError(getError($data));
+
+ $this->terminateWithSuccess(getData($data)[0]);
+ }
+
+ public function getSyncStatus()
+ {
+ $language = getUserLanguage() == 'German' ? 0 : 1;
+
+ $this->_ci->KalenderStatusModel->addSelect('*, array_to_json(bezeichnung_mehrsprachig::varchar[])->>' . $language .' AS bezeichnung, status_kurzbz');
+ $this->_ci->KalenderStatusModel->db->where_in('status_kurzbz', self::ALLOWED_STATUS);
+ $data = $this->_ci->KalenderStatusModel->load();
+
+ if (isError($data))
+ $this->terminateWithError(getError($data));
+
+ $this->terminateWithSuccess(getData($data));
+ }
+
+
+ public function add()
+ {
+ $formData = $this->input->post('formData');
+
+ $this->_ci->form_validation->set_data($formData);
+
+ $this->_ci->form_validation->set_rules('oe_kurzbz',"oe_kurzbz","required");
+ $this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required");
+ $this->_ci->form_validation->set_rules('sync_status_kurzbz',"sync_status_kurzbz","required");
+
+ if($this->_ci->form_validation->run() === FALSE)
+ $this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
+
+ $ausbildungssemester = isEmptyString($formData['ausbildungssemester']) ? null : $formData['ausbildungssemester'];
+ $studienplan_id = isEmptyString($formData['studienplan_id']) ? null : $formData['studienplan_id'];
+
+ $existing = $this->_checkIfExist(
+ $formData['oe_kurzbz'],
+ $formData['studiensemester_kurzbz'],
+ $ausbildungssemester,
+ $studienplan_id
+ );
+
+ if (isError($existing))
+ $this->terminateWithError(getError($existing));
+
+ if (hasData($existing))
+ {
+ $this->terminateWithError('Es existiert bereits ein Eintrag.');
+ }
+
+ $result = $this->_ci->KalendersyncstatusModel->insert(
+ array(
+ 'oe_kurzbz' => $formData['oe_kurzbz'],
+ 'studiensemester_kurzbz' => $formData['studiensemester_kurzbz'],
+ 'datum_bis' => $formData['datum_bis'],
+ 'studienplan_id' => $studienplan_id,
+ 'ausbildungssemester' => $ausbildungssemester,
+ 'sync_status_kurzbz' => $formData['sync_status_kurzbz'],
+ 'mail' => $formData['mail'],
+ 'insertvon' => getAuthUID()
+ )
+ );
+
+ $this->terminateWithSuccess($result);
+ }
+
+
+ public function start()
+ {
+ $formData = $this->input->post('formData');
+
+ $this->_ci->form_validation->set_data($formData);
+
+ $this->_ci->form_validation->set_rules('oe_kurzbz',"oe_kurzbz","required");
+ $this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required");
+ $this->_ci->form_validation->set_rules('sync_status_kurzbz',"sync_status_kurzbz","required");
+
+ if($this->_ci->form_validation->run() === FALSE)
+ $this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
+
+ $result = $this->_ci->kalendersynclib->runManual($formData['oe_kurzbz'], $formData['studiensemester_kurzbz'], $formData['ausbildungssemester'], $formData['studienplan_id'], $formData['mail'], $formData['datum_bis'], $formData['sync_status_kurzbz']);
+
+ if (isError($result))
+ $this->terminateWithError(getError($result));
+
+ $this->terminateWithSuccess($result);
+ }
+ public function updateSync()
+ {
+ $formData = $this->input->post('formData');
+
+ $this->_ci->form_validation->set_data($formData);
+
+ $this->_ci->form_validation->set_rules('kalender_syncstatus_id',"kalender_syncstatus_id","required");
+ $this->_ci->form_validation->set_rules('oe_kurzbz',"oe_kurzbz","required");
+ $this->_ci->form_validation->set_rules('studiensemester_kurzbz',"studiensemester_kurzbz","required");
+ $this->_ci->form_validation->set_rules('sync_status_kurzbz',"sync_status_kurzbz","required");
+
+ if($this->_ci->form_validation->run() === FALSE)
+ $this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
+
+ $ausbildungssemester = isEmptyString($formData['ausbildungssemester']) ? null : $formData['ausbildungssemester'];
+ $studienplan_id = isEmptyString($formData['studienplan_id']) ? null : $formData['studienplan_id'];
+
+ $existing = $this->_checkIfExist(
+ $formData['oe_kurzbz'],
+ $formData['studiensemester_kurzbz'],
+ $ausbildungssemester,
+ $studienplan_id,
+ $formData['kalender_syncstatus_id']
+ );
+
+ if (isError($existing))
+ $this->terminateWithError(getError($existing));
+
+ if (hasData($existing))
+ {
+ $this->terminateWithError('Es existiert bereits ein Eintrag.');
+ }
+
+ $this->_ci->KalendersyncstatusModel->update(
+ array('kalender_syncstatus_id' => $formData['kalender_syncstatus_id']),
+ array(
+ 'oe_kurzbz' => $formData['oe_kurzbz'],
+ 'studiensemester_kurzbz' => $formData['studiensemester_kurzbz'],
+ 'datum_bis' => $formData['datum_bis'],
+ 'studienplan_id' => $studienplan_id,
+ 'ausbildungssemester' => $ausbildungssemester,
+ 'sync_status_kurzbz' => $formData['sync_status_kurzbz'],
+ 'mail' => $formData['mail'],
+ 'updatevon' => getAuthUID(),
+ 'updateamum' => date('c'),
+ )
+ );
+ }
+ public function getStudienplan()
+ {
+ $this->_ci->form_validation->set_data($_GET);
+ $this->_ci->form_validation->set_rules('oe_kurzbz',"oe_kurzbz","required");
+
+ if($this->_ci->form_validation->run() === FALSE)
+ $this->terminateWithValidationErrors($this->_ci->form_validation->error_array());
+
+ $oe_kurzbz = $this->_ci->input->get('oe_kurzbz', TRUE);
+ $studiensemester_kurzbz = $this->_ci->input->get('studiensemester_kurzbz', TRUE);
+ $ausbildungssemester = $this->_ci->input->get('ausbildungssemester', TRUE);
+
+ $this->_ci->StudiengangModel->addJoin('tbl_organisationseinheit', 'oe_kurzbz');
+ $is_studiengang = $this->_ci->StudiengangModel->loadWhere(array('oe_kurzbz' => $oe_kurzbz));
+
+ if (hasData($is_studiengang) && !isEmptyString($studiensemester_kurzbz))
+ {
+ $studiengang = getData($is_studiengang)[0];
+
+ $studienplaene = $this->_ci->StudienplanModel->getStudienplaeneBySemester(
+ $studiengang->studiengang_kz,
+ $studiensemester_kurzbz,
+ isEmptyString($ausbildungssemester) ? null : $ausbildungssemester
+ );
+
+
+ $data = $this->getDataOrTerminateWithError($studienplaene);
+
+ $unique = array();
+ foreach ($data as $row)
+ {
+ $unique[$row->studienplan_id] = (object) array(
+ 'studienplan_id' => $row->studienplan_id,
+ 'studienordnung_id' => $row->studienordnung_id,
+ 'bezeichnung' => $row->bezeichnung,
+ 'orgform_kurzbz' => $row->orgform_kurzbz
+ );
+ }
+
+ $this->terminateWithSuccess(array_values($unique));
+ }
+ $this->terminateWithSuccess();
+ }
+
+ private function _checkIfExist($oe_kurzbz, $studiensemester_kurzbz, $ausbildungssemester, $studienplan_id, $kalender_syncstatus_id = null)
+ {
+ $this->_ci->KalendersyncstatusModel->db->where('oe_kurzbz', $oe_kurzbz);
+ $this->_ci->KalendersyncstatusModel->db->where('studiensemester_kurzbz', $studiensemester_kurzbz);
+
+ if (is_null($ausbildungssemester))
+ $this->_ci->KalendersyncstatusModel->db->where('ausbildungssemester IS NULL', null, false);
+ else
+ $this->_ci->KalendersyncstatusModel->db->where('ausbildungssemester', $ausbildungssemester);
+
+ if (is_null($studienplan_id))
+ $this->_ci->KalendersyncstatusModel->db->where('studienplan_id IS NULL', null, false);
+ else
+ $this->_ci->KalendersyncstatusModel->db->where('studienplan_id', $studienplan_id);
+
+ if (!is_null($kalender_syncstatus_id))
+ $this->_ci->KalendersyncstatusModel->db->where('kalender_syncstatus_id !=', $kalender_syncstatus_id);
+ return $this->_ci->KalendersyncstatusModel->load();
+ }
+
+}
diff --git a/application/libraries/KalenderLib.php b/application/libraries/KalenderLib.php
index 19c800e3f..efc401ad1 100644
--- a/application/libraries/KalenderLib.php
+++ b/application/libraries/KalenderLib.php
@@ -1489,88 +1489,4 @@ class KalenderLib
return $history_entries;
}
- //TODO in eine eigen Lib?
- public function sync()
- {
- $this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
-
- $this->_ci->KalenderModel->addSelect('tbl_kalender.*, tbl_kalender_ort.ort_kurzbz, tbl_kalender_ort.location');
- $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id', 'LEFT');
- $this->_ci->KalenderModel->db->where_in('status_kurzbz', array('sync_live', 'sync_preview', 'todelete', 'planning', 'preview'));
- $this->_ci->KalenderModel->addOrder('tbl_kalender.kalender_id', 'DESC');
- $to_update = $this->_ci->KalenderModel->load();
-
- if (!hasData($to_update))
- return success();
-
- $mail_infos = [];
-
- foreach (getData($to_update) as $entry)
- {
- if ($entry->status_kurzbz === 'todelete')
- {
- $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'deleted'));
- $mail_infos[] = array('entry' => $entry, 'new_status' => 'deleted', 'notify' => array('lektor', 'student'));
- }
-
- if ($entry->status_kurzbz === 'sync_preview')
- {
- $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'preview'));
- $this->_archiveVorgaenger($entry->vorgaenger_kalender_id, true);
- $mail_infos[] = array('entry' => $entry, 'new_status' => 'preview', 'notify' => array('lektor'));
- }
-
- if ($entry->status_kurzbz === 'sync_live' || $entry->status_kurzbz === 'planning')
- {
- $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'live'));
- $this->_archiveVorgaenger($entry->vorgaenger_kalender_id, false);
- $mail_infos[] = array('entry' => $entry, 'new_status' => 'live', 'notify' => array('lektor', 'student'));
- }
-
- if ($entry->status_kurzbz === 'preview')
- {
- $current = getData($this->_ci->KalenderModel->load(array('kalender_id' => $entry->kalender_id)))[0];
- if ($current->status_kurzbz === 'archived')
- continue;
-
- $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'live'));
- $this->_archiveVorgaenger($entry->vorgaenger_kalender_id, false);
- $mail_infos[] = array('entry' => $entry, 'new_status' => 'live', 'notify' => array('lektor', 'student'));
- }
- }
-
- $this->_ci->load->library('KalenderNotificationLib');
- $this->_ci->kalendernotificationlib->sendMails($mail_infos);
- return success();
-
- }
-
- private function _archiveVorgaenger($vorgaenger_kalender_id, $stop_at_live = false)
- {
- if (!$vorgaenger_kalender_id) return;
-
- $current_id = $vorgaenger_kalender_id;
-
- while ($current_id)
- {
- $vorgaenger = $this->_ci->KalenderModel->load(array('kalender_id' => $current_id));
- if (!hasData($vorgaenger)) return;
-
- $vorgaenger = getData($vorgaenger)[0];
-
- if ($stop_at_live && $vorgaenger->status_kurzbz === 'live')
- return;
-
- if (in_array($vorgaenger->status_kurzbz, array('preview', 'live')))
- {
- $this->_ci->KalenderModel->update(
- array('kalender_id' => $current_id),
- array('status_kurzbz' => 'archived')
- );
- }
-
- $current_id = $vorgaenger->vorgaenger_kalender_id;
- }
- }
-
}
diff --git a/application/libraries/KalenderNotificationLib.php b/application/libraries/KalenderNotificationLib.php
index d611eb247..acf14bae7 100644
--- a/application/libraries/KalenderNotificationLib.php
+++ b/application/libraries/KalenderNotificationLib.php
@@ -10,7 +10,7 @@ class KalenderNotificationLib
$this->_ci =& get_instance();
$this->_ci->load->library('MailLib');
$this->_ci->load->config('tempus');
-
+ $this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
}
public function sendMails($mail_infos)
@@ -90,6 +90,7 @@ class KalenderNotificationLib
if (!empty($student_deleted))
$student_entries .= $this->_addToList($student_deleted, 'gelöscht') . '
';
+ //TODO (david) richtigen Empfänger
if (!empty($lektor_entries))
$this->_ci->maillib->send('', 'ma0048@technikum-wien.at', 'Lektor Tempus Update', $lektor_entries);
if (!empty($student_entries))
diff --git a/application/libraries/KalenderSyncLib.php b/application/libraries/KalenderSyncLib.php
new file mode 100644
index 000000000..ee0197e20
--- /dev/null
+++ b/application/libraries/KalenderSyncLib.php
@@ -0,0 +1,336 @@
+ array('planning', 'sync_preview'),
+ 'live' => array('planning', 'sync_preview', 'preview', 'sync_live')
+ );
+
+ public function __construct()
+ {
+ $this->_ci =& get_instance();
+
+ $this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
+ $this->_ci->load->model('ressource/Kalendersyncstatus_model', 'KalendersyncstatusModel');
+ $this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel');
+ }
+
+ public function run($studiensemester_kurzbz)
+ {
+ $this->_ci->KalendersyncstatusModel->db->where('studiensemester_kurzbz', $studiensemester_kurzbz);
+
+ $configs = $this->_ci->KalendersyncstatusModel->load();
+
+ if (isError($configs))
+ return $configs;
+
+ if (!hasData($configs))
+ return success([]);
+
+ $results = [];
+ $mail_infos_gesamt = [];
+
+ foreach (getData($configs) as $config)
+ {
+
+ $ziel_status = isEmptyString($config->sync_status_kurzbz) ? 'live' : $config->sync_status_kurzbz;
+
+ $relevante_stati = array_merge($this->status_map[$ziel_status], array('todelete'));
+
+ $kalender_ids_result = $this->getKalenderIds(
+ $config->oe_kurzbz,
+ $config->studiensemester_kurzbz,
+ $relevante_stati,
+ $config->ausbildungssemester,
+ $config->studienplan_id,
+ $config->datum_bis
+ );
+
+ if (isError($kalender_ids_result))
+ return $kalender_ids_result;
+
+ $kalender_ids = getData($kalender_ids_result);
+
+ $sync_result = $this->_syncKalenderIds($kalender_ids, $ziel_status);
+
+ if (isError($sync_result))
+ return $sync_result;
+
+ $sync_data = getData($sync_result);
+
+ if ((bool) $config->mail)
+ $mail_infos_gesamt = array_merge($mail_infos_gesamt, $sync_data['mail_infos']);
+
+ $results[] = [
+ 'kalender_syncstatus_id' => $config->kalender_syncstatus_id,
+ 'oe_kurzbz' => $config->oe_kurzbz,
+ 'ausbildungssemester' => $config->ausbildungssemester,
+ 'studienplan_id' => $config->studienplan_id,
+ 'ziel_status' => $ziel_status,
+ 'mail' => (bool) $config->mail,
+ 'synced' => $sync_data['synced']
+ ];
+ }
+
+ if (!empty($mail_infos_gesamt))
+ {
+ $this->_ci->load->library('KalenderNotificationLib');
+ $this->_ci->kalendernotificationlib->sendMails($mail_infos_gesamt);
+ }
+
+ return success($results);
+ }
+
+ public function runManual($oe_kurzbz, $studiensemester_kurzbz, $ausbildungssemester = null, $studienplan_id = null, $mail = true, $datum_bis = null, $ziel_status = 'live')
+ {
+ $relevante_stati = array_merge($this->status_map[$ziel_status], array('todelete'));
+
+ $kalender_ids_result = $this->getKalenderIds(
+ $oe_kurzbz,
+ $studiensemester_kurzbz,
+ $relevante_stati,
+ $ausbildungssemester,
+ $studienplan_id,
+ $datum_bis
+ );
+
+ if (isError($kalender_ids_result))
+ return $kalender_ids_result;
+
+ $kalender_ids = getData($kalender_ids_result);
+
+ $sync_result = $this->_syncKalenderIds($kalender_ids, $ziel_status);
+
+ if (isError($sync_result))
+ return $sync_result;
+
+ $sync_data = getData($sync_result);
+
+ if ($mail && !empty($sync_data['mail_infos']))
+ {
+ $this->_ci->load->library('KalenderNotificationLib');
+ $this->_ci->kalendernotificationlib->sendMails($sync_data['mail_infos']);
+ }
+
+ return success(['synced' => $sync_data['synced']]);
+ }
+
+ public function sync()
+ {
+ $this->_ci->KalenderModel->addSelect('tbl_kalender.*, tbl_kalender_ort.ort_kurzbz, tbl_kalender_ort.location');
+ $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id', 'LEFT');
+ $this->_ci->KalenderModel->db->where_in('status_kurzbz', array('sync_live', 'sync_preview', 'todelete'));
+ $this->_ci->KalenderModel->addOrder('tbl_kalender.kalender_id', 'DESC');
+ $to_update = $this->_ci->KalenderModel->load();
+
+ if (isError($to_update))
+ return $to_update;
+
+ if (!hasData($to_update))
+ return success(['synced' => 0]);
+
+ $mail_infos = [];
+ $synced = 0;
+
+ foreach (getData($to_update) as $entry)
+ {
+ if ($entry->status_kurzbz === 'todelete')
+ {
+ $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'deleted'));
+ $mail_infos[] = array('entry' => $entry, 'new_status' => 'deleted', 'notify' => array('lektor', 'student'));
+ $synced++;
+ }
+
+ if ($entry->status_kurzbz === 'sync_preview')
+ {
+ $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'preview'));
+ $this->_archiveVorgaenger($entry->vorgaenger_kalender_id, true);
+ $mail_infos[] = array('entry' => $entry, 'new_status' => 'preview', 'notify' => array('lektor'));
+ $synced++;
+ }
+
+ if ($entry->status_kurzbz === 'sync_live')
+ {
+ $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'live'));
+ $this->_archiveVorgaenger($entry->vorgaenger_kalender_id, false);
+ $mail_infos[] = array('entry' => $entry, 'new_status' => 'live', 'notify' => array('lektor', 'student'));
+ $synced++;
+ }
+ }
+
+ $this->_ci->load->library('KalenderNotificationLib');
+ $this->_ci->kalendernotificationlib->sendMails($mail_infos);
+
+ return success(['synced' => $synced]);
+ }
+
+ public function getKalenderIds($oe_kurzbz, $studiensemester_kurzbz, $status_kurzbz_list, $ausbildungssemester = null, $studienplan_id = null, $datum_bis = null)
+ {
+ if (empty($status_kurzbz_list))
+ return success([]);
+
+ $studiengang_result = $this->_getStudiengang($oe_kurzbz);
+
+ if (isError($studiengang_result))
+ return $studiengang_result;
+
+ $studiengang_kz = getData($studiengang_result);
+
+ $this->_ci->KalenderModel->addDistinct('tbl_kalender.kalender_id');
+ $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id');
+ $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit', 'tbl_kalender.kalender_id = tbl_kalender_lehreinheit.kalender_id');
+ $this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheit', 'tbl_kalender_lehreinheit.lehreinheit_id = tbl_lehreinheit.lehreinheit_id');
+ $this->_ci->KalenderModel->addJoin('lehre.tbl_lehrveranstaltung', 'tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id');
+
+ //TODO (david) oe_kurzbz rekursiv?
+ if (!is_null($studiengang_kz))
+ $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.studiengang_kz', $studiengang_kz);
+ else
+ $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.oe_kurzbz', $oe_kurzbz);
+
+ $this->_ci->KalenderModel->db->where('tbl_lehreinheit.studiensemester_kurzbz', $studiensemester_kurzbz);
+ $this->_ci->KalenderModel->db->where_in('tbl_kalender.status_kurzbz', $status_kurzbz_list);
+
+ $this->_ci->KalenderModel->db->where('NOT EXISTS (
+ SELECT 1 FROM lehre.tbl_kalender nachfolger
+ WHERE nachfolger.vorgaenger_kalender_id = tbl_kalender.kalender_id)', null, false);
+
+ if (!is_null($studienplan_id))
+ {
+ $this->_ci->KalenderModel->addJoin('lehre.tbl_studienplan_lehrveranstaltung', 'tbl_lehrveranstaltung.lehrveranstaltung_id = tbl_studienplan_lehrveranstaltung.lehrveranstaltung_id');
+ $this->_ci->KalenderModel->db->where('tbl_studienplan_lehrveranstaltung.studienplan_id', $studienplan_id);
+ }
+
+ if (!is_null($ausbildungssemester))
+ $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.semester', $ausbildungssemester);
+
+ if (!is_null($datum_bis))
+ {
+ $end_date = date('Y-m-d', strtotime($datum_bis . ' +1 day'));
+ $this->_ci->KalenderModel->db->where('tbl_kalender.von <', $end_date);
+ }
+
+ $result = $this->_ci->KalenderModel->load();
+
+ if (isError($result))
+ return $result;
+
+ if (!hasData($result))
+ return success([]);
+
+ return success(array_column(getData($result), 'kalender_id'));
+ }
+
+
+ private function _archiveVorgaenger($vorgaenger_kalender_id, $stop_at_live = false)
+ {
+ if (!$vorgaenger_kalender_id) return;
+
+ $current_id = $vorgaenger_kalender_id;
+
+ while ($current_id)
+ {
+ $vorgaenger = $this->_ci->KalenderModel->load(array('kalender_id' => $current_id));
+ if (!hasData($vorgaenger)) return;
+
+ $vorgaenger = getData($vorgaenger)[0];
+
+ if ($stop_at_live && $vorgaenger->status_kurzbz === 'live')
+ return;
+
+ if (in_array($vorgaenger->status_kurzbz, array('preview', 'live')))
+ {
+ $this->_ci->KalenderModel->update(
+ array('kalender_id' => $current_id),
+ array('status_kurzbz' => 'archived')
+ );
+ }
+
+ $current_id = $vorgaenger->vorgaenger_kalender_id;
+ }
+ }
+
+ private function _syncKalenderIds($kalender_ids, $ziel_status)
+ {
+ if (empty($kalender_ids))
+ return success(['synced' => 0, 'mail_infos' => []]);
+
+ $this->_ci->KalenderModel->addSelect('tbl_kalender.*, tbl_kalender_ort.ort_kurzbz, tbl_kalender_ort.location');
+ $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id', 'LEFT');
+ $this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids);
+ $this->_ci->KalenderModel->addOrder('tbl_kalender.kalender_id', 'DESC');
+ $to_update = $this->_ci->KalenderModel->load();
+
+ if (isError($to_update))
+ return $to_update;
+
+ if (!hasData($to_update))
+ return success(['synced' => 0, 'mail_infos' => []]);
+
+ $mail_infos = [];
+ $synced = 0;
+
+ foreach (getData($to_update) as $entry)
+ {
+ if ($entry->status_kurzbz === 'todelete')
+ {
+ $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'deleted'));
+ $mail_infos[] = array('entry' => $entry, 'new_status' => 'deleted', 'notify' => array('lektor', 'student'));
+ $synced++;
+ continue;
+ }
+
+ if ($ziel_status === 'preview' && in_array($entry->status_kurzbz, array('planning', 'sync_preview')))
+ {
+ $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'preview'));
+ $this->_archiveVorgaenger($entry->vorgaenger_kalender_id, true);
+ $mail_infos[] = array('entry' => $entry, 'new_status' => 'preview', 'notify' => array('lektor'));
+ $synced++;
+ continue;
+ }
+
+ if ($ziel_status === 'live' && in_array($entry->status_kurzbz, array('planning', 'sync_preview', 'sync_live')))
+ {
+ $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'live'));
+ $this->_archiveVorgaenger($entry->vorgaenger_kalender_id, false);
+ $mail_infos[] = array('entry' => $entry, 'new_status' => 'live', 'notify' => array('lektor', 'student'));
+ $synced++;
+ continue;
+ }
+
+ if ($ziel_status === 'live' && $entry->status_kurzbz === 'preview')
+ {
+ $current = getData($this->_ci->KalenderModel->load(array('kalender_id' => $entry->kalender_id)))[0];
+ if ($current->status_kurzbz === 'archived')
+ continue;
+
+ $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'live'));
+ $this->_archiveVorgaenger($entry->vorgaenger_kalender_id, false);
+ $mail_infos[] = array('entry' => $entry, 'new_status' => 'live', 'notify' => array('lektor', 'student'));
+ $synced++;
+ }
+ }
+
+ return success(['synced' => $synced, 'mail_infos' => $mail_infos]);
+ }
+
+
+ private function _getStudiengang($oe_kurzbz)
+ {
+ $this->_ci->StudiengangModel->addJoin('tbl_organisationseinheit', 'oe_kurzbz');
+ $result = $this->_ci->StudiengangModel->loadWhere(array('oe_kurzbz' => $oe_kurzbz));
+
+ if (isError($result))
+ return $result;
+
+ if (!hasData($result))
+ return success(null);
+
+ return success(getData($result)[0]->studiengang_kz);
+ }
+}
\ No newline at end of file
diff --git a/application/models/ressource/Kalendersyncstatus_model.php b/application/models/ressource/Kalendersyncstatus_model.php
new file mode 100644
index 000000000..f1cd8aedf
--- /dev/null
+++ b/application/models/ressource/Kalendersyncstatus_model.php
@@ -0,0 +1,14 @@
+dbTable = 'lehre.tbl_kalender_syncstatus';
+ $this->pk = 'kalender_syncstatus_id';
+ }
+}
diff --git a/application/views/Tempus.php b/application/views/Tempus.php
index 346ad56ad..0bd9cb697 100644
--- a/application/views/Tempus.php
+++ b/application/views/Tempus.php
@@ -7,11 +7,13 @@
'vue3' => true,
'primevue3' => true,
'tabulator5' => true,
+ 'filtercomponent' => true,
+ 'navigationcomponent' => true,
'vuedatepicker11' => true,
'phrases' => array(
'global',
'ui',
- 'notiz',
+ 'lehre'
),
'customCSSs' => [
'public/css/components/vue-datepicker.css',
diff --git a/public/js/api/factory/tempus/sync.js b/public/js/api/factory/tempus/sync.js
new file mode 100644
index 000000000..0dd8d9eb5
--- /dev/null
+++ b/public/js/api/factory/tempus/sync.js
@@ -0,0 +1,83 @@
+/**
+ * Copyright (C) 2026 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 .
+ */
+
+export default {
+ getSyncs(studiensemester_kurzbz) {
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/tempus/KalenderSync/getSyncs',
+ params: { studiensemester_kurzbz }
+ };
+ },
+ loadSync(kalender_syncstatus_id) {
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/tempus/KalenderSync/loadSync',
+ params: { kalender_syncstatus_id }
+ };
+ },
+ getSyncStatus() {
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/tempus/KalenderSync/getSyncStatus',
+ };
+ },
+ add(formData) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/tempus/KalenderSync/add',
+ params: { formData }
+ };
+ },
+ delete(kalender_syncstatus_id) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/tempus/KalenderSync/delete',
+ params: { kalender_syncstatus_id }
+ };
+ },
+ start(formData) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/tempus/KalenderSync/start',
+ params: { formData }
+ };
+ },
+ updateSync(formData) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/tempus/KalenderSync/updateSync',
+ params: { formData }
+ };
+ },
+ getStudienplan(oe_kurzbz, studiensemester_kurzbz, ausbildungssemester) {
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/tempus/KalenderSync/getStudienplan',
+ params: { oe_kurzbz, studiensemester_kurzbz, ausbildungssemester }
+ };
+ },
+
+ getMaxSemester(studiengang_kzs) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/stv/status/getMaxSemester/',
+ params: { studiengang_kzs }
+ };
+ },
+
+};
\ No newline at end of file
diff --git a/public/js/apps/Tempus.js b/public/js/apps/Tempus.js
index 160aa3383..b1159da02 100644
--- a/public/js/apps/Tempus.js
+++ b/public/js/apps/Tempus.js
@@ -16,6 +16,7 @@
*/
import FhcTempus from "../components/Tempus/Tempus.js";
+import FhcTempusSync from "../components/Tempus/TempusSync.js";
import Phrasen from "../plugins/Phrasen.js";
import {capitalize} from "../helpers/StringHelpers.js";
@@ -27,6 +28,7 @@ const router = VueRouter.createRouter({
history: VueRouter.createWebHistory(),
routes: [
{ path: `/${ciPath}/Tempus`, component: FhcTempus },
+ { path: `/${ciPath}/Tempus/sync`, component: FhcTempusSync },
]
});
diff --git a/public/js/components/Tempus/Modal.js b/public/js/components/Tempus/Modal.js
new file mode 100644
index 000000000..981479aea
--- /dev/null
+++ b/public/js/components/Tempus/Modal.js
@@ -0,0 +1,340 @@
+import BsModal from '../Bootstrap/Modal.js';
+import CoreForm from '../Form/Form.js';
+import FormValidation from '../Form/Validation.js';
+import FormInput from '../Form/Input.js';
+
+import ApiTempusSync from '../../api/factory/tempus/sync.js';
+
+export default {
+ components: {
+ BsModal,
+ CoreForm,
+ FormValidation,
+ FormInput
+ },
+ props: {
+ config: Object,
+ stsem_kurzbz: String
+ },
+ emits: [
+ 'saved'
+ ],
+ data() {
+ return {
+ mode: 'new',
+ formData: {},
+ studienplaene: [],
+ max_semester: null,
+ syncStati: []
+ };
+ },
+ computed: {
+ semester() {
+ if (!this.max_semester)
+ return [];
+
+ return Array.from({length: this.max_semester}, (_, index) => index);
+ }
+ },
+ watch: {
+ 'formData.oe_kurzbz'(newOrganisation) {
+ if (!newOrganisation) {
+ this.max_semester = null;
+ this.studienplaene = [];
+ return;
+ }
+
+ let filtered_org = this.config.organisationen.filter(org => org.oe_kurzbz === newOrganisation)[0];
+
+ if (filtered_org && filtered_org.studiengang_kz)
+ this.getSemester(filtered_org.studiengang_kz);
+ else
+ this.max_semester = null;
+
+ this.getStudienplan();
+ },
+ 'formData.studiensemester_kurzbz'() {
+ this.getStudienplan();
+ },
+ 'formData.ausbildungssemester'() {
+ this.getStudienplan();
+ }
+ },
+ methods: {
+
+ openNew() {
+ this.$refs.modal.hide();
+
+ this.mode = 'new';
+ this.max_semester = null;
+ this.studienplaene = [];
+ this.formData = {
+ oe_kurzbz: null,
+ studiensemester_kurzbz: this.stsem_kurzbz ?? null,
+ datum_bis: null,
+ studienplan_id: null,
+ ausbildungssemester: null,
+ sync_status_kurzbz: null,
+ mail: false
+ };
+
+ this.$refs.form.clearValidation()
+ this.$refs.modal.show()
+ },
+ openEdit(entry) {
+ if (!entry || entry.kalender_syncstatus_id === undefined || entry.kalender_syncstatus_id === null)
+ {
+ return;
+ }
+
+ this.$refs.modal.hide();
+ this.mode = 'edit';
+
+ this.$api
+ .call(ApiTempusSync.loadSync(entry.kalender_syncstatus_id))
+ .then(result => {
+ this.formData = result.data;
+ return this.$refs.form.clearValidation();
+ })
+ .then(() => this.$refs.modal.show())
+ .catch(this.$fhcAlert.handleSystemError);
+ },
+
+ openStart() {
+ this.$refs.modal.hide();
+
+ this.mode = 'start';
+ this.max_semester = null;
+ this.studienplaene = [];
+ this.formData = {
+ oe_kurzbz: null,
+ studiensemester_kurzbz: this.stsem_kurzbz ?? null,
+ datum_bis: null,
+ sync_status_kurzbz: null,
+ studienplan_id: null,
+ ausbildungssemester: null,
+ mail: false
+ };
+
+ this.$refs.form.clearValidation()
+ this.$refs.modal.show()
+ },
+ getSemester(stg_kz) {
+ this.$api.call(ApiTempusSync.getMaxSemester([stg_kz]))
+ .then(response => response.data)
+ .then(response => {
+ this.max_semester = response;
+
+ let found = this.semester.filter(semester => semester === this.formData.ausbildungssemester);
+ if (found.length === 0)
+ this.formData.ausbildungssemester = null;
+ })
+ .catch(error => {
+ this.$fhcAlert.handleSystemError(error);
+ });
+ },
+ getStudienplan() {
+ if (!this.formData.oe_kurzbz || !this.formData.studiensemester_kurzbz) {
+ this.studienplaene = [];
+ return;
+ }
+
+ this.$api.call(ApiTempusSync.getStudienplan(
+ this.formData.oe_kurzbz,
+ this.formData.studiensemester_kurzbz,
+ this.formData.ausbildungssemester ?? null
+ ))
+ .then(response => response.data)
+ .then(response => {
+ this.studienplaene = response;
+ let found = this.studienplaene.filter(plan => plan.studienplan_id === this.formData.studienplan_id);
+ if (found.length === 0)
+ this.formData.studienplan_id = null;
+ })
+ .catch(error => {
+ this.$fhcAlert.handleSystemError(error);
+ });
+ },
+ save() {
+ if (this.mode === 'start')
+ this.start();
+ else if (this.mode === 'new')
+ this.insert();
+ else
+ this.update();
+ },
+ insert() {
+ this.$refs.form
+ .call(ApiTempusSync.add(this.formData))
+ .then(() => {
+ this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
+ this.$emit('saved');
+ this.$refs.modal.hide();
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ },
+ update() {
+ this.$refs.form
+ .call(ApiTempusSync.updateSync(this.formData))
+ .then(() => {
+ this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
+ this.$emit('saved');
+ this.$refs.modal.hide();
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ },
+ start() {
+ this.$refs.form
+ .call(ApiTempusSync.start(this.formData))
+ .then(() => {
+ this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
+ this.$emit('saved');
+ this.$refs.modal.hide();
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ }
+ },
+ created() {
+ console.log(this.config)
+ this.$api
+ .call(ApiTempusSync.getSyncStatus())
+ .then(result => this.syncStati = result.data)
+ .catch(error => this.$fhcAlert.handleSystemError(error));
+ },
+ template: `
+
+
+ {{ $p.t('lehre', 'tempus_sync_new') }}
+ {{ $p.t('lehre', 'tempus_sync_edit') }}
+ {{ $p.t('lehre', 'tempus_sync_start') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+};
\ No newline at end of file
diff --git a/public/js/components/Tempus/TempusSync.js b/public/js/components/Tempus/TempusSync.js
new file mode 100644
index 000000000..c97164984
--- /dev/null
+++ b/public/js/components/Tempus/TempusSync.js
@@ -0,0 +1,196 @@
+import {CoreFilterCmpt} from '../filter/Filter.js';
+import {CoreNavigationCmpt} from '../navigation/Navigation.js';
+import FormInput from "../Form/Input.js";
+
+import CoreBaseLayout from '../../components/layout/BaseLayout.js';
+import ApiTempusSync from '../../api/factory/tempus/sync.js';
+import TempusSyncModal from './Modal.js';
+
+export default {
+ props: {
+ config: Object,
+ defaultSemester: String
+ },
+ components: {
+ CoreFilterCmpt,
+ CoreBaseLayout,
+ CoreNavigationCmpt,
+ FormInput,
+ TempusSyncModal
+ },
+ data() {
+ return {
+ studiensemester_kurzbz: this.defaultSemester ?? null
+ }
+ },
+ watch: {
+ studiensemester_kurzbz()
+ {
+ this.reloadTable();
+ }
+ },
+ computed: {
+ tabulatorOptions() {
+ return {
+ index: "kalender_syncstatus_id",
+ ajaxURL: 'dummy',
+ ajaxRequestFunc: () => this.$api.call(ApiTempusSync.getSyncs(this.studiensemester_kurzbz)),
+ ajaxResponse: (url, params, response) => {
+ return response.data || [];
+ },
+ layout: 'fitDataStretch',
+ placeholder: "Keine Daten verfügbar",
+ persistenceID: "2026_07_21_tempus_sync_v1",
+ locale: true,
+ columns: [
+ {
+ formatter: 'rowSelection',
+ titleFormatter: 'rowSelection',
+ headerSort: false,
+ width: 40
+ },
+ {titlePhrase: 'lehre/organisationseinheit', field: 'oebezeichnung'},
+ {titlePhrase: 'lehre/studiensemester', field: 'studiensemester_kurzbz'},
+ {titlePhrase: 'lehre/bis_title', field: 'datum_bis',
+ formatter: (cell) => {
+ const value = cell.getValue();
+ if (!value) return ''
+ const date = luxon.DateTime.fromSQL(value)
+ return date.isValid ? date.toFormat('dd.MM.yyyy') : value
+ },
+ minWidth: 100
+ },
+ {titlePhrase: 'lehre/studienplan', field: 'studienplanbezeichnung', minWidth: 200},
+ {titlePhrase: 'lehre/ausbildungssemester', field: 'ausbildungssemester'},
+ {titlePhrase: 'global/status', field: 'sync_status_kurzbz'},
+ {titlePhrase: 'lehre/mail_benachrichtigung', field: 'mail', formatter: 'tickCross', hozAlign: 'center', minWidth: 80},
+ {titlePhrase:'global/insertamum', field: 'insertamum',
+ formatter: (cell) => {
+ const value = cell.getValue();
+ if (!value) return ''
+ const date = luxon.DateTime.fromSQL(value)
+ return date.isValid ? date.toFormat('dd.MM.yyyy HH:mm') : value
+ },
+ visible: false
+ },
+ {titlePhrase: 'global/insertvon', field: 'insertvon', visible: false},
+ {titlePhrase: 'global/updateamum', field: 'updateamum',
+ formatter: (cell) => {
+ const value = cell.getValue();
+ if (!value) return ''
+ const date = luxon.DateTime.fromSQL(value)
+ return date.isValid ? date.toFormat('dd.MM.yyyy HH:mm') : value
+ },
+ visible: false
+ },
+ {titlePhrase: 'global/updatevon', field: 'updatevon', visible: false},
+ {
+ titlePhrase: 'global/aktionen',
+ field: 'actions',
+ headerSort: false,
+ hozAlign: 'center',
+ width: 120,
+ formatter: (cell) => {
+ let container = document.createElement('div');
+ container.className = "d-flex gap-2";
+ let button = document.createElement('button');
+ button.className = 'btn btn-outline-secondary';
+ button.innerHTML = '';
+ button.addEventListener('click', () =>
+ this.$refs.syncModal.openEdit(cell.getRow().getData())
+ );
+
+ container.append(button);
+
+ button = document.createElement('button');
+ button.className = 'btn btn-outline-secondary';
+ button.innerHTML = '';
+ button.addEventListener('click', evt => {
+ evt.stopPropagation();
+ this.$fhcAlert
+ .confirmDelete()
+ .then(result => result ? cell.getData().kalender_syncstatus_id : Promise.reject({handled:true}))
+ .then(kalender_syncstatus_id => this.$api.call(ApiTempusSync.delete(kalender_syncstatus_id)))
+ .then(() => {
+ this.reloadTable();
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ });
+ container.append(button);
+
+ return container;
+ }
+ }
+ ],
+ }
+ }
+ },
+
+ methods: {
+ openNewModal()
+ {
+ this.$refs.syncModal.openNew();
+ },
+ reloadTable()
+ {
+ this.$refs.syncTable.reloadTable();
+ },
+ openStartModal()
+ {
+ this.$refs.syncModal.openStart();
+ }
+ },
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+};
\ No newline at end of file
diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php
index cc64bb47f..34317ad04 100644
--- a/system/dbupdate_3.4.php
+++ b/system/dbupdate_3.4.php
@@ -99,6 +99,7 @@ require_once('dbupdate_3.4/75888_reihungstest_mehrfachdurchfuehrung.php');
require_once('dbupdate_3.4/76150_perm_other_lv_plan.php');
require_once('dbupdate_3.4/68957_dashboard_bookmark_neue_Spalte_sort.php');
require_once('dbupdate_3.4/68530_Dashboard_Cleanup.php');
+require_once('dbupdate_3.4/77949_tempus_synchronisation.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo 'Pruefe Tabellen und Attribute!
';
diff --git a/system/dbupdate_3.4/77949_tempus_synchronisation.php b/system/dbupdate_3.4/77949_tempus_synchronisation.php
new file mode 100644
index 000000000..5773a31d0
--- /dev/null
+++ b/system/dbupdate_3.4/77949_tempus_synchronisation.php
@@ -0,0 +1,38 @@
+db_query("SELECT kalender_syncstatus_id FROM lehre.tbl_kalender_syncstatus LIMIT 1"))
+{
+ $qry = "CREATE TABLE lehre.tbl_kalender_syncstatus
+ (
+ kalender_syncstatus_id bigserial NOT NULL,
+ oe_kurzbz character varying(32),
+ studiensemester_kurzbz character varying(32),
+ datum_bis date,
+ studienplan_id integer,
+ ausbildungssemester smallint,
+ sync_status_kurzbz character varying(32),
+ mail boolean DEFAULT false NOT NULL,
+ insertamum timestamp DEFAULT now(),
+ insertvon character varying(32),
+ updateamum timestamp,
+ updatevon character varying(32),
+ CONSTRAINT tbl_kalender_syncstatus_pk PRIMARY KEY (kalender_syncstatus_id)
+ );
+
+ ALTER TABLE lehre.tbl_kalender_syncstatus ADD CONSTRAINT tbl_kalender_syncstatus_oe_kurzbz_fk FOREIGN KEY (oe_kurzbz) REFERENCES public.tbl_organisationseinheit (oe_kurzbz) ON DELETE RESTRICT ON UPDATE CASCADE;
+ ALTER TABLE lehre.tbl_kalender_syncstatus ADD CONSTRAINT tbl_kalender_syncstatus_studiensemester_kurzbz_fk FOREIGN KEY (studiensemester_kurzbz) REFERENCES public.tbl_studiensemester(studiensemester_kurzbz) ON DELETE RESTRICT ON UPDATE CASCADE;
+ ALTER TABLE lehre.tbl_kalender_syncstatus ADD CONSTRAINT tbl_kalender_syncstatus_studienplan_id_fk FOREIGN KEY (studienplan_id) REFERENCES lehre.tbl_studienplan (studienplan_id) ON DELETE RESTRICT ON UPDATE CASCADE;
+ ALTER TABLE lehre.tbl_kalender_syncstatus ADD CONSTRAINT tbl_kalender_syncstatus_status_sync_status_kurzbz_fk FOREIGN KEY (sync_status_kurzbz) REFERENCES lehre.tbl_kalender_status (status_kurzbz) ON DELETE RESTRICT ON UPDATE CASCADE;
+
+ GRANT SELECT, UPDATE ON lehre.tbl_kalender_syncstatus_kalender_syncstatus_id_seq TO vilesci;
+ GRANT SELECT ON lehre.tbl_kalender_syncstatus_kalender_syncstatus_id_seq TO web;
+ GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_syncstatus to vilesci;
+ GRANT SELECT ON lehre.tbl_kalender_typ to web;";
+
+ if(!$db->db_query($qry))
+ echo 'lehre.tbl_kalender_syncstatus: '.$db->db_last_error().'
';
+ else
+ echo ' lehre.tbl_kalender_syncstatus: Tabelle hinzugefuegt
';
+
+}
\ No newline at end of file
diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php
index 827e03261..02f550c63 100644
--- a/system/phrasesupdate.php
+++ b/system/phrasesupdate.php
@@ -3363,6 +3363,126 @@ $phrases = array(
)
)
),
+ array(
+ 'app' => 'core',
+ 'category' => 'lehre',
+ 'phrase' => 'mail_benachrichtigung',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Mail-Benachrichtigung',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Email notification',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'lehre',
+ 'phrase' => 'bis_title',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Bis',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Until',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'lehre',
+ 'phrase' => 'errorDeleting',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Fehler beim Löschen.',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Error deleting.',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'lehre',
+ 'phrase' => 'tempus_sync_start',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Synchronisierung manuell starten',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Start synchronization manually',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'lehre',
+ 'phrase' => 'tempus_sync_edit',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Sync-Eintrag bearbeiten',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Edit sync entry',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'lehre',
+ 'phrase' => 'tempus_sync_new',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Neuer Sync-Eintrag',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'New sync entry',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
array(
'app' => 'core',
'category' => 'lehre',