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 */`
+ $emit('update:date', newDate, newMode)"
+ @update:mode="(newMode, newDate) => $emit('update:mode', newMode, newDate)"
+ @update:range="updateRange"
+ >
+
+
+
+
+
+
+
+
+
+
+ `
+}
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.showname}}
+ {{course.tag}}
+
+
+
+
`
+}
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';
+}