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: ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ` +}; \ 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',