From 352fc53e748311e107e881cb8a8ea0e40d488842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96sterreicher?= Date: Mon, 20 Oct 2025 11:01:50 +0200 Subject: [PATCH] =?UTF-8?q?Erster=20Prototyp=20f=C3=BCr=20Tempus=20Neu=20D?= =?UTF-8?q?B=20und=20GUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/controllers/Tempus.php | 40 +++ .../controllers/api/frontend/v1/Kalender.php | 171 +++++++++++ .../controllers/api/frontend/v1/Tempus.php | 94 ++++++ .../controllers/system/MigrateKalender.php | 122 ++++++++ application/libraries/KalenderLib.php | 177 +++++++++++ .../ressource/Kalender_Lehreinheit_model.php | 15 + .../models/ressource/Kalender_Ort_model.php | 14 + .../models/ressource/Kalender_model.php | 14 + .../Stundenplandev_Kalender_model.php | 14 + application/views/Tempus.php | 46 +++ public/css/Tempus.css | 130 ++++++++ public/js/api/factory/coursepicker.js | 10 + public/js/api/factory/kalender.js | 60 ++++ public/js/apps/Tempus.js | 43 +++ public/js/components/Calendar/Tempus.js | 168 ++++++++++ public/js/components/Tempus/Coursepicker.js | 89 ++++++ public/js/components/Tempus/Tempus.js | 287 ++++++++++++++++++ system/dbupdate_3.4.php | 1 + system/dbupdate_3.4/46975_tempus.php | 134 ++++++++ 19 files changed, 1629 insertions(+) create mode 100644 application/controllers/Tempus.php create mode 100644 application/controllers/api/frontend/v1/Kalender.php create mode 100644 application/controllers/api/frontend/v1/Tempus.php create mode 100644 application/controllers/system/MigrateKalender.php create mode 100644 application/libraries/KalenderLib.php create mode 100644 application/models/ressource/Kalender_Lehreinheit_model.php create mode 100644 application/models/ressource/Kalender_Ort_model.php create mode 100644 application/models/ressource/Kalender_model.php create mode 100644 application/models/ressource/Stundenplandev_Kalender_model.php create mode 100644 application/views/Tempus.php create mode 100644 public/css/Tempus.css create mode 100644 public/js/api/factory/coursepicker.js create mode 100644 public/js/api/factory/kalender.js create mode 100644 public/js/apps/Tempus.js create mode 100644 public/js/components/Calendar/Tempus.js create mode 100644 public/js/components/Tempus/Coursepicker.js create mode 100644 public/js/components/Tempus/Tempus.js create mode 100644 system/dbupdate_3.4/46975_tempus.php diff --git a/application/controllers/Tempus.php b/application/controllers/Tempus.php new file mode 100644 index 000000000..139f0b690 --- /dev/null +++ b/application/controllers/Tempus.php @@ -0,0 +1,40 @@ +method] = ['admin:r', 'assistenz:r']; + parent::__construct($permissions); + + // Load Libraries + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + + // Load Config + $this->load->config('calendar'); + } + + /** + * @return void + */ + public function _remap() + { + + $this->load->view('Tempus', [ + 'permissions' => [ + 'admin' => $this->permissionlib->isBerechtigt('admin') + ], + 'variables' => [ + 'semester_aktuell' => $this->variablelib->getVar('semester_aktuell'), + 'timezone' => $this->config->item('timezone') + ] + ]); + } +} diff --git a/application/controllers/api/frontend/v1/Kalender.php b/application/controllers/api/frontend/v1/Kalender.php new file mode 100644 index 000000000..519853912 --- /dev/null +++ b/application/controllers/api/frontend/v1/Kalender.php @@ -0,0 +1,171 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +class Kalender extends FHCAPI_Controller +{ + + /** + * Object initialization + */ + public function __construct() + { + + parent::__construct([ + 'getRoomplan' => self::PERM_LOGGED, + 'Stunden' => self::PERM_LOGGED, + 'Reservierungen' => self::PERM_LOGGED, + 'getStundenplan' => self::PERM_LOGGED, + 'getLehreinheitStudiensemester' => self::PERM_LOGGED, + 'updateKalenderEvent' => 'lehre/lvplan:rw', + 'addKalenderEvent' => 'lehre/lvplan:rw' + ]); + + $this->load->library('LogLib'); + $this->loglib->setConfigs(array( + 'classIndex' => 5, + 'functionIndex' => 5, + 'lineIndex' => 4, + 'dbLogType' => 'API', // required + 'dbExecuteUser' => 'RESTful API' + )); + + $this->uid = getAuthUID(); + + $this->load->library('form_validation'); + + //load models + //$this->load->model('ressource/Stundenplan_model', 'StundenplanModel'); + //$this->load->model('ressource/Reservierung_model', 'ReservierungModel'); + + $this->load->library('KalenderLib'); + + + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * fetches Stunden layout from database + * @access public + * + */ + public function Stunden() + { + $this->load->model('ressource/Stunde_model', 'StundeModel'); + + $stunden = $this->StundeModel->load(); + + $stunden = $this->getDataOrTerminateWithError($stunden); + + $this->terminateWithSuccess($stunden); + } + + /** + * fetches stundenplan events from a Room and start/end date + * @access public + * + */ + public function getRoomplan() + { + + // form validation + $this->load->library('form_validation'); + $this->form_validation->set_data($_GET); + $this->form_validation->set_rules('start_date',"start_date","required"); + $this->form_validation->set_rules('end_date',"end_date","required"); + $this->form_validation->set_rules('ort_kurzbz',"ort_kurzbz","required"); + if($this->form_validation->run() === FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array()); + + // storing the get parameter in local variables + $start_date = $this->input->get('start_date', TRUE); + $end_date = $this->input->get('end_date', TRUE); + $ort_kurzbz = $this->input->get('ort_kurzbz', TRUE); + + $stundenplan_data =$this->kalenderlib->getRoomData($ort_kurzbz, $start_date, $end_date); + + $this->terminateWithSuccess($stundenplan_data); + } + + public function updateKalenderEvent() + { + // form validation + $this->load->library('form_validation'); + $this->form_validation->set_data($_POST); + $this->form_validation->set_rules('kalender_id',"kalender_id","required"); + $this->form_validation->set_rules('ort_kurzbz',"ort_kurzbz","required"); + $this->form_validation->set_rules('start_date',"start_date","required"); + $this->form_validation->set_rules('end_date',"end_date","optional"); + + if($this->form_validation->run() === FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array()); + + // storing the get parameter in local variables + $kalender_id = $this->input->post('kalender_id', TRUE); + $ort_kurzbz = $this->input->post('ort_kurzbz', TRUE); + $start_date = $this->input->post('start_date', TRUE); + $end_date = $this->input->post('end_date', TRUE); + + + // Was passiert hier? + // Raumänderung, Tagesänderung, Start / Ende Zeit korrektur + // Ist das alles ein Endpunkt? + $stundenplan_data =$this->kalenderlib->updateKalenderEvent($this->uid,$kalender_id, $ort_kurzbz, $start_date, $end_date); + + $this->terminateWithSuccess($stundenplan_data); + } + + public function addKalenderEvent() + { + // form validation + $this->load->library('form_validation'); + $this->form_validation->set_data($_POST); + $this->form_validation->set_rules('lehreinheit_id',"kalender_id","required"); + $this->form_validation->set_rules('ort_kurzbz',"ort_kurzbz","required"); + $this->form_validation->set_rules('start_date',"start_date","required"); + $this->form_validation->set_rules('end_date',"end_date","required"); + + if($this->form_validation->run() === FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array()); + + // storing the get parameter in local variables + $lehreinheit_id = $this->input->post('lehreinheit_id', TRUE); + $ort_kurzbz = $this->input->post('ort_kurzbz', TRUE); + $start_date = $this->input->post('start_date', TRUE); + $end_date = $this->input->post('end_date', TRUE); + + $this->kalenderlib->addKalenderEvent($this->uid, $ort_kurzbz, $start_date, $end_date, $lehreinheit_id); + + $this->terminateWithSuccess(); + } + + // gets the reservierungen of a room if the ort_kurzbz parameter is supplied otherwise gets the reservierungen of the stundenplan of a student + public function Reservierungen($ort_kurzbz = null) + { + $this->terminateWithSuccess(); + } + + public function getLehreinheitStudiensemester($lehreinheit_id) + { + $this->load->model('education/Lehreinheit_model', 'LehreinheitModel'); + $this->LehreinheitModel->addSelect(["studiensemester_kurzbz"]); + $result = $this->LehreinheitModel->load($lehreinheit_id); + $result = current($this->getDataOrTerminateWithError($result))->studiensemester_kurzbz; + $this->terminateWithSuccess($result); + } +} diff --git a/application/controllers/api/frontend/v1/Tempus.php b/application/controllers/api/frontend/v1/Tempus.php new file mode 100644 index 000000000..64824f152 --- /dev/null +++ b/application/controllers/api/frontend/v1/Tempus.php @@ -0,0 +1,94 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +class Tempus extends FHCAPI_Controller +{ + + /** + * Object initialization + */ + public function __construct() + { + + parent::__construct([ + 'getCourses' => self::PERM_LOGGED, + ]); + + $this->load->library('LogLib'); + $this->loglib->setConfigs(array( + 'classIndex' => 5, + 'functionIndex' => 5, + 'lineIndex' => 4, + 'dbLogType' => 'API', // required + 'dbExecuteUser' => 'RESTful API' + )); + + $this->load->library('form_validation'); + + //load models + //$this->load->model('ressource/Stundenplan_model', 'StundenplanModel'); + //$this->load->model('ressource/Reservierung_model', 'ReservierungModel'); + + + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + + /** + * fetches courses + * @access public + * + */ + public function getCourses() + { + // form validation + $this->load->library('form_validation'); + $this->form_validation->set_data($_GET); + $this->form_validation->set_rules('searchfilter',"searchfilter","required"); + if($this->form_validation->run() === FALSE) $this->terminateWithValidationErrors($this->form_validation->error_array()); + + // storing the get parameter in local variables + $searchfilter = $this->input->get('searchfiler', TRUE); + + // TODO implement Loading Data + $course_data = array( + array( + 'lehreinheit_id'=>'1', + 'bezeichnung' => 'Englisch 1', + 'studiengang_kurzbz' => 'BMR', + 'semester' => '1', + 'kurzbz' => 'ENG', + 'lektoren' => array('OesterAn','KindlMa') + ), + array( + 'lehreinheit_id'=>'2', + 'bezeichnung' => 'Mahtematik 1', + 'studiengang_kurzbz' => 'BMR', + 'semester' => '1', + 'kurzbz' => 'MAT', + 'lektoren' => array('BamberHa') + ) + ); + + $this->terminateWithSuccess($course_data); + } +} diff --git a/application/controllers/system/MigrateKalender.php b/application/controllers/system/MigrateKalender.php new file mode 100644 index 000000000..610a871db --- /dev/null +++ b/application/controllers/system/MigrateKalender.php @@ -0,0 +1,122 @@ +load->model('ressource/Kalender_model', 'KalenderModel'); + $this->load->model('ressource/Kalender_Lehreinheit_model', 'KalenderLehreinheitModel'); + $this->load->model('ressource/Kalender_Ort_model', 'KalenderOrtModel'); + $this->load->model('ressource/Stundenplandev_Kalender_model', 'SyncModel'); + } + + /** + * Everything has a beginning + */ + public function index() + { + $von = date('Y-m-d') // TODO + $bis = date('Y-m-d') // TODO + + $db = new DB_Model(); + $stpldevsql = ' + SELECT *, + (SELECT beginn FROM lehre.tbl_stunde WHERE stunde=tbl_stundenplandev.stunde) as beginn, + (SELECT ende FROM lehre.tbl_stunde WHERE stunde=tbl_stundenplandev.stunde) as ende + FROM + lehre.tbl_stundenplandev WHERE datum>=? and datum<=? ORDER BY datum, stunde, unr'; + + $stpldev = $db->execReadOnlyQuery($stpldevsql, array($von, $bis)); + if (hasData($stpldev)) + { + // Pruefen ob der Eintrag schon in Sync Tabelle vorhanden ist + // Wenn neuere Änderungen vorhanden dann Update + // Wenn keine Änderungen seit leztem Sync dann Ueberspringen + // Wenn noch nicht vorhanden neu anlegen + // Danach ggf pruefen welceh Eintraege in der zwischenzeit geloescht wurden und + // in der neuen Tabelle auch archivieren oder loeschen + + $data = getData($stpldev); + foreach($data as $rowstpl) + { + $SyncResult = $this->SyncModel->loadWhere( + array('stundenplandev_id' => $rowstpl->stundenplandev_id) + ); + if(hasData($SyncResult)) + { + //bereits vorhanden + // TODO Update + } + else + { + // Neuen Eintrag anlegen + + $von = $rowstpl->datum.' '.$rowstpl->beginn; + $bis = $rowstpl->datum.' '.$rowstpl->ende; + $typ = 'lehreinheit'; + $status = 'visible_student'; + $insertamum = $rowstpl->insertamum; + $insertvon = $rowstpl->insertvon; + $updateamum = $rowstpl->updateamum; + $updatevon = $rowstpl->updatevon; + + $resultKalenderInsert = $this->KalenderModel->insert( + array( + 'von' => $von, + 'bis' => $bis, + 'typ' => $typ, + 'status_kurzbz' => $status, + 'vorgaenger_kalender_id' => null, + 'insertamum' => $insertamum, + 'insertvon' => $insertvon, + 'updateamum' => $updateamum, + 'updatevon' => $updatevon + ) + ); + + if(isSuccess($resultKalenderInsert)) + { + $kalender_id = getData($resultKalenderInsert); + + $resultKalenderInsert = $this->KalenderLehreinheitModel->insert( + array( + 'kalender_id' => $kalender_id, + 'lehreinheit_id' => $rowstpl->lehreinheit_id, + ) + ); + + $resultKalenderInsert = $this->KalenderOrtModel->insert( + array( + 'kalender_id' => $kalender_id, + 'ort_kurzbz' => $rowstpl->ort_kurzbz, + ) + ); + + $resultSyncInsert = $this->SyncModel->insert( + array( + 'stundenplandev_id' => $rowstpl->stundenplandev_id, + 'kalender_id' => $kalender_id, + 'lastupdate' => date('Y-m-d H:i:s') + ) + ); + + } + } + } + } + } +} diff --git a/application/libraries/KalenderLib.php b/application/libraries/KalenderLib.php new file mode 100644 index 000000000..fcc3f00c9 --- /dev/null +++ b/application/libraries/KalenderLib.php @@ -0,0 +1,177 @@ +ci =& get_instance(); + + $this->ci->load->model('ressource/Kalender_model', 'KalenderModel'); + $this->ci->load->model('ressource/Kalender_Lehreinheit_model', 'KalenderLehreinheitModel'); + $this->ci->load->model('ressource/Kalender_Ort_model', 'KalenderOrtModel'); + $this->ci->load->model('education/Lehreinheit_model', 'LehreinheitModel'); + $this->ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + $this->ci->load->model('education/LehreinheitMitarbeiter_model', 'LehreinheitMitarbeiterModel'); + + } + + public function getRoomData($ort_kurzbz, $start_date, $end_date) + { + $data = $this->ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'kalender_id'); + $data = $this->ci->KalenderModel->loadWhere(array( + 'von >=' => $start_date, + 'bis <= '=>$end_date, + 'ort_kurzbz'=>$ort_kurzbz + )); + + $stundenplan_data = array(); + if(isSuccess($data) && hasData($data)) + { + $data = getData($data); + foreach($data as $rowstpl) + { + $obj = new stdClass(); + $obj->type='lehreinheit'; + + $von = new DateTime($rowstpl->von); + $bis = new DateTime($rowstpl->bis); + + $obj->beginn = $von->format('H:i:s'); + $obj->ende = $bis->format('H:i:s'); + $obj->datum = $von->format('Y-m-d'); + $obj->topic = 'undefined'; + $obj->lektor = array(); + $obj->gruppe = array(); + $obj->isostart = $von->format('c'); + $obj->isoend = $bis->format('c'); + $obj->tooltip = 'tip'; + + $obj->lehreinheit_id = array(); + + $lehreinheiten = $this->ci->KalenderLehreinheitModel->loadWhere(array('kalender_id'=>$rowstpl->kalender_id)); + if(isSuccess($lehreinheiten) && hasData($lehreinheiten)) + { + $lehreinheiten = getData($lehreinheiten); + foreach($lehreinheiten as $le) + { + $obj->lehreinheit_id[] = $le->lehreinheit_id; + + $lehreinheitdata = $this->ci->LehreinheitModel->loadWhere(array('lehreinheit_id'=>$le->lehreinheit_id)); + + if(isSuccess($lehreinheitdata) && hasData($lehreinheitdata)) + { + $ledata = getData($lehreinheitdata)[0]; + + + $lvid = $ledata->lehrveranstaltung_id; + $lehrfach_id = $ledata->lehrfach_id; + $obj->lehrform = $ledata->lehrform_kurzbz; + + $lehreinheitmitarbeiterdata = $this->ci->LehreinheitMitarbeiterModel->loadWhere(array('lehreinheit_id'=>$le->lehreinheit_id)); + $lemitarbeiterdata = getData($lehreinheitmitarbeiterdata); + + foreach($lemitarbeiterdata as $rowma) + { + $obj->lektor[] = array( + "mitarbeiter_uid"=> $rowma->mitarbeiter_uid, + "vorname"=>$rowma->mitarbeiter_uid, + "nachname"=>$rowma->mitarbeiter_uid, + "kurzbz"=>$rowma->mitarbeiter_uid + ); + } + } + else + { + // TODO + } + } + } + + $lehrfachdata = $this->ci->LehrveranstaltungModel->loadWhere(array('lehrveranstaltung_id' => $lehrfach_id)); + $lfdata = getData($lehrfachdata)[0]; + + $lehrveranstaltungdata = $this->ci->LehrveranstaltungModel->loadWhere(array('lehrveranstaltung_id' => $lvid)); + $lvdata = getData($lehrveranstaltungdata)[0]; + + $obj->topic = $lfdata->kurzbz.' '.$obj->lehrform; + + $orte = $this->ci->KalenderOrtModel->loadWhere(array('kalender_id'=>$rowstpl->kalender_id)); + $obj->ort_kurzbz = ''; + if(isSuccess($orte) && hasData($orte)) + { + $ortedata = getdata($orte); + foreach($ortedata as $ort); + { + $obj->ort_kurzbz .= $ort->ort_kurzbz; + } + } + $obj->titel = ''; + $obj->lehrfach = $lfdata->kurzbz; + $obj->lehrfach_bez = $lfdata->bezeichnung; + $obj->organisationseinheit = $lvdata->oe_kurzbz; + $obj->farbe = $lfdata->farbe; + $obj->lehrveranstaltung_id = $lvid; + $obj->kalender_id = $rowstpl->kalender_id; + + $stundenplan_data[] = $obj; + } + } + return $stundenplan_data; + } + + public function addKalenderEvent($user, $ort_kurzbz, $start_date, $end_date, $lehreinheit_id) + { + $kalenderresult = $this->ci->KalenderModel->insert(array( + 'von' => $start_date, + 'bis' => $end_date, + 'typ' => 'lehreinheit', + 'status_kurzbz' => 'planning', + 'insertvon' => $user, + 'insertamum' => date('Y-m-d H:i:s') + )); + + if(isSuccess($kalenderresult) && hasData($kalenderresult)) + { + $kalender_id = getData($kalenderresult); + + + $kalenderlehreinheitresult = $this->ci->KalenderLehreinheitModel->insert(array( + 'kalender_id' => $kalender_id, + 'lehreinheit_id' => $lehreinheit_id + )); + + if(isSuccess($kalenderlehreinheitresult)) + { + $kalenderOrtresult = $this->ci->KalenderOrtModel->insert(array( + 'kalender_id'=>$kalender_id, + 'ort_kurzbz'=>$ort_kurzbz + )); + } + + } + } + + public function updateKalenderEvent($user, $kalender_id, $ort_kurzbz, $start_date, $end_date) + { + /*TODO Checks: + Von-Tag muss gleich dem Bis-Tag sein + Bis darf nicht vor von liegen + + History erstellen + Sync Status setzen + */ + $this->ci->KalenderModel->update($kalender_id, + array( + 'von'=>$start_date, + 'updateamum'=>date('Y-m-d H:i:s'), + 'updatevon' => $user + ) + ); + return success(); + } +} diff --git a/application/models/ressource/Kalender_Lehreinheit_model.php b/application/models/ressource/Kalender_Lehreinheit_model.php new file mode 100644 index 000000000..008797055 --- /dev/null +++ b/application/models/ressource/Kalender_Lehreinheit_model.php @@ -0,0 +1,15 @@ +dbTable = 'lehre.tbl_kalender_lehreinheit'; + $this->pk = array('kalender_id','lehreinheit_id'); + $this->hasSequence = false; + } +} diff --git a/application/models/ressource/Kalender_Ort_model.php b/application/models/ressource/Kalender_Ort_model.php new file mode 100644 index 000000000..d45a45da5 --- /dev/null +++ b/application/models/ressource/Kalender_Ort_model.php @@ -0,0 +1,14 @@ +dbTable = 'lehre.tbl_kalender_ort'; + $this->pk = 'kalender_ort_id'; + } +} diff --git a/application/models/ressource/Kalender_model.php b/application/models/ressource/Kalender_model.php new file mode 100644 index 000000000..2ca8e6ac1 --- /dev/null +++ b/application/models/ressource/Kalender_model.php @@ -0,0 +1,14 @@ +dbTable = 'lehre.tbl_kalender'; + $this->pk = 'kalender_id'; + } +} diff --git a/application/models/ressource/Stundenplandev_Kalender_model.php b/application/models/ressource/Stundenplandev_Kalender_model.php new file mode 100644 index 000000000..c055b69e8 --- /dev/null +++ b/application/models/ressource/Stundenplandev_Kalender_model.php @@ -0,0 +1,14 @@ +dbTable = 'sync.tbl_stundenplandev_kalender'; + $this->pk = 'stundenplandev_kalender_id'; + } +} diff --git a/application/views/Tempus.php b/application/views/Tempus.php new file mode 100644 index 000000000..01ce19075 --- /dev/null +++ b/application/views/Tempus.php @@ -0,0 +1,46 @@ + 'Tempus', + 'axios027' => true, + 'bootstrap5' => true, + 'fontawesome6' => true, + 'vue3' => true, + 'vuedatepicker11' => true, + 'primevue3' => true, + 'tabulator5' => true, + 'phrases' => array( + 'global', + 'ui', + 'notiz', + ), + 'customCSSs' => [ + 'public/css/components/vue-datepicker.css', + 'public/css/components/primevue.css', + 'public/css/components/calendar.css', + 'public/css/Tempus.css' + ], + 'customJSs' => [ + #'vendor/npm-asset/primevue/tree/tree.min.js', + #'vendor/npm-asset/primevue/toast/toast.min.js' + 'vendor/moment/luxonjs/luxon.min.js' + ], + 'customJSModules' => [ + 'public/js/apps/Tempus.js' + ] + ); + + $this->load->view('templates/FHC-Header', $includesArray); +?> +
+ + +
+ +load->view('templates/FHC-Footer', $includesArray); ?> diff --git a/public/css/Tempus.css b/public/css/Tempus.css new file mode 100644 index 000000000..26f3679d4 --- /dev/null +++ b/public/css/Tempus.css @@ -0,0 +1,130 @@ +@import './Fhc.css'; +@import './components/searchbar/searchbar.css'; +@import './components/verticalsplit.css'; +@import './components/FilterComponent.css'; +@import './components/Tabs.css'; +@import './components/Notiz.css'; + +html { + font-size: .875em; + height: 100%; +} + +body { + /*display: flex;*/ + height: 100%; +} +.heightfull { + height: 95%; +} +.navbar-dark .navbar-brand:focus { + box-shadow: 0 0 0 .25rem rgba(13,110,253,.25); + z-index: 3; +} +#main { + height: 100%; +} +.tempus { + height: 100%; +} +.searchbar { + margin-right: 0!important; +} +.searchbar > .input-group { + margin-right: 0!important; +} +.searchbar > .input-group > * { + border-radius: 0!important; +} + +#sidebarMenu { + width: 0%; +} + +.tabulator-row.disabled.tabulator-row-odd .tabulator-cell { + color: var(--gray-400); +} +.tabulator-row.disabled.tabulator-row-even .tabulator-cell { + color: var(--gray-500); +} + +/* Dropdown Toolbar Interessent, submenu */ +.dropend .dropdown-toggle.d-flex::after { + height: 0; +} + +@media (min-width: 768px) { + #sidebarMenu { + visibility: visible!important; + transform: none; + position: inherit; + z-index: 1; + } +} + +.toast.toast-success { + color: #0f5132; + background-color: #d1e7dd!important; + border-color: #badbcc!important; +} +.toast.toast-danger { + color: #842029; + background-color: #f8d7da!important; + border-color: #f5c2c7!important; +} + +.has-filter .fa-filter { + color: var(--bs-success); +} + +#parkinglot { + border: 1px dashed; + width: 300px; + height: 100px; + color: #AAAAAA; + text-align: center; + font-size: large; + margin-top: 20px; + +} + +#coursechooser { + width: 300px; + min-height: 100px; + font-size: large; + margin-top: 50px; + border: 1px solid #ccc; + background: #eee; + text-align: left; + padding-left: 15px; +} + +#coursechooserheader { + font-weight: bold; + font-size: medium; +} + +#coursechooserfooter { + font-size: small; + color: #AAAAAA; +} + +.eckerl { + width: 200px; + margin: 10px 0; + padding: 2px 4px; + background: #00649c; + color: #fff; + font-size: .85em; + cursor: pointer; + border-radius: 2px; + box-shadow: 3px 3px 3px #bbb; +} + +:root{ + --fhc-calendar-pane-height: calc(100vh - 120px); +} + +.eckerltest { + box-shadow: 3px 3px 3px #ccc; +} diff --git a/public/js/api/factory/coursepicker.js b/public/js/api/factory/coursepicker.js new file mode 100644 index 000000000..aefe15619 --- /dev/null +++ b/public/js/api/factory/coursepicker.js @@ -0,0 +1,10 @@ + +export default { + getCourses(searchfilter) { + return { + method: 'get', + url: '/api/frontend/v1/tempus/getCourses', + params: { searchfilter } + }; + } +}; diff --git a/public/js/api/factory/kalender.js b/public/js/api/factory/kalender.js new file mode 100644 index 000000000..f0fd32e13 --- /dev/null +++ b/public/js/api/factory/kalender.js @@ -0,0 +1,60 @@ + +export default { + getRoomplan(ort_kurzbz, start_date, end_date) { + return { + method: 'get', + url: '/api/frontend/v1/Kalender/getRoomplan', + params: { ort_kurzbz, start_date, end_date } + }; + }, + getStundenplan(start_date, end_date) { + return { + method: 'get', + url: '/api/frontend/v1/Kalender/getStundenplan', + params: { start_date, end_date} + }; + }, + getStunden() { + return { + method: 'get', + url: '/api/frontend/v1/Kalender/Stunden', + params: {} + }; + }, + getOrtReservierungen(ort_kurzbz, start_date, end_date) { + return { + method: 'get', + url: `/api/frontend/v1/Kalender/Reservierungen/${ort_kurzbz}`, + params: { start_date, end_date} + }; + }, + getStundenplanReservierungen(start_date, end_date) { + return { + method: 'get', + url: '/api/frontend/v1/Kalender/Reservierungen', + params: { start_date, end_date } + }; + }, + getLehreinheitStudiensemester(lehreinheit_id) { + return { + method: 'get', + url: `/api/frontend/v1/Kalender/getLehreinheitStudiensemester/${lehreinheit_id}`, + params: {} + }; + }, + updateKalenderEvent(kalender_id, ort_kurzbz, start_date, end_date) { + return { + method: 'post', + url: '/api/frontend/v1/Kalender/updateKalenderEvent', + params: { kalender_id, ort_kurzbz, start_date, end_date} + }; + }, + addKalenderEvent(lehreinheit_id, ort_kurzbz, start_date, end_date) { + return { + method: 'post', + url: '/api/frontend/v1/Kalender/addKalenderEvent', + params: { lehreinheit_id, ort_kurzbz, start_date, end_date} + }; + }, + +}; diff --git a/public/js/apps/Tempus.js b/public/js/apps/Tempus.js new file mode 100644 index 000000000..e5596479e --- /dev/null +++ b/public/js/apps/Tempus.js @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2024 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import FhcTempus from "../components/Tempus/Tempus.js"; +import fhcapifactory from "./api/fhcapifactory.js"; + +import Phrasen from "../plugins/Phrasen.js"; + + +const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router; + +const router = VueRouter.createRouter({ + history: VueRouter.createWebHistory(), + routes: [ + { path: `/${ciPath}/Tempus`, component: FhcTempus }, + ] +}); + +const app = Vue.createApp(); + +app + .use(router) + .use(primevue.config.default, { + zIndex: { + overlay: 1100 + } + }) + .use(Phrasen) + .mount('#main'); diff --git a/public/js/components/Calendar/Tempus.js b/public/js/components/Calendar/Tempus.js new file mode 100644 index 000000000..34e8ac211 --- /dev/null +++ b/public/js/components/Calendar/Tempus.js @@ -0,0 +1,168 @@ +import FhcCalendar from "./Base.js"; + +import ApiLvPlan from '../../api/factory/lvPlan.js'; + +import { useEventLoader } from '../../composables/EventLoader.js'; + +import ModeDay from './Mode/Day.js'; +import ModeWeek from './Mode/Week.js'; +import ModeMonth from './Mode/Month.js'; + +export default { + name: "CalendarTempus", + components: { + FhcCalendar + }, + inject: [ + "renderers" + ], + props: { + timezone: { + type: String, + required: true + }, + date: { + type: [Date, String, Number, luxon.DateTime], + default: luxon.DateTime.local() + }, + mode: { + type: String, + default: 'Week' + }, + getPromiseFunc: { + type: Function, + required: true + } + }, + emits: [ + "update:date", + "update:mode", + "update:range", + "drop" + ], + data() { + return { + modes: { + week: Vue.markRaw(ModeWeek), + month: Vue.markRaw(ModeMonth) + }, + modeOptions: { + day: { + emptyMessage: Vue.computed(() => this.$p.t('lehre/noLvFound')), + emptyMessageDetails: Vue.computed(() => this.$p.t('lehre/noLvFound')) + }, + week: { + collapseEmptyDays: false + } + }, + teachingunits: null + }; + }, + computed: { + backgrounds() { + let now = luxon.DateTime.now().setZone(this.timezone); + + if (this.mode == 'Month') + return [ + { + class: 'background-past', + end: now.startOf('day') + } + ]; + + return [ + { + class: 'background-past', + end: now, + label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false }) + } + ]; + } + }, + methods: { + eventStyle(event) { + if (!event.farbe) + return undefined; + return '--event-bg:#' + event.farbe; + }, + updateRange(rangeInterval) { + this.rangeInterval = rangeInterval; + this.$emit('update:range', rangeInterval); + }, + ondrop(e, start, end){ + this.$emit('drop', e, start, end); + } + }, + setup(props, context) { + const rangeInterval = Vue.ref(null); + + const { events, lv } = useEventLoader(rangeInterval, props.getPromiseFunc); + + Vue.watch(lv, newValue => { + context.emit('update:lv', newValue); + }); + + return { + rangeInterval, + events, + lv + }; + }, + created() { + this.$api + .call(ApiLvPlan.getStunden()) + .then(res => { + return this.teachingunits = res.data.map(el => ({ + id: el.stunde, + start: el.beginn, + end: el.ende + })); + }); + }, + template: /* html */` + + + + ` +} diff --git a/public/js/components/Tempus/Coursepicker.js b/public/js/components/Tempus/Coursepicker.js new file mode 100644 index 000000000..8130aab2a --- /dev/null +++ b/public/js/components/Tempus/Coursepicker.js @@ -0,0 +1,89 @@ +import ApiCoursePicker from '../../api/factory/coursepicker.js'; + +export default { + components: { + }, + provide() { + return { + }; + }, + data() { + return { + courses: null + } + }, + props: { + + }, + computed: { + }, + methods: { + loadCourses: function(){ + + Promise.allSettled([ + this.$api.call(ApiCoursePicker.getCourses("test")), + ]).then((result) => { + let promise_events = []; + result.forEach((promise_result) => { + if (promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success") { + let data = promise_result.value.data; + if (data && data.forEach) { + + data.forEach((entry, i) => { + entry.showname = entry.studiengang_kurzbz+entry.semester+' - ' + entry.kurzbz + ' ' + entry.lektoren.toString(); + entry.tag = ''; + entry.mode = 'single'; + }); + } + promise_events = promise_events.concat(data); + } + }) + this.courses = promise_events; + + }); + }, + dragstart: function(evt, course) { + const transferdata = { + type: 'lehreinheit', + id: course.lehreinheit_id, + mode: course.mode + }; + + event.dataTransfer.setData('text', JSON.stringify(transferdata)); + }, + keydown: function(evt, course) { + + switch(evt.key) + { + case "1": + course.tag = '#Singleweek'; + course.mode = 'single'; + break + case "2": + course.tag = '#Multiweek'; + course.mode = 'multi'; + break + } + + } + + }, + created() { + this.loadCourses(); + //document.addEventListener('keydown', function () { console.log("a"); }); + }, + mounted() { + }, + template: /*html*/` +
+
+ Course + +
+ {{course.showname}} + {{course.tag}} +
+ Drag & Drop on Calender +
+
` +} diff --git a/public/js/components/Tempus/Tempus.js b/public/js/components/Tempus/Tempus.js new file mode 100644 index 000000000..6bad39517 --- /dev/null +++ b/public/js/components/Tempus/Tempus.js @@ -0,0 +1,287 @@ +/** + * Copyright (C) 2024 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import VueDatePicker from '../vueDatepicker.js.php' +import CoreSearchbar from "../searchbar/searchbar.js"; +import VerticalSplit from "../verticalsplit/verticalsplit.js"; +import FhcCalendar from "../Calendar/Tempus.js"; +import FhcCoursepicker from "../Tempus/Coursepicker.js"; +import ApiKalender from '../../api/factory/kalender.js'; +import ApiSearchbar from "../../api/factory/searchbar.js"; +import ApiRenderers from '../../api/factory/renderers.js'; + +export default { + name: "Tempus", + components: { + CoreSearchbar, + VerticalSplit, + FhcCalendar, + FhcCoursepicker + }, + props: { + defaultSemester: String, + config: Object, + permissions: Object, + tempusRoot: String, + cisRoot: String, + activeAddons: String, // semicolon separated list of active addons + viewData: Object, + }, + provide() { + return { + cisRoot: this.cisRoot, + defaultSemester: this.defaultSemester, + $reloadList: () => { + this.$refs.stvList.reload(); + }, + renderers: Vue.computed(() => this.renderers), + } + }, + data() { + return { + selected: [], + searchbaroptions: { + origin: 'tempus', + cssclass: "position-relative", + calcheightonly: true, + types: [ + //"student", + "raum", + //"mitarbeiter" + ], + actions: { + raum: { + defaultaction: { + type: "function", + action: this.setOrt + }, + childactions: [ + ] + } + } + }, + lv_id: null, + events: null, + minimized: false, + calendarDate: luxon.DateTime.local(), //new CalendarDate(new Date()), + currentlySelectedEvent: null, + //currentDay: new Date(), + studiensemesterKurzbz: this.defaultSemester, + lists: { + nations: [], + sprachen: [], + geschlechter: [] + }, + renderers: null, + ort_kurzbz: 'EDV_A5.08', + } + }, + methods: { + setOrt: function(data) + { + // Wenn bei der Suche ein Ort ausgewaehlt wird, dann wir der Ort gesetzt und ein Reload getriggert durch den watcher + this.ort_kurzbz = data.ort_kurzbz; + }, + handleChangeDate() { + }, + handleChangeMode() { + }, + searchfunction(params) { + return this.$api.call(ApiSearchbar.search(params)); + }, + getPromiseFunc(start, end) { + return [ + this.$api.call(ApiKalender.getRoomplan(this.ort_kurzbz, '2025-10-01','2025-10-30')),//start.toISODate(), end.toISODate())), + ]; + }, + parkingdrop: function(evt) + { + evt.preventDefault(); + var data = JSON.parse(evt.dataTransfer.getData("text")); + alert('parked Data:'+data.id); + console.log(data); + }, + dropHandler: function(event, start, end) + { + let day = start.date.toFormat('yyyy-MM-dd'); + let time = start.date.toFormat('hh:mm'); + + let dropdata = JSON.parse(event.dataTransfer.getData('text')) + + if(dropdata.type=='kalender') + { + let kalender_id = dropdata.id; + + Promise.allSettled([ + this.$api.call(ApiKalender.updateKalenderEvent(kalender_id, this.ort_kurzbz, day+' '+time, null)) + ]).then((result) => { + let promise_events = []; + result.forEach((promise_result) => { + if (promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success") + { + // TODO - reload + } + }) + }); + } + else if(dropdata.type=='lehreinheit') + { + // TODO Calculate end time + let lehreinheit_id = dropdata.id; + let start_time = day+' '+time; + let end_time = start.date.plus({ minutes: 45 }).toFormat('yyyy-MM-dd hh:mm'); + alert("mode:"+dropdata.mode); + + Promise.allSettled([ + this.$api.call(ApiKalender.addKalenderEvent(lehreinheit_id, this.ort_kurzbz, start_time, end_time)) + ]).then((result) => { + let promise_events = []; + result.forEach((promise_result) => { + if (promise_result.status === 'fulfilled' && promise_result.value.meta.status === "success") { + + // TODO - reload + } + }) + }); + } + else + { + alert("Unbekannte Daten gedroppt"); + } + }, + onRightClick: function(evt) { + this.$refs.EventContextMenu.show(evt); + } + }, + watch: { + ort_kurzbz: function (newValue, oldValue) { + // Raumansicht laden wenn der Ort geaendert wird + } + }, + computed: { + currentDay() { + return luxon.DateTime.now().setZone(this.config.timezone).toISODate(); + }, + currentMode() { + return 'week'; + }, + }, + async created() + { + await this.$api + .call(ApiRenderers.loadRenderers()) + .then(res => res.data) + .then(data => { + for (let rendertype of Object.keys(data)) { + let modalTitle = null; + let modalContent = null; + let calendarEvent = null; + if (data[rendertype].modalTitle) + modalTitle = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].modalTitle))); + if (data[rendertype].modalContent) + modalContent = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].modalContent))); + if (data[rendertype].calendarEvent) + calendarEvent = Vue.markRaw(Vue.defineAsyncComponent(() => import(data[rendertype].calendarEvent))); + + if (data[rendertype].calendarEventStyles){ + var head = document.head; + if(!head.querySelector(`link[href="${data[rendertype].calendarEventStyles}"]`)){ + var link = document.createElement("link"); + link.type = "text/css"; + link.rel = "stylesheet"; + link.href = data[rendertype].calendarEventStyles; + head.appendChild(link); + } + } + + if(this.renderers === null) { + this.renderers = {}; + } + if (!this.renderers[rendertype]) { + this.renderers[rendertype] = {} + } + this.renderers[rendertype].modalTitle = modalTitle; + this.renderers[rendertype].modalContent = modalContent; + this.renderers[rendertype].calendarEvent = calendarEvent; + } + }); + }, + mounted() { + + + }, + template: ` +
+ +
+
+ + +
+ +
+
+
+
` +}; diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index e51d83a70..42807ded0 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -84,6 +84,7 @@ require_once('dbupdate_3.4/60882_lehrfaecherverteilung_favorites.php'); require_once('dbupdate_3.4/66982_berufsschule.php'); require_once('dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php'); require_once('dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php'); +require_once('dbupdate_3.4/46975_tempus.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

Pruefe Tabellen und Attribute!

'; diff --git a/system/dbupdate_3.4/46975_tempus.php b/system/dbupdate_3.4/46975_tempus.php new file mode 100644 index 000000000..1e7d9a89a --- /dev/null +++ b/system/dbupdate_3.4/46975_tempus.php @@ -0,0 +1,134 @@ +db_query("SELECT kalender_id FROM lehre.tbl_kalender LIMIT 1")) +{ + $qry = "CREATE TABLE lehre.tbl_kalender ( + kalender_id bigserial NOT NULL, + von timestamp NOT NULL, + bis timestamp NOT NULL, + typ character varying(32), + status_kurzbz character varying(32), + vorgaenger_kalender_id bigint, + insertamum timestamp DEFAULT now(), + insertvon character varying(32), + updateamum timestamp DEFAULT now(), + updatevon character varying(32), + CONSTRAINT tbl_kalender_pk PRIMARY KEY (kalender_id) + ); + + COMMENT ON TABLE lehre.tbl_kalender IS 'Schedule Calendar Events'; + + CREATE TABLE lehre.tbl_kalender_typ ( + typ character varying(32) NOT NULL, + CONSTRAINT tbl_kalender_typ_pk PRIMARY KEY (typ) + ); + + COMMENT ON TABLE lehre.tbl_kalender_typ IS 'Type of Calendar Events'; + + INSERT INTO lehre.tbl_kalender_typ (typ) VALUES (E'lehreinheit'); + INSERT INTO lehre.tbl_kalender_typ (typ) VALUES (E'reservierung'); + INSERT INTO lehre.tbl_kalender_typ (typ) VALUES (E'event'); + + CREATE TABLE lehre.tbl_kalender_lehreinheit ( + lehreinheit_id integer NOT NULL, + kalender_id bigint NOT NULL, + CONSTRAINT tbl_kalender_lehreinheit_pk PRIMARY KEY (lehreinheit_id,kalender_id) + ); + + COMMENT ON TABLE lehre.tbl_kalender_lehreinheit IS 'Connects Calender Events to Courses'; + + ALTER TABLE lehre.tbl_kalender_lehreinheit ADD CONSTRAINT tbl_lehreinheit_fk FOREIGN KEY (lehreinheit_id) + REFERENCES lehre.tbl_lehreinheit (lehreinheit_id) MATCH FULL + ON DELETE RESTRICT ON UPDATE CASCADE; + + CREATE TABLE lehre.tbl_kalender_ort ( + kalender_ort_id bigserial NOT NULL, + location text, + ort_kurzbz character varying(32), + kalender_id bigint, + CONSTRAINT tbl_kalender_ort_pk PRIMARY KEY (kalender_ort_id) + ); + + COMMENT ON TABLE lehre.tbl_kalender_ort IS E'Connects one Calendar Entry to multiple Rooms'; + + COMMENT ON COLUMN lehre.tbl_kalender_ort.location IS E'Text Description if not a physical inhouse Room (External Location, Conference Link, etc)'; + + ALTER TABLE lehre.tbl_kalender_ort ADD CONSTRAINT tbl_kalender_fk FOREIGN KEY (kalender_id) + REFERENCES lehre.tbl_kalender (kalender_id) MATCH FULL + ON DELETE RESTRICT ON UPDATE CASCADE; + + ALTER TABLE lehre.tbl_kalender_ort ADD CONSTRAINT tbl_ort_fk FOREIGN KEY (ort_kurzbz) + REFERENCES public.tbl_ort (ort_kurzbz) MATCH FULL + ON DELETE RESTRICT ON UPDATE CASCADE; + + ALTER TABLE lehre.tbl_kalender_lehreinheit ADD CONSTRAINT tbl_kalender_fk FOREIGN KEY (kalender_id) + REFERENCES lehre.tbl_kalender (kalender_id) MATCH FULL + ON DELETE RESTRICT ON UPDATE CASCADE; + + CREATE TABLE lehre.tbl_kalender_status ( + status_kurzbz character varying(32) NOT NULL, + bezeichnung text, + CONSTRAINT tbl_kalender_status_pk PRIMARY KEY (status_kurzbz) + ); + + COMMENT ON TABLE lehre.tbl_kalender_status IS 'Calender visibility Status'; + + INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung) VALUES (E'planning', E'planning'); + INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung) VALUES (E'tosync', E'tosync'); + INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung) VALUES (E'todelete', E'todelete'); + INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung) VALUES (E'visible_lektor', E'Sichtbar für Lektoren'); + INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung) VALUES (E'deleted', E'deleted'); + INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung) VALUES (E'archived', E'archived'); + INSERT INTO lehre.tbl_kalender_status (status_kurzbz, bezeichnung) VALUES (E'visible_student', E'Sichtbar für Studierende'); + + ALTER TABLE lehre.tbl_kalender ADD CONSTRAINT tbl_kalender_status_fk FOREIGN KEY (status_kurzbz) + REFERENCES lehre.tbl_kalender_status (status_kurzbz) MATCH FULL + ON DELETE RESTRICT ON UPDATE CASCADE; + + ALTER TABLE lehre.tbl_kalender ADD CONSTRAINT tbl_kalender_typ_fk FOREIGN KEY (typ) + REFERENCES lehre.tbl_kalender_typ (typ) MATCH FULL + ON DELETE RESTRICT ON UPDATE CASCADE; + + GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender to vilesci; + GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender to web; + + GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_status to vilesci; + GRANT SELECT ON lehre.tbl_kalender_status to web; + + GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_lehreinheit to vilesci; + GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_lehreinheit to web; + + GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_ort to vilesci; + GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_ort to web; + + GRANT SELECT, UPDATE, INSERT, DELETE ON lehre.tbl_kalender_typ to vilesci; + GRANT SELECT ON lehre.tbl_kalender_typ to web; + + CREATE TABLE sync.tbl_stundenplandev_kalender( + stundenplandev_kalender_id bigserial NOT NULL, + stundenplandev_id integer NOT NULL, + kalender_id bigint NOT NULL, + lastupdate timestamp, + CONSTRAINT tbl_stundenplandev_kalender_pk PRIMARY KEY (stundenplandev_kalender_id) + ); + + GRANT SELECT, UPDATE, INSERT, DELETE ON sync.tbl_stundenplandev_kalender to vilesci; + COMMENT ON TABLE sync.tbl_stundenplandev_kalender IS 'Migration from old Stundenplan to new Kalender Table'; + + GRANT USAGE ON lehre.tbl_kalender_kalender_id_seq TO vilesci; + GRANT USAGE ON lehre.tbl_kalender_kalender_id_seq TO web; + GRANT USAGE ON sync.tbl_stundenplandev_kalender_stundenplandev_kalender_id_seq TO vilesci; + GRANT USAGE ON lehre.tbl_kalender_ort_kalender_ort_id_seq TO vilesci; + + CREATE INDEX idx_kalender_ort_kalender_id ON lehre.tbl_kalender_ort USING btree (kalender_id); + CREATE INDEX idx_kalender_ort_kalender_id_ort_kurzbz ON lehre.tbl_kalender_ort USING btree (ort_kurzbz, kalender_id); + CREATE INDEX idx_kalender_von ON lehre.tbl_kalender USING btree (von); + "; + + if(!$db->db_query($qry)) + echo 'lehre.tbl_kalender: '.$db->db_last_error().'
'; + else + echo '
lehre.tbl_kalender: neue Tabellen hinzugefuegt'; +}