diff --git a/application/config/routes.php b/application/config/routes.php index bb0b0f65e..7a42e6c69 100644 --- a/application/config/routes.php +++ b/application/config/routes.php @@ -140,6 +140,8 @@ $route['api/frontend/v1/treemenudata/lvverwaltung/stdsem/(:any)/emp/(:any)/stg/( // load routes from extensions, also look for environment-specific configs $subdirs = ['application/config/extensions', 'application/config/' . ENVIRONMENT . '/extensions']; +$route['api/frontend/v1/tempus/Tags/getTagsByCalendar/(:num)'] = 'api/frontend/v1/tempus/Tags/getTagsByCalendar/$1'; + foreach($subdirs as $subdir) { if(is_dir($subdir)) diff --git a/application/config/tempus.php b/application/config/tempus.php index e3e18b954..833f5655f 100644 --- a/application/config/tempus.php +++ b/application/config/tempus.php @@ -2,4 +2,11 @@ $config['send_update_mails'] = false; $config['calendar_start'] = 7; -$config['calendar_end'] = 23; \ No newline at end of file +$config['calendar_end'] = 23; + + +$config['tempus_tags'] = [ + 'prioone' => ['readonly' => false], + 'priotwo' => ['readonly' => true], + 'hinweis' => ['readonly' => false], +]; \ No newline at end of file diff --git a/application/controllers/Tempus.php b/application/controllers/Tempus.php index 139f0b690..f0e2623c3 100644 --- a/application/controllers/Tempus.php +++ b/application/controllers/Tempus.php @@ -16,25 +16,54 @@ 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->OrganisationseinheitModel->addJoin('tbl_studiengang', 'oe_kurzbz', 'LEFT'); + + $this->OrganisationseinheitModel->addSelect('public.tbl_organisationseinheit.organisationseinheittyp_kurzbz, + public.tbl_organisationseinheit.bezeichnung, + public.tbl_studiengang.studiengang_kz, + public.tbl_organisationseinheit.oe_kurzbz, + tbl_studiengang.typ, + COALESCE(public.tbl_studiengang.bezeichnung, \'\') as stgbezeichnung'); + $this->OrganisationseinheitModel->addOrder('tbl_organisationseinheit.aktiv', 'DESC'); + $this->OrganisationseinheitModel->addOrder('tbl_organisationseinheit.organisationseinheittyp_kurzbz'); + $organisationen = getData($this->OrganisationseinheitModel->loadWhere(array("tbl_organisationseinheit.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') + 'admin' => $this->permissionlib->isBerechtigt('admin'), + 'stundenraster' => $this->permissionlib->isBerechtigt('basis/tempus_stundenraster'), ], - '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/Config.php b/application/controllers/api/frontend/v1/tempus/Config.php index 30be2cc6f..69711d455 100644 --- a/application/controllers/api/frontend/v1/tempus/Config.php +++ b/application/controllers/api/frontend/v1/tempus/Config.php @@ -11,10 +11,10 @@ class Config extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'get' => ['admin:r', 'assistenz:r'], - 'getHeader' => ['admin:r', 'assistenz:r'], - 'set' => ['admin:r', 'assistenz:r'], - 'updateCollision' => ['admin:r', 'assistenz:r'], + 'get' => ['admin:r', 'assistenz:rw', 'lehre/lvplan:rw'], + 'getHeader' => ['admin:r', 'assistenz:rw', 'lehre/lvplan:rw'], + 'set' => ['admin:r', 'assistenz:rw', 'lehre/lvplan:rw'], + 'updateCollision' => ['admin:r', 'assistenz:rw', 'lehre/lvplan:rw'], ]); // Load Phrases @@ -32,7 +32,17 @@ class Config extends FHCAPI_Controller { $config = []; - $result = $this->_ci->VariableModel->getVariables(getAuthUID(), ['ignore_kollision', 'kollision_student', 'ignore_reservierung', 'ignore_zeitsperre', 'ignore_resources_collisions']); + $var_array = array( + 'ignore_kollision', + 'kollision_student', + 'ignore_reservierung', + 'ignore_zeitsperre', + 'ignore_resources_collisions', + 'roomless_planning', + 'priority_room_planning', + 'dialog_room_planning' + ); + $result = $this->_ci->VariableModel->getVariables(getAuthUID(), $var_array); $data = $this->getDataOrTerminateWithError($result); $config['ignore_kollision'] = [ @@ -67,6 +77,25 @@ class Config extends FHCAPI_Controller "value" => ($data['ignore_resources_collisions'] ?? 'false') === 'true' ]; + $config['roomless_planning'] = [ + "type" => "checkbox", + "label" => $this->p->t('ui', 'roomless_planning'), + "value" => ($data['roomless_planning'] ?? 'false') === 'true' + ]; + + $config['priority_room_planning'] = [ + "type" => "checkbox", + "label" => $this->p->t('ui', 'priority_room_planning'), + "value" => ($data['priority_room_planning'] ?? 'false') === 'true' + ]; + + $config['dialog_room_planning'] = [ + "type" => "checkbox", + "label" => $this->p->t('ui', 'dialog_room_planning'), + "value" => ($data['dialog_room_planning'] ?? 'false') === 'true' + ]; + + $this->terminateWithSuccess($config); } public function getHeader() @@ -92,31 +121,26 @@ class Config extends FHCAPI_Controller public function set() { - $this->_ci->VariableModel->setVariable( - getAuthUID(), + $configs = array( 'ignore_kollision', - $this->input->post('ignore_kollision') === true ? 'true' : 'false' - ); - $this->_ci->VariableModel->setVariable( - getAuthUID(), 'kollision_student', - $this->input->post('kollision_student') === true ? 'true' : 'false' - ); - $this->_ci->VariableModel->setVariable( - getAuthUID(), 'ignore_reservierung', - $this->input->post('ignore_reservierung') === true ? 'true' : 'false' - ); - $this->_ci->VariableModel->setVariable( - getAuthUID(), 'ignore_zeitsperre', - $this->input->post('ignore_zeitsperre') === true ? 'true' : 'false' - ); - $this->_ci->VariableModel->setVariable( - getAuthUID(), 'ignore_resources_collisions', - $this->input->post('ignore_resources_collisions') === true ? 'true' : 'false' + 'roomless_planning', + 'priority_room_planning', + 'dialog_room_planning' ); + + foreach ($configs as $config) + { + $this->_ci->VariableModel->setVariable( + getAuthUID(), + $config, + $this->input->post($config) === true ? 'true' : 'false' + ); + } + $this->terminateWithSuccess(); } diff --git a/application/controllers/api/frontend/v1/tempus/Coursepicker.php b/application/controllers/api/frontend/v1/tempus/Coursepicker.php index fd4ca5c75..28b10920f 100644 --- a/application/controllers/api/frontend/v1/tempus/Coursepicker.php +++ b/application/controllers/api/frontend/v1/tempus/Coursepicker.php @@ -5,11 +5,13 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); class Coursepicker extends FHCAPI_Controller { private $_ci; + + const ALLOWED_COURSE_FILTER = ['uid', 'stg']; + public function __construct() { parent::__construct([ - 'search' => self::PERM_LOGGED, - 'getByStg' => self::PERM_LOGGED + 'getCourses' => 'lehre/lvplan:rw', ]); $this->_ci = &get_instance(); @@ -22,88 +24,40 @@ 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() + public function getCourses() { $this->_ci->form_validation->set_data($_POST); - $this->_ci->form_validation->set_rules('studiengaenge[]',"studiengaenge","required"); $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()); - $studiengaenge = $this->input->post('studiengaenge'); - $studiensemester_kurzbz = $this->input->post('studiensemester_kurzbz'); + $studiensemester_kurzbz = $this->_ci->input->post('studiensemester_kurzbz', TRUE); + $filter = $this->_checkFilter(self::ALLOWED_COURSE_FILTER); + + $this->_getBasePlan($studiensemester_kurzbz); + + $this->_ci->LehreinheitModel->db->group_start(); + if (isset($filter->stg)) + $this->_addStgWhere($filter->stg); + + if (isset($filter->uid)) + { + $uid_array = (array) $filter->uid; + $this->_ci->LehreinheitModel->db->or_where_in('tbl_benutzer.uid', $uid_array); + } + + $this->_ci->LehreinheitModel->db->group_end(); + + $this->_ci->LehreinheitModel->addOrder('lehreinheit_id'); + $result = $this->_ci->LehreinheitModel->load(); + $result = hasData($result) ? getData($result) : array(); + $this->terminateWithSuccess($this->_mapEvents($result)); + } + + private function _getBasePlan($studiensemester_kurzbz) + { $this->_ci->LehreinheitModel->addSelect(' tbl_lehreinheit.lehreinheit_id, tbl_lehreinheit.lvnr, @@ -142,6 +96,15 @@ class Coursepicker extends FHCAPI_Controller $this->_ci->LehreinheitModel->addJoin('public.tbl_benutzer', 'ma.mitarbeiter_uid = tbl_benutzer.uid'); $this->_ci->LehreinheitModel->addJoin('public.tbl_person', 'tbl_benutzer.person_id = tbl_person.person_id'); + $this->_ci->LehreinheitModel->db->group_start(); + $this->_ci->LehreinheitModel->db->where('tbl_lehrform.verplanen', true); + $this->_ci->LehreinheitModel->db->where('tbl_lehreinheit.studiensemester_kurzbz', $studiensemester_kurzbz); + $this->_ci->LehreinheitModel->db->group_end(); + + } + + private function _addStgWhere($studiengaenge) + { $this->_ci->LehreinheitModel->db->group_start(); $first = true; foreach ($studiengaenge as $studiengang) @@ -165,14 +128,10 @@ class Coursepicker extends FHCAPI_Controller $this->_ci->LehreinheitModel->db->group_end(); } $this->_ci->LehreinheitModel->db->group_end(); + } - $result = $this->_ci->LehreinheitModel->loadWhere(array( - 'tbl_lehrform.verplanen' => true, - 'tbl_lehreinheit.studiensemester_kurzbz' => $studiensemester_kurzbz - )); - - $result = hasData($result) ? getData($result) : array(); - + private function _mapEvents($result) + { $lehreinheit_ids = array_values(array_unique(array_column($result, 'lehreinheit_id'))); $offen_map = array(); @@ -281,10 +240,8 @@ class Coursepicker extends FHCAPI_Controller $group->verplant = $this->_formatArr($group->verplant); $group->offenestunden = $this->_formatArr($group->offenestunden); } - - $this->terminateWithSuccess(array_values($grouped)); + return array_values($grouped); } - private function _formatArr($arr) { $values = array_values(array_unique($arr)); @@ -296,5 +253,24 @@ class Coursepicker extends FHCAPI_Controller return $formatted; } + private function _checkFilter($filters) + { + $filter_valid = false; + $filter_object = new stdClass(); + foreach ($filters as $filter) + { + if ($this->_ci->input->post($filter)) + { + $filter_valid = true; + $filter_object->$filter = $this->_ci->input->post($filter); + } + } + + if (!$filter_valid) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + return $filter_object; + } + } diff --git a/application/controllers/api/frontend/v1/tempus/Favorites.php b/application/controllers/api/frontend/v1/tempus/Favorites.php index 94ee39707..3bfb5b9c1 100644 --- a/application/controllers/api/frontend/v1/tempus/Favorites.php +++ b/application/controllers/api/frontend/v1/tempus/Favorites.php @@ -7,8 +7,8 @@ class Favorites extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'index' => self::PERM_LOGGED, - 'set' => self::PERM_LOGGED + 'index' => 'lehre/lvplan:rw', + 'set' => 'lehre/lvplan:rw' ]); // Load models diff --git a/application/controllers/api/frontend/v1/tempus/Info.php b/application/controllers/api/frontend/v1/tempus/Info.php index b8ab2b9b0..9ef71da3e 100644 --- a/application/controllers/api/frontend/v1/tempus/Info.php +++ b/application/controllers/api/frontend/v1/tempus/Info.php @@ -12,7 +12,7 @@ class Info extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'getStudiengaenge' => self::PERM_LOGGED, + 'getStudiengaenge' => 'lehre/lvplan:rw', ]); $this->_ci =& get_instance(); diff --git a/application/controllers/api/frontend/v1/tempus/Kalender.php b/application/controllers/api/frontend/v1/tempus/Kalender.php index 63b1ac9d5..ccf6fdf75 100644 --- a/application/controllers/api/frontend/v1/tempus/Kalender.php +++ b/application/controllers/api/frontend/v1/tempus/Kalender.php @@ -8,18 +8,19 @@ class Kalender extends FHCAPI_Controller const ALLOWED_PLAN_FILTER = ['ort', 'uid', 'stg']; const ALLOWED_ROOM_FILTER = ['lehreinheit_id', 'kalender_id']; - const ALLOWED_TO_UPDATE = ['start_time', 'end_time', 'ort_kurzbz']; + const ALLOWED_TO_UPDATE = ['start_time', 'end_time', 'orte']; /** * Object initialization */ public function __construct() { parent::__construct([ - 'getStunden' => self::PERM_LOGGED, - 'getCalendarHours' => self::PERM_LOGGED, - 'getPlan' => self::PERM_LOGGED, - 'getPlanByOrt' => self::PERM_LOGGED, - 'getRaumvorschlag' => self::PERM_LOGGED, + 'getStunden' => 'lehre/lvplan:rw', + 'getCalendarHours' => 'lehre/lvplan:rw', + 'getPlan' => 'lehre/lvplan:rw', + 'getPlanByOrt' => 'lehre/lvplan:rw', + 'getRaumvorschlag' => 'lehre/lvplan:rw', + 'getRaumvorschlagSlots' => 'lehre/lvplan:rw', 'getLehreinheiten' => 'lehre/lvplan:rw', 'getHistory' => 'lehre/lvplan:rw', 'deleteEntry' => 'lehre/lvplan:rw', @@ -27,8 +28,8 @@ class Kalender extends FHCAPI_Controller 'syncToStudent' => 'lehre/lvplan:rw', 'getPlanLecturer' =>'lehre/lvplan:rw', 'getPlanStudent' => 'lehre/lvplan:rw', - 'getZeitwuensche' => self::PERM_LOGGED, - 'getZeitsperren' => self::PERM_LOGGED, + 'getZeitwuensche' => 'lehre/lvplan:rw', + 'getZeitsperren' => 'lehre/lvplan:rw', 'updateKalenderEvent' => 'lehre/lvplan:rw', 'deleteOrtEntry' => 'lehre/lvplan:rw', 'deleteFromKalenderEvent' => 'lehre/lvplan:rw', @@ -44,6 +45,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' @@ -227,7 +229,7 @@ class Kalender extends FHCAPI_Controller $updateFields = $this->_checkUpdate($this->_ci->input->post('updatedInfos', TRUE)); $kalender_id = $this->_ci->input->post('kalender_id', TRUE); - $result = $this->_ci->kalenderlib->updateKalenderEvent($kalender_id, $updateFields->ort_kurzbz ?? null, $updateFields->start_time ?? null, $updateFields->end_time ?? null); + $result = $this->_ci->kalenderlib->updateKalenderEvent($kalender_id, $updateFields->orte ?? null, $updateFields->start_time ?? null, $updateFields->end_time ?? null, $updateFields->location ?? null); if (isError($result)) $this->terminateWithError(getError($result), $result->code); @@ -260,6 +262,22 @@ class Kalender extends FHCAPI_Controller $this->terminateWithSuccess($this->_ci->raumvorschlaglib->getVorschlaege($filter->kalender_id)); } + public function getRaumvorschlagSlots() + { + $this->_ci->form_validation->set_data($_GET); + $this->_ci->form_validation->set_rules('lehreinheit_id',"lehreinheit_id","required"); + $this->_ci->form_validation->set_rules('start_date',"start_date","required"); + $this->_ci->form_validation->set_rules('end_date',"end_date","required"); + + if($this->_ci->form_validation->run() === FALSE) + $this->terminateWithValidationErrors($this->_ci->form_validation->error_array()); + + $lehreinheit_id = $this->_ci->input->get('lehreinheit_id', TRUE); + $start_date = $this->_ci->input->get('start_date', TRUE); + $end_date = $this->_ci->input->get('end_date', TRUE); + $this->terminateWithSuccess(getData($this->_ci->raumvorschlaglib->getVorschlaegeSlots($lehreinheit_id, $start_date, $end_date))); + } + public function getLehreinheiten() { $this->_ci->form_validation->set_data($_GET); @@ -311,7 +329,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..13e4cc012 --- /dev/null +++ b/application/controllers/api/frontend/v1/tempus/KalenderSync.php @@ -0,0 +1,304 @@ + ['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, 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'); + $this->_ci->KalenderStatusModel->addJoin('public.tbl_studiensemester', 'tbl_kalender_syncstatus.studiensemester_kurzbz = tbl_studiensemester.studiensemester_kurzbz'); + $this->_ci->KalenderStatusModel->addOrder('public.tbl_studiensemester.start', 'DESC'); + + + if ($studiensemester_kurzbz === 'all') + $data = $this->_ci->KalendersyncstatusModel->load(); + else + $data = $this->_ci->KalendersyncstatusModel->loadWhere(array('tbl_kalender_syncstatus.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/controllers/api/frontend/v1/tempus/OperationalResourceToCalenderAPI.php b/application/controllers/api/frontend/v1/tempus/OperationalResourceToCalenderAPI.php index f5a09deff..76f6fd369 100644 --- a/application/controllers/api/frontend/v1/tempus/OperationalResourceToCalenderAPI.php +++ b/application/controllers/api/frontend/v1/tempus/OperationalResourceToCalenderAPI.php @@ -9,9 +9,9 @@ class OperationalResourceToCalenderAPI extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'getAssignedResourcesByCalender' => ['admin:r', 'assistenz:r'], - 'storeResourcesToCalendarRelationship' => ['admin:w', 'assistenz:w'], - 'getSchedulableResourcesByCalendar' => ['admin:r', 'assistenz:r'], + 'getAssignedResourcesByCalender' => ['admin:r', 'assistenz:r', 'lehre/lvplan:rw'], + 'storeResourcesToCalendarRelationship' => ['admin:w', 'assistenz:w', 'lehre/lvplan:rw'], + 'getSchedulableResourcesByCalendar' => ['admin:r', 'assistenz:r', 'lehre/lvplan:rw'], ]); $this->load->model('ressource/BetriebsmittelKalender_model', 'BetriebsmittelKalenderModel'); diff --git a/application/controllers/api/frontend/v1/tempus/Reservierung.php b/application/controllers/api/frontend/v1/tempus/Reservierung.php index 1b1baa963..5a7067bcf 100644 --- a/application/controllers/api/frontend/v1/tempus/Reservierung.php +++ b/application/controllers/api/frontend/v1/tempus/Reservierung.php @@ -162,9 +162,9 @@ class Reservierung extends FHCAPI_Controller if ($this->_ci->form_validation->run() === FALSE) $this->terminateWithValidationErrors($this->_ci->form_validation->error_array()); - $ort_kurzbz = $this->_ci->input->post('ort_kurzbz', TRUE); + $orte = $this->_ci->input->post('orte', TRUE); - if (empty($ort_kurzbz) || !is_array($ort_kurzbz)) + if (empty($orte) || (empty($orte['ort_kurzbz']) && empty($orte['location']))) { $this->terminateWithValidationErrors(['ort_kurzbz' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Raum'])]); } @@ -189,7 +189,7 @@ class Reservierung extends FHCAPI_Controller $teilnehmer[] = array('uid' => getAuthUID(), 'rolle' => 'organisator'); - $result = $this->_ci->kalenderlib->addReservierung($titel, $beschreibung, $ort_kurzbz, $start_date, $end_date, $teilnehmer, $specialFinalGroups, $specialGroups, $groups); + $result = $this->_ci->kalenderlib->addReservierung($titel, $beschreibung, $orte, $start_date, $end_date, $teilnehmer, $specialFinalGroups, $specialGroups, $groups); if (isError($result)) diff --git a/application/controllers/api/frontend/v1/tempus/Tags.php b/application/controllers/api/frontend/v1/tempus/Tags.php new file mode 100644 index 000000000..c5223cd9a --- /dev/null +++ b/application/controllers/api/frontend/v1/tempus/Tags.php @@ -0,0 +1,53 @@ + self::BERECHTIGUNG_KURZBZ, + 'getTags' => self::BERECHTIGUNG_KURZBZ, + 'getTagsByCalendar' => self::BERECHTIGUNG_KURZBZ, + 'addTag' => self::BERECHTIGUNG_KURZBZ, + 'updateTag' => self::BERECHTIGUNG_KURZBZ, + 'doneTag' => self::BERECHTIGUNG_KURZBZ, + 'deleteTag' => self::BERECHTIGUNG_KURZBZ + ]); + + $this->config->load('tempus'); + } + + public function getTag($readonly_tags = null) + { + parent::getTag($this->config->item('tempus_tags')); + } + public function getTags($tags = null) + { + parent::getTags($this->config->item('tempus_tags')); + } + public function getTagsByCalendar($eindeutige_kalender_gruppen_id = null) + { + parent::getTagsByAssignmentTypeValue('eindeutige_kalender_gruppen_id', $eindeutige_kalender_gruppen_id, $this->config->item('tempus_tags')); + } + public function addTag($withZuordnung = true, $updatable_tags = null) + { + parent::addTag(true, $this->config->item('tempus_tags')); + } + public function updateTag($updatable_tags = null) + { + parent::updateTag($this->config->item('tempus_tags')); + } + public function deleteTag($withZuordnung = true, $updatable_tags = null) + { + parent::deleteTag(true, $this->config->item('tempus_tags')); + } + public function doneTag($updatable_tags = null) + { + parent::doneTag($this->config->item('tempus_tags')); + } +} diff --git a/application/controllers/system/MigrateKalender.php b/application/controllers/system/MigrateKalender.php index 15b6d28ce..67de262dd 100644 --- a/application/controllers/system/MigrateKalender.php +++ b/application/controllers/system/MigrateKalender.php @@ -121,7 +121,14 @@ class MigrateKalender extends CLI_Controller $ids = is_array($block->stundenplandev_ids) ? $block->stundenplandev_ids : explode(',', $block->stundenplandev_ids); /*$ids = array_map('intval', $ids);*/ - $this->SyncModel->db->where_in('stundenplandev_id', $ids); + $this->SyncModel->db->group_start(); + $where_ids_chunks = array_chunk($ids,25); + foreach($where_ids_chunks as $where_ids) + { + $this->SyncModel->db->or_where_in('stundenplandev_id', $where_ids); + } + $this->SyncModel->db->group_end(); + $sync_result = $this->SyncModel->load(); if (!hasData($sync_result)) @@ -230,7 +237,14 @@ class MigrateKalender extends CLI_Controller $ids = is_array($block->reservierung_ids) ? $block->reservierung_ids : explode(',', $block->reservierung_ids); - $this->SyncReservierungModel->db->where_in('reservierung_id', $ids); + $this->SyncReservierungModel->db->group_start(); + $where_ids_chunks = array_chunk($ids,25); + foreach($where_ids_chunks as $where_ids) + { + $this->SyncReservierungModel->db->or_where_in('reservierung_id', $where_ids); + } + $this->SyncReservierungModel->db->group_end(); + $sync_result = $this->SyncReservierungModel->load(); if (!hasData($sync_result)) diff --git a/application/core/Tag_Controller.php b/application/core/Tag_Controller.php index c4b6edb81..3659dfbff 100644 --- a/application/core/Tag_Controller.php +++ b/application/core/Tag_Controller.php @@ -44,7 +44,6 @@ class Tag_Controller extends FHCAPI_Controller { $language = $this->_getLanguageIndex(); $index_bezeichnung_mehrsprachig = $language - 1; - $id = $this->input->get('id'); if (is_array($readonly_tags) && !isEmptyArray($readonly_tags)) @@ -123,7 +122,36 @@ class Tag_Controller extends FHCAPI_Controller $notiztypen = $this->NotiztypModel->loadWhere(array('aktiv' => true)); $this->terminateWithSuccess(hasData($notiztypen) ? getData($notiztypen) : array()); } + public function getTagsByAssignmentTypeValue($zuordnung_typ, $zuordnung_id, $tags = null) + { + $language = $this->_getLanguageIndex(); + $index_bezeichnung_mehrsprachig = $language - 1; + $this->NotizzuordnungModel->addSelect( + 'tbl_notizzuordnung.notiz_id as notiz_id, + typ_kurzbz as tag_typ_kurzbz, + array_to_json(bezeichnung_mehrsprachig::varchar[])->>'. $index_bezeichnung_mehrsprachig. ' as bezeichnung, + style, + beschreibung, + tag, + tbl_notiz.erledigt as done + ' + ); + + if (is_array($tags) && !isEmptyArray($tags)) + { + $tags = $this->_filterTag($tags, false); + $this->NotizzuordnungModel->db->where_in('tbl_notiz_typ.typ_kurzbz', $tags); + } + + $this->NotizzuordnungModel->addJoin('public.tbl_notiz', 'public.tbl_notizzuordnung.notiz_id = public.tbl_notiz.notiz_id'); + $this->NotizzuordnungModel->addJoin('public.tbl_notiz_typ', 'public.tbl_notiz.typ = public.tbl_notiz_typ.typ_kurzbz'); + + $this->NotizzuordnungModel->addOrder('prioritaet'); + + $notiztypen = $this->NotizzuordnungModel->loadWhere(array('aktiv' => true, $zuordnung_typ => $zuordnung_id)); + $this->terminateWithSuccess(hasData($notiztypen) ? getData($notiztypen) : array()); + } public function addTag($withZuordnung = true, $updatable_tags = null) { $postData = $this->getPostJson(); @@ -143,7 +171,7 @@ class Tag_Controller extends FHCAPI_Controller if (!in_array($postData->tag_typ_kurzbz, $tags)) $this->terminateWithError($this->p->t('ui', 'keineBerechtigung')); } - + if ($withZuordnung) { $return = array(); @@ -152,7 +180,7 @@ class Tag_Controller extends FHCAPI_Controller $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); $values = array_unique($postData->values); - + foreach ($values as $value) { $insertResult = $this->addNotiz($postData); diff --git a/application/libraries/KalenderLib.php b/application/libraries/KalenderLib.php index 44c2fe71d..f39e257bd 100644 --- a/application/libraries/KalenderLib.php +++ b/application/libraries/KalenderLib.php @@ -17,13 +17,13 @@ class KalenderLib $this->_ci->load->model('ressource/BetriebsmittelKalender_model', 'BetriebsmittelKalenderModel'); $this->_ci->load->model('education/Lehreinheit_model', 'LehreinheitModel'); $this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); - $this->_ci->load->model('education/LehreinheitMitarbeiter_model', 'LehreinheitMitarbeiterModel'); $this->_ci->load->model('ressource/Ort_model', 'OrtModel'); $this->_ci->load->model('organisation/gruppe_model', 'GruppeModel'); $this->_ci->load->model('organisation/Lehrverband_model', 'LehrverbandModel'); $this->_ci->load->model('ressource/Stunde_model', 'StundeModel'); $this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); $this->_ci->load->model('system/Variable_model', 'VariableModel'); + $this->_ci->load->model('system/Sprache_model', 'SpracheModel'); $this->_ci->load->model('organisation/Ferien_model', 'FerienModel'); @@ -36,59 +36,75 @@ class KalenderLib private function _getBasePlan($start_date, $end_date) { + $language = $this->getLanguageIndex(); + $index_bezeichnung_mehrsprachig = $language - 1; + $end_date = date('Y-m-d', strtotime($end_date . ' +1 day')); $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id, - tbl_kalender.eindeutige_kalender_gruppen_id, - tbl_kalender.status_kurzbz, - tbl_kalender.typ, - tbl_kalender.von, - tbl_kalender.bis, - tbl_kalender_ort.ort_kurzbz, - tbl_lehreinheit.lehreinheit_id, - tbl_lehreinheit.lehrveranstaltung_id, - tbl_lehreinheit.lehrfach_id, - tbl_lehreinheit.lehrform_kurzbz, - tbl_lehrveranstaltung.oe_kurzbz, - lehrfach.kurzbz AS lehrfach_kurzbz, - lehrfach.bezeichnung AS lehrfach_bezeichnung, - lehrfach.farbe, - COALESCE(orginasator.uid, tbl_lehreinheitmitarbeiter.mitarbeiter_uid) as mitarbeiter_uid, - COALESCE(reservierung_person.vorname, tbl_person.vorname) as vorname, - COALESCE(reservierung_person.nachname, tbl_person.nachname) as nachname, - COALESCE(reservierung_ma.kurzbz, tbl_mitarbeiter.kurzbz) AS ma_kurzbz, - teilnehmergruppe.gruppe_kurzbz as teilnehmerg_grp, - COALESCE ( - UPPER(tbl_studiengang.typ || tbl_studiengang.kurzbz) || - COALESCE(verbandgruppe.semester::varchar, \'\') || - COALESCE(verbandgruppe.verband::varchar, \'\') || - COALESCE(verbandgruppe.gruppe, \'\'), - \'\') as verband_grp, - tbl_kalender_event.beschreibung, - tbl_kalender_event.titel, - teilmitarbeiter.kurzbz as teilnehmer_kurzbz, - teilperson.vorname as teilnehmer_vorname, - teilperson.nachname as teilnehmer_nachname, - teilbenutzer.uid as teilnehmer_uid, - CASE - WHEN tbl_lehreinheitgruppe.gruppe_kurzbz IS NULL THEN - COALESCE( - UPPER(le_studiengang.typ || le_studiengang.kurzbz) || - COALESCE(tbl_lehreinheitgruppe.semester::varchar, \'\') || - COALESCE(tbl_lehreinheitgruppe.verband::varchar, \'\') || - COALESCE(tbl_lehreinheitgruppe.gruppe, \'\'), - \'\') - ELSE tbl_lehreinheitgruppe.gruppe_kurzbz - END AS lehreinheit_gruppe_bezeichnung, - tbl_lehreinheitgruppe.gruppe_kurzbz as le_gruppe_kurzbz, - tbl_lehreinheitgruppe.studiengang_kz as le_studiengang_kz, - tbl_lehreinheitgruppe.semester as le_semester, - tbl_lehreinheitgruppe.verband as le_verband, - tbl_lehreinheitgruppe.gruppe as le_gruppe, - le_gruppe.direktinskription as le_direktinskription, - - - '); + tbl_kalender.eindeutige_kalender_gruppen_id, + tbl_kalender.status_kurzbz, + tbl_kalender.typ, + tbl_kalender.von, + tbl_kalender.bis, + tbl_kalender_ort.location, + tbl_kalender_ort.ort_kurzbz, + tbl_lehreinheit.lehreinheit_id, + tbl_lehreinheit.lehrveranstaltung_id, + tbl_lehreinheit.lehrfach_id, + tbl_lehreinheit.lehrform_kurzbz, + tbl_lehrveranstaltung.oe_kurzbz, + lehrfach.kurzbz AS lehrfach_kurzbz, + lehrfach.bezeichnung AS lehrfach_bezeichnung, + lehrfach.farbe, + COALESCE(orginasator.uid, tbl_lehreinheitmitarbeiter.mitarbeiter_uid) as mitarbeiter_uid, + COALESCE(reservierung_person.vorname, tbl_person.vorname) as vorname, + COALESCE(reservierung_person.nachname, tbl_person.nachname) as nachname, + COALESCE(reservierung_ma.kurzbz, tbl_mitarbeiter.kurzbz) AS ma_kurzbz, + teilnehmergruppe.gruppe_kurzbz as teilnehmerg_grp, + COALESCE ( + UPPER(tbl_studiengang.typ || tbl_studiengang.kurzbz) || + COALESCE(verbandgruppe.semester::varchar, \'\') || + COALESCE(verbandgruppe.verband::varchar, \'\') || + COALESCE(verbandgruppe.gruppe, \'\'), + \'\') as verband_grp, + tbl_kalender_event.beschreibung, + tbl_kalender_event.titel, + teilmitarbeiter.kurzbz as teilnehmer_kurzbz, + teilperson.vorname as teilnehmer_vorname, + teilperson.nachname as teilnehmer_nachname, + teilbenutzer.uid as teilnehmer_uid, + CASE + WHEN tbl_lehreinheitgruppe.gruppe_kurzbz IS NULL THEN + COALESCE( + UPPER(le_studiengang.typ || le_studiengang.kurzbz) || + COALESCE(tbl_lehreinheitgruppe.semester::varchar, \'\') || + COALESCE(tbl_lehreinheitgruppe.verband::varchar, \'\') || + COALESCE(tbl_lehreinheitgruppe.gruppe, \'\'), + \'\') + ELSE tbl_lehreinheitgruppe.gruppe_kurzbz + END AS lehreinheit_gruppe_bezeichnung, + tbl_lehreinheitgruppe.gruppe_kurzbz as le_gruppe_kurzbz, + tbl_lehreinheitgruppe.studiengang_kz as le_studiengang_kz, + tbl_lehreinheitgruppe.semester as le_semester, + tbl_lehreinheitgruppe.verband as le_verband, + tbl_lehreinheitgruppe.gruppe as le_gruppe, + le_gruppe.direktinskription as le_direktinskription, + tag_data_agg.tags as tags, + resource_data_agg.resources as resources, + CASE + WHEN vonstunde.stunde IS NOT NULL AND bisstunde.stunde IS NOT NULL + THEN bisstunde.stunde - vonstunde.stunde + 1 + ELSE ROUND( + COALESCE( + ( + SELECT SUM(EXTRACT(EPOCH FROM (LEAST(stunde.ende, tbl_kalender.bis::time) - GREATEST(stunde.beginn, tbl_kalender.von::time))) / 60) / 45 + FROM lehre.tbl_stunde stunde + WHERE stunde.beginn < tbl_kalender.bis::time AND stunde.ende > tbl_kalender.von::time) + ,0) + ,2 + ) END AS verplante_stunden + '); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_lehreinheit', 'tbl_kalender.kalender_id = tbl_kalender_lehreinheit.kalender_id', 'LEFT'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event', 'tbl_kalender.kalender_id = tbl_kalender_event.kalender_id', 'LEFT'); @@ -134,6 +150,72 @@ class KalenderLib ); $this->_ci->KalenderModel->addJoin('public.tbl_studiengang le_studiengang', 'le_lehrverband.studiengang_kz = le_studiengang.studiengang_kz', 'LEFT'); + // Include tags in the query + $this->_ci->load->config('tempus'); + $tags = $this->_ci->config->item('tempus_tags'); + + $whereTags = ''; + if (is_array($tags) && !isEmptyArray($tags)) { + $tags = array_keys($tags); + + foreach ($tags as $key => $tag) { + $tags[$key] = $this->_ci->KalenderModel->escape($tag); + } + $whereTags = " AND nt.typ_kurzbz IN (" . implode(",", $tags) . ")"; + } + $subQueryTag = "( + SELECT + tag.eindeutige_kalender_gruppen_id, + COALESCE(json_agg(tag ORDER BY tag.done, tag.prioritaet), '[]'::json) AS tags + FROM ( + SELECT DISTINCT ON (n.notiz_id) + n.notiz_id AS id, + nt.typ_kurzbz, + array_to_json(nt.bezeichnung_mehrsprachig::varchar[])->>". $index_bezeichnung_mehrsprachig. " as beschreibung, + n.text AS notiz, + nt.style, + n.erledigt AS done, + nt.prioritaet AS prioritaet, + nz.eindeutige_kalender_gruppen_id + FROM public.tbl_notizzuordnung AS nz + JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id + JOIN public.tbl_notiz_typ AS nt ON n.typ = nt.typ_kurzbz " . $whereTags . " + ) AS tag + GROUP BY tag.eindeutige_kalender_gruppen_id + ) AS tag_data_agg"; + + $this->_ci->KalenderModel->addJoin($subQueryTag, 'tag_data_agg.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_kalender_gruppen_id', 'LEFT'); + // End of tags inclusion + + // Include assigned operational resources in the query + $subQueryResource = "( + SELECT + resource.eindeutige_kalender_gruppen_id, + COALESCE(json_agg(resource ORDER BY resource.beschreibung), '[]'::json) AS resources + FROM ( + SELECT + betriebsmittel_kalender.*, + betriebsmittel.beschreibung, + betriebsmittel.verplanen + FROM lehre.tbl_betriebsmittel_kalender AS betriebsmittel_kalender + JOIN wawi.tbl_betriebsmittel AS betriebsmittel + ON betriebsmittel.betriebsmittel_id = betriebsmittel_kalender.betriebsmittel_id + WHERE betriebsmittel.verplanen = TRUE + ) AS resource + GROUP BY resource.eindeutige_kalender_gruppen_id + ) AS resource_data_agg"; + + $this->_ci->KalenderModel->addJoin( + $subQueryResource, + 'resource_data_agg.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_kalender_gruppen_id', + 'LEFT' + ); + // End of operational resources inclusion + + + $this->_ci->KalenderModel->db->join('lehre.tbl_stunde vonstunde', 'tbl_kalender.von::time = vonstunde.beginn', 'LEFT', FALSE); + $this->_ci->KalenderModel->db->join('lehre.tbl_stunde bisstunde', 'tbl_kalender.bis::time = bisstunde.ende', 'LEFT', FALSE); + $this->_ci->KalenderModel->db->where('tbl_kalender.von >=', $start_date); $this->_ci->KalenderModel->db->where('tbl_kalender.bis <', $end_date); @@ -157,6 +239,8 @@ class KalenderLib { $von = new DateTime($row->von); $bis = new DateTime($row->bis); + $resources = isset($row->resources) ? $row->resources : []; + $parsedResources = is_string($resources) ? json_decode($resources, true) : $resources; $events[$id] = (object) [ 'eindeutige_kalender_gruppen_id' => $row->eindeutige_kalender_gruppen_id, @@ -169,11 +253,13 @@ class KalenderLib 'tooltip' => 'tip', 'status_kurzbz' => $row->status_kurzbz, 'ort_kurzbz' => [], + 'location' => null, 'lehrform' => isset($row->lehrform_kurzbz) ? $row->lehrform_kurzbz : '', 'lehrfach' => isset($row->lehrfach_kurzbz) ? $row->lehrfach_kurzbz : '', 'lehrfach_bez' => isset($row->lehrfach_bezeichnung) ? $row->lehrfach_bezeichnung : '', 'farbe' => isset($row->farbe) ? $row->farbe : '', 'lehrveranstaltung_id' => $row->lehrveranstaltung_id, + 'verplante_stunden' => $row->verplante_stunden, 'organisationseinheit' => isset($row->oe_kurzbz) ? $row->oe_kurzbz : '', 'kalender_id' => $id, 'lehreinheit_id' => [], @@ -185,7 +271,9 @@ class KalenderLib 'beschreibung' => isset($row->beschreibung) ? $row->beschreibung : '', 'topic' => [], 'collisions' => false, - 'has_assigned_resources' => isset($row->has_assigned_resources) ? $row->has_assigned_resources : false, + 'has_assigned_resources' => !empty($parsedResources), + 'resources' => is_array($parsedResources) ? $parsedResources : [], + 'tags' => isset($row->tags) ? $row->tags : [], ]; } @@ -195,6 +283,9 @@ class KalenderLib if (isset($row->ort_kurzbz) && $row->ort_kurzbz !== '' && !in_array($row->ort_kurzbz, $events[$id]->ort_kurzbz)) $events[$id]->ort_kurzbz[] = $row->ort_kurzbz; + if (isset($row->location) && $row->location !== '' && is_null($events[$id]->location)) + $events[$id]->location = $row->location; + $topic = trim((isset($row->lehrfach_kurzbz) ? $row->lehrfach_kurzbz : '').' '.(isset($row->lehrform_kurzbz) ? $row->lehrform_kurzbz : '')); if ($topic !== '' && !in_array($topic, $events[$id]->topic)) $events[$id]->topic[] = $topic; @@ -301,8 +392,6 @@ class KalenderLib return $this->_mapEvents($data); } - - public function getForRaumvorschlag($start_date, $end_date, $lektor_uids = [], $gruppen_kurzbz = [], $lehrverband_gruppen = []) { $start_date = date('Y-m-d', strtotime($start_date)); @@ -328,6 +417,7 @@ class KalenderLib foreach ($lehrverband_gruppen as $lvg) { + $lvg = (array) $lvg; $this->_ci->KalenderModel->db->or_group_start(); $this->_ci->KalenderModel->db->where('tbl_lehreinheitgruppe.studiengang_kz', $lvg['studiengang_kz']); $this->_ci->KalenderModel->db->where('tbl_lehreinheitgruppe.semester', $lvg['semester']); @@ -474,7 +564,7 @@ class KalenderLib { $this->_getBasePlan($start_date, $end_date); - $this->_ci->KalenderModel->db->where('status_kurzbz', 'live'); + $this->_ci->KalenderModel->db->where_in('status_kurzbz', array('live', 'to_delete_live')); $data = $this->_ci->KalenderModel->load(); @@ -606,6 +696,17 @@ class KalenderLib } + private function _checkCollisionForLehreinheit($start_date, $end_date, $lehreinheit_id, $ort_kurzbz) + { + $checkerData = new stdClass(); + $checkerData->von = $start_date; + $checkerData->bis = $end_date; + $checkerData->lehreinheit_id = $lehreinheit_id; + $checkerData->ort_kurzbz = $ort_kurzbz; + + return $this->_ci->collisionchecker->run($checkerData); + } + public function addToKalenderEvent($target_kalender_id, $lehreinheit_id) { $entryResult = $this->_loadKalenderEntry($target_kalender_id); @@ -613,7 +714,15 @@ class KalenderLib $kalender_entry = getData($entryResult); - if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted'))) + if (!$this->_checkPermission($kalender_entry->lehreinheit_id)) + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'), + 'errorCode' => 'not_allowed' + ]); + } + + if (in_array($kalender_entry->status_kurzbz, array('to_delete', 'to_delete_live', 'to_delete_preview', 'deleted'))) { return error([ 'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'), @@ -656,7 +765,15 @@ class KalenderLib $kalender_entry = getData($entryResult); - if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted'))) + if (!$this->_checkPermission($kalender_entry->lehreinheit_id)) + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'), + 'errorCode' => 'not_allowed' + ]); + } + + if (in_array($kalender_entry->status_kurzbz, array('to_delete', 'to_delete_live', 'to_delete_preview', 'deleted'))) { return error([ 'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'), @@ -748,16 +865,6 @@ class KalenderLib return ['kalender_id' => $kalender_id, 'result' => $ortresult]; } - $entryResult = $this->_loadKalenderEntry($kalender_id); - if (isError($entryResult)) - return ['kalender_id' => $kalender_id, 'result' => $entryResult]; - - $kalender_entry = getData($entryResult); - $errors = $this->_ci->collisionchecker->run($kalender_entry); - - if (!empty($errors)) - return ['kalender_id' => $kalender_id, 'result' => error($errors)]; - return ['kalender_id' => $kalender_id, 'result' => $kalenderlehreinheitresult]; } @@ -822,8 +929,6 @@ class KalenderLib 'false' ); - $this->_ci->KalenderModel->db->trans_start(); - while ($rest > 0 && new DateTime($current_date) < $ende) { $max_skip = 52; @@ -864,18 +969,9 @@ class KalenderLib $start_str = $current_date . ' ' . $start_time; $end_str = $current_date . ' ' . $end_time; - $insert = $this->_insertKalenderEventRaw($start_str, $end_str, $lehreinheit_id, $ort_kurzbz); - $kalender_id = $insert['kalender_id']; - $insert_result = $insert['result']; + $collisions = $this->_checkCollisionForLehreinheit($start_str, $end_str, $lehreinheit_id, $ort_kurzbz); - $collisions = isError($insert_result) ? getError($insert_result) : []; - - $raum_vorschlaege = []; - if ($kalender_id) - { - $vorschlaege = $this->_ci->raumvorschlaglib->getVorschlaege($kalender_id); - $raum_vorschlaege = is_array($vorschlaege) ? $vorschlaege : []; - } + $raum_vorschlaege = $this->_ci->raumvorschlaglib->getVorschlaegeByLehreinheit($lehreinheit_id, $start_str, $end_str); $plan[] = [ 'datum' => $current_date, @@ -884,14 +980,13 @@ class KalenderLib 'block' => $current_block, 'ort_kurzbz' => $ort_kurzbz, 'collisions' => $collisions, - 'raum_vorschlaege' => $raum_vorschlaege, + 'raum_vorschlaege' => is_array($raum_vorschlaege) ? $raum_vorschlaege : [], ]; $rest -= $current_block; $current_date = $this->_addWeeks($current_date, $wochenrythmus); } - $this->_ci->KalenderModel->db->trans_rollback(); $this->_ci->VariableModel->setVariable( getAuthUID(), @@ -954,6 +1049,49 @@ class KalenderLib public function addKalenderEvent($start_date, $end_date, $lehreinheit_id, $ort_kurzbz) { + if (!$this->_checkPermission((array)$lehreinheit_id)) + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'), + 'errorCode' => 'not_allowed' + ]); + } + + if (isEmptyString($ort_kurzbz)) + { + $errors = $this->_checkCollisionForLehreinheit($start_date, $end_date, $lehreinheit_id, $ort_kurzbz); + + if (!empty($errors)) + return error($errors); + + $raum_vorschlaege = $this->_ci->raumvorschlaglib->getVorschlaegeByLehreinheit($lehreinheit_id, $start_date, $end_date); + + if (($this->_ci->variablelib->getVar('dialog_room_planning') ?? 'false') === 'true') + { + return success([ + 'needs_room_selection' => true, + 'raum_vorschlaege' => is_array($raum_vorschlaege) ? $raum_vorschlaege : [], + ]); + } + + if (($this->_ci->variablelib->getVar('priority_room_planning') ?? 'false') === 'true') + { + $ort_kurzbz = (is_array($raum_vorschlaege)) ? $raum_vorschlaege[0]['ort_kurzbz'] : null; + } + else if (!(($this->_ci->variablelib->getVar('roomless_planning') ?? 'false') === 'true')) + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'roomless_planning_error'), + 'errorCode' => 'roomless_planning_error' + ]); + } + } + + $errors = $this->_checkCollisionForLehreinheit($start_date, $end_date, $lehreinheit_id, $ort_kurzbz); + + if (!empty($errors)) + return error($errors); + $this->_ci->KalenderModel->db->trans_start(); $insert = $this->_insertKalenderEventRaw($start_date, $end_date, $lehreinheit_id, $ort_kurzbz); @@ -969,14 +1107,14 @@ class KalenderLib return $result; } - public function addReservierung($titel, $beschreibung, $ort_kurzbz, $start_date, $end_date, $teilnehmer, $specialFinalGroups, $specialGroups, $groups) + public function addReservierung($titel, $beschreibung, $orte, $start_date, $end_date, $teilnehmer, $specialFinalGroups, $specialGroups, $groups) { - if (!is_null($ort_kurzbz)) + if (!is_null($orte) && !isEmptyArray($orte['ort_kurzbz'])) { $this->_ci->KalenderModel->addSelect('1'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'kalender_id'); - $this->_ci->KalenderModel->db->where_in('ort_kurzbz', $ort_kurzbz); + $this->_ci->KalenderModel->db->where_in('ort_kurzbz', $orte['ort_kurzbz']); $reservierung_vorhanden = $this->_ci->KalenderModel->load(array ( 'von <=' => $end_date, 'bis >=' => $start_date, @@ -1037,19 +1175,25 @@ class KalenderLib } } - /* foreach ($specialGroups as $group) + if (isSuccess($kalendereventresult) && !isEmptyArray($orte['ort_kurzbz'])) { - $this->_addSpecialGroupToEvent($kalender_id, $group['gruppe_kurzbz'], $group['rolle']); + $ortresult = $this->_addKalenderOrt($kalender_id, $orte['ort_kurzbz']); + if (isError($ortresult)) + { + $this->_ci->KalenderModel->db->trans_rollback(); + return $ortresult; + } } - foreach ($groups as $group) + if (isSuccess($kalendereventresult) && !is_null($orte['location'])) { - $this->_addGroupToEvent($kalender_id, $group['stg_kz'], $group['semester'], $group['verband'], $group['gruppe'], $group['rolle']); - }*/ + $ortresult = $this->_ci->KalenderOrtModel->insert( + array ( + 'kalender_id' => $kalender_id, + 'location' => $orte['location'] + ) + ); - if (isSuccess($kalendereventresult) && !is_null($ort_kurzbz)) - { - $ortresult = $this->_addKalenderOrt($kalender_id, $ort_kurzbz); if (isError($ortresult)) { $this->_ci->KalenderModel->db->trans_rollback(); @@ -1249,26 +1393,39 @@ class KalenderLib return success(); } - public function updateKalenderEvent($kalender_id, $ort_kurzbz = null, $start_time = null, $end_time = null) + public function updateKalenderEvent($kalender_id, $orte = null, $start_time = null, $end_time = null) { $entryResult = $this->_loadKalenderEntry($kalender_id); if (isError($entryResult)) return $entryResult; $kalender_entry = getData($entryResult); + if (!$this->_checkPermission($kalender_entry->lehreinheit_id)) + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'), + 'errorCode' => 'not_allowed' + ]); + } + $calendarResources = $this->_ci->BetriebsmittelKalenderModel->loadWhere(['eindeutige_kalender_gruppen_id' => $kalender_entry->eindeutige_kalender_gruppen_id]); if (isError($calendarResources)) return $calendarResources; - + $calendarResourcesItems = getData($calendarResources); $calendarResourcesIDs = []; - if (is_array($calendarResourcesItems)) { + if (is_array($calendarResourcesItems)) { $calendarResourcesIDs = array_map(function($resource) { return $resource->betriebsmittel_id; }, getData($calendarResources)); - } + } $kalender_entry->betriebsmittel_ids = $calendarResourcesIDs; - $kalender_entry->ort_kurzbz = !is_null($ort_kurzbz) ? (array) $ort_kurzbz : $kalender_entry->ort_kurzbz; + + if (!is_null($orte)) + { + $kalender_entry->ort_kurbz = !isEmptyArray($orte['ort_kurzbz'] ?? null) ? (array) $orte['ort_kurzbz'] : $kalender_entry->ort_kurzbz; + $kalender_entry->location = array_key_exists('location', $orte) && !is_null($orte['location']) ? $orte['location'] : $kalender_entry->location; + } $kalender_entry->von = $start_time ?? $kalender_entry->von; $kalender_entry->bis = $end_time ?? $kalender_entry->bis; @@ -1278,9 +1435,9 @@ class KalenderLib $result = $kalender_entry; - if (!is_null($ort_kurzbz)) + if (!is_null($orte)) { - $result = $this->updateOrt($kalender_id, $ort_kurzbz); + $result = $this->updateOrt($kalender_id, $orte); if (isError($result)) return $result; if (hasData($result)) @@ -1307,7 +1464,7 @@ class KalenderLib if ($kalender_entry->typ === 'lehreinheit') { - if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted'))) + if (in_array($kalender_entry->status_kurzbz, array('to_delete', 'to_delete_live', 'to_delete_preview', 'deleted'))) { return error([ 'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'), @@ -1363,9 +1520,11 @@ class KalenderLib return success($result); } - public function updateOrt($kalender_id, $ort_kurzbz_array) + public function updateOrt($kalender_id, $orte_array) { - $ort_kurzbz_array = (array)$ort_kurzbz_array; + $ort_kurzbz_neu = (array)($orte_array['ort_kurzbz'] ?? []); + $location_neu = $orte_array['location'] ?? null; + $entryResult = $this->_loadKalenderEntry($kalender_id); if (isError($entryResult)) return $entryResult; @@ -1373,7 +1532,15 @@ class KalenderLib if ($kalender_entry->typ === 'lehreinheit') { - if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted'))) + if (!$this->_checkPermission($kalender_entry->lehreinheit_id)) + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'), + 'errorCode' => 'not_allowed' + ]); + } + + if (in_array($kalender_entry->status_kurzbz, array('to_delete', 'to_delete_live', 'to_delete_preview', 'deleted'))) { return error([ 'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'), @@ -1382,14 +1549,30 @@ class KalenderLib } if (in_array($kalender_entry->status_kurzbz, array('live', 'preview'))) - return $this->_createHistoryEntry($kalender_entry, null, null, $ort_kurzbz_array); + return $this->_createHistoryEntry($kalender_entry, null, null, $orte_array); } $bestehende = $this->_ci->KalenderOrtModel->load(array('kalender_id' => $kalender_id)); - $alte_orte = hasData($bestehende) ? array_column(getData($bestehende), 'ort_kurzbz') : []; + $alte_orte = hasData($bestehende) ? getData($bestehende) : []; - $zu_loeschen = array_diff($alte_orte, $ort_kurzbz_array); - $neu_anzulegen = array_diff($ort_kurzbz_array, $alte_orte); + + $alte_orte_kurzbz = array_values(array_filter(array_map(function ($ort) { + return $ort->ort_kurzbz; + }, $alte_orte))); + + $alte_location = null; + + foreach ($alte_orte as $ort) + { + if (!is_null($ort->location)) + { + $alte_location = $ort->location; + break; + } + } + + $zu_loeschen = array_diff($alte_orte_kurzbz, $ort_kurzbz_neu); + $neu_anzulegen = array_diff($ort_kurzbz_neu, $alte_orte_kurzbz); if (!empty($zu_loeschen)) { @@ -1398,12 +1581,34 @@ class KalenderLib $this->_ci->KalenderOrtModel->db->delete('lehre.tbl_kalender_ort'); } - foreach ($neu_anzulegen as $ort_kurzbz) + foreach ($neu_anzulegen as $ort) { - $result = $this->_addKalenderOrt($kalender_id, $ort_kurzbz); + $result = $this->_addKalenderOrt($kalender_id, $ort); if (isError($result)) return $result; } + if ($alte_location !== $location_neu) + { + if (!is_null($alte_location)) + { + $this->_ci->KalenderOrtModel->db->where('kalender_id', $kalender_id); + $this->_ci->KalenderOrtModel->db->where('ort_kurzbz IS NULL', null, false); + $this->_ci->KalenderOrtModel->db->delete('lehre.tbl_kalender_ort'); + } + + if (!isEmptyString($location_neu)) + { + $ortresult = $this->_ci->KalenderOrtModel->insert( + array ( + 'kalender_id' => $kalender_id, + 'location' => $location_neu + ) + ); + + if (isError($ortresult)) return $ortresult; + } + } + return success(); } @@ -1414,6 +1619,14 @@ class KalenderLib $kalender_entry = getData($entryResult); + if (!$this->_checkPermission($kalender_entry->lehreinheit_id)) + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'), + 'errorCode' => 'not_allowed' + ]); + } + $allowed = array( 'planning' => array('sync_preview', 'sync_live'), 'sync_preview' => array('sync_live'), @@ -1478,22 +1691,21 @@ class KalenderLib if (!is_null($ort_kurzbz_array)) { - $new_orte = (array) $ort_kurzbz_array; - $new_locations = array_fill(0, count($new_orte), null); + $new_orte = (array) ($ort_kurzbz_array['ort_kurzbz'] ?? []); + $new_locations = $ort_kurzbz_array['location'] ?? null; } else { $new_orte = (array) $kalender_entry->ort_kurzbz; - $new_locations = (array) $kalender_entry->location; + $new_locations = $kalender_entry->location; } - foreach ($new_orte as $index => $ort) + foreach ($new_orte as $ort) { $ortresult = $this->_ci->KalenderOrtModel->insert( array( 'kalender_id' => $new_kalender_id, - 'ort_kurzbz' => $ort, - 'location' => $new_locations[$index] ?? null + 'ort_kurzbz' => $ort ) ); @@ -1501,6 +1713,19 @@ class KalenderLib return $ortresult; } + if (!is_null($new_locations)) + { + $locresult = $this->_ci->KalenderOrtModel->insert( + array( + 'kalender_id' => $new_kalender_id, + 'location' => $new_locations + ) + ); + + if (!isSuccess($locresult)) + return $locresult; + } + return success($new_kalender_id); } private function _loadKalenderEntry($kalender_id) @@ -1523,8 +1748,8 @@ class KalenderLib $this->_ci->KalenderModel->addJoin( '(SELECT kalender_id, - array_agg(ort_kurzbz ORDER BY ort_kurzbz) AS ort_kurzbz, - array_agg(location ORDER BY ort_kurzbz) AS location + array_agg(ort_kurzbz ORDER BY ort_kurzbz) FILTER(WHERE ort_kurzbz is NOT NULL) AS ort_kurzbz, + MAX(location) AS location FROM lehre.tbl_kalender_ort GROUP BY kalender_id) orte', 'tbl_kalender.kalender_id = orte.kalender_id', @@ -1540,7 +1765,7 @@ class KalenderLib $entry = getData($result)[0]; $entry->lehreinheit_id = $entry->lehreinheit_id ?? []; $entry->ort_kurzbz = $entry->ort_kurzbz ?? []; - $entry->location = $entry->location ?? []; + $entry->location = $entry->location ?? null; return success($entry); } @@ -1595,44 +1820,76 @@ class KalenderLib private function _deleteTypLehreinheit($entry) { - $result_lehreinheit = $this->_ci->KalenderLehreinheitModel->loadWhere(array('kalender_id' => $entry->kalender_id)); - if (isError($result_lehreinheit)) return $result_lehreinheit; - $has_lehreinheit = hasData($result_lehreinheit) ? getData($result_lehreinheit)[0] : false; + $entryResult = $this->_loadKalenderEntry($entry->kalender_id); - $result_ort = $this->_ci->KalenderOrtModel->loadWhere(array('kalender_id' => $entry->kalender_id)); - if (isError($result_ort)) return $result_ort; - $has_ort = hasData($result_ort) ? getData($result_ort)[0] : false; - if ($entry->status_kurzbz === 'planning') + if (isError($entryResult)) return $entryResult; + + $kalender_entry = getData($entryResult); + + if (!$this->_checkPermission($kalender_entry->lehreinheit_id)) { - if ($has_ort) - $this->_deleteOrtEntry($has_ort); - - if ($has_lehreinheit) - $this->_deleteLehreinheitEntry($has_lehreinheit); - - $this->_ci->KalenderModel->delete(array('tbl_kalender.kalender_id' => $entry->kalender_id)); + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'keineBerechtigung'), + 'errorCode' => 'not_allowed' + ]); } - else if ($entry->status_kurzbz === 'sync_preview' || $entry->status_kurzbz === 'sync_live') + + + if ($kalender_entry->status_kurzbz === 'sync_preview' || $kalender_entry->status_kurzbz === 'sync_live' || $kalender_entry->status_kurzbz === 'planning') { - $history = $this->getHistory($entry->kalender_id); - $history = hasData($history) ? getData($history) : false; + $history = $this->getHistory($kalender_entry->kalender_id, false, false); - if ($has_ort) - $result = $this->_deleteOrtEntry($has_ort); + $history = hasData($history) && getData($history); - if ($has_lehreinheit) - $result = $this->_deleteLehreinheitEntry($has_lehreinheit); + if ($history) + { + $result = $this->_ci->KalenderModel->update( + array('kalender_id' => $kalender_entry->kalender_id), + array('status_kurzbz' => 'to_delete','status_kurzbz_max' => 'planning') + ); - $this->_ci->KalenderModel->delete(array('tbl_kalender.kalender_id' => $entry->kalender_id)); + if (isError($result)) + return error($result); + } + else + { + + if (!isEmptyArray($kalender_entry->ort_kurzbz)) + { + $result = $this->_deleteOrtEntry($kalender_entry); + if (isError($result)) + return error($result); + } + + + if (!isEmptyArray($kalender_entry->lehreinheit_id)) + { + $result = $this->_deleteLehreinheitEntry($kalender_entry); + if (isError($result)) + return error($result); + } + + $result = $this->_ci->KalenderModel->delete(array('tbl_kalender.kalender_id' => $kalender_entry->kalender_id)); + + if (isError($result)) + return error($result); + } } - else if ($entry->status_kurzbz === 'preview' || $entry->status_kurzbz === 'live') + else if ($kalender_entry->status_kurzbz === 'preview' || $kalender_entry->status_kurzbz === 'live') { - //TODO (david) überprüfen ob sinnvoll, verschwindet direkt in den ansichten (student, lecturer) + if ($kalender_entry->status_kurzbz === 'preview') + $new_status = 'to_delete_preview'; + else + $new_status = 'to_delete_live'; + $result = $this->_ci->KalenderModel->update( - array('kalender_id' => $entry->kalender_id), - array('status_kurzbz' => 'todelete') + array('kalender_id' => $kalender_entry->kalender_id), + array('status_kurzbz' => $new_status) ); + + if (isError($result)) + return error($result); } return success(); @@ -1657,11 +1914,11 @@ class KalenderLib return $this->_ci->KalenderEventModel->delete(array('kalender_id' => $entry->kalender_id)); } - public function getHistory($kalender_id, $only_previous = false) + public function getHistory($kalender_id, $only_previous = false, $include_self = true) { $dbModel = new DB_Model(); - $history_entries = $dbModel->execReadOnlyQuery(" - WITH RECURSIVE + $history_sql =" + WITH RECURSIVE vorgaenger(kalender_id, vorgaenger_kalender_id) AS ( SELECT kalender_id, vorgaenger_kalender_id FROM lehre.tbl_kalender @@ -1700,97 +1957,55 @@ class KalenderLib SELECT kalender_id FROM nachfolger ) combined JOIN lehre.tbl_kalender USING(kalender_id) - LEFT JOIN orte USING(kalender_id) - ORDER BY kalender_id DESC - - " . ($only_previous ? ' LIMIT 1' : ''), array($kalender_id, $kalender_id)); + LEFT JOIN orte USING(kalender_id)"; - return $history_entries; + $params = array($kalender_id, $kalender_id); + + if (!$include_self) + { + $history_sql .= " WHERE kalender_id != ?"; + $params[] = $kalender_id; + } + + $history_sql .= " ORDER BY kalender_id DESC" . ($only_previous ? ' LIMIT 1' : ''); + + return $dbModel->execReadOnlyQuery($history_sql, $params); } - //TODO in eine eigen Lib? - public function sync() + private function _checkPermission($lehreinheit_ids) { - $this->_ci->load->model('ressource/Kalender_model', 'KalenderModel'); + if (isEmptyArray($lehreinheit_ids)) + return true; - $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(); + $allowed = false; - if (!hasData($to_update)) - return success(); + $this->_ci->LehreinheitModel->addSelect('studiengang_kz'); + $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrveranstaltung', 'tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id'); + $this->_ci->LehreinheitModel->db->where_in('lehreinheit_id', $lehreinheit_ids); - $mail_infos = []; + $lehreinheit = $this->_ci->LehreinheitModel->load(); + if (!hasData($lehreinheit)) + return error('Lehreinheit nicht gefunden'); - foreach (getData($to_update) as $entry) + $studiengaenge = array_unique(array_column(getData($lehreinheit), 'studiengang_kz')); + + foreach ($studiengaenge as $studiengang_kz) { - if ($entry->status_kurzbz === 'todelete') + if ($this->_ci->permissionlib->isBerechtigt('lehre/lvplan', 'suid', $studiengang_kz)) { - $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'deleted')); - $this->_archiveVorgaenger($entry->vorgaenger_kalender_id, false); - $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')); + $allowed = true; + break; } } - $this->_ci->load->library('KalenderNotificationLib'); - $this->_ci->kalendernotificationlib->sendMails($mail_infos); - return success(); - + return $allowed; } - private function _archiveVorgaenger($vorgaenger_kalender_id, $stop_at_live = false) + private function getLanguageIndex() { - if (!$vorgaenger_kalender_id) return; + $this->_ci->SpracheModel->addSelect('index'); + $result = $this->_ci->SpracheModel->loadWhere(array('sprache' => getUserLanguage())); - $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; - } + return hasData($result) ? getData($result)[0]->index : 1; } - } 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..fce0ecd5d --- /dev/null +++ b/application/libraries/KalenderSyncLib.php @@ -0,0 +1,429 @@ + 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'); + $this->_ci->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel'); + $this->_ci->load->library('KalenderLib'); + } + + 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('to_delete', 'to_delete_live', 'to_delete_preview')); + + $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('to_delete', 'to_delete_live', 'to_delete_preview')); + + $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() + { + $mail_infos_gesamt = []; + $synced_gesamt = 0; + + $todelete_ids_result = $this->_getManuallySetStatus(array('to_delete', 'to_delete_live', 'to_delete_preview')); + + if (isError($todelete_ids_result)) + return $todelete_ids_result; + + $todelete_result = $this->_syncKalenderIds(getData($todelete_ids_result), null); + + if (isError($todelete_result)) + return $todelete_result; + + $todelete_data = getData($todelete_result); + $mail_infos_gesamt = array_merge($mail_infos_gesamt, $todelete_data['mail_infos']); + $synced_gesamt += $todelete_data['synced']; + + $preview_ids_result = $this->_getManuallySetStatus(array('sync_preview')); + + if (isError($preview_ids_result)) + return $preview_ids_result; + + $preview_result = $this->_syncKalenderIds(getData($preview_ids_result), 'preview'); + + if (isError($preview_result)) + return $preview_result; + + $preview_data = getData($preview_result); + $mail_infos_gesamt = array_merge($mail_infos_gesamt, $preview_data['mail_infos']); + $synced_gesamt += $preview_data['synced']; + + $live_ids_result = $this->_getManuallySetStatus(array('sync_live')); + + if (isError($live_ids_result)) + return $live_ids_result; + + $live_result = $this->_syncKalenderIds(getData($live_ids_result), 'live'); + + if (isError($live_result)) + return $live_result; + + $live_data = getData($live_result); + $mail_infos_gesamt = array_merge($mail_infos_gesamt, $live_data['mail_infos']); + $synced_gesamt += $live_data['synced']; + + if (!empty($mail_infos_gesamt)) + { + $this->_ci->load->library('KalenderNotificationLib'); + $this->_ci->kalendernotificationlib->sendMails($mail_infos_gesamt); + } + + return success(['synced' => $synced_gesamt]); + } + + private function _getManuallySetStatus($status_kurzbz_list) + { + if (empty($status_kurzbz_list)) + return success([]); + + $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id'); + $this->_ci->KalenderModel->db->where_in('status_kurzbz', $status_kurzbz_list); + $result = $this->_ci->KalenderModel->load(); + + if (isError($result)) + return $result; + + if (!hasData($result)) + return success([]); + + return success(array_column(getData($result), 'kalender_id')); + } + + 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); + + if (!hasData($studiengang_result)) + { + $organisationseinheiten = $this->_ci->OrganisationseinheitModel->getChilds($oe_kurzbz); + $oe_kurzbz = hasData($organisationseinheiten) ? array_column(getData($organisationseinheiten), 'oe_kurzbz') : []; + } + + $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'); + + if (!is_null($studiengang_kz)) + { + $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.studiengang_kz', $studiengang_kz); + } + else + { + $this->_ci->KalenderModel->db->where_in('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) + { + $return_array = array(); + if (!$vorgaenger_kalender_id) return success($return_array); + + $current_id = $vorgaenger_kalender_id; + + while ($current_id) + { + $vorgaenger = $this->_ci->KalenderModel->load(array('kalender_id' => $current_id)); + if (!hasData($vorgaenger)) return success($return_array); + + $vorgaenger = getData($vorgaenger)[0]; + + if ($stop_at_live && $vorgaenger->status_kurzbz === 'live') + return success($return_array); + + if (in_array($vorgaenger->status_kurzbz, array('preview', 'live'))) + { + $this->_ci->KalenderModel->update( + array('kalender_id' => $current_id), + array('status_kurzbz' => 'archived', 'status_kurzbz_max' => $vorgaenger->status_kurzbz) + ); + + $return_array[] = $vorgaenger; + } + + $current_id = $vorgaenger->vorgaenger_kalender_id; + } + return success($return_array); + } + + 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 (in_array($entry->status_kurzbz, array('to_delete', 'to_delete_live', 'to_delete_preview'))) + { + $notify = array(); + if ($entry->status_kurzbz === 'to_delete_live') + { + $status_max = 'live'; + $notify = array('lektor', 'student'); + } + else if ($entry->status_kurzbz === 'to_delete_preview') + { + $status_max = 'preview'; + $notify = array('lektor'); + } + else if ($entry->status_kurzbz === 'to_delete') + { + $status_max = 'planning'; + } + + $this->_ci->KalenderModel->update(array('kalender_id' => $entry->kalender_id), array('status_kurzbz' => 'deleted', 'status_kurzbz_max' => $status_max)); + $mail_infos[] = array('entry' => $entry, 'new_status' => 'deleted', 'notify' => $notify); + + $vorgaenger_array = $this->_archiveVorgaenger($entry->vorgaenger_kalender_id); + + + + if (hasData($vorgaenger_array)) + { + foreach(getData($vorgaenger_array) as $vorgaenger) + { + $archive_notify = array(); + + if ($vorgaenger->status_kurzbz === 'preview') + $archive_notify = array('lektor'); + else if ($vorgaenger->status_kurzbz === 'live') + { + $archive_notify = array('student', 'lektor'); + + $history = $this->_ci->kalenderlib->getHistory($vorgaenger->kalender_id, false, false); + if (hasData($history)) + { + $history = getData($history); + + foreach ($history as $entry) + { + if ($entry->status_kurzbz === 'deleted' && $entry->status_kurzbz_max === 'preview') + { + $archive_notify = array('student'); + break; + } + } + } + } + + $mail_infos[] = array('entry' => $vorgaenger, 'new_status' => 'deleted', 'notify' => $archive_notify); + } + } + + $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/libraries/RaumvorschlagLib.php b/application/libraries/RaumvorschlagLib.php index 8cb4aa8a4..2953de428 100644 --- a/application/libraries/RaumvorschlagLib.php +++ b/application/libraries/RaumvorschlagLib.php @@ -22,13 +22,14 @@ class RaumvorschlagLib $this->_ci->load->model('ressource/Ort_model', 'OrtModel'); $this->_ci->load->model('organisation/gruppe_model', 'GruppeModel'); $this->_ci->load->model('organisation/Lehrverband_model', 'LehrverbandModel'); - - $this->_ci->load->model('education/Lehreinheitgruppe_model', 'LehreinheitgruppeModel'); + $this->_ci->load->model('ressource/Zeitsperre_model', 'ZeitsperreModel'); + $this->_ci->load->model('ressource/Stunde_model', 'StundeModel'); $this->_ci->load->model('education/Lehreinheitgruppe_model', 'LehreinheitgruppeModel'); $this->_ci->load->library('CollisionChecker'); $this->_ci->load->library('KalenderLib'); $this->_ci->load->library('PhrasesLib', array('ui')); + $this->_ci->load->library('VariableLib', array('uid' => getAuthUID())); } @@ -36,47 +37,295 @@ class RaumvorschlagLib { $event = $this->_ci->kalenderlib->getByKalenderId($kalender_id); $event = $event[0]; + return $this->_getVorschlaegeForEvent($event); + } - $lektor_uids = array_column($event->lektor, 'mitarbeiter_uid'); - $gruppen_kurzbz = array_values(array_filter(array_column($event->gruppe, 'gruppe_kurzbz'))); + public function getVorschlaegeByLehreinheit($lehreinheit_id, $von, $bis) + { + $teilnehmer = $this->_getLehreinheitTeilnehmer($lehreinheit_id); - $lehrverband_gruppen = array_values(array_filter($event->gruppe, function($gruppe) + $event = (object) [ + 'kalender_id' => null, + 'lehreinheit_id' => [$lehreinheit_id], + 'isostart' => (new DateTime($von))->format('c'), + 'isoend' => (new DateTime($bis))->format('c'), + 'datum' => (new DateTime($von))->format('Y-m-d'), + 'lektor' => $teilnehmer->lektor_data, + 'gruppe' => $teilnehmer->gruppen_data, + ]; + + return $this->_getVorschlaegeForEvent($event); + } + + public function getVorschlaegeSlots($lehreinheit_id, $start_date, $end_date) + { + $lehreinheit_result = $this->_ci->LehreinheitModel->load($lehreinheit_id); + if (!hasData($lehreinheit_result)) + return error('Lehreinheit nicht gefunden'); + $lehreinheit = getData($lehreinheit_result)[0]; + + $block = $lehreinheit->stundenblockung; + if ($block <= 0) + return error('Stundenblockung ist ungültig'); + + $this->_ci->StundeModel->addOrder('stunde', 'ASC'); + $stunden_result = $this->_ci->StundeModel->load(); + $stunden = hasData($stunden_result) ? array_values(getData($stunden_result)) : []; + + if (empty($stunden)) + return error('Kein Stunden-Raster gefunden'); + + $teilnehmer = $this->_getLehreinheitTeilnehmer($lehreinheit_id); + $grp = $this->_splitGruppen($teilnehmer->gruppen_data); + $lektor_uids = array_column($teilnehmer->lektor_data, 'mitarbeiter_uid'); + + $verplante_events = $this->_ci->kalenderlib->getForRaumvorschlag( + $start_date, + $end_date, + $lektor_uids, + $grp->gruppen_kurzbz, + $grp->lehrverband_gruppen + ); + + $start_day = strtotime($start_date); + $end_day = strtotime($end_date); + + $moegliche_slots = array(); + + $verplante_zeitsperren = $this->_getZeitsperen($start_date, $end_date, $lektor_uids); + while ($start_day <= $end_day) + { + $tag = date('Y-m-d', $start_day); + + $verplante_tages_events = array_values(array_filter($verplante_events, function($event) use ($tag) + { + return $event->datum === $tag; + })); + + foreach ($stunden as $index => $start_stunde) + { + if (!isset($stunden[$index + $block - 1])) + continue; + + $end_stunde = $stunden[$index + $block - 1]; + + $slot_start = $tag . ' ' . $start_stunde->beginn; + $slot_end = $tag . ' ' . $end_stunde->ende; + + if (!$this->_isFrei($slot_start, $slot_end, $verplante_zeitsperren, $lektor_uids, 'lektor')) + continue; + + if (!$this->_isFrei($slot_start, $slot_end, $verplante_tages_events, $lektor_uids, 'lektor')) + continue; + + if (!$this->_isFrei($slot_start, $slot_end, $verplante_tages_events, $grp->gruppen_kurzbz, 'gruppe')) + continue; + + if (!$this->_isFrei($slot_start, $slot_end, $verplante_tages_events, $grp->lehrverband_gruppen_bezeichnung, 'lehrverband')) + continue; + + $moegliche_slots[] = [ + 'isostart' => (new DateTime($slot_start))->format('c'), + 'isoend' => (new DateTime($slot_end))->format('c'), + 'marker_isoend' => (new DateTime($tag . ' ' . $start_stunde->ende))->format('c'), + ]; + } + + $start_day = strtotime('+1 day', $start_day); + } + + + $slots = []; + foreach ($moegliche_slots as $slot) + { + $event_obj = (object) [ + 'kalender_id' => null, + 'lehreinheit_id' => [$lehreinheit_id], + 'isostart' => $slot['isostart'], + 'isoend' => $slot['isoend'], + ]; + + $raumkandidaten = $this->_getRaumkandidaten($event_obj); + + if (empty($raumkandidaten)) + continue; + + $ratings = $this->_getRatings($verplante_events, $slot, $raumkandidaten, $lektor_uids, $grp->gruppen_kurzbz, $grp->lehrverband_gruppen_bezeichnung); + $best = $ratings[0]; + $anzahl_weitere = count($ratings) - 1; + + $slots[] = [ + 'isostart' => $slot['isostart'], + 'isoend' => $slot['isoend'], + 'marker_isoend' => $slot['marker_isoend'], + 'rating' => $this->_scoreToRating($best['score']), + 'label' => $anzahl_weitere > 0 ? $best['ort_kurzbz'] . ' (' . $best['score'] . ') + ' . $anzahl_weitere . ' weitere' : $best['ort_kurzbz'] . ' (' . $best['score'] . ')', + 'raeume' => $ratings, + ]; + } + return success($slots); + } + + private function _getZeitsperen($start_date, $end_date, $uids) + { + + if (empty($uids)) return []; + + if (($this->_ci->variablelib->getVar('ignore_kollision') === 'true') || ($this->_ci->variablelib->getVar('ignore_zeitsperre') === 'true')) return []; + + + $this->_ci->ZeitsperreModel->addSelect('mitarbeiter_uid, vondatum, vonstunde_z.beginn as von_beginn, bisdatum, bisstunde_z.ende as bis_ende'); + $this->_ci->ZeitsperreModel->addJoin('lehre.tbl_stunde vonstunde_z', 'vonstunde_z.stunde = tbl_zeitsperre.vonstunde', 'LEFT'); + $this->_ci->ZeitsperreModel->addJoin('lehre.tbl_stunde bisstunde_z', 'bisstunde_z.stunde = tbl_zeitsperre.bisstunde', 'LEFT'); + $this->_ci->ZeitsperreModel->db->where('zeitsperretyp_kurzbz !=', 'ZVerfueg'); + $this->_ci->ZeitsperreModel->db->where('(tbl_zeitsperre.vondatum + COALESCE(vonstunde_z.beginn, \'00:00\'))::timestamp <', $end_date); + $this->_ci->ZeitsperreModel->db->where('(tbl_zeitsperre.bisdatum + COALESCE(bisstunde_z.ende, \'23:59\'))::timestamp >', $start_date); + + $this->_ci->ZeitsperreModel->db->where_in('mitarbeiter_uid', $uids); + $result = $this->_ci->ZeitsperreModel->load(); + + if (!hasData($result)) return []; + + $events = []; + foreach (getData($result) as $row) + { + $von = new DateTime($row->vondatum . ' ' . ($row->von_beginn ?? '00:00')); + $bis = new DateTime($row->bisdatum . ' ' . ($row->bis_ende ?? '23:59')); + + $events[] = (object)[ + 'isostart' => $von->format('c'), + 'isoend' => $bis->format('c'), + 'lektor' => [['mitarbeiter_uid' => $row->mitarbeiter_uid]] + ]; + } + return $events; + } + + private function _getLehreinheitTeilnehmer($lehreinheit_id) + { + $lektoren_result = $this->_ci->LehreinheitMitarbeiterModel->loadWhere(['lehreinheit_id' => $lehreinheit_id]); + $lektor_data = []; + if (hasData($lektoren_result)) + { + $lektor_data = getData($lektoren_result); + } + + $gruppen_result = $this->_ci->LehreinheitgruppeModel->getByLehreinheit($lehreinheit_id); + $gruppen_data = []; + if (hasData($gruppen_result)) + { + $gruppen_rows = getData($gruppen_result); + + foreach ($gruppen_rows as $gruppe) + { + $gruppen_data[] = (array) $gruppe; + } + } + + + return (object)[ + 'lektor_data' => $lektor_data, + 'gruppen_data' => $gruppen_data, + ]; + + } + + private function _splitGruppen($gruppen_data) + { + $gruppen_kurzbz = array_values(array_filter(array_column($gruppen_data, 'gruppe_kurzbz'))); + $lehrverband_gruppen = array_values(array_filter($gruppen_data, function($gruppe) { return empty($gruppe['gruppe_kurzbz']); })); + $lehrverband_gruppen_bezeichnung = array_column($lehrverband_gruppen, 'bezeichnung'); + + return (object)[ + 'gruppen_kurzbz' => $gruppen_kurzbz, + 'lehrverband_gruppen' => $lehrverband_gruppen, + 'lehrverband_gruppen_bezeichnung' => $lehrverband_gruppen_bezeichnung + ]; + + } + //TODO (david) umbauen auf CollisionChecks + private function _isFrei($slot_start, $slot_end, $events, $check_array, $type) + { + if (empty($check_array)) return true; + + $slot_start_ts = strtotime($slot_start); + $slot_end_ts = strtotime($slot_end); + + foreach ($events as $event) + { + $event_start_ts = strtotime($event->isostart); + $event_end_ts = strtotime($event->isoend); + + if ($event_end_ts <= $slot_start_ts || $event_start_ts >= $slot_end_ts) + continue; + + $event_uids = $this->_getIds($event, $type); + + if (!empty(array_intersect($event_uids, $check_array))) + return false; + } + + return true; + } + + private function _scoreToRating($score) + { + if ($score >= 90) + return 'good'; + if ($score >= 60) + return 'mid'; + return 'bad'; + } + private function _getVorschlaegeForEvent($event) + { + $raumkandidaten = $this->_getRaumkandidaten($event); + if (empty($raumkandidaten)) return []; + + $lektor_uids = array_column($event->lektor, 'mitarbeiter_uid'); + $grp = $this->_splitGruppen($event->gruppe); $tages_events = $this->_ci->kalenderlib->getForRaumvorschlag( $event->datum, $event->datum, $lektor_uids, - $gruppen_kurzbz, - $lehrverband_gruppen + $grp->gruppen_kurzbz, + $grp->lehrverband_gruppen ); - $lektor_davor = $this->_getEventDavor($tages_events, $event->isostart, $lektor_uids, 'lektor'); - $gruppen_davor = $this->_getEventDavor($tages_events, $event->isostart, $gruppen_kurzbz, 'gruppe'); + return $this->_getRatings($tages_events, $event, $raumkandidaten, $lektor_uids, $grp->gruppen_kurzbz, $grp->lehrverband_gruppen_bezeichnung); + } + private function _getRatings($events, $event, $raumkandidaten, $lektor_uids, $gruppen_kurzbz, $lehrverband_gruppen_bezeichnung) + { + $event = (object)$event; + $lektor_davor = $this->_getEventDavor($events, $event->isostart, $lektor_uids, 'lektor'); + $gruppen_davor = $this->_getEventDavor($events, $event->isostart, $gruppen_kurzbz, 'gruppe'); + $lehrverband_davor = $this->_getEventDavor($events, $event->isostart, $lehrverband_gruppen_bezeichnung, 'lehrverband'); $lektor_davor_ort = $lektor_davor ? $this->_getOrtDetails($lektor_davor->ort_kurzbz) : null; $gruppen_davor_ort = $gruppen_davor ? $this->_getOrtDetails($gruppen_davor->ort_kurzbz) : null; - - $kandidaten = $this->_getRaumkandidaten($event); - if (empty($kandidaten)) return []; + $lehrverband_davor_ort = $lehrverband_davor ? $this->_getOrtDetails($lehrverband_davor->ort_kurzbz) : null; $unique = []; - foreach ($kandidaten as $raum) + foreach ($raumkandidaten as $raum) { $unique[$raum->ort_kurzbz] = $raum; } $kandidaten = array_values($unique); + $ratings = []; foreach ($kandidaten as $raum) { - $rating = ['ort_kurzbz' => $raum->ort_kurzbz, 'score' => 100, 'details' => []]; + $rating = ['ort_kurzbz' => $raum->ort_kurzbz, 'score' => 100, 'details' => [], + 'raumtyp_kurzbz' => $raum->raumtyp_kurzbz, 'max_person' => $raum->max_person]; $this->_rateLektor($rating, $raum, $lektor_davor_ort); $this->_rateGruppen($rating, $raum, $gruppen_davor_ort); + $this->_rateGruppen($rating, $raum, $lehrverband_davor_ort); Events::trigger('room_rating', function & () use (&$rating) { @@ -204,10 +453,7 @@ class RaumvorschlagLib if (empty($event->ort_kurzbz)) continue; - if ($type === 'lektor') - $event_uids = array_column($event->lektor, 'mitarbeiter_uid'); - else - $event_uids = array_column($event->gruppe, 'gruppe_kurzbz'); + $event_uids = $this->_getIds($event, $type); if (empty(array_intersect($event_uids, $uids))) continue; @@ -219,6 +465,20 @@ class RaumvorschlagLib return $kandidat; } + private function _getIds($event, $type) + { + $event_uids = array(); + + if ($type === 'lektor') + $event_uids = array_column($event->lektor, 'mitarbeiter_uid'); + else if ($type === 'gruppe') + $event_uids = array_column($event->gruppe, 'gruppe_kurzbz'); + else if ($type === 'lehrverband') + $event_uids = array_column($event->gruppe, 'bezeichnung'); + + return $event_uids; + } + private function _getRaumkandidaten($event) { $raumtyp = null; @@ -240,9 +500,19 @@ class RaumvorschlagLib $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id'); $this->_ci->KalenderModel->db->where('tbl_kalender.von <', $event->isoend); $this->_ci->KalenderModel->db->where('tbl_kalender.bis >', $event->isostart); - $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $event->kalender_id); - $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['deleted']); + if (!is_null($event->kalender_id)) + { + $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $event->kalender_id); + } + $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['deleted', 'to_delete', 'to_delete_live', 'to_delete_preview', 'archived']); $this->_ci->KalenderModel->db->where('tbl_kalender_ort.ort_kurzbz IS NOT NULL', null, false); + $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 + ); + $belegte = $this->_ci->KalenderModel->load(); $belegte_orte = hasData($belegte) ? array_column(getData($belegte), 'ort_kurzbz') : []; @@ -273,7 +543,7 @@ class RaumvorschlagLib private function _getFreieRaeume($raumtyp, $belegte_orte) { - $this->_ci->OrtModel->addSelect('ort_kurzbz, stockwerk, standort_id'); + $this->_ci->OrtModel->addSelect('ort_kurzbz, stockwerk, standort_id, max_person, raumtyp_kurzbz'); $this->_ci->OrtModel->addJoin('public.tbl_ortraumtyp', 'ort_kurzbz'); if (!empty($raumtyp)) @@ -290,11 +560,4 @@ class RaumvorschlagLib return $this->_ci->OrtModel->load(); } - - - - - - - } diff --git a/application/libraries/collision/checks/LectureCollisionCheck.php b/application/libraries/collision/checks/LectureCollisionCheck.php index 21635e203..750f7c5ca 100644 --- a/application/libraries/collision/checks/LectureCollisionCheck.php +++ b/application/libraries/collision/checks/LectureCollisionCheck.php @@ -10,6 +10,7 @@ class LectureCollisionCheck implements ICollisionCheck $this->_ci =& get_instance(); $this->_ci->load->model('ressource/Kalender_model', 'KalenderModel'); $this->_ci->load->model('ressource/Zeitsperre_model', 'ZeitsperreModel'); + $this->_ci->load->model('education/Lehreinheitmitarbeiter_model', 'LehreinheitmitarbeiterModel'); $this->_ci->load->library('VariableLib', array('uid' => getAuthUID())); $this->_ci->load->library('PhrasesLib', array('ui')); @@ -22,11 +23,17 @@ class LectureCollisionCheck implements ICollisionCheck public function check($data) { - if (!isset($data->von, $data->bis, $data->kalender_id)) return []; + if (!isset($data->von, $data->bis)) return []; + if (empty($data->kalender_id) && empty($data->lehreinheit_id) && empty($data->uids)) return []; if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return []; - $uids = $this->_getUids($data->kalender_id); + if (!empty($data->kalender_id)) + $uids = $this->_getUids($data->kalender_id); + else if (!empty($data->lehreinheit_id)) + $uids = $this->_getUidsFromLehreinheit($data->lehreinheit_id); + else + $uids = $data->uids; if (empty($uids)) return []; @@ -58,9 +65,17 @@ class LectureCollisionCheck implements ICollisionCheck $this->_ci->KalenderModel->db->where('other_kalender.kalender_id != tbl_kalender.kalender_id', null, false); $this->_ci->KalenderModel->db->where('other_kalender.von < tbl_kalender.bis', null, false); $this->_ci->KalenderModel->db->where('other_kalender.bis > tbl_kalender.von', null, false); - $this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']); + $this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')); $this->_ci->KalenderModel->db->where_not_in('current_lehreinheit_ma.mitarbeiter_uid', $kollisionsfreie_user); - $this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids); + + $this->_ci->KalenderModel->db->group_start(); + $where_ids_chunks = array_chunk($kalender_ids,25); + foreach($where_ids_chunks as $where_ids) + { + $this->_ci->KalenderModel->db->or_where_in('tbl_kalender.kalender_id', $where_ids); + } + $this->_ci->KalenderModel->db->group_end(); + $this->_ci->KalenderModel->db->where( 'other_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', null, false @@ -85,9 +100,17 @@ class LectureCollisionCheck implements ICollisionCheck $this->_ci->KalenderModel->db->where('other_kalender.kalender_id != tbl_kalender.kalender_id', null, false); $this->_ci->KalenderModel->db->where('other_kalender.von < tbl_kalender.bis', null, false); $this->_ci->KalenderModel->db->where('other_kalender.bis > tbl_kalender.von', null, false); - $this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']); + $this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview']); $this->_ci->KalenderModel->db->where_not_in('current_lehreinheit_ma.mitarbeiter_uid', $kollisionsfreie_user); - $this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids); + + $this->_ci->KalenderModel->db->group_start(); + $where_ids_chunks = array_chunk($kalender_ids,25); + foreach($where_ids_chunks as $where_ids) + { + $this->_ci->KalenderModel->db->or_where_in('tbl_kalender.kalender_id', $where_ids); + } + $this->_ci->KalenderModel->db->group_end(); + $this->_ci->KalenderModel->db->where( 'other_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', null, false @@ -114,7 +137,13 @@ class LectureCollisionCheck implements ICollisionCheck $this->_ci->KalenderModel->db->where('(z.vondatum + COALESCE(vonstunde_z.beginn, \'00:00\'))::timestamp < tbl_kalender.bis', null, false); $this->_ci->KalenderModel->db->where('(z.bisdatum + COALESCE(bisstunde_z.ende, \'23:59\'))::timestamp > tbl_kalender.von', null, false); $this->_ci->KalenderModel->db->where_not_in('current_lehreinheit_ma.mitarbeiter_uid', $kollisionsfreie_user); - $this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids); + $this->_ci->KalenderModel->db->group_start(); + $where_ids_chunks = array_chunk($kalender_ids,25); + foreach($where_ids_chunks as $where_ids) + { + $this->_ci->KalenderModel->db->or_where_in('tbl_kalender.kalender_id', $where_ids); + } + $this->_ci->KalenderModel->db->group_end(); $result = $this->_ci->KalenderModel->load(); @@ -159,6 +188,20 @@ class LectureCollisionCheck implements ICollisionCheck return array_unique(array_merge($mitarbeiter_uids, $event_teilnehmer)); } + private function _getUidsFromLehreinheit($lehreinheit_id) + { + $kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER); + + $result = $this->_ci->LehreinheitmitarbeiterModel->loadWhere(array('lehreinheit_id' => $lehreinheit_id)); + + if (isError($result) || !hasData($result)) return []; + + $uids = array_column(getData($result), 'mitarbeiter_uid'); + $uids = array_diff(array_filter($uids), $kollisionsfreie_user); + + return array_values(array_unique($uids)); + } + private function _checkLehreinheit($uids, $data) { $kollisionsfreie_user = unserialize(KOLLISIONSFREIE_USER); @@ -170,8 +213,9 @@ class LectureCollisionCheck implements ICollisionCheck $this->_ci->KalenderModel->addJoin('lehre.tbl_lehreinheitmitarbeiter', 'lehreinheit_id', 'LEFT'); $this->_ci->KalenderModel->db->where_in('mitarbeiter_uid', $uids); - $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id); - $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete')); + if (!empty($data->kalender_id)) + $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id); + $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')); $this->_ci->KalenderModel->db->where_not_in('mitarbeiter_uid', $kollisionsfreie_user); $this->_ci->KalenderModel->db->where( 'tbl_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', @@ -207,8 +251,9 @@ class LectureCollisionCheck implements ICollisionCheck $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_event_teilnehmer', 'tbl_kalender_event.kalender_id = tbl_kalender_event_teilnehmer.kalender_id', 'LEFT'); $this->_ci->KalenderModel->db->where_in('tbl_kalender_event_teilnehmer.uid', $uids); - $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id); - $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete')); + if (!empty($data->kalender_id)) + $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id); + $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')); $this->_ci->KalenderModel->db->where_not_in('uid', $kollisionsfreie_user); $this->_ci->KalenderModel->db->where( 'tbl_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', diff --git a/application/libraries/collision/checks/ResourcesCollisionCheck.php b/application/libraries/collision/checks/ResourcesCollisionCheck.php index 0c957965d..da163977e 100644 --- a/application/libraries/collision/checks/ResourcesCollisionCheck.php +++ b/application/libraries/collision/checks/ResourcesCollisionCheck.php @@ -20,7 +20,7 @@ class ResourcesCollisionCheck implements ICollisionCheck public function check($data) { - if (!isset($data->betriebsmittel_ids, $data->von, $data->bis, $data->kalender_id)) return []; + if (!isset($data->betriebsmittel_ids, $data->von, $data->bis)) return []; if (empty($data->betriebsmittel_ids)) return []; @@ -31,9 +31,10 @@ class ResourcesCollisionCheck implements ICollisionCheck $this->_ci->KalenderModel->addJoin('lehre.tbl_betriebsmittel_kalender bk', 'bk.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_kalender_gruppen_id'); $this->_ci->KalenderModel->addJoin('wawi.tbl_betriebsmittel bm', 'bm.betriebsmittel_id = bk.betriebsmittel_id'); - $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id); + if (!empty($data->kalender_id)) + $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id); $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', null, false); - $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']); + $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview']); $this->_ci->KalenderModel->db->where_in('bk.betriebsmittel_id', $data->betriebsmittel_ids); @@ -66,8 +67,16 @@ class ResourcesCollisionCheck implements ICollisionCheck $this->_ci->KalenderModel->db->where('other_kalender.kalender_id != tbl_kalender.kalender_id', null, false); $this->_ci->KalenderModel->db->where('other_kalender.von < tbl_kalender.bis', null, false); $this->_ci->KalenderModel->db->where('other_kalender.bis > tbl_kalender.von', null, false); - $this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']); - $this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids); + $this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview']); + + $this->_ci->KalenderModel->db->group_start(); + $where_ids_chunks = array_chunk($kalender_ids,25); + foreach($where_ids_chunks as $where_ids) + { + $this->_ci->KalenderModel->db->or_where_in('tbl_kalender.kalender_id', $where_ids); + } + $this->_ci->KalenderModel->db->group_end(); + $this->_ci->KalenderModel->db->where('other_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', null, false); $result = $this->_ci->KalenderModel->load(); diff --git a/application/libraries/collision/checks/RoomCollisionCheck.php b/application/libraries/collision/checks/RoomCollisionCheck.php index b59d79028..6e25f3ef0 100644 --- a/application/libraries/collision/checks/RoomCollisionCheck.php +++ b/application/libraries/collision/checks/RoomCollisionCheck.php @@ -20,16 +20,17 @@ class RoomCollisionCheck implements ICollisionCheck public function check($data) { - if (!isset($data->ort_kurzbz, $data->von, $data->bis, $data->kalender_id) || isEmptyArray($data->ort_kurzbz)) return []; + if (!isset($data->ort_kurzbz, $data->von, $data->bis) || isEmptyArray($data->ort_kurzbz)) return []; if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return []; $this->_ci->KalenderModel->addSelect('kalender_id, ort_kurzbz, von, bis'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'kalender_id'); - $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id); + if (!empty($data->kalender_id)) + $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id); $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', null, false); - $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']); + $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')); $this->_ci->KalenderModel->db->where_in('ort_kurzbz', $data->ort_kurzbz); $result = $this->_ci->KalenderModel->loadWhere(array( @@ -61,8 +62,16 @@ class RoomCollisionCheck implements ICollisionCheck $this->_ci->KalenderModel->db->where('other_kalender.kalender_id != tbl_kalender.kalender_id', null, false); $this->_ci->KalenderModel->db->where('other_kalender.von < tbl_kalender.bis', null, false); $this->_ci->KalenderModel->db->where('other_kalender.bis > tbl_kalender.von', null, false); - $this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']); - $this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids); + $this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', array('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview')); + + $this->_ci->KalenderModel->db->group_start(); + $where_ids_chunks = array_chunk($kalender_ids,25); + foreach($where_ids_chunks as $where_ids) + { + $this->_ci->KalenderModel->db->or_where_in('tbl_kalender.kalender_id', $where_ids); + } + $this->_ci->KalenderModel->db->group_end(); + $this->_ci->KalenderModel->db->where('other_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', null, false); $result = $this->_ci->KalenderModel->load(); diff --git a/application/libraries/collision/checks/StudentCollisionCheck.php b/application/libraries/collision/checks/StudentCollisionCheck.php index 4ffeb071e..bb9ce10db 100644 --- a/application/libraries/collision/checks/StudentCollisionCheck.php +++ b/application/libraries/collision/checks/StudentCollisionCheck.php @@ -19,7 +19,8 @@ class StudentCollisionCheck implements ICollisionCheck public function check($data) { - if (!isset($data->von, $data->bis, $data->kalender_id)) return []; + if (!isset($data->von, $data->bis)) return []; + if (!isset($data->kalender_id) && !isset($data->lehreinheit_id)) return []; if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return []; if ($this->_ci->variablelib->getVar('kollision_student') !== 'true') return []; @@ -30,7 +31,9 @@ class StudentCollisionCheck implements ICollisionCheck $dbModel = new DB_Model(); - $qry1 = " + if (isset($data->kalender_id)) + { + $qry1 = " SELECT DISTINCT tbl_benutzergruppe.uid FROM lehre.tbl_kalender JOIN lehre.tbl_kalender_lehreinheit USING(kalender_id) @@ -60,13 +63,48 @@ class StudentCollisionCheck implements ICollisionCheck AND (tbl_lehreinheitgruppe.gruppe = tbl_studentlehrverband.gruppe OR tbl_lehreinheitgruppe.gruppe IS NULL OR btrim(tbl_lehreinheitgruppe.gruppe::text) = '' OR tbl_studentlehrverband.gruppe IS NULL) WHERE tbl_kalender.kalender_id = ? AND tbl_studentlehrverband.student_uid NOT IN ($placeholders) + "; - "; + $qry_1_param = $data->kalender_id; + } + else + { + $qry1 = " + SELECT DISTINCT tbl_benutzergruppe.uid + FROM lehre.tbl_lehreinheit + JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id + JOIN public.tbl_gruppe + ON tbl_gruppe.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz + AND tbl_gruppe.semester = tbl_lehreinheitgruppe.semester + AND tbl_gruppe.gruppe_kurzbz = tbl_lehreinheitgruppe.gruppe_kurzbz + JOIN public.tbl_benutzergruppe ON tbl_benutzergruppe.gruppe_kurzbz = tbl_gruppe.gruppe_kurzbz + AND tbl_benutzergruppe.studiensemester_kurzbz = tbl_lehreinheit.studiensemester_kurzbz + + WHERE tbl_lehreinheit.lehreinheit_id = ? + AND tbl_benutzergruppe.uid NOT IN ($placeholders) + + UNION ALL + + SELECT DISTINCT tbl_studentlehrverband.student_uid AS uid + FROM lehre.tbl_lehreinheit + JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id + JOIN public.tbl_studentlehrverband + ON tbl_studentlehrverband.studiengang_kz = tbl_lehreinheitgruppe.studiengang_kz + AND tbl_studentlehrverband.semester = tbl_lehreinheitgruppe.semester + AND tbl_studentlehrverband.studiensemester_kurzbz = tbl_lehreinheit.studiensemester_kurzbz + AND (tbl_lehreinheitgruppe.verband = tbl_studentlehrverband.verband OR tbl_lehreinheitgruppe.verband IS NULL OR btrim(tbl_lehreinheitgruppe.verband::text) = '' OR tbl_studentlehrverband.verband IS NULL) + AND (tbl_lehreinheitgruppe.gruppe = tbl_studentlehrverband.gruppe OR tbl_lehreinheitgruppe.gruppe IS NULL OR btrim(tbl_lehreinheitgruppe.gruppe::text) = '' OR tbl_studentlehrverband.gruppe IS NULL) + WHERE tbl_lehreinheit.lehreinheit_id = ? + AND tbl_studentlehrverband.student_uid NOT IN ($placeholders) + "; + + $qry_1_param = $data->lehreinheit_id; + } $result1 = $dbModel->execReadOnlyQuery($qry1, array_merge( - [$data->kalender_id], + [$qry_1_param], $kollisionsfreie_user, - [$data->kalender_id], + [$qry_1_param], $kollisionsfreie_user )); @@ -74,6 +112,9 @@ class StudentCollisionCheck implements ICollisionCheck $curUids = array_flip(array_column(getData($result1), 'uid')); + $exclude_kalender = isset($data->kalender_id) ? "AND tbl_kalender.kalender_id != ?" : ""; + $exclude_kalender_param = isset($data->kalender_id) ? [$data->kalender_id] : []; + $qry2 = " SELECT DISTINCT tbl_kalender.kalender_id, tbl_kalender.von, tbl_kalender.bis, tbl_benutzergruppe.uid FROM lehre.tbl_kalender @@ -88,8 +129,8 @@ class StudentCollisionCheck implements ICollisionCheck AND tbl_benutzergruppe.studiensemester_kurzbz = tbl_lehreinheit.studiensemester_kurzbz WHERE tbl_kalender.von < ? AND tbl_kalender.bis > ? - AND tbl_kalender.kalender_id != ? - AND tbl_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete') + $exclude_kalender + AND tbl_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview') AND tbl_benutzergruppe.uid NOT IN ($placeholders) AND NOT EXISTS ( SELECT 1 FROM lehre.tbl_kalender vorgaenger @@ -97,7 +138,7 @@ class StudentCollisionCheck implements ICollisionCheck ) UNION ALL - + SELECT DISTINCT tbl_kalender.kalender_id, tbl_kalender.von, tbl_kalender.bis, tbl_studentlehrverband.student_uid AS uid FROM lehre.tbl_kalender JOIN lehre.tbl_kalender_lehreinheit ON tbl_kalender_lehreinheit.kalender_id = tbl_kalender.kalender_id @@ -111,8 +152,8 @@ class StudentCollisionCheck implements ICollisionCheck AND (tbl_lehreinheitgruppe.gruppe = tbl_studentlehrverband.gruppe OR tbl_lehreinheitgruppe.gruppe IS NULL OR btrim(tbl_lehreinheitgruppe.gruppe::text) = '' OR tbl_studentlehrverband.gruppe IS NULL) WHERE tbl_kalender.von < ? AND tbl_kalender.bis > ? - AND tbl_kalender.kalender_id != ? - AND tbl_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete') + $exclude_kalender + AND tbl_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview') AND tbl_studentlehrverband.student_uid NOT IN ($placeholders) AND NOT EXISTS ( SELECT 1 FROM lehre.tbl_kalender vorgaenger @@ -121,10 +162,8 @@ class StudentCollisionCheck implements ICollisionCheck "; $result2 = $dbModel->execReadOnlyQuery($qry2, array_merge( - [$data->bis, $data->von, $data->kalender_id], - $kollisionsfreie_user, - [$data->bis, $data->von, $data->kalender_id], - $kollisionsfreie_user + [$data->bis, $data->von], $exclude_kalender_param, $kollisionsfreie_user, + [$data->bis, $data->von], $exclude_kalender_param, $kollisionsfreie_user )); if (isError($result2) || !hasData($result2)) return []; @@ -175,7 +214,7 @@ class StudentCollisionCheck implements ICollisionCheck ON other_kalender.kalender_id != current_kalender.kalender_id AND other_kalender.von < current_kalender.bis AND other_kalender.bis > current_kalender.von - AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete') + AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview') AND NOT EXISTS ( SELECT 1 FROM lehre.tbl_kalender vorgaenger WHERE vorgaenger.vorgaenger_kalender_id = other_kalender.kalender_id @@ -213,7 +252,7 @@ class StudentCollisionCheck implements ICollisionCheck ON other_kalender.kalender_id != current_kalender.kalender_id AND other_kalender.von < current_kalender.bis AND other_kalender.bis > current_kalender.von - AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete') + AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview') AND NOT EXISTS ( SELECT 1 FROM lehre.tbl_kalender vorgaenger WHERE vorgaenger.vorgaenger_kalender_id = other_kalender.kalender_id diff --git a/application/libraries/collision/checks/VerbandCollisionCheck.php b/application/libraries/collision/checks/VerbandCollisionCheck.php index 40baf38a2..98ae3efee 100644 --- a/application/libraries/collision/checks/VerbandCollisionCheck.php +++ b/application/libraries/collision/checks/VerbandCollisionCheck.php @@ -21,18 +21,58 @@ class VerbandCollisionCheck implements ICollisionCheck public function check($data) { - if (!isset($data->von, $data->bis, $data->kalender_id)) return []; + if (!isset($data->von, $data->bis)) return []; + if (!isset($data->kalender_id) && !isset($data->lehreinheit_id)) return []; if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return []; $kollision_student = $this->_ci->variablelib->getVar('kollision_student') === 'false'; $kollision_reservierung = $this->_ci->variablelib->getVar('ignore_reservierung') === 'false'; + if (!isset($data->kalender_id)) $kollision_reservierung = false; + if (!$kollision_student && !$kollision_reservierung) return []; $dbModel = new DB_Model(); $collisions = []; + $group_matching_qry = " + ( + ( + current_lehreinheitguppe.gruppe_kurzbz IS NULL + AND other_lehreinheitguppe.gruppe_kurzbz IS NULL + AND ( + current_lehreinheitguppe.verband IS NULL + OR ( + current_lehreinheitguppe.verband = other_lehreinheitguppe.verband + AND (current_lehreinheitguppe.gruppe IS NULL OR other_lehreinheitguppe.gruppe IS NULL OR current_lehreinheitguppe.gruppe = other_lehreinheitguppe.gruppe) + ) + ) + ) + OR + ( + current_lehreinheitguppe.gruppe_kurzbz IS NOT NULL + AND other_lehreinheitguppe.gruppe_kurzbz IS NOT NULL + AND current_gruppe.direktinskription IS NOT TRUE + AND other_gruppe.direktinskription IS NOT TRUE + ) + OR + ( + ( + current_lehreinheitguppe.gruppe_kurzbz IS NULL + AND other_lehreinheitguppe.gruppe_kurzbz IS NOT NULL + AND other_gruppe.direktinskription IS NOT TRUE + ) + OR + ( + current_lehreinheitguppe.gruppe_kurzbz IS NOT NULL + AND other_lehreinheitguppe.gruppe_kurzbz IS NULL + AND current_gruppe.direktinskription IS NOT TRUE + ) + ) + ) + "; + if ($kollision_student) { $union_event = ""; @@ -47,100 +87,111 @@ class VerbandCollisionCheck implements ICollisionCheck "; } - $sql_gruppen = " - SELECT - other_kalender.von, - other_kalender.bis, - COALESCE( - other_lehreinheitguppe.gruppe_kurzbz, - UPPER(stg.typ::text || stg.kurzbz::text) || '-' || other_lehreinheitguppe.semester || - COALESCE(other_lehreinheitguppe.verband::text, '') || - COALESCE(other_lehreinheitguppe.gruppe::text, '') - ) AS gruppenname - FROM lehre.tbl_kalender current_kalender + $other_lehreinheitgruppe_subquery = " + SELECT tbl_lehreinheitgruppe.studiengang_kz, tbl_lehreinheitgruppe.semester, tbl_lehreinheitgruppe.verband, tbl_lehreinheitgruppe.gruppe, + tbl_lehreinheitgruppe.gruppe_kurzbz, tbl_kalender_lehreinheit.kalender_id + FROM lehre.tbl_kalender_lehreinheit + JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id + JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id + ". $union_event ." + "; - JOIN ( - SELECT tbl_lehreinheitgruppe.studiengang_kz, tbl_lehreinheitgruppe.semester, tbl_lehreinheitgruppe.verband, tbl_lehreinheitgruppe.gruppe, - tbl_lehreinheitgruppe.gruppe_kurzbz, tbl_kalender_lehreinheit.kalender_id - FROM lehre.tbl_kalender_lehreinheit - JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id - JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id - ". $union_event ." - ) current_lehreinheitguppe ON current_lehreinheitguppe.kalender_id = current_kalender.kalender_id - - LEFT JOIN public.tbl_gruppe current_gruppe - ON current_gruppe.gruppe_kurzbz = current_lehreinheitguppe.gruppe_kurzbz - - JOIN lehre.tbl_kalender other_kalender - ON other_kalender.kalender_id != current_kalender.kalender_id - AND other_kalender.von < ? - AND other_kalender.bis > ? - - JOIN ( - SELECT tbl_lehreinheitgruppe.studiengang_kz, tbl_lehreinheitgruppe.semester, tbl_lehreinheitgruppe.verband, tbl_lehreinheitgruppe.gruppe, - tbl_lehreinheitgruppe.gruppe_kurzbz, tbl_kalender_lehreinheit.kalender_id - FROM lehre.tbl_kalender_lehreinheit - JOIN lehre.tbl_lehreinheit ON tbl_lehreinheit.lehreinheit_id = tbl_kalender_lehreinheit.lehreinheit_id - JOIN lehre.tbl_lehreinheitgruppe ON tbl_lehreinheitgruppe.lehreinheit_id = tbl_lehreinheit.lehreinheit_id - ". $union_event ." - ) other_lehreinheitguppe ON other_lehreinheitguppe.kalender_id = other_kalender.kalender_id - - LEFT JOIN public.tbl_gruppe other_gruppe - ON other_gruppe.gruppe_kurzbz = other_lehreinheitguppe.gruppe_kurzbz - - LEFT JOIN public.tbl_studiengang stg - ON stg.studiengang_kz = other_lehreinheitguppe.studiengang_kz - - WHERE current_kalender.kalender_id = ? - AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete') - AND current_lehreinheitguppe.studiengang_kz = other_lehreinheitguppe.studiengang_kz - AND current_lehreinheitguppe.semester = other_lehreinheitguppe.semester - AND ( - ( - current_lehreinheitguppe.gruppe_kurzbz IS NULL - AND other_lehreinheitguppe.gruppe_kurzbz IS NULL - AND ( - current_lehreinheitguppe.verband IS NULL - OR ( - current_lehreinheitguppe.verband = other_lehreinheitguppe.verband - AND (current_lehreinheitguppe.gruppe IS NULL OR other_lehreinheitguppe.gruppe IS NULL OR current_lehreinheitguppe.gruppe = other_lehreinheitguppe.gruppe) - ) - ) + if (isset($data->kalender_id)) + { + $sql_gruppen = " + SELECT + other_kalender.von, + other_kalender.bis, + COALESCE( + other_lehreinheitguppe.gruppe_kurzbz, + UPPER(stg.typ::text || stg.kurzbz::text) || '-' || other_lehreinheitguppe.semester || + COALESCE(other_lehreinheitguppe.verband::text, '') || + COALESCE(other_lehreinheitguppe.gruppe::text, '') + ) AS gruppenname + FROM lehre.tbl_kalender current_kalender + + JOIN ( + ". $other_lehreinheitgruppe_subquery ." + ) current_lehreinheitguppe ON current_lehreinheitguppe.kalender_id = current_kalender.kalender_id + + LEFT JOIN public.tbl_gruppe current_gruppe + ON current_gruppe.gruppe_kurzbz = current_lehreinheitguppe.gruppe_kurzbz + + JOIN lehre.tbl_kalender other_kalender + ON other_kalender.kalender_id != current_kalender.kalender_id + AND other_kalender.von < ? + AND other_kalender.bis > ? + + JOIN ( + ". $other_lehreinheitgruppe_subquery ." + ) other_lehreinheitguppe ON other_lehreinheitguppe.kalender_id = other_kalender.kalender_id + + LEFT JOIN public.tbl_gruppe other_gruppe + ON other_gruppe.gruppe_kurzbz = other_lehreinheitguppe.gruppe_kurzbz + + LEFT JOIN public.tbl_studiengang stg + ON stg.studiengang_kz = other_lehreinheitguppe.studiengang_kz + + WHERE current_kalender.kalender_id = ? + AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview') + AND current_lehreinheitguppe.studiengang_kz = other_lehreinheitguppe.studiengang_kz + AND current_lehreinheitguppe.semester = other_lehreinheitguppe.semester + AND ". $group_matching_qry ." + AND other_kalender.kalender_id NOT IN ( + SELECT vorgaenger_kalender_id + FROM lehre.tbl_kalender + WHERE vorgaenger_kalender_id IS NOT NULL ) - OR - ( - current_lehreinheitguppe.gruppe_kurzbz IS NOT NULL - AND other_lehreinheitguppe.gruppe_kurzbz IS NOT NULL - AND current_gruppe.direktinskription IS NOT TRUE - AND other_gruppe.direktinskription IS NOT TRUE - ) - OR - ( - ( - current_lehreinheitguppe.gruppe_kurzbz IS NULL - AND other_lehreinheitguppe.gruppe_kurzbz IS NOT NULL - AND other_gruppe.direktinskription IS NOT TRUE - ) - OR - ( - current_lehreinheitguppe.gruppe_kurzbz IS NOT NULL - AND other_lehreinheitguppe.gruppe_kurzbz IS NULL - AND current_gruppe.direktinskription IS NOT TRUE - ) - ) - ) - AND other_kalender.kalender_id NOT IN ( - SELECT vorgaenger_kalender_id - FROM lehre.tbl_kalender - WHERE vorgaenger_kalender_id IS NOT NULL - ) - "; + "; - $result = $dbModel->execReadOnlyQuery($sql_gruppen, [ - $data->bis, - $data->von, - $data->kalender_id, - ]); + $params = [$data->bis, $data->von, $data->kalender_id]; + } + else + { + $sql_gruppen = " + SELECT + other_kalender.von, + other_kalender.bis, + COALESCE( + other_lehreinheitguppe.gruppe_kurzbz, + UPPER(stg.typ::text || stg.kurzbz::text) || '-' || other_lehreinheitguppe.semester || + COALESCE(other_lehreinheitguppe.verband::text, '') || + COALESCE(other_lehreinheitguppe.gruppe::text, '') + ) AS gruppenname + FROM lehre.tbl_lehreinheitgruppe current_lehreinheitguppe + LEFT JOIN public.tbl_gruppe current_gruppe + ON current_gruppe.gruppe_kurzbz = current_lehreinheitguppe.gruppe_kurzbz + + JOIN lehre.tbl_kalender other_kalender + ON other_kalender.von < ? + AND other_kalender.bis > ? + + JOIN ( + ". $other_lehreinheitgruppe_subquery ." + ) other_lehreinheitguppe ON other_lehreinheitguppe.kalender_id = other_kalender.kalender_id + + LEFT JOIN public.tbl_gruppe other_gruppe + ON other_gruppe.gruppe_kurzbz = other_lehreinheitguppe.gruppe_kurzbz + + LEFT JOIN public.tbl_studiengang stg + ON stg.studiengang_kz = other_lehreinheitguppe.studiengang_kz + + WHERE current_lehreinheitguppe.lehreinheit_id = ? + AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview') + AND current_lehreinheitguppe.studiengang_kz = other_lehreinheitguppe.studiengang_kz + AND current_lehreinheitguppe.semester = other_lehreinheitguppe.semester + AND ". $group_matching_qry ." + AND other_kalender.kalender_id NOT IN ( + SELECT vorgaenger_kalender_id + FROM lehre.tbl_kalender + WHERE vorgaenger_kalender_id IS NOT NULL + ) + "; + + $params = [$data->bis, $data->von, $data->lehreinheit_id]; + } + + $result = $dbModel->execReadOnlyQuery($sql_gruppen, $params); if (!isError($result) && hasData($result)) { @@ -187,7 +238,7 @@ class VerbandCollisionCheck implements ICollisionCheck WHERE current_event_teilnehmer.kalender_id = ? AND current_event_teilnehmer.rolle_kurzbz = 'teilnehmer' - AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete') + AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview') AND current_event_teilnehmer.studiengang_kz = other_event_teilnehmer.studiengang_kz AND current_event_teilnehmer.semester = other_event_teilnehmer.semester AND ( @@ -301,7 +352,7 @@ class VerbandCollisionCheck implements ICollisionCheck ON other_gruppe.gruppe_kurzbz = other_lehreinheitguppe.gruppe_kurzbz WHERE current_kalender.kalender_id IN ({$placeholders}) - AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete') + AND other_kalender.status_kurzbz NOT IN ('archived', 'deleted', 'to_delete', 'to_delete_live', 'to_delete_preview') AND current_lehreinheitguppe.studiengang_kz = other_lehreinheitguppe.studiengang_kz AND current_lehreinheitguppe.semester = other_lehreinheitguppe.semester AND ( 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 d82be1426..db49c57db 100644 --- a/application/views/Tempus.php +++ b/application/views/Tempus.php @@ -7,11 +7,14 @@ 'vue3' => true, 'primevue3' => true, 'tabulator5' => true, + 'filtercomponent' => true, + 'navigationcomponent' => true, 'vuedatepicker11' => true, + 'tags' => true, 'phrases' => array( 'global', 'ui', - 'notiz', + 'lehre' ), 'customCSSs' => [ 'public/css/components/vue-datepicker.css', diff --git a/public/css/Studentenverwaltung.css b/public/css/Studentenverwaltung.css index 870a58bdf..470ea03e9 100644 --- a/public/css/Studentenverwaltung.css +++ b/public/css/Studentenverwaltung.css @@ -8,6 +8,8 @@ @import './components/Notiz.css'; @import './components/Messages.css'; @import './components/AppMenu.css'; +@import './components/horizontalsplit.css'; + html { font-size: .875em; diff --git a/public/css/Tempus.css b/public/css/Tempus.css index 3e32789e1..6ca71025b 100644 --- a/public/css/Tempus.css +++ b/public/css/Tempus.css @@ -110,15 +110,23 @@ body { background-color: var(--event-bg); } -.parkingevent { +.course-parker { + position: relative; border: 1px dashed !important; - border-color: #AAAAAA; padding: 0.5rem; - margin-bottom: 0.5rem; - margin-left: 0.5rem; - margin-right: 0.5rem; + margin: 0.5rem; + background-color: var(--event-bg); + color: var(--event-color, var(--fhc-calendar-color-event, #000)); } +.unpark-btn { + position: absolute; + top: 2px; + right: 2px; + padding: 0 4px; + border: none; + background: transparent; +} :root{ --fhc-calendar-pane-height: calc(100vh - 120px); } @@ -171,6 +179,21 @@ body { } +.bg-raumvorschlag-slot { + opacity: .70; +} + +.bg-raumvorschlag-slot.rating-good { + background-color: var(--bs-success); +} +.bg-raumvorschlag-slot.rating-mid { + background-color: var(--bs-warning); +} +.bg-raumvorschlag-slot.rating-bad { + background-color: var(--bs-danger); +} + + .wish-w--2 { background-color: #FF2200; } @@ -185,3 +208,9 @@ body { } +.room-option { + display: grid; + grid-template-columns: 20px minmax(80px, 110px) minmax(100px, 1fr) auto 20px 50px; + align-items: center; + width: 100%; +} diff --git a/public/css/components/calendar/events.css b/public/css/components/calendar/events.css index 8946c1856..f224c6f7b 100644 --- a/public/css/components/calendar/events.css +++ b/public/css/components/calendar/events.css @@ -86,6 +86,9 @@ pointer-events: none; background-color: transparent !important; border: none !important; + width: fit-content; + margin-left: auto; + margin-right: auto; } .fhc-resize-bar--top { diff --git a/public/css/components/calendar/frame.css b/public/css/components/calendar/frame.css index d17cc06b4..68b907179 100644 --- a/public/css/components/calendar/frame.css +++ b/public/css/components/calendar/frame.css @@ -68,9 +68,16 @@ /* Grid */ .fhc-calendar-base-grid { --fhc-calendar-axis-collapsible: .2fr; + --fhc-calendar-axis-collapsible-manual: .1fr; + background-color: var(--fhc-calendar-bg-gap); grid-gap: var(--fhc-calendar-gap-header, calc(var(--fhc-calendar-gap, 1px) * 2)); } + +.fhc-calendar-base-grid-line-collapsed { + overflow: hidden; +} + .fhc-calendar-base-grid .grid-header { background-color: var(--fhc-calendar-bg-header-gap, var(--fhc-calendar-bg-gap)); } diff --git a/public/css/tags.css b/public/css/tags.css index 0230cfbda..ec32f0c69 100644 --- a/public/css/tags.css +++ b/public/css/tags.css @@ -17,6 +17,13 @@ transform: scale(1.15); } +/* add prop that disasbles hover and cursor*/ +.tag.disabled:hover +{ + transform: none; + cursor: default; +} + .tag_rot { background-color: #ff0000ff; } diff --git a/public/js/api/factory/tempus/coursepicker.js b/public/js/api/factory/tempus/coursepicker.js index 92ff4fa6b..80b201a00 100644 --- a/public/js/api/factory/tempus/coursepicker.js +++ b/public/js/api/factory/tempus/coursepicker.js @@ -1,16 +1,9 @@ export default { - search(query) { - return { - method: 'get', - url: 'api/frontend/v1/tempus/coursepicker/search', - params: { query } - }; - }, - getByStg(studiengaenge, studiensemester_kurzbz) { + getCourses(filter, studiensemester_kurzbz) { return { method: 'post', - url: 'api/frontend/v1/tempus/coursepicker/getByStg', - params: { studiengaenge, studiensemester_kurzbz } + url: 'api/frontend/v1/tempus/coursepicker/getCourses', + params: { ...filter, studiensemester_kurzbz } }; }, diff --git a/public/js/api/factory/tempus/kalender.js b/public/js/api/factory/tempus/kalender.js index 4afe587e0..046aec385 100644 --- a/public/js/api/factory/tempus/kalender.js +++ b/public/js/api/factory/tempus/kalender.js @@ -14,7 +14,15 @@ export default { } }; }, - getPlanLecturer(start_date, end_date, collisionCheck = true, maxDailyEventLimit = null) + getRaumvorschlagSlots(lehreinheit_id, start_date, end_date) + { + return { + method: 'get', + url: '/api/frontend/v1/tempus/Kalender/getRaumvorschlagSlots', + params: { lehreinheit_id, start_date, end_date } + }; + }, + getPlanLecturer(start_date, end_date) { return { method: 'get', diff --git a/public/js/api/factory/tempus/reservierung.js b/public/js/api/factory/tempus/reservierung.js index 7f91a6817..d0aa76bb8 100644 --- a/public/js/api/factory/tempus/reservierung.js +++ b/public/js/api/factory/tempus/reservierung.js @@ -48,11 +48,11 @@ export default { url: '/api/frontend/v1/tempus/Reservierung/getRollen', }; }, - addReservierung(titel, beschreibung, ort_kurzbz, start_date, end_date, teilnehmer, specialFinalGroups, specialGroups, groups) { + addReservierung(titel, beschreibung, orte, start_date, end_date, teilnehmer, specialFinalGroups, specialGroups, groups) { return { method: 'post', url: '/api/frontend/v1/tempus/Reservierung/addReservierung', - params: { titel, beschreibung, ort_kurzbz, start_date, end_date, teilnehmer, specialFinalGroups, specialGroups, groups} + params: { titel, beschreibung, orte, start_date, end_date, teilnehmer, specialFinalGroups, specialGroups, groups} }; }, 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/api/factory/tempus/tag.js b/public/js/api/factory/tempus/tag.js new file mode 100644 index 000000000..6b8a4ce01 --- /dev/null +++ b/public/js/api/factory/tempus/tag.js @@ -0,0 +1,59 @@ +export default { + + getTag(data) + { + return { + method: 'get', + url: '/api/frontend/v1/tempus/Tags/getTag', + params: data + }; + }, + + getTags(data) + { + return { + method: 'get', + url: '/api/frontend/v1/tempus/Tags/getTags' + }; + }, + getTagsByCalendar(calenderGroupId) + { + return { + method: 'get', + url: '/api/frontend/v1/tempus/Tags/getTagsByCalendar/' + calenderGroupId, + }; + }, + addTag(data) + { + return { + method: 'post', + url: '/api/frontend/v1/tempus/Tags/addTag', + params: data + }; + }, + updateTag(data) + { + return { + method: 'post', + url: '/api/frontend/v1/tempus/Tags/updateTag', + params: data + }; + }, + doneTag(data) + { + return { + method: 'post', + url: '/api/frontend/v1/tempus/Tags/doneTag', + params: data + }; + }, + + deleteTag(data) + { + return { + method: 'post', + url: '/api/frontend/v1/tempus/Tags/deleteTag', + params: data + }; + }, +}; 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/Calendar/Base.js b/public/js/components/Calendar/Base.js index 85c64ac18..360c9afa2 100644 --- a/public/js/components/Calendar/Base.js +++ b/public/js/components/Calendar/Base.js @@ -137,12 +137,14 @@ export default { "update:date", "update:mode", "update:range", + "update:date-range", "drop" ], data() { return { internalView: null, internalDate: null, + pickedRangeEnd: null, modalEvent: null }; }, @@ -272,6 +274,10 @@ export default { } }, + handleDateRange({ start, end }) { + this.pickedRangeEnd = end; + this.$emit('update:date-range', { start, end }); + }, showEventModal(eventObj) { this.modalEvent = eventObj; this.$refs.modal.show(); @@ -300,6 +306,7 @@ export default { class="card-header" v-model:date="cDate" v-model:mode="cMode" + @update:date-range="handleDateRange" @prev="clickPrev" @next="clickNext" @click:mode="$emit('click:mode', $event)" @@ -316,6 +323,7 @@ export default { :is="modes ? modes[cMode] : null || 'div'" ref="mode" v-model:current-date="cDate" + :range-end="pickedRangeEnd" @update:range="$emit('update:range', $event)" @request-modal-open="showEventModal" @request-modal-close="hideEventModal" diff --git a/public/js/components/Calendar/Base/Grid.js b/public/js/components/Calendar/Base/Grid.js index d63a8d7e6..df322c151 100644 --- a/public/js/components/Calendar/Base/Grid.js +++ b/public/js/components/Calendar/Base/Grid.js @@ -61,7 +61,11 @@ export default { flipAxis: Boolean, allDayEvents: Boolean, axisMainCollapsible: Boolean, - snapToGrid: Boolean + snapToGrid: Boolean, + dayVisibility: { + type: Array, + default: null + } }, data() { return { @@ -160,7 +164,17 @@ export default { hasValidEvents() { return this.events.find(e => e.length); }, - styleGridCols() { + styleGridCols() + { + if (this.dayVisibility) + { + let anyVisible = this.dayVisibility.some(day => day); + if (anyVisible) + { + return this.axisMain.map((day, i) => this.isDayCollapsed(i) ? 'var(--fhc-calendar-axis-collapsible-manual, 0.1fr)' : '1fr').join(' '); + } + } + let cols = 'repeat(' + this.axisMain.length + ', 1fr)'; if (this.axisMainCollapsible) { if (this.hasValidEvents) @@ -479,6 +493,16 @@ export default { } }); }, + isDayCollapsed(index) + { + if (this.dayVisibility) + { + let anyVisible = this.dayVisibility.some(day => day); + if (anyVisible) + return !this.dayVisibility[index]; + } + return this.axisMainCollapsible && this.hasValidEvents && !this.events[index].length; + } }, setup() { @@ -504,7 +528,7 @@ export default { v-for="(date, index) in axisMain" :key="index" class="main-header" - :class="{'collapsed-header': axisMainCollapsible && hasValidEvents && !events[index].length}" + :class="{'collapsed-header': isDayCollapsed(index)}" :style="'grid-' + axisCol + ':' + (2+index)" > @@ -592,6 +616,7 @@ export default { :date="date" :events="eventsNormal[index]" :backgrounds="backgrounds[index]" + :class="{ 'fhc-calendar-base-grid-line-collapsed': isDayCollapsed(index) }" style="position:relative" @resize-start="handleResizeStart" :style="'grid-' + axisRow + ':1/-1;grid-' + axisCol + ':' + (1+index)" diff --git a/public/js/components/Calendar/Base/Grid/Line/EventCard.js b/public/js/components/Calendar/Base/Grid/Line/EventCard.js index 3851fea14..2a78ed348 100644 --- a/public/js/components/Calendar/Base/Grid/Line/EventCard.js +++ b/public/js/components/Calendar/Base/Grid/Line/EventCard.js @@ -9,6 +9,10 @@ export default { event: { type: Object, required: true }, parked: Boolean }, + emits: [ + 'select-kw', + 'unpark' + ], computed: { dragKalenderCollection() { return this.event @@ -16,21 +20,48 @@ export default { topicString() { return Array.isArray(this.event.orig.topic) ? this.event.orig.topic.join(', ') : this.event.orig.topic; }, + datum() { + return luxon.DateTime.fromISO(this.event.orig.datum).toFormat('dd.MM.yyyy') + }, + kw() { + return luxon.DateTime.fromISO(this.event.orig.datum).startOf('week', { useLocaleWeeks: true }).localWeekNumber + }, }, + methods: { + eventStyle(event) { + if (!event.farbe) + return undefined; + return '--event-bg:#' + event.farbe; + }, + jumpToKw() + { + this.$emit('select-kw', this.kw); + }, + unpark(evt) + { + this.$emit('unpark', this.event); + } + }, + template: `
+
{{ topicString || event.orig.titel || event.orig.lehrfach }}
- {{ event.orig.datum }} {{ event.orig.beginn }}–{{ event.orig.ende }} - · {{ event.orig.ort_kurzbz }} + KW: {{ kw }} {{ datum }} +
+
+ {{ event.orig.beginn }}–{{ event.orig.ende }}
` -}; +}; \ No newline at end of file diff --git a/public/js/components/Calendar/Base/Header.js b/public/js/components/Calendar/Base/Header.js index 39eea114b..7c3fcafcb 100644 --- a/public/js/components/Calendar/Base/Header.js +++ b/public/js/components/Calendar/Base/Header.js @@ -32,7 +32,8 @@ export default { "prev", "click:mode", "update:date", - "update:mode" + "update:mode", + "update:date-range" ], data() { return { @@ -109,6 +110,7 @@ export default { class="btn btn-outline-secondary border-0" @click="$emit('prev')" :disabled="open" + v-if="mode !== 'tableList'" > @@ -116,6 +118,7 @@ export default { :mode="mode" :date="date" @update:date="$emit('update:date', $event)" + @update:date-range="$emit('update:date-range', $event)" @open="open = true" @closed="open = false" :list-length="modeOptions.length" @@ -124,6 +127,7 @@ export default { class="btn btn-outline-secondary border-0" @click="$emit('next')" :disabled="open" + v-if="mode !== 'tableList'" > @@ -131,4 +135,4 @@ export default { ` -} +} \ No newline at end of file diff --git a/public/js/components/Calendar/Base/Header/Datepicker.js b/public/js/components/Calendar/Base/Header/Datepicker.js index 98a26945b..3e2ec8327 100644 --- a/public/js/components/Calendar/Base/Header/Datepicker.js +++ b/public/js/components/Calendar/Base/Header/Datepicker.js @@ -24,8 +24,14 @@ export default { } }, emits: [ - "update:date" + "update:date", + "update:date-range" ], + data() { + return { + rangeEnd: null + }; + }, computed: { convertedDate() { // convert to target TZ then strip TZ Information @@ -34,34 +40,37 @@ export default { }, current() { switch (this.mode) { - case "month": - return {month: this.convertedDate.month-1, year: this.convertedDate.year}; - case "list": - return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.listLength }).ts - 1]; - case "week": - case "tableList": - return [this.convertedDate.startOf('week', { useLocaleWeeks: true }).ts, this.convertedDate.endOf('week', { useLocaleWeeks: true }).ts]; - case "day": - return this.convertedDate; - default: - return null; + case "month": + return {month: this.convertedDate.month-1, year: this.convertedDate.year}; + case "list": + return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.listLength }).ts - 1]; + case "week": + return [this.convertedDate.startOf('week', { useLocaleWeeks: true }).ts, this.convertedDate.endOf('week', { useLocaleWeeks: true }).ts]; + case "tableList": + return [this.convertedDate.startOf('day').ts, (this.rangeEnd || this.convertedDate).endOf('day').ts]; + case "day": + return this.convertedDate; + default: + return null; } }, title() { switch (this.mode) { - case "month": - return this.date.toLocaleString({ month: 'long', year: 'numeric' }); - case "week": - case "tableList": - var year = this.date.localWeekYear; - var week = this.date.toFormat('nn'); - return this.$p.t('calendar/year_kw', { year, week }); - case "list": - return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.listLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL); - case "day": - return this.date.toLocaleString(luxon.DateTime.DATE_FULL); - default: - return 'View not Supported'; + case "month": + return this.date.toLocaleString({ month: 'long', year: 'numeric' }); + case "week": + var year = this.date.localWeekYear; + var week = this.date.toFormat('nn'); + return this.$p.t('calendar/year_kw', { year, week }); + case "tableList": + const end = this.rangeEnd ? this.rangeEnd.setZone(this.timezone, { keepLocalTime: true }) : this.date; + return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + end.toLocaleString(luxon.DateTime.DATE_FULL); + case "list": + return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.listLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL); + case "day": + return this.date.toLocaleString(luxon.DateTime.DATE_FULL); + default: + return 'View not Supported'; } }, weekStart() { @@ -72,20 +81,28 @@ export default { update(value) { let date; switch (this.mode) { - case "month": - value.month++; - date = luxon.DateTime.fromObject(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); - break; - case "list": - case "week": - case "tableList": - date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); - break; - case "day": - date = luxon.DateTime.fromJSDate(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); - break; - default: - return; // Don't update if the value is invalid! + case "month": + value.month++; + date = luxon.DateTime.fromObject(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); + break; + case "list": + case "week": + date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); + break; + case "tableList": + if (!Array.isArray(value) || !value[0] || !value[1]) + return; + this.rangeEnd = luxon.DateTime.fromJSDate(value[1]); + const start = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); + const end = this.rangeEnd.setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); + this.$emit('update:date', start); + this.$emit('update:date-range', { start, end }); + return; + case "day": + date = luxon.DateTime.fromJSDate(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale); + break; + default: + return; // Don't update if the value is invalid! } this.$emit("update:date", date); }, @@ -99,8 +116,8 @@ export default { @update:model-value="update" :format="() => title" :month-picker="mode == 'month'" - :week-picker="mode == 'week' || mode == 'tableList'" - :range="mode == 'list' ? { autoRange: listLength - 1 } : false" + :week-picker="mode == 'week'" + :range="mode == 'list' ? { autoRange: listLength - 1 } : (mode == 'tableList' ? { partialRange: false } : false)" :text-input="mode == 'day'" :week-start="weekStart" :week-numbers="{ type: weekNumbers }" diff --git a/public/js/components/Calendar/Mode/Table.js b/public/js/components/Calendar/Mode/Table.js index 3676114af..529927d5f 100644 --- a/public/js/components/Calendar/Mode/Table.js +++ b/public/js/components/Calendar/Mode/Table.js @@ -1,109 +1,57 @@ -import BaseSlider from '../Base/Slider.js'; import TableView from './Table/View.js'; export default { name: "ModeTable", components: { - BaseSlider, TableView }, props: { currentDate: { type: luxon.DateTime, required: true + }, + rangeEnd: { + type: luxon.DateTime, + default: null } }, emits: [ - "update:currentDate", "update:range", - "click", - "requestModalOpen" ], data() { return { focusDate: this.currentDate, - rangeOffset: 0 + focusEnd: this.rangeEnd || this.currentDate }; }, computed: { range() { - let first = this.focusDate.startOf('week', { useLocaleWeeks: true }); - let last = this.focusDate.endOf('week', { useLocaleWeeks: true }); - - if (this.rangeOffset != 0) { - if (this.rangeOffset < 0) { - first = first.plus({ weeks: this.rangeOffset }); - } else { - last = last.plus({ weeks: this.rangeOffset }); - } - } - - return luxon.Interval.fromDateTimes(first, last); + return luxon.Interval.fromDateTimes(this.focusDate.startOf('day'), this.focusEnd.endOf('day')); } }, watch: { - currentDate() { - if (this.currentDate.locale != this.focusDate.locale) { - this.focusDate = this.currentDate; - this.$emit('update:range', this.range); - } else { - this.rangeOffset = this.currentDate.startOf('week', { useLocaleWeeks: true }).diff(this.focusDate.startOf('week', { useLocaleWeeks: true }), 'weeks').weeks; - if (this.rangeOffset) { - this.$emit('update:range', this.range); - this.$refs.slider.slidePages(this.rangeOffset).then(this.updatePage); - } - } + currentDate(newDate) { + this.focusDate = newDate; + if (!this.rangeEnd) + this.focusEnd = newDate; + this.$emit('update:range', this.range); + }, + rangeEnd(newEnd) { + this.focusEnd = newEnd || this.focusDate; + this.$emit('update:range', this.range); } }, methods: { - prevPage() { - this.rangeOffset = this.$refs.slider.target - 1; - this.$emit('update:range', this.range); - this.$refs.slider.prevPage().then(this.updatePage); - }, - nextPage() { - this.rangeOffset = this.$refs.slider.target + 1; - this.$emit('update:range', this.range); - this.$refs.slider.nextPage().then(this.updatePage); - }, - updatePage(weeks) { - const newFocusDate = this.focusDate.plus({ weeks }); - this.focusDate = newFocusDate; - this.rangeOffset = 0; - this.$emit('update:currentDate', this.focusDate); - this.$emit('update:range', this.range); - }, - viewAttrs(weeks) { - const day = this.focusDate.plus({ weeks }); - return { ...this.$attrs, day }; - }, - handleClickDefaults(evt) { - switch (evt.detail.source) { - case 'day': - // default: Set current-date - this.$emit('update:currentDate', evt.detail.value); - break; - case 'event': - // default: Request Modal - this.$emit('requestModalOpen', { event: evt.detail.value }); - break; - } - } }, mounted() { this.$emit('update:range', this.range); }, template: ` -
- - - - - +
+ + +
` -} +} \ No newline at end of file diff --git a/public/js/components/Calendar/Mode/Table/View.js b/public/js/components/Calendar/Mode/Table/View.js index cee1bfe7d..6f3553f7b 100644 --- a/public/js/components/Calendar/Mode/Table/View.js +++ b/public/js/components/Calendar/Mode/Table/View.js @@ -1,8 +1,6 @@ import {CoreFilterCmpt} from "../../../../components/filter/Filter.js"; import BsModal from '../../../Bootstrap/Modal.js'; import FormInput from "../../../Form/Input.js"; -import ApiDetails from "../../../../api/lehrveranstaltung/details.js"; - export default { name: "TableView", @@ -20,6 +18,10 @@ export default { day: { type: luxon.DateTime, required: true + }, + end: { + type: luxon.DateTime, + required: true } }, data() @@ -30,10 +32,13 @@ export default { }, computed: { start() { - return this.day.startOf('week', { useLocaleWeeks: true }); + return this.day.startOf('day'); + }, + tableEnd() { + return this.end.endOf('day'); }, preparedEvents() { - const end = this.start.plus({ days: 7 }); + const end = this.tableEnd; return this.events .filter(e => e.start < end && e.end > this.start) .sort((a, b) => a.start.ts - b.start.ts) @@ -112,15 +117,6 @@ export default { this.$refs.tableViewTable?.tabulator?.setData(newData); } }, -/* mounted() { - - this.$api.call(ApiDetails.getRaumtyp()) - .then(result => { - this.raumtyp_array = result.data; - }) - .catch(this.$fhcAlert.handleSystemError); - - },*/ template: /* html */`
luxon.Duration.fromObject({ hours: i + start })); } + }, + dayVisibility() { + return this.axisMain.map(date => this.visibleDays[date.weekday -1]) } }, methods: { isToday(date) { return date.hasSame(luxon.DateTime.now().setZone(this.timezone), 'day'); + }, + toggleDay(day) + { + this.visibleDays.splice(day, 1, !this.visibleDays[day]) + localStorage.setItem('fhc-calender-visible-days', JSON.stringify(this.visibleDays)) } }, template: /* html */` @@ -60,9 +76,10 @@ export default { :axis-main-collapsible="collapseEmptyDays" :snap-to-grid="!!timeGrid" all-day-events + :day-visibility="dayVisibility" > -