diff --git a/application/controllers/api/frontend/v1/tempus/Config.php b/application/controllers/api/frontend/v1/tempus/Config.php index 1f96d240a..e20716226 100644 --- a/application/controllers/api/frontend/v1/tempus/Config.php +++ b/application/controllers/api/frontend/v1/tempus/Config.php @@ -14,6 +14,7 @@ class Config extends FHCAPI_Controller 'get' => ['admin:r', 'assistenz:r'], 'getHeader' => ['admin:r', 'assistenz:r'], 'set' => ['admin:r', 'assistenz:r'], + 'updateCollision' => ['admin:r', 'assistenz:r'], ]); // Load Phrases @@ -23,12 +24,12 @@ class Config extends FHCAPI_Controller $this->_ci = &get_instance(); $this->_ci->load->model('ressource/Kalenderstatus_model', 'KalenderStatusModel'); + $this->_ci->load->model('system/Variable_model', 'VariableModel'); + $this->_ci->load->library('VariableLib', array('uid' => getAuthUID())); } public function get() { - $this->_ci->load->model('system/Variable_model', 'VariableModel'); - $config = []; $result = $this->_ci->VariableModel->getVariables(getAuthUID(), ['ignore_kollision', 'kollision_student', 'ignore_reservierung', 'ignore_zeitsperre']); @@ -87,8 +88,6 @@ class Config extends FHCAPI_Controller public function set() { - $this->_ci->load->model('system/Variable_model', 'VariableModel'); - $this->_ci->VariableModel->setVariable( getAuthUID(), 'ignore_kollision', @@ -112,5 +111,14 @@ class Config extends FHCAPI_Controller $this->terminateWithSuccess(); } + public function updateCollision() + { + $original_ignore = $this->_ci->variablelib->getVar('ignore_kollision'); + $this->_ci->VariableModel->setVariable( + getAuthUID(), + 'ignore_kollision', + $original_ignore === 'true' ? 'false' : 'true' + ); + } } diff --git a/application/controllers/api/frontend/v1/tempus/Coursepicker.php b/application/controllers/api/frontend/v1/tempus/Coursepicker.php index 2d0eecd94..686b39023 100644 --- a/application/controllers/api/frontend/v1/tempus/Coursepicker.php +++ b/application/controllers/api/frontend/v1/tempus/Coursepicker.php @@ -211,7 +211,7 @@ class Coursepicker extends FHCAPI_Controller 'studiensemester_kurzbz' => $row->studiensemester_kurzbz, 'fachbereich_kurzbz' => isset($row->fachbereich_kurzbz) ? $row->fachbereich_kurzbz : null, 'lektoren' => array(), - 'lehreinheit_id' => array(), + 'lehreinheit_id' => $lehreinheit_id, 'lvnr' => array(), 'lehrfach' => array(), 'lehrform' => array(), @@ -235,7 +235,6 @@ class Coursepicker extends FHCAPI_Controller 'name' => $row->vorname . ' ' . $row->nachname, ); - $group->lehreinheit_id[] = $row->lehreinheit_id; $group->lvnr[] = $row->lvnr; $group->lehrfach[] = $row->lehrfach; $group->lehrform[] = $row->lehrform; diff --git a/application/libraries/KalenderLib.php b/application/libraries/KalenderLib.php index e3ce260ac..19c800e3f 100644 --- a/application/libraries/KalenderLib.php +++ b/application/libraries/KalenderLib.php @@ -344,43 +344,91 @@ class KalenderLib $this->_getBasePlan($start_date, $end_date); if (!is_null($ort)) - $this->_ci->KalenderModel->db->where_in('tbl_kalender_ort.ort_kurzbz', $ort); + { + $ort_array = (array) $ort; + $escaped_orte = array(); + + foreach ($ort_array as $ort) + { + $escaped_orte[] = $this->_ci->KalenderModel->db->escape($ort); + } + $in_list = '(' . implode(',', $escaped_orte) . ')'; + + $this->_ci->KalenderModel->db->where( + "(EXISTS ( + SELECT 1 + FROM lehre.tbl_kalender_ort filter_ort + WHERE filter_ort.kalender_id = tbl_kalender.kalender_id + AND filter_ort.ort_kurzbz IN $in_list + ))" + ); + } if (!is_null($uids)) { - $this->_ci->KalenderModel->db->group_start(); - $this->_ci->KalenderModel->db->where_in('tbl_lehreinheitmitarbeiter.mitarbeiter_uid', $uids); - $this->_ci->KalenderModel->db->or_where_in('orginasator.uid', $uids); - $this->_ci->KalenderModel->db->or_where_in('teilnehmer.uid', $uids); - $this->_ci->KalenderModel->db->group_end(); + $uid_array = (array) $uids; + $db = $this->_ci->KalenderModel->db; + + $escaped_uids = array(); + foreach ($uid_array as $uid) + $escaped_uids[] = $db->escape($uid); + $in_list = '(' . implode(',', $escaped_uids) . ')'; + + $this->_ci->KalenderModel->db->where( + "(EXISTS ( + SELECT 1 + FROM lehre.tbl_lehreinheitmitarbeiter filter_lem + WHERE filter_lem.lehreinheit_id = tbl_lehreinheit.lehreinheit_id + AND filter_lem.mitarbeiter_uid IN $in_list + ) + OR EXISTS ( + SELECT 1 + FROM lehre.tbl_kalender_event_teilnehmer filter_org + WHERE filter_org.kalender_id = tbl_kalender.kalender_id + AND filter_org.rolle_kurzbz = 'organisator' + AND filter_org.uid IN $in_list + ) + OR EXISTS ( + SELECT 1 + FROM lehre.tbl_kalender_event_teilnehmer filter_teil + WHERE filter_teil.kalender_id = tbl_kalender.kalender_id + AND filter_teil.rolle_kurzbz = 'teilnehmer' + AND filter_teil.uid IN $in_list + ))" + ); } if (!is_null($studiengaenge)) { + $db = $this->_ci->KalenderModel->db; + $or_conditions = array(); - $this->_ci->KalenderModel->db->group_start(); - $first = true; foreach ($studiengaenge as $studiengang) { - if ($first) - $this->_ci->KalenderModel->db->group_start(); - else - { - $this->_ci->KalenderModel->db->or_group_start(); - } - $first = false; - - $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.studiengang_kz', $studiengang['studiengang_kz']); + $conditions = array(); + $conditions[] = 'filter_lv.studiengang_kz = ' . $db->escape($studiengang['studiengang_kz']); if (isset($studiengang['semester'])) - $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.semester', $studiengang['semester']); + $conditions[] = 'filter_lv.semester = ' . $db->escape($studiengang['semester']); if (isset($studiengang['orgform_kurzbz'])) - $this->_ci->KalenderModel->db->where('tbl_lehrveranstaltung.orgform_kurzbz', $studiengang['orgform_kurzbz']); + $conditions[] = 'filter_lv.orgform_kurzbz = ' . $db->escape($studiengang['orgform_kurzbz']); - $this->_ci->KalenderModel->db->group_end(); + $or_conditions[] = '(' . implode(' AND ', $conditions) . ')'; } - $this->_ci->KalenderModel->db->group_end(); + + $or_block = implode(' OR ', $or_conditions); + + $this->_ci->KalenderModel->db->where( + "(EXISTS ( + SELECT 1 + FROM lehre.tbl_kalender_lehreinheit filter_kl + JOIN lehre.tbl_lehreinheit filter_le ON filter_le.lehreinheit_id = filter_kl.lehreinheit_id + JOIN lehre.tbl_lehrveranstaltung filter_lv ON filter_lv.lehrveranstaltung_id = filter_le.lehrveranstaltung_id + WHERE filter_kl.kalender_id = tbl_kalender.kalender_id + AND ($or_block) + ))" + ); } $this->_ci->KalenderModel->db->where('NOT EXISTS ( diff --git a/public/js/api/factory/tempus/config.js b/public/js/api/factory/tempus/config.js index 235fddfe6..2eb5db74c 100644 --- a/public/js/api/factory/tempus/config.js +++ b/public/js/api/factory/tempus/config.js @@ -34,5 +34,11 @@ export default { url: 'api/frontend/v1/tempus/config/set', params }; + }, + updateCollision () { + return { + method: 'post', + url: 'api/frontend/v1/tempus/config/updateCollision' + }; } }; \ No newline at end of file diff --git a/public/js/components/AppConfig.js b/public/js/components/AppConfig.js index b6b6aaeac..c806326dc 100644 --- a/public/js/components/AppConfig.js +++ b/public/js/components/AppConfig.js @@ -79,26 +79,30 @@ export default { this.$fhcAlert.alertSuccess(this.$p.t('ui/settings_saved')); }) .catch(this.$fhcAlert.handleSystemErrors); + }, + reload() + { + this.$api + .call(this.endpoints.get()) + .then(res => { + Object.keys(res.data).forEach(key => { + const binding = { ...res.data[key] }; + delete binding.value; + delete binding.options; + const options = res.data[key].options; + this.tempValues[key] = res.data[key].value; + this.setup[key] = { + binding, + options + }; + }); + this.$emit('update:modelValue', { ...this.tempValues }); + }) + .catch(this.$fhcAlert.handleSystemErrors); } }, created() { - this.$api - .call(this.endpoints.get()) - .then(res => { - Object.keys(res.data).forEach(key => { - const binding = { ...res.data[key] }; - delete binding.value; - delete binding.options; - const options = res.data[key].options; - this.tempValues[key] = res.data[key].value; - this.setup[key] = { - binding, - options - }; - }); - this.$emit('update:modelValue', { ...this.tempValues }); - }) - .catch(this.$fhcAlert.handleSystemErrors); + this.reload(); }, template: /* html */` diff --git a/public/js/components/Calendar/Tempus.js b/public/js/components/Calendar/Tempus.js index 02aeba264..0b716add6 100644 --- a/public/js/components/Calendar/Tempus.js +++ b/public/js/components/Calendar/Tempus.js @@ -271,6 +271,12 @@ export default { > Reservierung +
+ + + {{ appConfig.ignore_kollision ? 'Kollisionscheck aus' : 'Kollisionscheck an' }} + +
` diff --git a/public/js/components/Tempus/Coursepicker.js b/public/js/components/Tempus/Coursepicker.js index a358f3522..6d55ef93c 100644 --- a/public/js/components/Tempus/Coursepicker.js +++ b/public/js/components/Tempus/Coursepicker.js @@ -25,7 +25,8 @@ export default { searchparam: '', allCourses: [], sortBy: null, - multiWeekIds: new Set() + multiWeekIds: new Set(), + favorites: new Set() } }, computed: { @@ -50,7 +51,7 @@ export default { { let dir = this.sortBy === 'lektor-asc' ? 1 : -1; - return result.sort((a, b) => { + result = result.sort((a, b) => { let an = a.lektoren?.[0]?.kurzbz ?? ''; let bn = b.lektoren?.[0]?.kurzbz ?? ''; return an.localeCompare(bn) * dir; @@ -58,22 +59,26 @@ export default { } else if (this.sortBy === 'kw-asc') { - return result.sort((a, b) => (a.start_kw ?? 0) - (b.start_kw ?? 0)); + result = result.sort((a, b) => (a.start_kw ?? 0) - (b.start_kw ?? 0)); } else if (this.sortBy === 'kw-desc') { - return result.sort((a, b) => (b.start_kw ?? 0) - (a.start_kw ?? 0)); + result = result.sort((a, b) => (b.start_kw ?? 0) - (a.start_kw ?? 0)); } else if (this.sortBy === 'stunden-asc') { - return result.sort((a, b) => (a.offenestunden ?? 0) - (b.offenestunden ?? 0)); + result = result.sort((a, b) => (a.offenestunden ?? 0) - (b.offenestunden ?? 0)); } else if (this.sortBy === 'stunden-desc') { - return result.sort((a, b) => (b.offenestunden ?? 0) - (a.offenestunden ?? 0)); + result = result.sort((a, b) => (b.offenestunden ?? 0) - (a.offenestunden ?? 0)); } - return result; + return [...result].sort((a, b) => { + let ap = this.isPinned(a) ? 1 : 0; + let bp = this.isPinned(b) ? 1 : 0; + return bp - ap; + }); } }, watch: { @@ -124,7 +129,7 @@ export default { showname: `${e.lehrfach} ${e.lehrform}`, orig: { type: 'lehreinheit', - lehreinheit_id: e.lehreinheit_id[0], + lehreinheit_id: e.lehreinheit_id, blockung: e.stundenblockung, entry: e, } @@ -175,7 +180,7 @@ export default { }, toggleMultiWeek(course) { - let id = course.lehreinheit_id[0]; + let id = course.lehreinheit_id; let weekIds = new Set(this.multiWeekIds); if (weekIds.has(id)) weekIds.delete(id); @@ -186,8 +191,27 @@ export default { }, isMultiWeek(course) { - return this.multiWeekIds.has(course.lehreinheit_id[0]); + return this.multiWeekIds.has(course.lehreinheit_id); }, + togglePin(course) + { + let id = course.lehreinheit_id; + let favs = new Set(this.favorites); + if (favs.has(id)) + favs.delete(id); + else + favs.add(id); + + this.favorites = favs; + localStorage.setItem('tempus_coursepicker_favs', JSON.stringify([...favs])); + }, + isPinned(course) + { + return this.favorites.has(course.lehreinheit_id); + }, + }, + mounted() { + this.favorites = new Set(JSON.parse(localStorage.getItem('tempus_coursepicker_favs') || '[]')); }, template: `
@@ -229,8 +253,8 @@ export default {
- Keine Lehreinheiten - Wähle einen Studiengang, um Lehreinheiten anzuzeigen. + {{$p.t('lehre', 'cptitleempty')}} + {{$p.t('lehre', 'cpempty')}}
-
+
{{ course.lehrfach }} {{ course.lehrform }} {{ course.raumtyp }} +
- {{ course.lehreinheit_id[0] }} + {{ course.lehreinheit_id }}
-
+
+ :key="verband" + class="text-truncate"> {{ verband }}
@@ -286,7 +318,7 @@ export default { {{ lektor.kurzbz }} - +{{ course.lektoren.length - 3 }} weitere... + +{{ course.lektoren.length - 3 }} {{$p.t('lehre', 'cpmore')}}
WR: {{ course.wochenrythmus }} Bl: {{ course.stundenblockung }} diff --git a/public/js/components/Tempus/Tempus.js b/public/js/components/Tempus/Tempus.js index d28ca39c2..66269a37a 100644 --- a/public/js/components/Tempus/Tempus.js +++ b/public/js/components/Tempus/Tempus.js @@ -364,6 +364,16 @@ export default { if (newDate && luxon.DateTime.isDateTime(newDate) && newDate.isValid) this.calendarDate = newDate.toISODate(); }, + updateCollision() + { + this.$api.call(ApiTempusConfig.updateCollision()) + .then(() => { + this.$refs.config.reload(); + this.$fhcAlert.alertSuccess(this.$p.t('ui/settings_saved')); + }) + .catch(this.$fhcAlert.handleSystemErrors); + + }, toggleStatus(selected) { if (!selected || selected.length === 0) { this.visibleStatus = ['all']; diff --git a/public/js/components/Tempus/shortcuts.js b/public/js/components/Tempus/shortcuts.js index 7b8116142..c984a3ce1 100644 --- a/public/js/components/Tempus/shortcuts.js +++ b/public/js/components/Tempus/shortcuts.js @@ -6,6 +6,18 @@ export function getTempusShortcuts(self) { skipWhenTyping: false, handler: () => self.focusSearchbar() }, + { + key: 'f', + ctrl: true, + skipWhenTyping: false, + handler: () => self.focusSearchbar() + }, + { + key: 'F9', + ctrl: false, + skipWhenTyping: false, + handler: () => self.updateCollision('month') + }, { key: '1', ctrl: true, diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index fb55d4cbe..6ca19b31a 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -3743,6 +3743,66 @@ $phrases = array( ) ) ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'cpempty', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Wähle einen Studiengang aus, um den LV-Teil anzuzeigen.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Select a degree program to display the teaching unit.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'cptitleempty', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Kein LV-Teil', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No teaching unit', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'cpmore', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'weitere...', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'more...', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'lehre',