From 7c1df847d94d3956bc763b94b73cebfe108dc9a1 Mon Sep 17 00:00:00 2001 From: Moritz Strohm Date: Mon, 29 Jan 2024 15:16:24 +0000 Subject: StEP 01354, closes #1354 Closes #1354 Merge request studip/studip!2116 --- app/controllers/calendar/calendar.php | 1350 +++++++++++------- app/controllers/calendar/contentbox.php | 134 +- app/controllers/calendar/date.php | 828 +++++++++++ app/controllers/calendar/group.php | 336 ----- app/controllers/calendar/instschedule.php | 190 --- app/controllers/calendar/schedule.php | 8 +- app/controllers/calendar/single.php | 571 -------- app/controllers/contact.php | 31 +- app/controllers/ical.php | 19 +- app/controllers/institute/overview.php | 4 - app/controllers/institute/schedule.php | 179 +++ app/controllers/settings/calendar.php | 9 +- app/views/calendar/calendar/add_courses.php | 15 + app/views/calendar/calendar/course.php | 1 + app/views/calendar/calendar/export.php | 45 + app/views/calendar/calendar/import.php | 30 + app/views/calendar/calendar/index.php | 1 + app/views/calendar/calendar/publish.php | 55 + app/views/calendar/calendar/share.php | 13 + app/views/calendar/contentbox/_termin.php | 107 +- app/views/calendar/contentbox/display.php | 69 +- app/views/calendar/date/_add_edit_form.php | 154 ++ app/views/calendar/date/add.php | 4 + app/views/calendar/date/delete.php | 51 + app/views/calendar/date/edit.php | 4 + app/views/calendar/date/index.php | 135 ++ app/views/calendar/date/move.php | 22 + app/views/calendar/group/_attendees.php | 82 -- app/views/calendar/group/_tooltip.php | 27 - app/views/calendar/group/_tooltip_year.php | 21 - app/views/calendar/group/day.php | 128 -- app/views/calendar/group/month.php | 110 -- app/views/calendar/group/week.php | 144 -- app/views/calendar/group/year.php | 122 -- app/views/calendar/instschedule/_entry_details.php | 29 - app/views/calendar/instschedule/index.php | 15 - app/views/calendar/schedule/stylesheet.php | 13 + app/views/calendar/single/_attendees.php | 31 - app/views/calendar/single/_calhead.php | 16 - app/views/calendar/single/_calhead_label_day.php | 3 - app/views/calendar/single/_calhead_label_week.php | 3 - app/views/calendar/single/_day.php | 90 -- app/views/calendar/single/_day_cell.php | 54 - app/views/calendar/single/_day_dayevents.php | 33 - app/views/calendar/single/_day_table.php | 28 - app/views/calendar/single/_edit_status.php | 48 - app/views/calendar/single/_event_data.php | 100 -- app/views/calendar/single/_include_month.php | 135 -- app/views/calendar/single/_jump_to.php | 13 - app/views/calendar/single/_select_calendar.php | 80 -- app/views/calendar/single/_select_category.php | 16 - app/views/calendar/single/_semester_filter.php | 30 - app/views/calendar/single/_tooltip.php | 113 -- app/views/calendar/single/day.php | 13 - app/views/calendar/single/edit.php | 454 ------ app/views/calendar/single/edit_status.php | 3 - app/views/calendar/single/event.php | 1 - app/views/calendar/single/export_calendar.php | 54 - app/views/calendar/single/import.php | 27 - app/views/calendar/single/manage_access.php | 99 -- app/views/calendar/single/month.php | 109 -- app/views/calendar/single/seminar_events.php | 104 -- app/views/calendar/single/share.php | 60 - app/views/calendar/single/week.php | 199 --- app/views/calendar/single/year.php | 145 -- app/views/calendar/stylesheet.php | 13 - app/views/course/cancel_dates/index.php | 2 +- app/views/course/dates/details-edit.php | 20 +- app/views/course/dates/details.php | 16 + app/views/course/details/index.php | 7 +- app/views/course/timesrooms/_cancel_form.php | 2 +- app/views/institute/overview/index.php | 1 - app/views/institute/schedule/index.php | 1 + app/views/settings/calendar.php | 78 +- app/views/settings/general.php | 2 +- config/config.inc.php.dist | 123 +- .../1.160_step_00283_update_calendar_settings.php | 12 +- db/migrations/5.4.1.1_alter_calendar_tables.php | 247 ++++ db/studip_default_data.sql | 2 +- lib/bootstrap-autoload.php | 1 + lib/calendar/CalendarColumn.class.php | 2 + lib/calendar/CalendarExport.class.php | 87 -- lib/calendar/CalendarExportException.class.php | 18 - lib/calendar/CalendarExportFile.class.php | 122 -- lib/calendar/CalendarImport.class.php | 91 -- lib/calendar/CalendarImportFile.class.php | 141 -- lib/calendar/CalendarParser.class.php | 132 -- lib/calendar/CalendarParserICalendar.class.php | 632 --------- lib/calendar/CalendarView.class.php | 4 +- lib/calendar/CalendarWeekView.class.php | 2 + lib/calendar/CalendarWidgetView.php | 2 + lib/calendar/CalendarWriter.class.php | 53 - lib/calendar/CalendarWriterICalendar.class.php | 629 --------- lib/calendar/EventData.class.php | 92 -- lib/calendar/EventSource.interface.php | 64 - lib/calendar/lib/CalendarError.class.php | 56 - lib/calendar/lib/ErrorHandler.class.php | 119 -- lib/classes/Event.class.php | 223 --- lib/classes/Event.interface.php | 184 +++ lib/classes/IcalExport.php | 17 +- .../JsonApi/Routes/Events/CourseEventsIndex.php | 16 +- .../JsonApi/Routes/Events/UserEventsIcal.php | 14 +- .../JsonApi/Routes/Events/UserEventsIndex.php | 9 +- lib/classes/JsonApi/SchemaMap.php | 5 +- lib/classes/JsonApi/Schemas/CalendarEvent.php | 24 +- lib/classes/JsonApi/Schemas/Course.php | 25 + lib/classes/JsonApi/Schemas/CourseEvent.php | 14 +- lib/classes/JsonApi/Schemas/Semester.php | 1 + lib/classes/Privacy.php | 2 +- lib/classes/UserManagement.class.php | 10 - lib/classes/calendar/Calendar.php | 187 --- lib/classes/calendar/CalendarInstscheduleModel.php | 148 -- lib/classes/calendar/CalendarScheduleModel.php | 2 + lib/classes/calendar/EventData.class.php | 100 ++ lib/classes/calendar/EventSource.interface.php | 64 + lib/classes/calendar/Helper.php | 107 ++ lib/classes/calendar/ICalendarExport.class.php | 638 +++++++++ lib/classes/calendar/ICalendarImport.class.php | 678 +++++++++ lib/classes/calendar/Owner.interface.php | 40 + lib/classes/calendar/SingleCalendar.php | 1488 -------------------- lib/classes/forms/SelectedRangesInput.php | 58 + lib/classes/searchtypes/StandardSearch.class.php | 20 +- lib/classes/sidebar/DateSelectWidget.php | 51 + lib/exceptions/FeatureDisabledException.php | 11 + lib/models/CalendarEvent.class.php | 1394 ------------------ lib/models/CalendarUser.class.php | 114 -- lib/models/ConsultationBooking.php | 10 +- lib/models/ConsultationEvent.php | 6 +- lib/models/ConsultationSlot.php | 32 +- lib/models/Contact.class.php | 16 +- lib/models/ContactGroup.class.php | 44 + lib/models/ContactGroupItem.class.php | 61 + lib/models/Course.class.php | 38 +- lib/models/CourseCancelledEvent.class.php | 149 -- lib/models/CourseDate.class.php | 188 ++- lib/models/CourseEvent.class.php | 596 -------- lib/models/CourseExDate.class.php | 174 ++- lib/models/CourseMarkedEvent.class.php | 132 -- lib/models/EventData.class.php | 144 -- lib/models/User.class.php | 54 +- lib/models/calendar/CalendarCourseDate.class.php | 34 + lib/models/calendar/CalendarCourseExDate.class.php | 35 + lib/models/calendar/CalendarDate.class.php | 944 +++++++++++++ .../calendar/CalendarDateAssignment.class.php | 714 ++++++++++ .../calendar/CalendarDateException.class.php | 40 + lib/modules/CoreCalendar.class.php | 9 +- lib/modules/CoreOverview.class.php | 2 +- lib/modules/ScheduleWidget.php | 42 +- lib/modules/TerminWidget.php | 7 +- lib/navigation/CalendarNavigation.php | 27 +- lib/navigation/ProfileNavigation.php | 2 +- lib/navigation/StartNavigation.php | 4 +- lib/seminar_open.php | 2 +- locale/de/LC_MAILS/_date_information.php | 28 + locale/de/LC_MAILS/date_changed.php | 10 + locale/de/LC_MAILS/date_created.php | 10 + locale/de/LC_MAILS/date_deleted.php | 6 + locale/de/LC_MAILS/date_participation.php | 14 + locale/en/LC_MAILS/_date_information.php | 24 + locale/en/LC_MAILS/date_changed.php | 10 + locale/en/LC_MAILS/date_created.php | 10 + locale/en/LC_MAILS/date_deleted.php | 6 + locale/en/LC_MAILS/date_participation.php | 14 + .../javascripts/bootstrap/calendar_dialog.js | 11 - resources/assets/javascripts/bootstrap/forms.js | 18 + .../assets/javascripts/bootstrap/fullcalendar.js | 1 + .../assets/javascripts/bootstrap/resources.js | 64 +- resources/assets/javascripts/entry-base.js | 1 - resources/assets/javascripts/init.js | 6 +- .../assets/javascripts/lib/calendar_dialog.js | 64 - resources/assets/javascripts/lib/dates.js | 4 +- resources/assets/javascripts/lib/datetime.js | 61 + resources/assets/javascripts/lib/fullcalendar.js | 227 ++- resources/assets/stylesheets/highcontrast.scss | 292 ---- resources/assets/stylesheets/less/calendar.less | 587 -------- resources/assets/stylesheets/scss/calendar.scss | 135 ++ resources/assets/stylesheets/scss/forms.scss | 19 +- resources/assets/stylesheets/studip.less | 1 - resources/assets/stylesheets/studip.scss | 1 + resources/vue/base-components.js | 12 + resources/vue/components/Datepicker.vue | 76 + resources/vue/components/EditableList.vue | 6 +- resources/vue/components/StudipDateTime.vue | 22 +- .../form_inputs/CalendarPermissionsTable.vue | 86 ++ .../vue/components/form_inputs/DateListInput.vue | 98 ++ .../vue/components/form_inputs/DayOfWeekSelect.vue | 60 + .../form_inputs/MyCoursesColouredTable.vue | 166 +++ .../vue/components/form_inputs/RepetitionInput.vue | 364 +++++ templates/forms/date_list_input.php | 3 + templates/forms/selected_ranges_input.php | 7 + templates/sidebar/date-select-widget.php | 13 + tests/jsonapi/UserEventsIcalTest.php | 22 +- tests/jsonapi/UserEventsIndexTest.php | 20 +- tests/jsonapi/_bootstrap.php | 1 + 194 files changed, 8942 insertions(+), 12678 deletions(-) create mode 100644 app/controllers/calendar/date.php delete mode 100644 app/controllers/calendar/group.php delete mode 100644 app/controllers/calendar/instschedule.php delete mode 100644 app/controllers/calendar/single.php create mode 100644 app/controllers/institute/schedule.php create mode 100644 app/views/calendar/calendar/add_courses.php create mode 100644 app/views/calendar/calendar/course.php create mode 100644 app/views/calendar/calendar/export.php create mode 100644 app/views/calendar/calendar/import.php create mode 100644 app/views/calendar/calendar/index.php create mode 100644 app/views/calendar/calendar/publish.php create mode 100644 app/views/calendar/calendar/share.php create mode 100644 app/views/calendar/date/_add_edit_form.php create mode 100644 app/views/calendar/date/add.php create mode 100644 app/views/calendar/date/delete.php create mode 100644 app/views/calendar/date/edit.php create mode 100644 app/views/calendar/date/index.php create mode 100644 app/views/calendar/date/move.php delete mode 100644 app/views/calendar/group/_attendees.php delete mode 100644 app/views/calendar/group/_tooltip.php delete mode 100644 app/views/calendar/group/_tooltip_year.php delete mode 100644 app/views/calendar/group/day.php delete mode 100644 app/views/calendar/group/month.php delete mode 100644 app/views/calendar/group/week.php delete mode 100644 app/views/calendar/group/year.php delete mode 100644 app/views/calendar/instschedule/_entry_details.php delete mode 100644 app/views/calendar/instschedule/index.php create mode 100644 app/views/calendar/schedule/stylesheet.php delete mode 100644 app/views/calendar/single/_attendees.php delete mode 100644 app/views/calendar/single/_calhead.php delete mode 100644 app/views/calendar/single/_calhead_label_day.php delete mode 100644 app/views/calendar/single/_calhead_label_week.php delete mode 100644 app/views/calendar/single/_day.php delete mode 100644 app/views/calendar/single/_day_cell.php delete mode 100644 app/views/calendar/single/_day_dayevents.php delete mode 100644 app/views/calendar/single/_day_table.php delete mode 100644 app/views/calendar/single/_edit_status.php delete mode 100644 app/views/calendar/single/_event_data.php delete mode 100644 app/views/calendar/single/_include_month.php delete mode 100644 app/views/calendar/single/_jump_to.php delete mode 100644 app/views/calendar/single/_select_calendar.php delete mode 100644 app/views/calendar/single/_select_category.php delete mode 100644 app/views/calendar/single/_semester_filter.php delete mode 100644 app/views/calendar/single/_tooltip.php delete mode 100644 app/views/calendar/single/day.php delete mode 100644 app/views/calendar/single/edit.php delete mode 100644 app/views/calendar/single/edit_status.php delete mode 100644 app/views/calendar/single/event.php delete mode 100644 app/views/calendar/single/export_calendar.php delete mode 100644 app/views/calendar/single/import.php delete mode 100644 app/views/calendar/single/manage_access.php delete mode 100644 app/views/calendar/single/month.php delete mode 100644 app/views/calendar/single/seminar_events.php delete mode 100644 app/views/calendar/single/share.php delete mode 100644 app/views/calendar/single/week.php delete mode 100644 app/views/calendar/single/year.php delete mode 100644 app/views/calendar/stylesheet.php create mode 100644 app/views/institute/schedule/index.php create mode 100644 db/migrations/5.4.1.1_alter_calendar_tables.php delete mode 100644 lib/calendar/CalendarExport.class.php delete mode 100644 lib/calendar/CalendarExportException.class.php delete mode 100644 lib/calendar/CalendarExportFile.class.php delete mode 100644 lib/calendar/CalendarImport.class.php delete mode 100644 lib/calendar/CalendarImportFile.class.php delete mode 100644 lib/calendar/CalendarParser.class.php delete mode 100644 lib/calendar/CalendarParserICalendar.class.php delete mode 100644 lib/calendar/CalendarWriter.class.php delete mode 100644 lib/calendar/CalendarWriterICalendar.class.php delete mode 100644 lib/calendar/EventData.class.php delete mode 100644 lib/calendar/EventSource.interface.php delete mode 100644 lib/calendar/lib/CalendarError.class.php delete mode 100644 lib/calendar/lib/ErrorHandler.class.php delete mode 100644 lib/classes/Event.class.php create mode 100644 lib/classes/Event.interface.php delete mode 100644 lib/classes/calendar/Calendar.php delete mode 100644 lib/classes/calendar/CalendarInstscheduleModel.php create mode 100644 lib/classes/calendar/EventData.class.php create mode 100644 lib/classes/calendar/EventSource.interface.php create mode 100644 lib/classes/calendar/Helper.php create mode 100644 lib/classes/calendar/ICalendarExport.class.php create mode 100644 lib/classes/calendar/ICalendarImport.class.php create mode 100644 lib/classes/calendar/Owner.interface.php delete mode 100644 lib/classes/calendar/SingleCalendar.php create mode 100644 lib/classes/forms/SelectedRangesInput.php create mode 100644 lib/classes/sidebar/DateSelectWidget.php create mode 100644 lib/exceptions/FeatureDisabledException.php delete mode 100644 lib/models/CalendarEvent.class.php delete mode 100644 lib/models/CalendarUser.class.php create mode 100644 lib/models/ContactGroup.class.php create mode 100644 lib/models/ContactGroupItem.class.php delete mode 100644 lib/models/CourseCancelledEvent.class.php delete mode 100644 lib/models/CourseEvent.class.php delete mode 100644 lib/models/CourseMarkedEvent.class.php delete mode 100644 lib/models/EventData.class.php create mode 100644 lib/models/calendar/CalendarCourseDate.class.php create mode 100644 lib/models/calendar/CalendarCourseExDate.class.php create mode 100644 lib/models/calendar/CalendarDate.class.php create mode 100644 lib/models/calendar/CalendarDateAssignment.class.php create mode 100644 lib/models/calendar/CalendarDateException.class.php create mode 100644 locale/de/LC_MAILS/_date_information.php create mode 100644 locale/de/LC_MAILS/date_changed.php create mode 100644 locale/de/LC_MAILS/date_created.php create mode 100644 locale/de/LC_MAILS/date_deleted.php create mode 100644 locale/de/LC_MAILS/date_participation.php create mode 100644 locale/en/LC_MAILS/_date_information.php create mode 100644 locale/en/LC_MAILS/date_changed.php create mode 100644 locale/en/LC_MAILS/date_created.php create mode 100644 locale/en/LC_MAILS/date_deleted.php create mode 100644 locale/en/LC_MAILS/date_participation.php delete mode 100644 resources/assets/javascripts/bootstrap/calendar_dialog.js delete mode 100644 resources/assets/javascripts/lib/calendar_dialog.js create mode 100644 resources/assets/javascripts/lib/datetime.js delete mode 100644 resources/assets/stylesheets/less/calendar.less create mode 100644 resources/assets/stylesheets/scss/calendar.scss create mode 100644 resources/vue/components/Datepicker.vue create mode 100644 resources/vue/components/form_inputs/CalendarPermissionsTable.vue create mode 100644 resources/vue/components/form_inputs/DateListInput.vue create mode 100644 resources/vue/components/form_inputs/DayOfWeekSelect.vue create mode 100644 resources/vue/components/form_inputs/MyCoursesColouredTable.vue create mode 100644 resources/vue/components/form_inputs/RepetitionInput.vue create mode 100644 templates/forms/date_list_input.php create mode 100644 templates/forms/selected_ranges_input.php create mode 100644 templates/sidebar/date-select-widget.php diff --git a/app/controllers/calendar/calendar.php b/app/controllers/calendar/calendar.php index 2e17ff2..eecf900 100644 --- a/app/controllers/calendar/calendar.php +++ b/app/controllers/calendar/calendar.php @@ -1,636 +1,896 @@ - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - * @since - */ class Calendar_CalendarController extends AuthenticatedController { public function before_filter(&$action, &$args) { parent::before_filter($action, $args); - PageLayout::setHelpKeyword('Basis.Terminkalender'); - $this->settings = $GLOBALS['user']->cfg->CALENDAR_SETTINGS; - if ($this->settings['start'] < 0 || $this->settings['start'] > 23) { - $this->settings['start'] = 0; - } - if ($this->settings['end'] < 0 || $this->settings['end'] > 23) { - $this->settings['end'] = 23; - } - if (!in_array($this->settings['view'], ['day','week','month','year'])) { - $this->settings['view'] = 'week'; - } - if (!is_array($this->settings)) { - $this->settings = Calendar::getDefaultUserSettings(); - } - URLHelper::bindLinkParam('atime', $this->atime); - $this->atime = Request::int('atime', time()); - $this->category = Request::int('category'); - $this->last_view = Request::option('last_view', - $this->settings['view']); - $this->action = $action; - $this->restrictions = [ - 'STUDIP_CATEGORY' => $this->category ?: null, - // hide events with status 3 (CalendarEvent::PARTSTAT_DECLINED) - 'STUDIP_GROUP_STATUS' => !empty($this->settings['show_declined']) ? null : [0,1,2,5] - ]; - if ($this->category) { - URLHelper::bindLinkParam('category', $this->category); - } - - $this->range_id = ''; - if (Config::get()->COURSE_CALENDAR_ENABLE - && !Request::get('self') - && Course::findCurrent()) { - $current_seminar = new Seminar(Course::findCurrent()); - if ($current_seminar->getSlotModule('calendar') instanceOf CoreCalendar) { - $this->range_id = $current_seminar->id; - Navigation::activateItem('/course/calendar'); - } - } - if (!$this->range_id) { - $this->range_id = Request::option('range_id', $GLOBALS['user']->id); - Navigation::activateItem('/calendar/calendar'); - URLHelper::bindLinkParam('range_id', $this->range_id); + if (!Context::isCourse() && Navigation::hasItem('/calendar')) { + Navigation::activateItem('/calendar'); } - - URLHelper::bindLinkParam('last_view', $this->last_view); } - protected function createSidebar($active = null, $calendar = null) - { - $active = $active ?: $this->last_view; - $sidebar = Sidebar::Get(); - - $views = new ViewsWidget(); - $views->addLink(_('Tag'), $this->url_for($this->base . 'day')) - ->setActive($active == 'day'); - $views->addLink(_('Woche'), $this->url_for($this->base . 'week')) - ->setActive($active == 'week'); - $views->addLink(_('Monat'), $this->url_for($this->base . 'month')) - ->setActive($active == 'month'); - $views->addLink(_('Jahr'), $this->url_for($this->base . 'year')) - ->setActive($active == 'year'); - $sidebar->addWidget($views); - } - protected function createSidebarFilter() + protected function buildSidebar($schedule = false) { - $tmpl_factory = $this->get_template_factory(); - - $filters = new SidebarWidget(); - $filters->setTitle('Auswahl'); - - $tmpl = $tmpl_factory->open('calendar/single/_jump_to'); - $tmpl->atime = $this->atime; - $tmpl->action = $this->action; - $tmpl->action_url = $this->url_for('calendar/single/jump_to'); - $filters->addElement(new WidgetElement($tmpl->render())); - - $tmpl = $tmpl_factory->open('calendar/single/_select_category'); - $tmpl->action_url = $this->url_for(); - $tmpl->category = $this->category; - $filters->addElement(new WidgetElement($tmpl->render())); - Sidebar::get()->addWidget($filters); - - if (Config::get()->CALENDAR_GROUP_ENABLE - || Config::get()->COURSE_CALENDAR_ENABLE) { - $tmpl = $tmpl_factory->open('calendar/single/_select_calendar'); - $tmpl->range_id = $this->range_id; - $tmpl->action_url = $this->url_for('calendar/group/switch'); - $tmpl->view = $this->action; - $filters->addElement(new WidgetElement($tmpl->render())); - - $settings = new OptionsWidget(); - $settings->addCheckbox( - _('Abgelehnte Termine anzeigen'), - $this->settings['show_declined'] ?? false, - $this->url_for($this->base . 'show_declined', ['show_declined' => 1]), - $this->url_for($this->base . 'show_declined', ['show_declined' => 0]) + $sidebar = Sidebar::get(); + + $actions = new ActionsWidget(); + if ($schedule) { + $actions->addLink( + _('Neuer Eintrag'), + $this->url_for('calendar/calendar/add_schedule_entry'), + Icon::create('add'), + ['data-dialog' => 'size=default'] + ); + } else { + $actions->addLink( + _('Termin anlegen'), + $this->url_for('calendar/date/add'), + Icon::create('add'), + ['data-dialog' => 'size=auto'] ); - Sidebar::get()->addWidget($settings); + } + + if (!$GLOBALS['perm']->have_perm('admin')) { + $actions->addLink( + _('Veranstaltung auswählen'), + $this->url_for('calendar/calendar/add_courses'), + Icon::create('add'), + ['data-dialog' => 'size=medium'] + ); + } + if (!$schedule) { + $actions->addLink( + _('Termine exportieren'), + $this->url_for('calendar/calendar/export'), + Icon::create('export'), + ['data-dialog' => 'size=auto'] + ); + $actions->addLink( + _('Termine importieren'), + $this->url_for('calendar/calendar/import'), + Icon::create('import'), + ['data-dialog' => 'size=auto'] + ); + $actions->addLink( + _('Kalender veröffentlichen'), + $this->url_for('calendar/calendar/publish'), + Icon::create('export'), + ['data-dialog' => 'size=auto'] + ); + } + if (!$schedule && Config::get()->CALENDAR_GROUP_ENABLE) { + $actions->addLink( + _('Kalender teilen'), + $this->url_for('calendar/calendar/share'), + Icon::create('share'), + ['data-dialog' => 'size=default'] + ); + } + $actions->addLink( + _('Drucken'), + 'javascript:void(window.print());', + Icon::create('print') + ); + $actions->addLink( + _('Einstellungen'), + $this->url_for('settings/calendar'), + Icon::create('settings'), + ['data-dialog' => 'size=auto;reload-on-close'] + ); + $sidebar->addWidget($actions); + + if (!$schedule) { + $date = new DateSelectWidget(); + $date->setDate(\Studip\Calendar\Helper::getDefaultCalendarDate()); + $date->setCalendarControl(true); + $sidebar->addWidget($date); } } - public function index_action() + protected function getUserCalendarSlotSettings() : array { - // switch to the view the user has selected in his personal settings - $default_view = $this->settings['view'] ?: 'week'; - - // Remove cid - if (Request::option('self')) { - Context::close(); - - $this->redirect(URLHelper::getURL('dispatch.php/' . $this->base - . $default_view . '/' . $GLOBALS['user']->id, [], true)); - } else { - $this->redirect(URLHelper::getURL('dispatch.php/' . $this->base - . $default_view)); - } + return [ + 'day' => \Studip\Calendar\Helper::getCalendarSlotDuration('day'), + 'week' => \Studip\Calendar\Helper::getCalendarSlotDuration('week'), + 'day_group' => \Studip\Calendar\Helper::getCalendarSlotDuration('day_group'), + 'week_group' => \Studip\Calendar\Helper::getCalendarSlotDuration('week_group') + ]; } - public function edit_action($range_id = null, $event_id = null) + public function index_action() { - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = new SingleCalendar($this->range_id); - $this->event = $this->calendar->getEvent($event_id); - - if ($this->event->isNew()) { - // $this->event = $this->calendar->getNewEvent(); - if (Request::get('isdayevent')) { - $this->event->setStart(mktime(0, 0, 0, date('n', $this->atime), - date('j', $this->atime), date('Y', $this->atime))); - $this->event->setEnd(mktime(23, 59, 59, date('n', $this->atime), - date('j', $this->atime), date('Y', $this->atime))); + PageLayout::setTitle(_('Kalender')); + + if (Request::isPost()) { + //In case the checkbox of the options widget is clicked, the resulting + //POST request must be catched here and result in a redirect. + CSRFProtection::verifyUnsafeRequest(); + if (Request::bool('show_declined')) { + $this->redirect('calendar/calendar', ['show_declined' => '1']); } else { - $this->event->setStart($this->atime); - $this->event->setEnd($this->atime + 3600); + $this->redirect('calendar/calendar'); } - $this->event->setAuthorId($GLOBALS['user']->id); - $this->event->setEditorId($GLOBALS['user']->id); - $this->event->setAccessibility('PRIVATE'); - if (!Request::isXhr()) { - PageLayout::setTitle($this->getTitle($this->calendar, _('Neuer Termin'))); + return; + } + + if (!Context::isCourse() && Navigation::hasItem('/calendar/calendar')) { + Navigation::activateItem('/calendar/calendar'); + } + + $view = Request::get('view', 'single'); + $group_view = false; + $timeline_view = false; + if (Config::get()->CALENDAR_GROUP_ENABLE) { + $group_view = in_array($view, ['group', 'timeline']); + $timeline_view = $view === 'timeline'; + } + + $calendar_owner = null; + $selected_group = null; + $user_id = Request::option('user_id', User::findCurrent()->id); + $group_id = Request::option('group_id'); + + if (Config::get()->CALENDAR_GROUP_ENABLE) { + if ($group_id) { + $selected_group = ContactGroup::find($group_id); + if ($selected_group->owner_id !== User::findCurrent()->id) { + //Thou shalt not see the groups of others! + throw new AccessDeniedException(_('Sie dürfen diesen Kalender nicht sehen!')); + } + $view = $view === 'timeline' ? 'timeline' : 'group'; + } elseif ($user_id) { + $calendar_owner = User::getCalendarOwner($user_id); + $view = 'single'; } } else { - // open read only events and course events not as form - // show information in dialog instead - if (!$this->event->havePermission(Event::PERMISSION_WRITABLE) - || $this->event instanceof CourseEvent) { - if (!$this->event instanceof CourseEvent && $this->event->attendees->count() > 1) { - if ($this->event->group_status) { - $this->redirect($this->url_for('calendar/single/edit_status/' . implode('/', - [$this->range_id, $this->event->event_id]))); - } else { - $this->redirect($this->url_for('calendar/single/event/' . implode('/', - [$this->range_id, $this->event->event_id]))); + //Show the calendar of the current user. + $view = 'single'; + $calendar_owner = User::findCurrent(); + } + + //Check for permissions: + $read_permissions = false; + $write_permissions = false; + if ($calendar_owner) { + $read_permissions = $calendar_owner->isCalendarReadable(); + $write_permissions = $calendar_owner->isCalendarWritable(); + } elseif ($selected_group) { + //Count on how many group member calendars the current user has read or write permissions: + foreach ($selected_group->items as $item) { + if ($item->user) { + if ($item->user->isCalendarReadable()) { + $read_permissions = true; + } + if ($item->user->isCalendarWritable()) { + $write_permissions = true; } - } else { - $this->redirect($this->url_for('calendar/single/event/' . implode('/', - [$this->range_id, $this->event->event_id]))); } - return null; - } - if (!Request::isXhr()) { - PageLayout::setTitle($this->getTitle($this->calendar, _('Termin bearbeiten'))); + if ($read_permissions && $write_permissions) { + //We only need to determine one read and one write permission to set the relevant fullcalendar + //properties. The action to add/edit a date determines in which calendars the current user + //may write into. + break; + } } } + if (!$read_permissions) { + throw new AccessDeniedException(_('Sie dürfen diesen Kalender nicht sehen!')); + } - if (Config::get()->CALENDAR_GROUP_ENABLE - && $this->calendar->getRange() == Calendar::RANGE_USER) { - - if (Config::get()->CALENDAR_GRANT_ALL_INSERT) { - $search_obj = SQLSearch::get("SELECT DISTINCT auth_user_md5.user_id, " - . "{$GLOBALS['_fullname_sql']['full_rev_username']} as fullname, " - . "auth_user_md5.perms, auth_user_md5.username " - . "FROM auth_user_md5 " - . "LEFT JOIN user_info ON (auth_user_md5.user_id = user_info.user_id) " - . 'WHERE auth_user_md5.user_id <> ' . DBManager::get()->quote($GLOBALS['user']->id) - . ' AND (username LIKE :input OR Vorname LIKE :input ' - . "OR CONCAT(Vorname,' ',Nachname) LIKE :input " - . "OR CONCAT(Nachname,' ',Vorname) LIKE :input " - . "OR Nachname LIKE :input OR {$GLOBALS['_fullname_sql']['full_rev']} LIKE :input " - . ") ORDER BY fullname ASC", - _('Person suchen'), 'user_id'); - } else { - $search_obj = SQLSearch::get("SELECT DISTINCT auth_user_md5.user_id, " - . "{$GLOBALS['_fullname_sql']['full_rev_username']} as fullname, " - . "auth_user_md5.perms, auth_user_md5.username " - . "FROM calendar_user " - . "LEFT JOIN auth_user_md5 ON calendar_user.owner_id = auth_user_md5.user_id " - . "LEFT JOIN user_info ON (auth_user_md5.user_id = user_info.user_id) " - . 'WHERE calendar_user.user_id = ' - . DBManager::get()->quote($GLOBALS['user']->id) - . ' AND calendar_user.permission > ' . Event::PERMISSION_READABLE - . ' AND auth_user_md5.user_id <> ' . DBManager::get()->quote($GLOBALS['user']->id) - . ' AND (username LIKE :input OR Vorname LIKE :input ' - . "OR CONCAT(Vorname,' ',Nachname) LIKE :input " - . "OR CONCAT(Nachname,' ',Vorname) LIKE :input " - . "OR Nachname LIKE :input OR {$GLOBALS['_fullname_sql']['full_rev']} LIKE :input " - . ") ORDER BY fullname ASC", - _('Person suchen'), 'user_id'); + $this->buildSidebar(false); + + $sidebar = Sidebar::get(); + + if (Config::get()->CALENDAR_GROUP_ENABLE) { + if ($calendar_owner && $calendar_owner->id === User::findCurrent()->id) { + //The user is viewing their own calendar. + $options = new OptionsWidget(); + $options->addCheckbox( + _('Abgelehnte Termine anzeigen'), + Request::bool('show_declined'), + $this->url_for('calendar/calendar', ['show_declined' => '1']), + $this->url_for('calendar/calendar') + ); + $sidebar->addWidget($options); } - // SEMBBS - // Eintrag von Terminen bereits ab PERMISSION_READABLE - /* - $search_obj = new SQLSearch('SELECT DISTINCT auth_user_md5.user_id, ' - . $GLOBALS['_fullname_sql']['full_rev'] . ' as fullname, username, perms ' - . 'FROM calendar_user ' - . 'LEFT JOIN auth_user_md5 ON calendar_user.owner_id = auth_user_md5.user_id ' - . 'LEFT JOIN user_info ON (auth_user_md5.user_id = user_info.user_id) ' - . 'WHERE calendar_user.user_id = ' - . DBManager::get()->quote($GLOBALS['user']->id) - . ' AND calendar_user.permission >= ' . Event::PERMISSION_READABLE - . ' AND (username LIKE :input OR Vorname LIKE :input ' - . "OR CONCAT(Vorname,' ',Nachname) LIKE :input " - . "OR CONCAT(Nachname,' ',Vorname) LIKE :input " - . 'OR Nachname LIKE :input OR ' - . $GLOBALS['_fullname_sql']['full_rev'] . ' LIKE :input ' - . ') ORDER BY fullname ASC', - _('Nutzer suchen'), 'user_id'); - // SEMBBS - * - */ - - - $this->quick_search = QuickSearch::get('user_id', $search_obj) - ->fireJSFunctionOnSelect('STUDIP.Messages.add_adressee') - ->withButton(); - - // $default_selected_user = array($this->calendar->getRangeId()); - $this->mps = MultiPersonSearch::get('add_adressees') - ->setLinkText(_('Mehrere Teilnehmende hinzufügen')) - // ->setDefaultSelectedUser($default_selected_user) - ->setTitle(_('Mehrere Teilnehmende hinzufügen')) - ->setExecuteURL($this->url_for($this->base . 'edit')) - ->setJSFunctionOnSubmit('STUDIP.Messages.add_adressees') - ->setSearchObject($search_obj); - $owners = SimpleORMapCollection::createFromArray( - CalendarUser::findByUser_id($this->calendar->getRangeId())) - ->pluck('owner_id'); - foreach (Calendar::getGroups($GLOBALS['user']->id) as $group) { - $this->mps->addQuickfilter( - $group->name, - $group->members->filter( - function ($member) use ($owners) { - if (in_array($member->user_id, $owners)) { - return $member; - } - })->pluck('user_id') + //Check if the user has groups. If so, display a select widget to select a group. + $groups = ContactGroup::findBySQL( + 'owner_id = :owner_id ORDER BY name ASC', + [ + 'owner_id' => User::findCurrent()->id + ] + ); + if ($groups) { + $available_groups = []; + + //Check if the user has at least read permissions for the calendar of one user of one group: + foreach ($groups as $group) { + foreach ($group->items as $item) { + if ($item->user && $item->user->isCalendarReadable()) { + $available_groups[] = $group; + break 1; + } + } + } + if ($available_groups) { + $group_select = new SelectWidget( + _('Gruppe'), + $this->url_for('calendar/calendar/index', ['view' => 'group']), + 'group_id' + ); + $options = [ + '' => _('(bitte wählen)') + ]; + foreach ($available_groups as $available_group) { + $options[$available_group->id] = $available_group->name; + } + $group_select->setOptions($options); + $group_select->setSelection($group_id); + $sidebar->addWidget($group_select); + } + } + //Get all calendars where the user has access to: + $other_users = User::findBySql( + "INNER JOIN `contact` c + ON `auth_user_md5`.`user_id` = c.`owner_id` + WHERE c.`user_id` = :current_user_id + AND c.`calendar_permissions` <> '' + ORDER BY `auth_user_md5`.`Vorname` ASC, `auth_user_md5`.`Nachname` ASC", + ['current_user_id' => User::findCurrent()->id] + ); + if ($other_users) { + $calendar_select = new SelectWidget( + _('Kalender auswählen'), + $this->url_for('calendar/calendar'), + 'user_id' ); + $select_options = [ + '' => _('(bitte wählen)'), + User::findCurrent()->id => _('Eigener Kalender') + ]; + foreach ($other_users as $user) { + $select_options[$user->id] = $user->getFullName(); + } + $calendar_select->setOptions($select_options, Request::get('user_id')); + $sidebar->addWidget($calendar_select); } } - $stored = false; - if (Request::submitted('store')) { - $stored = $this->storeEventData($this->event, $this->calendar); + if (Config::get()->CALENDAR_GROUP_ENABLE && $selected_group) { + $views = new ViewsWidget(); + $views->setTitle(_('Kalenderansicht')); + $views->addLink( + _('Gruppenkalender'), + $this->url_for('calendar/calendar', ['view' => 'group', 'group_id' => $group_id]) + )->setActive($view === 'group'); + $views->addLink( + _('Zeitleiste'), + $this->url_for('calendar/calendar', ['view' => 'timeline', 'group_id' => $group_id]) + )->setActive($view === 'timeline'); + $sidebar->addWidget($views); } - if ($stored !== false) { - if ($stored === 0) { - if (Request::isXhr()) { - header('X-Dialog-Close: 1'); - exit; - } else { - PageLayout::postMessage(MessageBox::success(_('Der Termin wurde nicht geändert.'))); - $this->relocate('calendar/single/' . $this->last_view, ['atime' => $this->atime]); + $calendar_resources = []; + $calendar_group_title = ''; + if ($group_view && $selected_group) { + //All users in the selected group that have granted read permissions to the user can be shown. + foreach ($selected_group->items as $item) { + if ($item->user && $item->user->isCalendarReadable()) { + $calendar_resources[] = [ + 'id' => $item->user_id, + 'title' => $item->user ? $item->user->getFullName() : '', + 'parent_name' => '' + ]; } - } else { - PageLayout::postMessage(MessageBox::success(_('Der Termin wurde gespeichert.'))); - $this->relocate('calendar/single/' . $this->last_view, ['atime' => $this->atime]); } + $calendar_group_title = $selected_group->name; } - $this->createSidebar('edit', $this->calendar); - $this->createSidebarFilter(); + $fullcalendar_studip_urls = []; + if ($write_permissions) { + if ($calendar_owner) { + $fullcalendar_studip_urls['add'] = $this->url_for('calendar/date/add', ['user_id' => $calendar_owner->id]); + } elseif ($selected_group) { + $fullcalendar_studip_urls['add'] = $this->url_for('calendar/date/add', ['group_id' => $group->id]); + } + } + + $calendar_settings = User::findCurrent()->getConfiguration()->CALENDAR_SETTINGS ?? []; + + //Map calendar settings to fullcalendar settings: + + $default_view = 'timeGridWeek'; + if ($timeline_view) { + $default_view = 'resourceTimelineWeek'; + if ($calendar_settings['view'] === 'day') { + $default_view = 'resourceTimelineDay'; + } + } elseif (!empty($calendar_settings['view'])) { + if ($calendar_settings['view'] === 'day') { + $default_view = 'timeGridDay'; + } elseif ($calendar_settings['view'] === 'month') { + $default_view = 'dayGridMonth'; + } + } + + $slot_durations = $this->getUserCalendarSlotSettings(); + + //Create the fullcalendar object: + $default_date = \Studip\Calendar\Helper::getDefaultCalendarDate(); + + $data_url_params = []; + if (Request::bool('show_declined')) { + $data_url_params['show_declined'] = '1'; + } + if ($timeline_view) { + $data_url_params['timeline_view'] = '1'; + } + + $this->fullcalendar = Studip\Fullcalendar::create( + _('Kalender'), + [ + 'editable' => $write_permissions, + 'selectable' => $write_permissions, + 'studip_urls' => $fullcalendar_studip_urls, + 'dialog_size' => 'auto', + 'minTime' => sprintf('%02u:00', $calendar_settings['start'] ?? 8), + 'maxTime' => sprintf('%02u:00', $calendar_settings['end'] ?? 20), + 'defaultDate' => $default_date->format('Y-m-d'), + 'allDaySlot' => !$group_view, + 'allDayText' => '', + 'header' => [ + 'left' => ( + $timeline_view + ? 'resourceTimelineWeek,resourceTimelineDay' + : 'dayGridYear,dayGridMonth,timeGridWeek,timeGridDay' + ), + 'right' => 'prev,today,next' + ], + 'weekNumbers' => true, + 'views' => [ + 'dayGridMonth' => [ + 'eventTimeFormat' => ['hour' => 'numeric', 'minute' => '2-digit'], + 'displayEventEnd' => true + ], + 'timeGridWeek' => [ + 'columnHeaderFormat' => ['weekday' => 'short', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true], + 'weekends' => $calendar_settings['type_week'] === 'LONG', + 'slotDuration' => $slot_durations['week'] + ], + 'timeGridDay' => [ + 'columnHeaderFormat' => ['weekday' => 'long', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true], + 'slotDuration' => $slot_durations['day'] + ], + 'resourceTimelineWeek' => [ + 'columnHeaderFormat' => ['weekday' => 'long', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true], + 'titleFormat' => ['year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit'], + 'weekends' => $calendar_settings['type_week'] === 'LONG', + 'slotDuration' => $slot_durations['week_group'] + ], + 'resourceTimelineDay' => [ + 'columnHeaderFormat' => ['weekday' => 'long', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true], + 'titleFormat' => ['year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit'], + 'slotDuration' => $slot_durations['day_group'] + ] + ], + 'defaultView' => $default_view, + 'timeGridEventMinHeight' => 20, + 'eventSources' => [ + [ + 'url' => $this->url_for( + ( + $group_view + ? 'calendar/calendar/calendar_group_data/' . $selected_group->id + : 'calendar/calendar/calendar_data/' . $calendar_owner->id + ), + $data_url_params + ), + 'method' => 'GET', + 'extraParams' => [] + ] + ], + 'resources' => $calendar_resources, + 'resourceLabelText' => $calendar_group_title + ] + ); } - public function edit_status_action($range_id, $event_id) + public function course_action($course_id) { - global $user; + PageLayout::setTitle(_('Veranstaltungskalender')); - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = new SingleCalendar($this->range_id); - $this->event = $this->calendar->getEvent($event_id); - $stored = false; - $old_status = $this->event->group_status; + if (!$course_id || !Config::get()->CALENDAR_GROUP_ENABLE || !Config::get()->COURSE_CALENDAR_ENABLE) { + throw new AccessDeniedException(_('Sie dürfen diesen Kalender nicht sehen!')); + } - if (Request::submitted('store')) { + $course = Course::find($course_id); + if (!$course) { + throw new AccessDeniedException(_('Sie dürfen diesen Kalender nicht sehen!')); + } - if ($this->event->isNew() - || !Config::get()->CALENDAR_GROUP_ENABLE - || !$this->calendar->havePermission(Calendar::PERMISSION_OWN) - || !$this->calendar->getRange() == Calendar::RANGE_USER - || !$this->event->havePermission(Event::PERMISSION_READABLE)) { - throw new AccessDeniedException(); + if (!$course->isVisibleForUser() || !$course->isCalendarReadable()) { + throw new AccessDeniedException(_('Sie dürfen diesen Kalender nicht sehen!')); + } + + if (Navigation::hasItem('/course/calendar')) { + Navigation::activateItem('/course/calendar'); + } + + $sidebar = Sidebar::get(); + + $actions = new ActionsWidget(); + $actions->addLink( + _('Termin anlegen'), + $this->url_for('calendar/date/add/course_' . $course->id), + Icon::create('add'), + ['data-dialog' => 'size=default'] + ); + $actions->addLink( + _('Drucken'), + 'javascript:void(window.print());', + Icon::create('print') + ); + $actions->addLink( + _('Einstellungen'), + $this->url_for('settings/calendar'), + Icon::create('settings'), + ['data-dialog' => 'reload-on-close'] + ); + $sidebar->addWidget($actions); + + $date = new DateSelectWidget(); + $date->setCalendarControl(true); + $sidebar->addWidget($date); + + //Create the fullcalendar object: + + $calendar_writable = $course->isCalendarWritable(); + $calendar_settings = User::findCurrent()->getConfiguration()->CALENDAR_SETTINGS ?? []; + $slot_settings = $this->getUserCalendarSlotSettings(); + + $fullcalendar_studip_urls = []; + if ($calendar_writable) { + $fullcalendar_studip_urls['add'] = $this->url_for('calendar/date/add/course_' . $course->id); + } + + $this->fullcalendar = Studip\Fullcalendar::create( + _('Veranstaltungskalender'), + [ + 'editable' => $calendar_writable, + 'selectable' => $calendar_writable, + 'studip_urls' => $fullcalendar_studip_urls, + 'minTime' => sprintf('%02u:00', $calendar_settings['start'] ?? 8), + 'maxTime' => sprintf('%02u:00', $calendar_settings['end'] ?? 20), + 'allDaySlot' => true, + 'allDayText' => '', + 'header' => [ + 'left' => 'dayGridYear,dayGridMonth,timeGridWeek,timeGridDay', + 'right' => 'prev,today,next' + ], + 'weekNumbers' => true, + 'views' => [ + 'dayGridMonth' => [ + 'eventTimeFormat' => ['hour' => 'numeric', 'minute' => '2-digit'], + 'displayEventEnd' => true + ], + 'timeGridWeek' => [ + 'columnHeaderFormat' => [ 'weekday' => 'short', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true ], + 'weekends' => $calendar_settings['type_week'] === 'LONG', + 'slotDuration' => $slot_settings['week'] + ], + 'timeGridDay' => [ + 'columnHeaderFormat' => [ 'weekday' => 'long', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true ], + 'slotDuration' => $slot_settings['day'] + ] + ], + 'defaultView' => 'timeGridWeek', + 'timeGridEventMinHeight' => 20, + 'eventSources' => [ + [ + 'url' => $this->url_for('calendar/calendar/calendar_data/course_' . $course->id), + 'method' => 'GET', + 'extraParams' => [] + ] + ] + ] + ); + } + + public function calendar_data_action($range_and_id) + { + $range_and_id = explode('_', $range_and_id); + $range = ''; + $range_id = ''; + if (!empty($range_and_id[1])) { + $range = $range_and_id[0]; + $range_id = $range_and_id[1]; + } + if (!$range) { + //Show the personal calendar of the current user: + $range = 'user'; + $range_id = User::findCurrent()->id; + } + $owner = null; + if (!$range_id) { + //Assume a user calendar. $range contains the user-ID. + $owner = User::getCalendarOwner($range); + } elseif ($range === 'user') { + $owner = User::getCalendarOwner($range_id); + } elseif ($range === 'course') { + $owner = Course::getCalendarOwner($range_id); + } + + if (!$owner || !$owner->isCalendarReadable()) { + throw new AccessDeniedException(_('Sie dürfen diesen Kalender nicht sehen!')); + } + + $begin = Request::getDateTime('start', \DateTime::RFC3339); + $end = Request::getDateTime('end', \DateTime::RFC3339); + if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) { + //No time range specified. + throw new InvalidArgumentException('Invalid parameters!'); + } + + $calendar_events = CalendarDateAssignment::getEvents( + $begin, + $end, + $owner->id, + ['PUBLIC', 'PRIVATE', 'CONFIDENTIAL'], + Request::bool('show_declined', false) + ); + + $result = []; + + foreach ($calendar_events as $date) { + $event = $date->toEventData(User::findCurrent()->id); + $result[] = $event->toFullcalendarEvent(); + } + + if ($range === 'user') { + //Include course dates of courses that shall be displayed in the calendar: + $course_dates = CalendarCourseDate::getEvents($begin, $end, $owner->id); + foreach ($course_dates as $course_date) { + $event = $course_date->toEventData(User::findCurrent()->id); + $event->background_colour = '#ffffff'; + $event->text_colour = '#000000'; + $event->border_colour = '#000000'; + $event->event_classes = []; + $result[] = $event->toFullcalendarEvent(); + } + //Include relevant cancelled course dates: + $cancelled_course_dates = CalendarCourseExDate::getEvents($begin, $end, $owner->id); + foreach ($cancelled_course_dates as $cancelled_course_date) { + $event = $cancelled_course_date->toEventData(User::findCurrent()->id); + $event->background_colour = '#ffffff'; + $event->text_colour = '#000000'; + $event->border_colour = '#000000'; + $event->event_classes = []; + $result[] = $event->toFullcalendarEvent(); } + } + //At this point, everything went fine. We can save the beginning as default date + //if the current user is looking at their own calendar: + if ($owner instanceof User && $owner->id === User::findCurrent()->id) { + $_SESSION['calendar_date'] = $begin->format('Y-m-d'); + } + $this->render_json($result); + } - $status = Request::int('status', 1); - if ($status > 0 && $status < 6) { - $this->event->group_status = $status; - $stored = $this->event->store(); + public function calendar_group_data_action($group_id) + { + $begin = Request::getDateTime('start', \DateTime::RFC3339); + $end = Request::getDateTime('end', \DateTime::RFC3339); + $timeline_view = Request::bool('timeline_view', false); + + if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) { + //No time range specified. + throw new InvalidArgumentException('Invalid parameters!'); + } + + $group = null; + $users = []; + if ($group_id) { + //Get the group first: + $group = ContactGroup::find($group_id); + if ($group->owner_id !== User::findCurrent()->id) { + throw new AccessDeniedException(); + } + foreach ($group->items as $item) { + if ($item->user->isCalendarReadable()) { + $users[] = $item->user; + } + } + if (!$users) { + //No user has granted read access to the calendar for the current user. + throw new AccessDeniedException(_('Sie dürfen diesen Kalender nicht sehen!')); } + } - if ($stored !== false) { - if ($stored === 0) { - if (Request::isXhr()) { - header('X-Dialog-Close: 1'); - exit; - } else { - PageLayout::postMessage(MessageBox::success(_('Der Teilnahmestatus wurde nicht geändert.'))); - $this->relocate('calendar/single/' . $this->last_view, ['atime' => $this->atime]); - } - } else { - // send message to organizer... - if ($this->event->author_id != $user->id) { - setTempLanguage($this->event->author_id); - $message = new messaging(); - $msg_text = sprintf(_('%s hat den Terminvorschlag für "%s" am %s von %s auf %s geändert.'), - get_fullname(), $this->event->getTitle(), - strftime('%c', $this->event->getStart()), - $this->event->toStringGroupStatus($old_status), $this->event->toStringGroupStatus()); - if ($status == CalendarEvent::PARTSTAT_DELEGATED) { - $msg_text .= "\n" - . sprintf(_('Der Termin wird akzeptiert, aber %s nimmt nicht selbst am Termin teil.'), - get_fullname()); - } - $subject = sprintf(_('Terminvorschlag am %s von %s %s'), - strftime('%c', $this->event->getStart()), get_fullname(), $this->event->toStringGroupStatus()); - $msg_text .= "\n\n**" . _('Beginn') . ':** '; - if ($this->event->isDayEvent()) { - $msg_text .= strftime('%x ', $this->event->getStart()); - $msg_text .= _('ganztägig'); - } else { - $msg_text .= strftime('%c', $this->event->getStart()); - } - $msg_text .= "\n**" . _('Ende') . ':** '; - if ($this->event->isDayEvent()) { - $msg_text .= strftime('%x ', $this->event->getEnd()); - } else { - $msg_text .= strftime('%c', $this->event->getEnd()); - } - $msg_text .= "\n**" . _('Zusammenfassung') . ':** ' . $this->event->getTitle() . "\n"; - if ($event_data = $this->event->getDescription()) { - $msg_text .= '**' . _('Beschreibung') . ":** $event_data\n"; - } - if ($event_data = $this->event->toStringCategories()) { - $msg_text .= '**' . _('Kategorie') . ":** $event_data\n"; - } - if ($event_data = $this->event->toStringPriority()) { - $msg_text .= '**' . _('Priorität') . ":** $event_data\n"; - } - if ($event_data = $this->event->toStringAccessibility()) { - $msg_text .= '**' . _('Zugriff') . ":** $event_data\n"; - } - if ($event_data = $this->event->toStringRecurrence()) { - $msg_text .= '**' . _('Wiederholung') . ":** $event_data\n"; - } - $member = []; - foreach ($this->event->attendees as $attendee) { - if ($attendee->range_id == $this->event->getAuthorId()) { - $member[] = $attendee->user->getFullName() - . ' ('. _('Organisator') . ')'; - } else { - $member[] = $attendee->user->getFullName() - . ' (' . $this->event->toStringGroupStatus($attendee->group_status) - . ')'; - } - } - $msg_text .= '**' . _('Teilnehmende') . ':** ' . implode(', ', $member); - $msg_text .= "\n\n" . _('Hier kommen Sie direkt zum Termin in Ihrem Kalender:') . "\n"; - $msg_text .= URLHelper::getURL('dispatch.php/calendar/single/edit/' - . $this->event->getAuthorId() . '/' . $this->event->event_id); - $message->insert_message( - addslashes($msg_text), - [get_username($this->event->getAuthorId())], - $this->event->range_id, - '', '', '', '', addslashes($subject)); - restoreLanguage(); + $result = []; + + foreach ($users as $user) { + $events = CalendarDateAssignment::getEvents($begin, $end, $user->id); + if ($events) { + foreach ($events as $event) { + $data = $event->toEventData(User::findCurrent()->id); + if (!$timeline_view) { + $data->title = $user->getFullName(); } - PageLayout::postMessage(MessageBox::success(_('Der Teilnahmestatus wurde gespeichert.'))); - $this->relocate('calendar/single/' . $this->last_view, ['atime' => $this->atime]); + $result[] = $data->toFullcalendarEvent(); } } } - - $this->createSidebar('edit', $this->calendar); - $this->createSidebarFilter(); + $this->render_json($result); } - public function switch_action() + public function add_courses_action() { - $default_view = $this->settings['view'] ?: 'week'; - $view = Request::option('last_view', $default_view); - $this->range_id = Request::option('range_id', $GLOBALS['user']->id); - $object_type = get_object_type($this->range_id); - switch ($object_type) { - case 'user': - URLHelper::addLinkParam('cid', ''); - $this->redirect($this->url_for('calendar/single/' - . $view . '/' . $this->range_id)); - break; - case 'sem': - case 'inst': - case 'fak': - URLHelper::addLinkParam('cid', $this->range_id); - $this->redirect($this->url_for('calendar/single/' - . $view . '/' . $this->range_id)); - break; - case 'group': - URLHelper::addLinkParam('cid', ''); - $this->redirect($this->url_for('calendar/group/' - . $view . '/' . $this->range_id)); - break; + $selected_semester_pseudo_id = Request::option('semester_id'); + $this->selected_semesters_id = ''; + $this->available_semester_data = []; + $semesters = Semester::getAll(); + foreach ($semesters as $semester) { + $this->available_semester_data[$semester['id']] = [ + 'id' => $semester['id'], + 'name' => $semester['name'] + ]; } - } + $this->available_semester_data = array_reverse($this->available_semester_data); - public function jump_to_action() - { - $date = Request::get('jmp_date'); - if ($date) { - $atime = strtotime($date . strftime(' %T', $this->atime)); + if (!$selected_semester_pseudo_id) { + $selected_semester_pseudo_id = User::findCurrent()->getConfiguration()->MY_COURSES_SELECTED_CYCLE; + if (!Config::get()->MY_COURSES_ENABLE_ALL_SEMESTERS && $selected_semester_pseudo_id === 'all') { + $selected_semester_pseudo_id = 'next'; + } + if (!$selected_semester_pseudo_id) { + $selected_semester_pseudo_id = Config::get()->MY_COURSES_DEFAULT_CYCLE; + } + } + if ($selected_semester_pseudo_id === 'next') { + $semester = Semester::findNext(); + $this->selected_semester_id = $semester->id; + } elseif (in_array($selected_semester_pseudo_id, ['all', 'current'])) { + $semester = Semester::findCurrent(); + $this->selected_semester_id = $semester->id; + } elseif ($selected_semester_pseudo_id === 'last') { + $semester = Semester::findPrevious(); + $this->selected_semester_id = $semester->id; } else { - $atime = 'now'; + $this->selected_semester_id = $selected_semester_pseudo_id ?? ''; + if (!Semester::exists($this->selected_semesters_id)) { + $this->selected_semester_id = ''; + } + } + + $this->selected_course_ids = SimpleCollection::createFromArray( + CourseMember::findBySQL( + 'user_id = :user_id AND bind_calendar = 1', + ['user_id' => User::findCurrent()->id] + ) + )->pluck('seminar_id'); + + $this->semester_data = []; + $all_semesters = Semester::getAll(); + foreach ($all_semesters as $semester) { + $data = [ + 'id' => $semester->id, + 'name' => $semester->name, + 'courses' => [] + ]; + $this->semester_data[] = $data; + } + + if (Request::submitted('add')) { + CSRFProtection::verifyUnsafeRequest(); + + $course_ids = Request::getArray('courses_course_ids'); + foreach ($course_ids as $course_id => $selected) { + $course_membership = CourseMember::findOneBySQL( + 'seminar_id = :course_id AND user_id = :user_id', + [ + 'course_id' => $course_id, + 'user_id' => User::findCurrent()->id + ] + ); + if ($course_membership) { + $course_membership->bind_calendar = $selected ? '1' : '0'; + $course_membership->store(); + } + } + PageLayout::postSuccess(_('Die Zuordnung von Veranstaltungen zum Kalender wurde aktualisiert.')); + $this->redirect('calendar/calendar'); } - $action = Request::option('action', 'week'); - $this->range_id = $this->range_id ?: $GLOBALS['user']->id; - $this->redirect($this->url_for($this->base . $action, - ['atime' => $atime, 'range_id' => $this->range_id])); } - public function show_declined_action () + public function export_action() { - $config = UserConfig::get($GLOBALS['user']->id); - $this->settings['show_declined'] = Request::int('show_declined') ? '1' : '0'; - // var_dump($this->settings); exit; - $config->store('CALENDAR_SETTINGS', $this->settings); - $action = Request::option('action', 'week'); - $this->range_id = $this->range_id ?: $GLOBALS['user']->id; - $this->redirect($this->url_for($this->base . $action, - ['range_id' => $this->range_id])); + PageLayout::setTitle(_('Termine exportieren')); + $this->begin = new DateTimeImmutable(); + $this->end = $this->begin->add(new DateInterval('P1Y')); + $this->dates_to_export = 'user'; + if (Request::submitted('export')) { + CSRFProtection::verifyUnsafeRequest(); + $this->begin = Request::getDateTime('begin', 'd.m.Y'); + $this->end = Request::getDateTime('end', 'd.m.Y'); + if ($this->begin >= $this->end) { + PageLayout::postError(_('Der Startzeitpunkt darf nicht nach dem Endzeitpunkt liegen!')); + return; + } + $this->dates_to_export = Request::get('dates_to_export'); + if (!in_array($this->dates_to_export, ['user', 'course', 'all'])) { + PageLayout::postError(_('Bitte wählen Sie aus, welche Termine exportiert werden sollen!')); + return; + } + $ical = ''; + $calendar_export = new ICalendarExport(); + if ($this->dates_to_export === 'user') { + $ical = $calendar_export->exportCalendarDates(User::findCurrent()->id, $this->begin, $this->end); + } elseif ($this->dates_to_export === 'course') { + $ical = $calendar_export->exportCourseDates(User::findCurrent()->id, $this->begin, $this->end); + $ical .= $calendar_export->exportCourseExDates(User::findCurrent()->id, $this->begin, $this->end); + } elseif ($this->dates_to_export === 'all') { + $ical = $calendar_export->exportCalendarDates(User::findCurrent()->id, $this->begin, $this->end); + $ical .= $calendar_export->exportCourseDates(User::findCurrent()->id, $this->begin, $this->end); + $ical .= $calendar_export->exportCourseExDates(User::findCurrent()->id, $this->begin, $this->end); + } + $ical = $calendar_export->writeHeader() . $ical . $calendar_export->writeFooter(); + $this->response->add_header('Content-Type', 'text/calendar;charset=utf-8'); + $this->response->add_header('Content-Disposition', 'attachment; filename="studip.ics"'); + $this->response->add_header('Content-Transfer-Encoding', 'binary'); + $this->response->add_header('Pragma', 'public'); + $this->response->add_header('Cache-Control', 'private'); + $this->response->add_header('Content-Length', strlen($ical)); + $this->render_text($ical); + } } - protected function storeEventData(CalendarEvent $event, SingleCalendar $calendar) + public function import_action() {} + + public function import_file_action() { - $messages = []; - if (Request::int('isdayevent')) { - $dt_string = Request::get('start_date') . ' 00:00:00'; - } else { - $dt_string = sprintf( - '%s %u:%02u', - Request::get('start_date'), - Request::int('start_hour'), - Request::int('start_minute') - ); - } - $event->setStart($this->parseDateTime($dt_string)); - if (Request::int('isdayevent')) { - $dt_string = Request::get('end_date') . ' 23:59:59'; - } else { - $dt_string = sprintf( - '%s %u:%02u', - Request::get('end_date'), - Request::int('end_hour'), - Request::int('end_minute') - ); + if (Request::submitted('import')) { + CSRFProtection::verifySecurityToken(); + $range_id = Context::getId() ?? User::findCurrent()->id; + $calendar_import = new ICalendarImport($range_id); + $calendar_import->convertPublicToPrivate(Request::bool('import_as_private_imp')); + $calendar_import->import(file_get_contents($_FILES['importfile']['tmp_name'])); + $import_count = $calendar_import->getCountEvents(); + PageLayout::postSuccess(sprintf( + ngettext( + 'Ein Termin wurde importiert.', + 'Es wurden %u Termine importiert.', + $import_count + ), + $import_count + )); + $this->redirect($this->url_for('calendar/calendar/')); } - $event->setEnd($this->parseDateTime($dt_string)); + } - if (!$this->validate_datetime(sprintf('%02u:%02u',Request::int('start_hour'),Request::int('start_minute'))) - || !$this->validate_datetime(sprintf('%02u:%02u',Request::int('end_hour'),Request::int('end_minute'))) - ) { - $messages[] = _('Die Start- und/oder Endzeit ist ungültig!'); + public function share_action() + { + PageLayout::setTitle(_('Kalender teilen')); + if (!Config::get()->CALENDAR_GROUP_ENABLE) { + throw new FeatureDisabledException(); } - if ($event->getStart() > $event->getEnd()) { - $messages[] = _('Die Startzeit muss vor der Endzeit liegen.'); + $calendar_contacts = Contact::findBySql( + "JOIN `auth_user_md5` USING (`user_id`) + WHERE `contact`.`owner_id` = :user_id + AND `contact`.`calendar_permissions` <> '' + ORDER BY `auth_user_md5`.`Vorname`, `auth_user_md5`.`Nachname`", + [ + 'user_id' => User::findCurrent()->id + ] + ); + $user_data = []; + foreach ($calendar_contacts as $contact) { + $user_data[$contact->user_id] = [ + 'id' => $contact->user_id, + 'name' => $contact->friend->getFullName(), + 'write_permissions' => $contact->calendar_permissions === 'WRITE' + ]; } + $this->selected_users_json = json_encode($user_data, JSON_FORCE_OBJECT); + $this->searchtype = new StandardSearch('user_id', ['simple_name' => true]); - $event->setTitle(Request::get('summary', '')); - $event->event->description = Request::get('description', ''); - $event->setUserDefinedCategories(Request::get('categories', '')); - $event->event->location = Request::get('location', ''); - $event->event->category_intern = Request::int('category_intern', 1); - $event->setAccessibility(Request::option('accessibility', 'PRIVATE')); - $event->setPriority(Request::int('priority', 0)); + if (Request::submitted('share')) { + CSRFProtection::verifyUnsafeRequest(); + $selected_user_ids = Request::getArray('calendar_permissions', []); + $write_permissions = Request::getArray('calendar_write_permissions', []); - if (!$event->getTitle()) { - $messages[] = _('Es muss eine Zusammenfassung angegeben werden.'); - } + //Add/update contacts with calendar permissions: - $rec_type = Request::option('recurrence', 'single'); - $expire = Request::option('exp_c', 'never'); - $rrule = [ - 'linterval' => null, - 'sinterval' => null, - 'wdays' => null, - 'month' => null, - 'day' => null, - 'rtype' => 'SINGLE', - 'count' => null, - 'expire' => null - ]; - if ($expire == 'count') { - $rrule['count'] = Request::int('exp_count', 10); - } else if ($expire == 'date') { - if (Request::isXhr()) { - $exp_date = Request::get('exp_date'); - } else { - $exp_date = Request::get('exp_date'); - } - $exp_date = $exp_date ?: strftime('%x', time()); - $rrule['expire'] = $this->parseDateTime($exp_date . ' 12:00'); - } - switch ($rec_type) { - case 'daily': - if (Request::option('type_daily', 'day') === 'day') { - $rrule['linterval'] = Request::int('linterval_d', 1); - $rrule['rtype'] = 'DAILY'; - } else { - $rrule['linterval'] = 1; - $rrule['wdays'] = '12345'; - $rrule['rtype'] = 'WEEKLY'; + foreach ($selected_user_ids as $user_id) { + $user = User::find($user_id); + if (!$user) { + //No user? No contact! + continue; } - break; - case 'weekly': - $rrule['rtype'] = 'WEEKLY'; - $rrule['linterval'] = Request::int('linterval_w', 1); - $rrule['wdays'] = implode('', Request::intArray('wdays', - [strftime('%u', $event->getStart())])); - break; - case 'monthly': - $rrule['rtype'] = 'MONTHLY'; - if (Request::option('type_m', 'day') === 'day') { - $rrule['linterval'] = Request::int('linterval_m1', 1); - $rrule['day'] = Request::int('day_m', - strftime('%e', $event->getStart())); - } else { - $rrule['linterval'] = Request::int('linterval_m2', 1); - $rrule['sinterval'] = Request::int('sinterval_m', 1); - $rrule['wdays'] = Request::int('wday_m', - strftime('%u', $event->getStart())); + $contact = Contact::findOneBySql( + 'owner_id = :owner_id AND user_id = :user_id', + [ + 'owner_id' => User::findCurrent()->id, + 'user_id' => $user_id + ] + ); + if (!$contact) { + $contact = new Contact(); + $contact->owner_id = User::findCurrent()->id; + $contact->user_id = $user->id; } - break; - case 'yearly': - $rrule['rtype'] = 'YEARLY'; - $rrule['linterval'] = 1; - if (Request::option('type_y', 'day') === 'day') { - $rrule['day'] = Request::int('day_y', - strftime('%e', $event->getStart())); - $rrule['month'] = Request::int('month_y1', - date('n', $event->getStart())); + if (in_array($user->id, $write_permissions)) { + $contact->calendar_permissions = 'WRITE'; } else { - $rrule['sinterval'] = Request::int('sinterval_y', 1); - $rrule['wdays'] = Request::int('wday_y', - strftime('%u', $event->getStart())); - $rrule['month'] = Request::int('month_y2', - date('n', $event->getStart())); + $contact->calendar_permissions = 'READ'; } - break; - } - if (sizeof($messages)) { - PageLayout::postMessage(MessageBox::error(_('Bitte Eingaben korrigieren'), $messages)); - return false; - } else { - $event->setRecurrence($rrule); - $exceptions = array_diff(Request::getArray('exc_dates'), - Request::getArray('del_exc_dates')); - $event->setExceptions($this->parseExceptions($exceptions)); - // if this is a group event, store event in the calendars of each attendee - if (Config::get()->CALENDAR_GROUP_ENABLE) { - $attendee_ids = Request::optionArray('attendees'); - return $calendar->storeEvent($event, $attendee_ids); - } else { - return $calendar->storeEvent($event); + $contact->store(); } - } - } - /** - * Parses a string with exception dates from input form and returns an array - * with all dates as unix timestamp identified by an internally used pattern. - * - * @param string $exc_dates - * @return array An array of unix timestamps. - */ - protected function parseExceptions($exc_dates) { - $matches = []; - $dates = []; - preg_match_all('%(\d{1,2})\h*([/.])\h*(\d{1,2})\h*([/.])\h*(\d{4})\s*%', - implode(' ', $exc_dates), $matches, PREG_SET_ORDER); - foreach ($matches as $match) { - if ($match[2] == '/') { - $dates[] = strtotime($match[1].'/'.$match[3].'/'.$match[5]); + //Revoke calendar permissions for all users that aren't in the list + //of selected users: + if ($selected_user_ids) { + $stmt = DBManager::get()->prepare( + "UPDATE `contact` SET `calendar_permissions` = '' + WHERE `owner_id` = :owner_id + AND `user_id` NOT IN ( :user_ids )" + ); + $stmt->execute([ + 'owner_id' => User::findCurrent()->id, + 'user_ids' => $selected_user_ids + ]); } else { - $dates[] = strtotime($match[1].$match[2].$match[3].$match[4].$match[5]); + $stmt = DBManager::get()->prepare( + "UPDATE `contact` SET `calendar_permissions` = '' + WHERE `owner_id` = :owner_id" + ); + $stmt->execute(['owner_id' => User::findCurrent()->id]); } + + PageLayout::postSuccess( + _('Die Kalenderfreigaben wurden geändert.') + ); + $this->response->add_header('X-Dialog-Close', '1'); } - return $dates; } - /** - * Parses a string as date time in the format "j.n.Y H:i:s" and returns the - * corresponding unix time stamp. - * - * @param string $dt_string The date time string. - * @return int A unix time stamp - */ - protected function parseDateTime($dt_string) + public function publish_action() { - $dt_array = date_parse_from_format('j.n.Y H:i:s', $dt_string); - return mktime($dt_array['hour'], $dt_array['minute'], $dt_array['second'], - $dt_array['month'], $dt_array['day'], $dt_array['year']); - } + $this->short_id = null; + if (Request::submitted('delete_id')) { + CSRFProtection::verifySecurityToken(); + IcalExport::deleteKey(User::findCurrent()->id); + PageLayout::postSuccess(_('Die Adresse, unter der Ihre Termine abrufbar sind, wurde gelöscht')); + } + + if (Request::submitted('new_id')) { + CSRFProtection::verifySecurityToken(); + $this->short_id = IcalExport::setKey(User::findCurrent()->id); + PageLayout::postSuccess(_('Eine Adresse, unter der Ihre Termine abrufbar sind, wurde erstellt.')); + } else { + $this->short_id = IcalExport::getKeyByUser(User::findCurrent()->id); + } + $text = ''; + if (Request::submitted('submit_email')) { + $email_reg_exp = '/^([-.0-9=?A-Z_a-z{|}~])+@([-.0-9=?A-Z_a-z{|}~])+\.[a-zA-Z]{2,6}$/i'; + if (preg_match($email_reg_exp, Request::get('email')) !== 0) { + $subject = '[' .Config::get()->UNI_NAME_CLEAN . ']' . _('Exportadresse für Ihre Termine'); + $text .= _('Diese Email wurde vom Stud.IP-System verschickt. Sie können auf diese Nachricht nicht antworten.') . "\n\n"; + $text .= _('Über diese Adresse erreichen Sie den Export für Ihre Termine:') . "\n\n"; + $text .= $GLOBALS['ABSOLUTE_URI_STUDIP'] . 'dispatch.php/ical/index/' + . IcalExport::getKeyByUser(User::findCurrent()->id); + StudipMail::sendMessage(Request::get('email'), $subject, $text); + PageLayout::postSuccess(_('Die Adresse wurde verschickt!')); + } else { + PageLayout::postError(_('Bitte geben Sie eine gültige Email-Adresse an.')); + } + $this->short_id = IcalExport::getKeyByUser(User::findCurrent()->id); + } + PageLayout::setTitle(_('Kalender veröffentlichen')); + } } diff --git a/app/controllers/calendar/contentbox.php b/app/controllers/calendar/contentbox.php index 129fb59..5d77ed4 100644 --- a/app/controllers/calendar/contentbox.php +++ b/app/controllers/calendar/contentbox.php @@ -24,6 +24,7 @@ class Calendar_ContentboxController extends StudipController $this->admin = false; $this->single = false; $this->userRange = false; + $this->course_range = false; $this->termine = []; // Fetch time if needed @@ -36,6 +37,8 @@ class Calendar_ContentboxController extends StudipController $range_id = [$range_id]; } + $this->titles = []; + foreach ($range_id as $id) { switch (get_object_type($id, ['user', 'sem'])) { case 'user': @@ -44,6 +47,7 @@ class Calendar_ContentboxController extends StudipController break; case 'sem': $this->parseSeminar($id); + $this->course_range = true; break; } } @@ -79,93 +83,91 @@ class Calendar_ContentboxController extends StudipController private function parseSeminar($id) { $course = Course::find($id); - $dates = $course->getDatesWithExdates()->findBy('end_time', [$this->start, $this->start + $this->timespan], '><'); - $this->termine = []; - foreach ($dates as $courseDate) { - // Build info - $info = []; - if (count($courseDate->dozenten) > 0) { - $info[_('Durchführende Lehrende')] = implode(', ', $courseDate->dozenten->getFullname()); - } - if (count($courseDate->statusgruppen) > 0) { - $info[_('Beteiligte Gruppen')] = implode(', ', $courseDate->statusgruppen->getValue('name')); - } - - // Store for view - $description = ''; - if ($courseDate instanceof CourseExDate) { - $description = $courseDate->content; - } elseif ($courseDate->cycle instanceof SeminarCycleDate) { - $description = $courseDate->cycle->description; + $this->termine = $course->getDatesWithExdates()->findBy('end_time', [$this->start, $this->start + $this->timespan], '><'); + foreach ($this->termine as $course_date) { + if ($this->course_range) { + //Display only date and time: + $this->titles[$course_date->id] = $course_date->getFullname('include-room'); + } else { + //Include the course title: + $this->titles[$course_date->id] = $course_date->getFullname('verbose'); } - $this->termine[] = [ - 'id' => $courseDate->id, - 'chdate' => $courseDate->chdate, - 'title' => $courseDate->getFullname() . (count($courseDate->topics) > 0 ? ', ' . implode(', ', $courseDate->topics->getValue('title')) : ''), - 'description' => $description, - 'topics' => $courseDate->topics->toArray('title description'), - 'room' => $courseDate->getRoomName(), - 'info' => $info - ]; } } private function parseUser($id) { - $restrictions = $GLOBALS['user']->id === $id ? [] : ['CLASS' => 'PUBLIC']; - $events = SingleCalendar::getEventList( - $id, - $this->start, - $this->start + $this->timespan, - null, - $restrictions - ); + $begin = new DateTime(); + $begin->setTimestamp($this->start); + $end = new DateTime(); + $end->setTimestamp($this->start + $this->timespan); + $this->termine = []; - // Prepare termine - foreach ($events as $termin) { - // Exclude events that begin after the given time range - if ($termin->getStart() > $this->start + $this->timespan) { + + if ($GLOBALS['user']->id === $id) { + //The current user is looking at their dates. + //Get course dates, too: + $relevant_courses = Course::findBySQL( + "JOIN `seminar_user` USING (`seminar_id`) + WHERE `user_id` = :user_id", + ['user_id' => $id] + ); + foreach ($relevant_courses as $course) { + $course_dates = $course->getDatesWithExdates()->findBy('end_time', [$this->start, $this->start + $this->timespan], '><'); + foreach ($course_dates as $course_date) { + $this->titles[$course_date->id] = sprintf( + '%1$s: %2$s', + $course_date->course->name, + $course_date->getFullname() + ); + $this->termine[] = $course_date; + } + } + } + + //Get personal dates: + + $assignments = []; + if (User::findCurrent()->id === $id) { + $assignments = CalendarDateAssignment::getEvents($begin, $end, $id); + } else { + //Only show public events: + $assignments = CalendarDateAssignment::getEvents($begin, $end, $id, ['PUBLIC']); + } + foreach ($assignments as $assignment) { + //Exclude events that begin after the given time range: + if ($assignment->calendar_date->begin > $this->start + $this->timespan) { continue; } + $title = ''; + // Adjust title - if (date('Ymd', $termin->getStart()) == date('Ymd')) { - $title = _('Heute') . date(', H:i', $termin->getStart()); + if (date('Ymd', $assignment->calendar_date->begin) == date('Ymd')) { + $title = _('Heute') . date(', H:i', $assignment->calendar_date->begin); } else { - $title = mb_substr(strftime('%a', $termin->getStart()), 0, 2); - $title .= date('. d.m.Y, H:i', $termin->getStart()); + $title = mb_substr(strftime('%a', $assignment->calendar_date->begin), 0, 2); + $title .= date('. d.m.Y, H:i', $assignment->calendar_date->begin); } - if ($termin->getStart() < $termin->getEnd()) { - if (date('Ymd', $termin->getStart()) < date('Ymd', $termin->getEnd())) { - $title .= ' - ' . mb_substr(strftime('%a', $termin->getEnd()), 0, 2); - $title .= date('. d.m.Y, H:i', $termin->getEnd()); + if ($assignment->calendar_date->begin < $assignment->calendar_date->end) { + if (date('Ymd', $assignment->calendar_date->begin) < date('Ymd', $assignment->calendar_date->end)) { + $title .= ' - ' . mb_substr(strftime('%a', $assignment->calendar_date->end), 0, 2); + $title .= date('. d.m.Y, H:i', $assignment->calendar_date->end); } else { - $title .= ' - ' . date('H:i', $termin->getEnd()); + $title .= ' - ' . date('H:i', $assignment->calendar_date->end); } } - if ($termin->getTitle()) { - $tmp_titel = mila($termin->getTitle()); //Beschneiden des Titels - $title .= ', ' . $tmp_titel; + if ($assignment->calendar_date->title) { + //Cut the title: + $tmp_title = mila($assignment->calendar_date->title); + $title .= ', ' . $tmp_title; } + $this->titles[$assignment->getObjectId()] = $title; // Store for view - $this->termine[] = [ - 'id' => $termin->id, - 'type' => get_class($termin), - 'range_id' => $termin->range_id, - 'event_id' => $termin->event_id, - 'chdate' => $termin->chdate, - 'title' => $title, - 'description' => $termin->getDescription(), - 'room' => $termin->getLocation(), - 'info' => [ - _('Kategorie') => $termin->toStringCategories(), - _('Priorität') => $termin->toStringPriority(), - _('Sichtbarkeit') => $termin->toStringAccessibility(), - _('Wiederholung') => $termin->toStringRecurrence()] - ]; + $this->termine[] = $assignment; } } } diff --git a/app/controllers/calendar/date.php b/app/controllers/calendar/date.php new file mode 100644 index 0000000..32cf99c --- /dev/null +++ b/app/controllers/calendar/date.php @@ -0,0 +1,828 @@ + $data) { + $options[$key] = $data['name']; + } + if (!array_key_exists(255, $options)) { + $options[255] = _('Sonstige'); + } + return $options; + } + + protected function getCalendarOwner($range_and_id) + { + $range = ''; + $range_id = ''; + $range_and_id = explode('_', $range_and_id ?? []); + if (!empty($range_and_id[1])) { + $range = $range_and_id[0]; + $range_id = $range_and_id[1]; + } + if (!$range) { + //Show the personal calendar of the current user: + $range = 'user'; + $range_id = $GLOBALS['user']->id; + } + + $owner = null; + if (!$range_id) { + //Assume a user calendar. $range contains the user-ID. + $owner = User::getCalendarOwner($range); + } else { + if ($range === 'user') { + $owner = User::getCalendarOwner($range_id); + } elseif ($range === 'course') { + $owner = Course::getCalendarOwner($range_id); + } + } + + if (!$owner || !$owner->isCalendarReadable()) { + throw new AccessDeniedException(_('Sie dürfen diesen Kalender nicht sehen!')); + } + return $owner; + } + + /** + * A helper method to determine whether the current user may write the date. + * + * @return Studip\Calendar\Owner[] The owners in which the current user may add a date. + */ + protected function getCalendarOwnersWithWriteAccess(?CalendarDate $date, ?Studip\Calendar\Owner $owner) : array + { + $result = []; + if ($owner instanceof Course) { + //For course calendars, only the course can be the owner. + $result[$owner->id] = $owner; + return $result; + } + if ($date) { + foreach ($date->calendars as $calendar) { + if ($calendar->user) { + $result[$calendar->user->id] = $calendar->user; + } elseif ($calendar->course) { + $result[$calendar->course->id] = $calendar->course; + } + } + } else { + if ($group_id = Request::get('group_id')) { + $group = ContactGroup::find($group_id); + if ($group) { + foreach ($group->items as $item) { + if ($item->user && $item->user->isCalendarWritable()) { + $result[$item->user_id] = $item->user; + } + } + } + } elseif ($user_id = Request::get('user_id', $GLOBALS['user']->id)) { + $user = User::find($user_id); + if ($user && $user->isCalendarWritable()) { + $result[$user->id] = $user; + } + } + if ($other_calendar_ids = Request::getArray('other_calendar_ids')) { + foreach ($other_calendar_ids as $other_calendar_id) { + $user = User::find($other_calendar_id); + if ($user && $user->isCalendarWritable()) { + $result[$user->id] = $user; + } + } + } + } + return $result; + } + + public function index_action($date_id) + { + $this->date = CalendarDate::find($date_id); + if (!$this->date) { + PageLayout::postError(_('Der angegebene Termin wurde nicht gefunden.')); + return; + } + if (!$this->date->isVisible($GLOBALS['user']->id)) { + throw new AccessDeniedException(); + } + PageLayout::setTitle( + sprintf( + _('%1$s (am %2$s von %3$s bis %4$s Uhr)'), + $this->date->title, + date('d.m.Y', $this->date->begin), + date('H:i', $this->date->begin), + date('H:i', $this->date->end) + ) + ); + $this->selected_date = ''; + if ($this->date->repetition_type) { + $this->selected_date = Request::get('selected_date'); + } + $this->calendar_assignments = CalendarDateAssignment::findBySql( + "INNER JOIN `auth_user_md5` + ON `calendar_date_assignments`.`range_id` = `auth_user_md5`.`user_id` + WHERE + `calendar_date_id` = :calendar_date_id", + ['calendar_date_id' => $this->date->id] + ); + $this->participation_message = null; + $this->user_participation_status = ''; + $this->all_assignments_writable = false; + $this->is_group_date = count($this->calendar_assignments) > 1; + + if ($this->calendar_assignments) { + $writable_assignment_c = 0; + $more_than_one_assignment = count($this->calendar_assignments) > 1; + //Find the calendar assignment of the user and set the participation message + //according to the participation status. + foreach ($this->calendar_assignments as $index => $assignment) { + if ($assignment->range_id === $GLOBALS['user']->id && $this->is_group_date) { + $this->user_participation_status = $assignment->participation; + if ($assignment->participation === 'ACCEPTED') { + $this->participation_message = MessageBox::info(_('Sie nehmen am Termin teil.')); + } elseif ($assignment->participation === 'DECLINED') { + $this->participation_message = MessageBox::info(_('Sie nehmen nicht am Termin teil.')); + } elseif ($assignment->participation === 'ACKNOWLEDGED') { + $this->participation_message = MessageBox::info(_('Sie haben den Termin zur Kenntnis genommen.')); + } else { + $this->participation_message = MessageBox::info(_('Sie haben keine Angaben zur Teilnahme gemacht.')); + } + if ($more_than_one_assignment) { + $writable_assignment_c++; + } else { + //We don't need the users own assignment in the list of assignments + //when there is only one assignment to the users own calendar. + unset($this->calendar_assignments[$index]); + + } + } else { + if ($assignment->isWritable($GLOBALS['user']->id)) { + $writable_assignment_c++; + } + } + } + + $this->all_assignments_writable = $writable_assignment_c === count($this->calendar_assignments); + + //Order all calendar assignments by type and name: + uasort($this->calendar_assignments, function ($a, $b) { + $compare_name = ($a->course instanceof Course && $b->course instanceof Course) + || ($a->user instanceof User && $b->user instanceof User); + if ($compare_name) { + $a_name = ''; + if ($a->course instanceof Course) { + $a_name = $a->course->getFullname(); + } elseif ($a->user instanceof User) { + $a_name = $a->user->getFullName(); + } + $b_name = ''; + if ($b->course instanceof Course) { + $b_name = $b->course->getFullname(); + } elseif ($b->user instanceof User) { + $b_name = $b->user->getFullName(); + } + if ($a_name < $b_name) { + return -1; + } elseif ($a_name > $b_name) { + return 1; + } else { + return 0; + } + } else { + //Compare types. + $a_is_course = $a->course instanceof Course; + if ($a_is_course) { + return -1; + } else { + //$b is a course: + return 1; + } + } + }); + } + } + + public function add_action($range_and_id = '') + { + PageLayout::setTitle(_('Termin anlegen')); + + $owner = $this->getCalendarOwner($range_and_id); + + $this->date = new CalendarDate(); + if (Request::submitted('begin') && Request::submitted('end')) { + $this->date->begin = Request::get('begin'); + $this->date->end = Request::get('end'); + $this->date->repetition_end = $this->date->end; + } else { + $time = new DateTime(); + $time = $time->add(new DateInterval('PT1H')); + $time->setTime(intval($time->format('H')), 0, 0); + $this->date->begin = $time->getTimestamp(); + $time = $time->add(new DateInterval('PT30M')); + $this->date->end = $time->getTimestamp(); + $this->date->repetition_end = $this->date->end; + } + if ($owner instanceof Course) { + $this->form_post_link = $this->link_for('calendar/date/add/course_' . $owner->id); + } else { + //Personal calendar or group calendar + $this->form_post_link = $this->link_for('calendar/date/add'); + } + $this->handleForm('add', $owner); + } + + public function edit_action($date_id) + { + PageLayout::setTitle(_('Termin bearbeiten')); + + $this->date = CalendarDate::find($date_id); + if (!$this->date) { + throw new Exception(_('Der Termin wurde nicht gefunden!')); + } + //Set the repetition end date to the end of the date in case it isn't set: + if (!$this->date->repetition_end) { + $this->date->repetition_end = $this->date->end; + } + + $this->form_post_link = $this->link_for('calendar/date/edit/' . $this->date->id); + $this->handleForm(); + } + + protected function handleForm($mode = 'edit', $owner = null) + { + $this->form_errors = []; + $this->calendar_assignment_items = []; + + $this->writable_calendars = $this->getCalendarOwnersWithWriteAccess($mode === 'edit' ? $this->date : null, $owner); + if (!$this->writable_calendars) { + throw new AccessDeniedException(); + } + $this->user_id = Request::get('user_id', $owner->id ?? ''); + $this->group_id = ''; + if (!$owner) { + $this->group_id = Request::get('group_id'); + } + $this->owner_id = $owner ? $owner->id : ''; + + $this->category_options = $this->getCategoryOptions(); + $this->exceptions = []; + + if (!$owner || !($owner instanceof Course)) { + $this->user_quick_search_type = null; + $this->multi_person_search = null; + if (Config::get()->CALENDAR_GROUP_ENABLE) { + if (Config::get()->CALENDAR_GRANT_ALL_INSERT) { + $this->user_quick_search_type = new StandardSearch('user_id'); + } else { + //Only get those users where the current user has + //write access to the calendar. + $this->user_quick_search_type = new SQLSearch( + "SELECT + `auth_user_md5`.`user_id`, " + . $GLOBALS['_fullname_sql']['full'] . " AS fullname + FROM `auth_user_md5` + INNER JOIN `contact` + ON `auth_user_md5`.`user_id` = `contact`.`owner_id` + INNER JOIN `user_info` + ON `user_info`.`user_id` = `auth_user_md5`.`user_id` + WHERE + `auth_user_md5`.`user_id` <> " . DBManager::get()->quote($GLOBALS['user']->id) . " + AND `contact`.`user_id` = " . DBManager::get()->quote($GLOBALS['user']->id) . " + AND `contact`.`calendar_permissions` = 'WRITE' + AND ( + `auth_user_md5`.`username` LIKE :input + OR CONCAT(`auth_user_md5`.`Vorname`, ' ', `auth_user_md5`.`Nachname`) LIKE :input + OR CONCAT(`auth_user_md5`.`Nachname`, ' ', `auth_user_md5`.`Vorname`) LIKE :input + OR `auth_user_md5`.`Nachname` LIKE :input + OR " . $GLOBALS['_fullname_sql']['full'] . " LIKE :input + ) + GROUP BY `auth_user_md5`.`user_id` + ORDER BY fullname ASC", + _('Person suchen'), + 'user_id' + ); + } + } + } + + if ($this->date->isNew()) { + if (!($owner instanceof Course)) { + //Assign the date to the calendar of the current user by default: + $user = User::findCurrent(); + if ($user) { + $this->calendar_assignment_items[] = [ + 'value' => $user->id, + 'name' => $user->getFullName(), + 'deletable' => true + ]; + } + } + } else { + $exceptions = CalendarDateException::findBySql( + 'calendar_date_id = :date_id ORDER BY `date` ASC', + ['date_id' => $this->date->id] + ); + foreach ($exceptions as $exception) { + $this->exceptions[] = $exception->date; + } + + $calendars_assignments = CalendarDateAssignment::findByCalendar_date_id($this->date->id); + foreach ($calendars_assignments as $assignment) { + $range_avatar = $assignment->getRangeAvatar(); + $this->calendar_assignment_items[] = [ + 'value' => $assignment->range_id, + 'name' => $assignment->getRangeName(), + 'deletable' => true + ]; + } + } + + $this->all_day_event = false; + if ($mode === 'add' && Request::get('all_day') === '1') { + $this->all_day_event = true; + } else { + $begin = new DateTime(); + $begin->setTimestamp(intval($this->date->begin)); + $end = new DateTime(); + $end->setTimestamp(intval($this->date->end)); + $duration = $end->diff($begin); + if ($duration->h === 23 && $duration->i === 59 && $duration->s === 59 && $begin->format('H:i:s') === '00:00:00') { + //The event starts at midnight and ends on 23:59:59. It is an all-day event. + $this->all_day_event = true; + } + } + + if (!Request::isPost()) { + return; + } + if (Request::submitted('save')) { + CSRFProtection::verifyUnsafeRequest(); + + if ($this->date->isNew()) { + $this->date->author_id = $GLOBALS['user']->id; + } + $this->date->editor_id = $GLOBALS['user']->id; + + $begin = Request::getDateTime('begin', 'd.m.Y H:i'); + $end = Request::getDateTime('end', 'd.m.Y H:i'); + if (Request::get('all_day') === '1') { + $this->all_day_event = true; + $begin->setTime(0,0,0); + $end = clone $begin; + $end->setTime(23,59,59); + } + $this->date->begin = $begin->getTimestamp(); + $this->date->end = $end->getTimestamp(); + if (!$this->date->begin) { + $this->form_errors[_('Beginn')] = _('Bitte geben Sie einen Startzeitpunkt ein.'); + } + if (!$this->date->end && !$this->all_day_event) { + $this->form_errors[_('Ende')] = _('Bitte geben Sie einen Endzeitpunkt ein.'); + } + if ($this->date->begin && $this->date->end && ($this->date->end < $this->date->begin)) { + $this->form_errors[_('Ende')] = _('Der Startzeitpunkt darf nicht nach dem Endzeitpunkt liegen!'); + } + + $this->date->title = Request::get('title'); + if (!$this->date->title) { + $this->form_errors[_('Titel')] = _('Bitte geben Sie einen Titel ein.'); + } + + $this->date->access = Request::get('access'); + if (!in_array($this->date->access, ['PUBLIC', 'CONFIDENTIAL', 'PRIVATE'])) { + $this->form_errors[_('Zugriff')] = _('Bitte wählen Sie einen Zugriffstyp aus.'); + } + + $this->date->description = Request::get('description'); + + $this->date->category = Request::get('category'); + if (!in_array($this->date->category, array_keys($this->category_options))) { + $this->form_errors[_('Kategorie')] = _('Bitte wählen Sie eine gültige Kategorie aus.'); + } + + $this->date->user_category = Request::get('user_category'); + + $this->date->location = Request::get('location'); + + //Store the repetition information: + + $this->date->clearRepetitionFields(); + $this->date->repetition_type = Request::get('repetition_type', ''); + if (!in_array($this->date->repetition_type, ['', 'DAILY', 'WEEKLY', 'WORKDAYS', 'MONTHLY', 'YEARLY'])) { + $this->form_errors[_('Wiederholung')] = _('Bitte wählen Sie ein gültiges Wiederholungsintervall aus.'); + } + if ($this->date->repetition_type !== '') { + $this->date->interval = ''; + if (in_array($this->date->repetition_type, ['DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY'])) { + $this->date->interval = Request::get('repetition_interval'); + } + + if ($this->date->repetition_type === 'WEEKLY') { + $dow = array_unique(Request::getArray('repetition_dow')); + foreach ($dow as $day) { + if ($day < 1 || $day > 7) { + $this->form_errors[_('Wiederholung an bestimmtem Wochentag')] = _('Bitte wählen Sie einen Wochentag zwischen Montag und Sonntag aus.'); + } + } + $this->date->days = implode('', $dow); + } elseif ($this->date->repetition_type === 'WORKDAYS') { + //Special case: The "WORKDAYS" repetition type is a shorthand type + //for a weekly repetition from Monday to Friday. + $this->date->repetition_type = 'WEEKLY'; + $this->date->days = '12345'; + $this->date->interval = '1'; + } elseif ($this->date->repetition_type === 'MONTHLY') { + $month_type = Request::get('repetition_month_type'); + if ($month_type === 'dom') { + $this->date->offset = Request::get('repetition_dom'); + } elseif ($month_type === 'dow') { + $this->date->days = Request::get('repetition_dow'); + $this->date->offset = Request::get('repetition_dow_week'); + } + } elseif ($this->date->repetition_type === 'YEARLY') { + $month = Request::get('repetition_month'); + if ($month < 1 || $month > 12) { + $this->form_errors[_('Monat')] = _('Bitte wählen Sie einen Monat zwischen Januar und Dezember aus.'); + } + $this->date->month = $month; + $month_type = Request::get('repetition_month_type'); + if ($month_type === 'dom') { + $this->date->offset = Request::get('repetition_dom'); + } elseif ($month_type === 'dow') { + $this->date->days = Request::get('repetition_dow'); + $this->date->offset = Request::get('repetition_dow_week'); + } + } + + $end_type = Request::get('repetition_rep_end_type'); + if ($end_type === 'end_date') { + $end_date = Request::getDateTime('repetition_rep_end_date', 'd.m.Y'); + $end_date->setTime(23,59,59); + $this->date->repetition_end = $end_date->getTimestamp(); + } elseif ($end_type === 'end_count') { + $this->date->number_of_dates = Request::get('repetition_number_of_dates'); + } else { + //Repetition never ends: + $this->date->repetition_end = CalendarDate::NEVER_ENDING; + } + } + + $assigned_calendar_ids = Request::getArray('assigned_calendar_ids'); + if (!$assigned_calendar_ids || (count($assigned_calendar_ids) === 0)) { + $this->form_errors[_('Teilnehmende Personen')] = _('Der Termin ist keinem Kalender zugewiesen!'); + } + + if ($this->form_errors) { + return; + } + + $stored = false; + if ($this->date->isDirty()) { + $stored = $this->date->store(); + } else { + $stored = true; + } + if (!$stored) { + PageLayout::postError( + _('Beim Speichern des Termins ist ein Fehler aufgetreten.') + ); + return; + } + + //Assign the calendar date to all writable calendars. + + //Check the assigned calendar-IDs first if they are valid: + $valid_assigned_calendar_ids = []; + if (($owner instanceof Course)) { + //Set the course as calendar: + $allowed_calendar_ids = [$owner->id]; + } else { + //Assign the date to the calendars of all the selected users: + $allowed_calendar_ids = [$GLOBALS['user']->id]; + if (Config::get()->CALENDAR_GROUP_ENABLE) { + $allowed_calendar_results = $this->user_quick_search_type->getResults('%%%%'); + foreach ($allowed_calendar_results as $result) { + $allowed_calendar_ids[] = $result[0]; + } + } + } + + foreach ($assigned_calendar_ids as $assigned_calendar_id) { + if (Course::exists($assigned_calendar_id) || User::exists($assigned_calendar_id)) { + //Valid ID of an existing calendar (range-ID). + if (in_array($assigned_calendar_id, $allowed_calendar_ids)) { + //The calendar is writable. + $valid_assigned_calendar_ids[] = $assigned_calendar_id; + } + } + } + if (count($valid_assigned_calendar_ids) < 1) { + PageLayout::postError( + _('Die Zuweisungen des Termins zu Kalendern sind ungültig!') + ); + return; + } + + //Remove the date from all user calendars that aren't in the array of writable calendars. + CalendarDateAssignment::deleteBySQL( + '`range_id` NOT IN ( :owner_ids ) AND `calendar_date_id` = :calendar_date_id', + ['owner_ids' => $allowed_calendar_ids, 'calendar_date_id' => $this->date->id] + ); + + //Now add the date to all selected calendars: + foreach($valid_assigned_calendar_ids as $assigned_calendar_id) { + $assignment = CalendarDateAssignment::findOneBySql( + 'range_id = :assigned_calendar_id AND calendar_date_id = :calendar_date_id', + [ + 'assigned_calendar_id' => $assigned_calendar_id, + 'calendar_date_id' => $this->date->id + ] + ); + if (!$assignment) { + $assignment = new CalendarDateAssignment(); + $assignment->range_id = $assigned_calendar_id; + $assignment->calendar_date_id = $this->date->id; + $assignment->store(); + } + } + + //Clear all exceptions for the event and set them again: + CalendarDateException::deleteByCalendar_date_id($this->date->id); + $new_exceptions = Request::getArray('exceptions'); + $stored_c = 0; + foreach ($new_exceptions as $exception) { + $date_parts = explode('-', $exception); + if (count($date_parts) === 3) { + //Should be a valid date string. + $e = new CalendarDateException(); + $e->calendar_date_id = $this->date->id; + $e->date = $exception; + if ($e->store()) { + $stored_c++; + } + } + } + if ($stored_c === count($new_exceptions)) { + PageLayout::postSuccess(_('Der Termin wurde gespeichert.')); + } else { + PageLayout::postWarning(_('Der Termin wurde gespeichert, aber nicht mit allen Terminausfällen!')); + } + if (Request::submitted('selected_date')) { + $selected_date = Request::getDateTime('selected_date'); + if ($selected_date) { + //Set the calendar default date to the previously selected date: + $_SESSION['calendar_date'] = $selected_date->format('Y-m-d'); + } + } else { + //Set the calendar default date to the beginning of the date: + $_SESSION['calendar_date'] = $begin->format('Y-m-d'); + } + $this->response->add_header('X-Dialog-Close', '1'); + } + } + + public function move_action($date_id) + { + $this->date = CalendarDate::find($date_id); + if (!$this->date) { + throw new InvalidArgumentException( + _('Der angegebene Termin wurde nicht gefunden.') + ); + } + if (!$this->date->isWritable($GLOBALS['user']->id)) { + throw new AccessDeniedException( + _('Sie sind nicht berechtigt, diesen Termin zu ändern.') + ); + } + + $this->begin = Request::getDateTime('begin', \DateTime::RFC3339); + $this->end = Request::getDateTime('end', \DateTime::RFC3339); + if (!$this->begin || !$this->end) { + throw new InvalidArgumentException(); + } + + if ($this->date->repetition_type) { + PageLayout::setTitle(_('Verschieben eines Termins aus einer Terminserie')); + //Show the dialog to decide what shall be done with the repetition. + if (Request::submitted('move')) { + CSRFProtection::verifyUnsafeRequest(); + $repetition_handling = Request::get('repetition_handling'); + $store_old_date = false; + if ($repetition_handling === 'create_single_date') { + //Create a new date with the new time range and then + //create an exception for the old date. + $new_date = new CalendarDate(); + $new_date->setData($this->date->toArray()); + $new_date->id = $new_date->getNewId(); + $new_date->unique_id = ''; + $new_date->begin = $this->begin->getTimestamp(); + $new_date->end = $this->end->getTimestamp(); + $new_date->author_id = $GLOBALS['user']->id; + $new_date->editor_id = $GLOBALS['user']->id; + $new_date->clearRepetitionFields(); + $new_date->store(); + foreach ($this->date->calendars as $calendar) { + $new_date_calendar = new CalendarDateAssignment(); + $new_date_calendar->calendar_date_id = $new_date->id; + $new_date_calendar->range_id = $calendar->range_id; + $new_date_calendar->store(); + } + $exception = CalendarDateException::findBySQL( + '`calendar_date_id` = :calendar_date_id AND `date` = :date', + [ + 'calendar_date_id' => $this->date->id, + 'date' => $this->begin->format('Y-m-d') + ] + ); + if (!$exception) { + $exception = new CalendarDateException(); + $exception->calendar_date_id = $this->date->id; + $exception->date = $this->begin->format('Y-m-d'); + $exception->store(); + } + $this->response->add_header('X-Dialog-Close', '1'); + return; + } elseif ($repetition_handling === 'change_times') { + //Set the new time for begin and end: + $date_begin = new DateTime(); + $date_begin->setTimestamp($this->date->begin); + $date_begin->setTime( + intval($this->begin->format('H')), + intval($this->begin->format('i')), + intval($this->begin->format('s')) + ); + $this->date->begin = $date_begin->getTimestamp(); + $date_end = new DateTime(); + $date_end->setTimestamp($this->date->end); + $date_end->setTime( + intval($this->end->format('H')), + intval($this->end->format('i')), + intval($this->end->format('s')) + ); + $this->date->end = $date_end->getTimestamp(); + + //Set the editor-ID: + $this->date->editor_id = $GLOBALS['user']->id; + + $store_old_date = true; + } elseif ($repetition_handling === 'change_all') { + $this->date->begin = $this->begin->getTimestamp(); + if ($this->date->repetition_end && intval($this->date->repetition_end) != pow(2,31) - 1) { + //The repetition end date is set to one specific date. + //It must be recalculated from the end date. + $old_end = new DateTime(); + $old_end->setTimestamp($this->date->end); + $old_repetition_end = new DateTime(); + $old_repetition_end ->setTimestamp($this->date->repetition_end); + $distance = $old_end->diff($old_repetition_end); + $this->date->end = $this->end->getTimestamp(); + $new_repetition_end = clone $this->end; + $new_repetition_end = $new_repetition_end->add($distance); + $this->date->repetition_end = $new_repetition_end->getTimestamp(); + } + $this->date->end = $this->end->getTimestamp(); + + //Set the editor-ID: + $this->date->editor_id = $GLOBALS['user']->id; + + $store_old_date = true; + } else { + //Invalid choice. + PageLayout::postError(_('Ungültige Auswahl!')); + return; + } + if ($store_old_date) { + $success = false; + if ($this->date->isDirty()) { + $success = $this->date->store(); + } else { + $success = true; + } + if ($success) { + $this->response->add_header('X-Dialog-Close', '1'); + $this->render_nothing(); + } else { + throw new Exception(_('Der Termin konnte nicht gespeichert werden.')); + } + } + } + } else { + //Set the new date and time directly. + $this->date->begin = $this->begin->getTimestamp(); + $this->date->end = $this->end->getTimestamp(); + //Set the editor-ID: + $this->date->editor_id = $GLOBALS['user']->id; + + $success = false; + if ($this->date->isDirty()) { + $success = $this->date->store(); + } else { + $success = true; + } + if ($success) { + $this->response->add_header('X-Dialog-Close', '1'); + $this->render_nothing(); + } else { + throw new Exception(_('Der Termin konnte nicht gespeichert werden.')); + } + } + } + + public function delete_action($date_id) + { + PageLayout::setTitle(_('Termin löschen')); + $this->date = CalendarDate::find($date_id); + if (!$this->date) { + PageLayout::postError( + _('Der Termin wurde nicht gefunden!') + ); + $this->render_nothing(); + } + $this->date_has_repetitions = !empty($this->date->repetition_type); + $this->selected_date = null; + if ($this->date_has_repetitions) { + $this->selected_date = Request::getDateTime('selected_date'); + if (!$this->selected_date) { + $this->selected_date = new DateTime(); + $this->selected_date->setTimestamp($this->date->begin); + } + } + $this->repetition_handling = Request::get('repetition_handling', 'create_exception'); + if (Request::submitted('delete')) { + $delete_whole_date = false; + CSRFProtection::verifyUnsafeRequest(); + if ($this->date_has_repetitions) { + if ($this->repetition_handling === 'create_exception') { + $exception = new CalendarDateException(); + $exception->calendar_date_id = $this->date->id; + $exception->date = $this->selected_date->format('Y-m-d'); + if ($exception->store()) { + PageLayout::postSuccess( + sprintf( + _('Die Ausnahme am %s wurde der Terminserie hinzugefügt.'), + $this->selected_date->format('d.m.Y') + ) + ); + $this->response->add_header('X-Dialog-Close', '1'); + $this->render_nothing(); + } else { + PageLayout::postError( + sprintf( + _('Die Ausnahme am %s konnte der Terminserie nicht hinzugefügt werden.'), + $this->selected_date->format('d.m.Y') + ) + ); + } + } elseif ($this->repetition_handling === 'delete_all') { + $delete_whole_date = true; + } + } else { + $delete_whole_date = true; + } + if ($delete_whole_date) { + if ($this->date->delete()) { + if ($this->date_has_repetitions) { + PageLayout::postSuccess(_('Die Terminserie wurde gelöscht!')); + } else { + PageLayout::postSuccess(_('Der Termin wurde gelöscht!')); + } + $this->response->add_header('X-Dialog-Close', '1'); + $this->render_nothing(); + } else { + if ($this->date_has_repetitions) { + PageLayout::postError(_('Die Terminserie konnte nicht gelöscht werden!')); + } else { + PageLayout::postError(_('Der Termin konnte nicht gelöscht werden!')); + } + } + } + } + } + + public function participation_action($date_id) + { + $this->calendar_assignment = CalendarDateAssignment::find([$GLOBALS['user']->id, $date_id]); + if (!$this->calendar_assignment) { + throw new AccessDeniedException(); + } + CSRFProtection::verifyUnsafeRequest(); + + $participation = Request::get('participation'); + if (!in_array($participation, ['', 'ACCEPTED', 'DECLINED', 'ACKNOWLEDGED'])) { + throw new InvalidArgumentException(); + } + + $this->calendar_assignment->participation = $participation; + if ($this->calendar_assignment->isDirty()) { + $this->calendar_assignment->store(); + $this->calendar_assignment->sendParticipationStatus(); + } + $this->response->add_header('X-Dialog-Close', '1'); + PageLayout::postSuccess(_('Ihre Teilnahmestatus wurde geändert.')); + $this->render_nothing(); + } +} diff --git a/app/controllers/calendar/group.php b/app/controllers/calendar/group.php deleted file mode 100644 index f440a57..0000000 --- a/app/controllers/calendar/group.php +++ /dev/null @@ -1,336 +0,0 @@ -base = 'calendar/group/'; - parent::before_filter($action, $args); - } - - protected function createSidebar($active = 'week', $calendar = null) - { - parent::createSidebar($active, $calendar); - $sidebar = Sidebar::Get(); - $actions = new ActionsWidget(); - $actions->addLink(_('Termin anlegen'), - $this->url_for('calendar/group/edit'), - Icon::create('add'), - ['data-dialog' => 'size=auto']); - $actions->addLink(_('Kalender freigeben'), - $this->url_for('calendar/single/manage_access/' . $GLOBALS['user']->id, - ['group_filter' => $this->range_id]), - Icon::create('community'), - ['id' => 'calendar-open-manageaccess', - 'data-dialog' => '', 'data-dialogname' => 'manageaccess']); - $sidebar->addWidget($actions); - } - - protected function getTitle($group) - { - $title = sprintf(_('Terminkalender der Gruppe "%s"'), $group->name); - return $title; - } - - public function index_action() - { - // switch to the view the user has selected in his personal settings - $default_view = $this->settings['view'] ?: 'week'; - $this->redirect($this->url_for('calendar/group/' . $default_view)); - } - - public function edit_action($range_id = null, $event_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - // get group and the calendars of the members - // the first calendar is the calendar of the actual user - $this->calendar = new SingleCalendar($GLOBALS['user']->id); - $group = $this->getGroup($this->calendar); - $this->attendee_ids = []; - if ($group) { - $calendar_owners = CalendarUser::getOwners($GLOBALS['user']->id, - Calendar::PERMISSION_WRITABLE)->pluck('owner_id'); - $members = $group->members->pluck('user_id'); - $user_id = Request::option('user_id'); - $this->attendee_ids = array_intersect($calendar_owners, $members); - $this->attendee_ids[] = $GLOBALS['user']->id; - if ($user_id && in_array($user_id, $this->attendee_ids)) { - $this->attendee_ids = [$user_id]; - } - } - - $this->event = $this->calendar->getEvent($event_id); - - if ($this->event->isNew()) { - $this->event = $this->calendar->getNewEvent(); - if (Request::get('isdayevent')) { - $this->event->setStart(mktime(0, 0, 0, date('n', $this->atime), - date('j', $this->atime), date('Y', $this->atime))); - $this->event->setEnd(mktime(23, 59, 59, date('n', $this->atime), - date('j', $this->atime), date('Y', $this->atime))); - } else { - $this->event->setStart($this->atime); - $this->event->setEnd($this->atime + 3600); - } - $this->event->setAuthorId($GLOBALS['user']->id); - $this->event->setEditorId($GLOBALS['user']->id); - $this->event->setAccessibility('PRIVATE'); - if ($this->attendee_ids) { - foreach ($this->attendee_ids as $attendee_id) { - $attendee_event = clone $this->event; - $attendee_event->range_id = $attendee_id; - $this->attendees[] = $attendee_event; - } - } - if (!Request::isXhr()) { - PageLayout::setTitle($this->getTitle($this->calendar, _('Neuer Termin'))); - } - } else { - // open read only events and course events not as form - // show information in dialog instead - if (!$this->event->havePermission(Event::PERMISSION_WRITABLE) - || $this->event instanceof CourseEvent) { - $this->redirect($this->url_for('calendar/single/event/' . implode('/', - [$this->range_id, $this->event->event_id]))); - return null; - } - $this->attendees = $this->event->attendees; - if (!Request::isXhr()) { - PageLayout::setTitle($this->getTitle($this->calendar, _('Termin bearbeiten'))); - } - } - - if (Config::get()->CALENDAR_GROUP_ENABLE - && $this->calendar->getRange() == Calendar::RANGE_USER) { - $search_obj = new SQLSearch("SELECT auth_user_md5.user_id, {$GLOBALS['_fullname_sql']['full_rev']} as fullname, username, perms " - . "FROM calendar_user " - . "LEFT JOIN auth_user_md5 ON calendar_user.owner_id = auth_user_md5.user_id " - . "LEFT JOIN user_info ON (auth_user_md5.user_id = user_info.user_id) " - . 'WHERE calendar_user.user_id = ' - . DBManager::get()->quote($GLOBALS['user']->id) - . ' AND calendar_user.permission > ' . Event::PERMISSION_READABLE - . ' AND (username LIKE :input OR Vorname LIKE :input ' - . "OR CONCAT(Vorname,' ',Nachname) LIKE :input " - . "OR CONCAT(Nachname,' ',Vorname) LIKE :input " - . "OR Nachname LIKE :input OR {$GLOBALS['_fullname_sql']['full_rev']} LIKE :input " - . ") ORDER BY fullname ASC", - _('Nutzer suchen'), 'user_id'); - $this->quick_search = QuickSearch::get('user_id', $search_obj) - ->fireJSFunctionOnSelect('STUDIP.Messages.add_adressee'); - - // $default_selected_user = array($this->calendar->getRangeId()); - $this->mps = MultiPersonSearch::get('add_adressees') - ->setLinkText(_('Mehrere Teilnehmende hinzufügen')) - // ->setDefaultSelectedUser($default_selected_user) - ->setTitle(_('Mehrere Teilnehmende hinzufügen')) - ->setExecuteURL($this->url_for($this->base . 'edit')) - ->setJSFunctionOnSubmit('STUDIP.Messages.add_adressees') - ->setSearchObject($search_obj); - $owners = SimpleORMapCollection::createFromArray( - CalendarUser::findByUser_id($this->calendar->getRangeId())) - ->pluck('owner_id'); - foreach (Calendar::getGroups($GLOBALS['user']->id) as $group) { - $this->mps->addQuickfilter( - $group->name, - $group->members->filter( - function ($member) use ($owners) { - if (in_array($member->user_id, $owners)) { - return $member; - } - })->pluck('user_id') - ); - } - } - - $stored = false; - if (Request::submitted('store')) { - $stored = $this->storeEventData($this->event, $this->calendar); - } - - if ($stored !== false) { - // switch back to group context - $this->range_id = $group->getId(); - if ($stored === 0) { - if (Request::isXhr()) { - header('X-Dialog-Close: 1'); - exit; - } else { - PageLayout::postSuccess(_('Der Termin wurde nicht geändert.')); - $this->relocate('calendar/group/' . $this->last_view, ['atime' => $this->atime]); - } - } else { - PageLayout::postSuccess(_('Der Termin wurde gespeichert.')); - $this->relocate('calendar/group/' . $this->last_view, ['atime' => $this->atime]); - } - } else { - $this->createSidebar('edit', $this->calendar); - $this->createSidebarFilter(); - $this->render_template('calendar/single/edit', $this->layout); - } - } - - public function day_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - // get group and the calendars of the members - // the first calendar is the calendar of the actual user - $this->calendars[0] = SingleCalendar::getDayCalendar( - $GLOBALS['user']->id, $this->atime); - $group = $this->getGroup($this->calendars[0]); - foreach ($group->members as $member) { - $calendar = new SingleCalendar($member->user_id); - if ($calendar->havePermission(Calendar::PERMISSION_READABLE)) { - $this->calendars[] = SingleCalendar::getDayCalendar($calendar, - $this->atime, null, $this->restrictions); - } - } - - PageLayout::setTitle($this->getTitle($group) - . ' - ' . _('Tagesansicht')); - Navigation::activateItem('/calendar/calendar'); - - $this->last_view = 'day'; - - $this->createSidebar('day'); - $this->createSidebarFilter(); - } - - /** - * Returns the Statusgruppe for the given calendar. - * - * @param SingleCalendar The calendar of the group owner. - * @return Statusgruppen The found group. - * @throws AccessDeniedException If the group does not exists or the owner - * of the calendar is not the owner of the group. - */ - private function getGroup($calendar) - { - $group = Statusgruppen::find($this->range_id); - if (!$group) { - throw new AccessDeniedException(); - } - // is the user the owner of this group - if ($group->range_id != $calendar->getRangeId()) { - // not the owner... - throw new AccessDeniedException(); - } - return $group; - } - - public function week_action($range_id = null) - { - $this->calendars = []; - $this->range_id = $range_id ?: $this->range_id; - $timestamp = mktime(12, 0, 0, date('n', $this->atime), - date('j', $this->atime), date('Y', $this->atime)); - $monday = $timestamp - 86400 * (strftime('%u', $timestamp) - 1); - $day_count = $this->settings['type_week'] == 'SHORT' ? 5 : 7; - // one calendar for each day for the actual user - for ($i = 0; $i < $day_count; $i++) { - // one calendar holds the events of one day - $this->calendars[0][$i] = - SingleCalendar::getDayCalendar($GLOBALS['user']->id, - $monday + $i * 86400, null, $this->restrictions); - } - // check and get the group - $group = $this->getGroup($this->calendars[0][0]); - $n = 1; - foreach ($group->members as $member) { - $calendar = new SingleCalendar($member->user_id); - if ($calendar->havePermission(Calendar::PERMISSION_READABLE)) { - for ($i = 0; $i < $day_count; $i++) { - $this->calendars[$n][$i] = - SingleCalendar::getDayCalendar($member->user_id, - $monday + $i * 86400, null, $this->restrictions); - } - $n++; - } - } - - PageLayout::setTitle($this->getTitle($group) - . ' - ' . _('Wochenansicht')); - Navigation::activateItem('/calendar/calendar'); - - $this->last_view = 'week'; - - $this->createSidebar('week'); - $this->createSidebarFilter(); - } - - public function month_action($range_id = null) - { - $this->calendars = []; - $this->range_id = $range_id ?: $this->range_id; - $month_start = mktime(12, 0, 0, date('n', $this->atime), 1, date('Y', $this->atime)); - $month_end = mktime(12, 0, 0, date('n', $this->atime), date('t', $this->atime), date('Y', $this->atime)); - $adow = strftime('%u', $month_start) - 1; - $cor = date('n', $this->atime) == 3 ? 1 : 0; - $this->first_day = $month_start - $adow * 86400; - $this->last_day = ((42 - ($adow + date('t', $this->atime))) % 7 + $cor) * 86400 + $month_end; - // one calendar each day for the actual user - for ($start_day = $this->first_day; $start_day <= $this->last_day; $start_day += 86400) { - $this->calendars[0][] = SingleCalendar::getDayCalendar( - $GLOBALS['user']->id, $start_day, null, $this->restrictions); - } - // check and get the group - $group = $this->getGroup($this->calendars[0][0]); - $n = 1; - // get the calendars of the group members - foreach ($group->members as $member) { - $calendar = new SingleCalendar($member->user_id); - if ($calendar->havePermission(Calendar::PERMISSION_READABLE)) { - for ($start_day = $this->first_day; $start_day <= $this->last_day; $start_day += 86400) { - $this->calendars[$n][] = - SingleCalendar::getDayCalendar($member->user_id, - $start_day, null, $this->restrictions); - } - $n++; - } - } - PageLayout::setTitle($this->getTitle($group) - . ' - ' . _('Monatssicht')); - Navigation::activateItem('/calendar/calendar'); - - $this->last_view = 'month'; - - $this->createSidebar('month'); - $this->createSidebarFilter(); - } - - public function year_action($range_id = null) - { - $this->calendars = []; - $this->count_lists = []; - - $this->range_id = $range_id ?: $this->range_id; - $start = mktime(0, 0, 0, 1, 1, date('Y', $this->atime)); - $end = mktime(23, 59, 59, 12, 31, date('Y', $this->atime)); - $this->calendars[0] = new SingleCalendar( - $GLOBALS['user']->id, $start, $end); - $this->count_lists[0] = $this->calendars[0]->getListCountEvents(); - - // check and get the group - $group = $this->getGroup($this->calendars[0]); - $n = 1; - // get the calendars of the group members - foreach ($group->members as $member) { - $calendar = new SingleCalendar($member->user_id); - if ($calendar->havePermission(Calendar::PERMISSION_READABLE)) { - $this->calendars[$n] = $calendar->setStart($start)->setEnd($end); - $this->count_lists[$n] = $this->calendars[$n]->getListCountEvents(); - $n++; - } - } - - PageLayout::setTitle($this->getTitle($group) - . ' - ' . _('Jahresansicht')); - Navigation::activateItem("/calendar/calendar"); - - $this->last_view = 'year'; - $this->createSidebar('year'); - $this->createSidebarFilter(); - } -} diff --git a/app/controllers/calendar/instschedule.php b/app/controllers/calendar/instschedule.php deleted file mode 100644 index 452e71d..0000000 --- a/app/controllers/calendar/instschedule.php +++ /dev/null @@ -1,190 +0,0 @@ - - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - * @since 2.0 - */ -class Calendar_InstscheduleController extends AuthenticatedController -{ - /** - * this action is the main action of the schedule-controller, setting the environment for the timetable, - * accepting a comma-separated list of days. - * - * @param string $days a list of an arbitrary mix of the numbers 0-6, separated with a comma (e.g. 1,2,3,4,5 (for Monday to Friday, the default)) - */ - function index_action($days = false) - { - if ($GLOBALS['perm']->have_perm('admin')) { - $inst_mode = true; - } - $my_schedule_settings = $GLOBALS['user']->cfg->SCHEDULE_SETTINGS; - // set the days to be displayed - if ($days === false) { - if (Request::getArray('days')) { - $this->days = array_keys(Request::getArray('days')); - } else { - $this->days = CalendarScheduleModel::getDisplayedDays($my_schedule_settings['glb_days']); - } - } else { - $this->days = explode(',', $days); - } - - // try to find the correct institute-id - $institute_id = Request::option('institute_id', Context::getId()); - - if (!$institute_id) { - $institute_id = $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT; - } - - if (!$institute_id || (in_array(get_object_type($institute_id), words('inst fak')) === false)) { - throw new Exception(sprintf(_('Kann Einrichtungskalendar nicht anzeigen!' - . 'Es wurde eine ungültige Instituts-Id übergeben (%s)!', $institute_id))); - } - - // load semester-data and current semester - $this->semesters = Semester::findAllVisible(false); - - if (Request::option('semester_id')) { - $this->current_semester = Semester::find(Request::option('semester_id')); - } else { - $this->current_semester = Semester::findCurrent(); - } - - $this->entries = (array)CalendarInstscheduleModel::getInstituteEntries($GLOBALS['user']->id, - $this->current_semester, 8, 20, $institute_id, $this->days); - - Navigation::activateItem('/course/main/schedule'); - PageLayout::setHelpKeyword('Basis.TerminkalenderStundenplan'); - PageLayout::setTitle(Context::getHeaderLine().' - '._('Veranstaltungs-Stundenplan')); - - $zoom = Request::int('zoom', 0); - $this->controller = $this; - $this->calendar_view = new CalendarWeekView($this->entries, 'instschedule'); - $this->calendar_view->setHeight(40 + (20 * $zoom)); - $this->calendar_view->setRange($my_schedule_settings['glb_start_time'], $my_schedule_settings['glb_end_time']); - $this->calendar_view->groupEntries(); // if enabled, group entries with same start- and end-date - - URLHelper::addLinkParam('zoom', $zoom); - URLHelper::addLinkParam('semester_id', $this->current_semester['semester_id']); - - $style_parameters = [ - 'whole_height' => $this->calendar_view->getOverallHeight(), - 'entry_height' => $this->calendar_view->getHeight() - ]; - - $factory = new Flexi_TemplateFactory($this->dispatcher->trails_root . '/views'); - PageLayout::addStyle($factory->render('calendar/stylesheet', $style_parameters)); - - if (Request::option('printview')) { - PageLayout::addStylesheet('print.css'); - - // remove all stylesheets that are not used for printing to have a more reasonable printing preview - PageLayout::addHeadElement('script', [], "$('head link[media=screen]').remove();"); - } else { - PageLayout::addStylesheet('print.css', ['media' => 'print']); - } - - Helpbar::Get()->addPlainText(_('Information'), _('Der Stundenplan zeigt die regelmäßigen Veranstaltungen dieser Einrichtung.'), Icon::create('info')); - - $views = new ViewsWidget(); - $views->addLink(_('klein'), URLHelper::getURL('', ['zoom' => 0]))->setActive($zoom == 0); - $views->addLink(_('mittel'), URLHelper::getURL('', ['zoom' => 2]))->setActive($zoom == 2); - $views->addLink(_('groß'), URLHelper::getURL('', ['zoom' => 4]))->setActive($zoom == 4); - $views->addLink(_('extra groß'), URLHelper::getURL('', ['zoom' => 7]))->setActive($zoom == 7); - - Sidebar::Get()->addWidget($views); - $actions = new ActionsWidget(); - $actions->addLink(_('Druckansicht'), - $this->url_for('calendar/instschedule/index/'. implode(',', $this->days), - ['printview' => 'true', - 'semester_id' => $this->current_semester['semester_id']]), - Icon::create('print'), - ['target' => '_blank']); - - // Only admins should have the ability to change their schedule settings here - they have no other schedule - if ($GLOBALS['perm']->have_perm('admin')) { - $actions->addLink(_("Darstellung ändern"), - $this->url_for('calendar/schedule/settings'), - Icon::create('admin'), - ['data-dialog' => ''] - ); - - // only show this setting if we have indeed a faculty where children might exist - if (Context::get()->isFaculty()) { - if ($GLOBALS['user']->cfg->MY_INSTITUTES_INCLUDE_CHILDREN) { - $actions->addLink(_("Untergeordnete Institute ignorieren"), - $this->url_for('calendar/instschedule/include_children/0'), - Icon::create('checkbox-checked') - ); - } else { - $actions->addLink(_("Untergeordnete Institute einbeziehen"), - $this->url_for('calendar/instschedule/include_children/1'), - Icon::create('checkbox-unchecked') - ); - } - } - } - - Sidebar::Get()->addWidget($actions); - $semesterSelector = new SemesterSelectorWidget($this->url_for('calendar/instschedule'), 'semester_id', 'post'); - $semesterSelector->includeAll(false); - Sidebar::Get()->addWidget($semesterSelector); - - } - - /** - * Returns an HTML fragment of a grouped entry in the schedule of an institute. - * - * @param string $start the start time of the group, e.g. "1000" - * @param string $end the end time of the group, e.g. "1200" - * @param string $seminars the IDs of the courses - * @param string $day numeric day to show - * - * @return void - */ - function groupedentry_action($start, $end, $seminars, $day) - { - $this->response->add_header('Content-Type', 'text/html; charset=utf-8'); - - // strucutre of an id: seminar_id-cycle_id - // we do not need the cycle id here, so we trash it. - $seminar_list = []; - - foreach (explode(',', $seminars) as $seminar) { - $zw = explode('-', $seminar); - $this->seminars[$zw[0]] = Seminar::getInstance($zw[0]); - } - - $this->start = mb_substr($start, 0, 2) .':'. mb_substr($start, 2, 2); - $this->end = mb_substr($end, 0, 2) .':'. mb_substr($end, 2, 2); - - $day_names = [_("Montag"),_("Dienstag"),_("Mittwoch"), - _("Donnerstag"),_("Freitag"),_("Samstag"),_("Sonntag")]; - - $this->day = $day_names[(int)$day]; - - $this->render_template('calendar/instschedule/_entry_details'); - } - - /** - * Toggle config setting to include children in schedule for the current faculty - * - * @param int $include_childs 0 / false to exclude children 1 / true to include them - */ - function include_children_action($include_childs) - { - $GLOBALS['user']->cfg->store('MY_INSTITUTES_INCLUDE_CHILDREN', $include_childs ? 1 : 0); - - $this->redirect('calendar/instschedule/index'); - } -} diff --git a/app/controllers/calendar/schedule.php b/app/controllers/calendar/schedule.php index 6fb51c4..bdd0f51 100644 --- a/app/controllers/calendar/schedule.php +++ b/app/controllers/calendar/schedule.php @@ -144,7 +144,7 @@ class Calendar_ScheduleController extends AuthenticatedController ]; $factory = new Flexi_TemplateFactory($this->dispatcher->trails_root . '/views'); - PageLayout::addStyle($factory->render('calendar/stylesheet', $style_parameters), 'screen, print'); + PageLayout::addStyle($factory->render('calendar/schedule/stylesheet', $style_parameters), 'screen, print'); if (Request::option('printview')) { $this->calendar_view->setReadOnly(); @@ -243,11 +243,7 @@ class Calendar_ScheduleController extends AuthenticatedController $this->render_template('calendar/schedule/_entry_course'); } else if ($id) { $entry_columns = CalendarScheduleModel::getScheduleEntries($GLOBALS['user']->id, 0, 0, $id); - $entries = []; - $entry_columns = array_pop($entry_columns); - if ($entry_columns) { - $entries = $entry_columns->getEntries(); - } + $entries = array_pop($entry_columns)->getEntries(); $this->show_entry = array_pop($entries); $this->render_template('calendar/schedule/_entry_schedule'); } diff --git a/app/controllers/calendar/single.php b/app/controllers/calendar/single.php deleted file mode 100644 index af802bf..0000000 --- a/app/controllers/calendar/single.php +++ /dev/null @@ -1,571 +0,0 @@ - - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - */ - -require_once 'app/controllers/calendar/calendar.php'; - -class Calendar_SingleController extends Calendar_CalendarController -{ - public function before_filter(&$action, &$args) { - $this->base = 'calendar/single/'; - parent::before_filter($action, $args); - } - - protected function createSidebar($active = null, $calendar = null) - { - parent::createSidebar($active, $calendar); - $sidebar = Sidebar::Get(); - if ($calendar->havePermission(Calendar::PERMISSION_WRITABLE)) { - $actions = new ActionsWidget(); - $actions->addLink( - _('Termin anlegen'), - $this->url_for('calendar/single/edit'), - Icon::create('add'), - ['data-dialog' => 'size=auto'] - ); - if ($calendar->havePermission(Calendar::PERMISSION_OWN)) { - if (Config::get()->CALENDAR_GROUP_ENABLE) { - $actions->addLink( - _('Kalender freigeben'), - $this->url_for('calendar/single/manage_access'), - Icon::create('community'), - [ - 'id' => 'calendar-open-manageaccess', - 'data-dialog' => '', - 'data-dialogname' => 'manageaccess' - ] - ); - } - $actions->addLink( - _('Veranstaltungstermine'), - $this->url_for('calendar/single/seminar_events'), - Icon::create('seminar'), - ['data-dialog' => 'size=auto'] - ); - } - $sidebar->addWidget($actions); - } - if ($calendar->havePermission(Calendar::PERMISSION_OWN)) { - $export = new ExportWidget(); - $export->addLink(_('Termine exportieren'), - $this->url_for('calendar/single/export_calendar'), - Icon::create('download'), - ['data-dialog' => 'size=auto'] - )->setActive($active == 'export_calendar'); - $export->addLink( - _('Termine importieren'), - $this->url_for('calendar/single/import'), - Icon::create('upload'), - ['data-dialog' => 'size=auto'] - )->setActive($active == 'import'); - $export->addLink( - _('Kalender teilen'), - $this->url_for('calendar/single/share'), - Icon::create('group2'), - ['data-dialog' => 'size=auto'] - )->setActive($active == 'share'); - $sidebar->addWidget($export); - } - } - - public function day_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = SingleCalendar::getDayCalendar($this->range_id, - $this->atime, null, $this->restrictions); - - PageLayout::setTitle($this->getTitle($this->calendar, _('Tagesansicht'))); - - $this->last_view = 'day'; - - $this->createSidebar('day', $this->calendar); - $this->createSidebarFilter(); - } - - public function week_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $timestamp = mktime(12, 0, 0, date('n', $this->atime), date('j', $this->atime), date('Y', $this->atime)); - $monday = $timestamp - 86400 * (strftime('%u', $timestamp) - 1); - $day_count = $this->settings['type_week'] == 'SHORT' ? 5 : 7; - - $this->calendars = []; - for ($i = 0; $i < $day_count; $i++) { - $this->calendars[$i] = - SingleCalendar::getDayCalendar( - $this->range_id, - $monday + $i * 86400, - null, - $this->restrictions - ); - } - - PageLayout::setTitle($this->getTitle($this->calendars[0], _('Wochenansicht'))); - - $this->last_view = 'week'; - - $this->createSidebar('week', $this->calendars[0]); - $this->createSidebarFilter(); - } - - public function month_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $month_start = mktime(12, 0, 0, date('n', $this->atime), 1, date('Y', $this->atime)); - $month_end = mktime(12, 0, 0, date('n', $this->atime), date('t', $this->atime), date('Y', $this->atime)); - $adow = strftime('%u', $month_start) - 1; - $cor = date('n', $this->atime) == 3 ? 1 : 0; - $this->first_day = $month_start - $adow * 86400; - $this->last_day = ((42 - ($adow + date('t', $this->atime))) % 7 + $cor) * 86400 + $month_end; - $this->calendars = []; - for ($start_day = $this->first_day; $start_day <= $this->last_day; $start_day += 86400) { - $this->calendars[] = SingleCalendar::getDayCalendar($this->range_id, - $start_day, null, $this->restrictions); - } - - PageLayout::setTitle($this->getTitle($this->calendars[0], _('Monatsansicht'))); - - $this->last_view = 'month'; - $this->createSidebar('month', $this->calendars[0]); - $this->createSidebarFilter(); - } - - public function year_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $start = mktime(0, 0, 0, 1, 1, date('Y', $this->atime)); - $end = mktime(23, 59, 59, 12, 31, date('Y', $this->atime)); - $this->calendar = new SingleCalendar($this->range_id, $start, $end); - $this->count_list = $this->calendar->getListCountEvents(null, null, - $this->restrictions); - - PageLayout::setTitle($this->getTitle($this->calendar, _('Jahresansicht'))); - - $this->last_view = 'year'; - $this->createSidebar('year', $this->calendar); - $this->createSidebarFilter(); - } - - public function event_action($range_id = null, $event_id = null) - { - PageLayout::setTitle(_('Termindaten')); - - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = new SingleCalendar($this->range_id); - $this->event = $this->calendar->getEvent($event_id); - - $this->createSidebar('edit', $this->calendar); - $this->createSidebarFilter(); - } - - public function delete_action($range_id, $event_id) - { - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = new SingleCalendar($this->range_id); - if ($this->calendar->deleteEvent($event_id, true)) { - PageLayout::postSuccess(_('Der Termin wurde gelöscht.')); - } - $this->redirect($this->url_for('calendar/single/' . $this->last_view)); - } - - public function delete_recurrence_action($range_id, $event_id, $atime) - { - $this->range_id = $range_id ?: $this->range_id; - $calendar = new SingleCalendar($this->range_id); - $event = $calendar->getEvent($event_id); - if ($event->getRecurrence('rtype') != 'SINGLE') { - $exceptions = $event->getExceptions(); - $exceptions[] = $atime; - $event->setExceptions($exceptions); - if ($event->store() !== false) { - PageLayout::postSuccess(strftime(_('Termin am %x aus Serie gelöscht.'), $atime)); - } - } - $this->redirect($this->url_for('calendar/single/' . $this->last_view)); - } - - public function export_event_action($event_id, $range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $calendar = new SingleCalendar($this->range_id); - $event = $calendar->getEvent($event_id); - if (!$event->isNew()) { - $calender_writer = new CalendarWriterICalendar(); - $export = new CalendarExportFile($calender_writer); - $export->exportFromObjects($event); - $export->sendFile(); - } - $this->render_nothing(); - } - - public function export_calendar_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = new SingleCalendar($this->range_id); - - if (Request::submitted('export')) { - $calender_writer = new CalendarWriterICalendar(); - $export = new CalendarExportFile($calender_writer); - if (Request::get('event_type') == 'user') { - $types = ['CalendarEvent']; - } else if (Request::get('event_type') == 'course') { - $types = ['CourseEvent', 'CourseCancelledEvent']; - } else { - $types = ['CalendarEvent', 'CourseEvent', 'CourseCancelledEvent']; - } - if (Request::get('export_time') == 'date') { - $exstart = $this->parseDateTime(Request::get('export_start')); - $exend = $this->parseDateTime(Request::get('export_end')); - } else { - $exstart = 0; - $exend = Calendar::CALENDAR_END; - } - $export->exportFromDatabase($this->calendar->getRangeId(), $exstart, $exend, $types); - $export->sendFile(); - $this->render_nothing(); - exit; - } - - PageLayout::setTitle($this->getTitle($this->calendar, _('Termine exportieren'))); - - $this->createSidebar('export_calendar', $this->calendar); - $this->createSidebarFilter(); - } - - public function import_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = new SingleCalendar($this->range_id); - - if ($this->calendar->havePermission(Calendar::PERMISSION_OWN)) { - if (Request::submitted('import')) { - CSRFProtection::verifySecurityToken(); - $calender_parser = new CalendarParserICalendar(); - $import = new CalendarImportFile($calender_parser, $_FILES['importfile']); - if (Request::get('import_as_private_imp')) { - $import->changePublicToPrivate(); - } - $import->importIntoDatabase($range_id); - $import_count = $import->getCount(); - PageLayout::postMessage(MessageBox::success( - sprintf('Es wurden %s Termine importiert.', $import_count))); - $this->redirect($this->url_for('calendar/single/' . $this->last_view)); - } - } - PageLayout::setTitle($this->getTitle($this->calendar, _('Termine importieren'))); - $this->createSidebar('import', $this->calendar); - $this->createSidebarFilter(); - } - - public function share_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = new SingleCalendar($this->range_id); - - $this->short_id = null; - if ($this->calendar->havePermission(Calendar::PERMISSION_OWN)) { - if (Request::submitted('delete_id')) { - CSRFProtection::verifySecurityToken(); - IcalExport::deleteKey($GLOBALS['user']->id); - PageLayout::postSuccess(_('Die Adresse, unter der Ihre Termine abrufbar sind, wurde gelöscht')); - } - - if (Request::submitted('new_id')) { - CSRFProtection::verifySecurityToken(); - $this->short_id = IcalExport::setKey($GLOBALS['user']->id); - PageLayout::postSuccess(_('Eine Adresse, unter der Ihre Termine abrufbar sind, wurde erstellt.')); - } else { - $this->short_id = IcalExport::getKeyByUser($GLOBALS['user']->id); - } - - $text = ""; - if (Request::submitted('submit_email')) { - $email_reg_exp = '/^([-.0-9=?A-Z_a-z{|}~])+@([-.0-9=?A-Z_a-z{|}~])+\.[a-zA-Z]{2,6}$/i'; - if (preg_match($email_reg_exp, Request::get('email')) !== 0) { - $subject = '[' .Config::get()->UNI_NAME_CLEAN . ']' . _('Exportadresse für Ihre Termine'); - $text .= _('Diese Email wurde vom Stud.IP-System verschickt. Sie können auf diese Nachricht nicht antworten.') . "\n\n"; - $text .= _('Über diese Adresse erreichen Sie den Export für Ihre Termine:') . "\n\n"; - $text .= $GLOBALS['ABSOLUTE_URI_STUDIP'] . 'dispatch.php/ical/index/' - . IcalExport::getKeyByUser($GLOBALS['user']->id); - StudipMail::sendMessage(Request::get('email'), $subject, $text); - PageLayout::postSuccess(_('Die Adresse wurde verschickt!')); - } else { - PageLayout::postError(_('Bitte geben Sie eine gültige Email-Adresse an.')); - } - $this->short_id = IcalExport::getKeyByUser($GLOBALS['user']->id); - } - } - PageLayout::setTitle($this->getTitle($this->calendar, _('Kalender teilen oder einbetten'))); - - $this->createSidebar('share', $this->calendar); - $this->createSidebarFilter(); - } - - public function manage_access_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = new SingleCalendar($this->range_id); - - $all_calendar_users = - CalendarUser::getUsers($this->calendar->getRangeId()); - - $this->filter_groups = Statusgruppen::findByRange_id( - $this->calendar->getRangeId()); - - $this->users = []; - $this->group_filter_selected = Request::option('group_filter', 'list'); - if ($this->group_filter_selected != 'list') { - $contact_group = Statusgruppen::find($this->group_filter_selected); - $calendar_users = []; - foreach ($contact_group->members as $member) { - $calendar_users[] = new CalendarUser([$this->calendar->getRangeId(), $member->user_id]); - } - $this->calendar_users = SimpleORMapCollection::createFromArray($calendar_users); - } else { - $this->group_filter_selected = 'list'; - $this->calendar_users = $all_calendar_users; - } - - $this->own_perms = []; - foreach ($this->calendar_users as $calendar_user) { - $other_user = CalendarUser::find([$calendar_user->user_id, $this->calendar->getRangeId()]); - if ($other_user) { - $this->own_perms[$calendar_user->user_id] = $other_user->permission; - } else { - $this->own_perms[$calendar_user->user_id] = Calendar::PERMISSION_FORBIDDEN; - } - $this->users[mb_strtoupper(SimpleCollection::translitLatin1($calendar_user->nachname[0]))][] = $calendar_user; - } - - ksort($this->users); - $this->users = array_map(function ($g) { - return SimpleCollection::createFromArray($g)->orderBy('nachname, vorname'); - }, $this->users); - - $this->mps = MultiPersonSearch::get('calendar-manage_access') - ->setTitle(_('Personhinzufügen')) - ->setLinkText(_('Person hinzufügen')) - ->setDefaultSelectedUser($all_calendar_users->pluck('user_id')) - ->setJSFunctionOnSubmit('STUDIP.CalendarDialog.closeMps') - ->setExecuteURL($this->url_for('calendar/single/add_users/' . $this->calendar->getRangeId())) - ->setSearchObject(new StandardSearch('user_id')); - - PageLayout::setTitle($this->getTitle($this->calendar, _('Kalender freigeben'))); - - $this->createSidebar('manage_access', $this->calendar); - $this->createSidebarFilter(); - } - - public function add_users_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = new SingleCalendar($this->range_id); - if (Request::isXhr()) { - $added_users = Request::optionArray('added_users'); - } else { - $mps = MultiPersonSearch::load('calendar-manage_access'); - $added_users = $mps->getAddedUsers(); - $mps->clearSession(); - } - - $added = 0; - foreach ($added_users as $user_id) { - $user_to_add = User::find($user_id); - if ($user_to_add) { - $calendar_user = new CalendarUser( - [$this->calendar->getRangeId(), $user_to_add->id]); - if ($calendar_user->isNew()) { - $calendar_user->permission = Calendar::PERMISSION_READABLE; - $added += $calendar_user->store(); - } - } - } - if ($added) { - PageLayout::postSuccess(sprintf( - ngettext( - 'Eine Person wurde mit der Berechtigung zum Lesen des Kalenders hinzugefügt.', - '%s Personen wurden mit der Berechtigung zum Lesen des Kalenders hinzugefügt.', - $added - ), - $added - )); - } - - if (Request::isXhr()) { - $this->response->add_header('X-Dialog-Close', 1); - $this->response->set_status(200); - $this->render_nothing(); - } else { - $this->redirect($this->url_for('calendar/single/manage_access/' . $this->calendar->getRangeId())); - } - } - - public function remove_user_action($range_id = null, $user_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $user_id = $user_id ?: Request::option('user_id'); - $this->calendar = new SingleCalendar($this->range_id); - $calendar_user = new CalendarUser([$this->calendar->getRangeId(), $user_id]); - if (!$calendar_user->isNew()) { - $name = $calendar_user->user->getFullname(); - $calendar_user->delete(); - } - if (Request::isXhr()) { - $this->response->set_status(200); - $this->render_nothing(); - } else { - PageLayout::postSuccess(sprintf(_('Person %s wurde entfernt.', htmlReady($name)))); - $this->redirect($this->url_for('calendar/single/manage_access/' . $this->calendar->getRangeId())); - } - } - - public function store_permissions_action($range_id = null) - { - $this->range_id = $range_id ?: $this->range_id; - $this->calendar = new SingleCalendar($this->range_id); - - $deleted = 0; - $read = 0; - $write = 0; - $submitted_permissions = Request::intArray('perm'); - foreach ($submitted_permissions as $user_id => $new_perm) { - $calendar_user = new CalendarUser([$this->calendar->getRangeId(), $user_id]); - if (!$calendar_user->isNew() && $new_perm == 1) { - $deleted += $calendar_user->delete(); - $new_perm = 0; - } - if ($new_perm >= Calendar::PERMISSION_READABLE - && $calendar_user->permission != $new_perm) { - $calendar_user->permission = $new_perm; - if ($calendar_user->store()) { - if ($new_perm == Calendar::PERMISSION_READABLE) { - $read++; - } else { - $write++; - } - } - } - } - $sum = $deleted + $read + $write; - if ($sum) { - if ($deleted) { - $details[] = sprintf(ngettext('Einer Person wurde die Berechtigungen entzogen.', - '%s Personen wurden die Berechtigungen entzogen.', $deleted), $deleted); - } - if ($read) { - $details[] = sprintf(ngettext('Eine Person wurde auf leseberechtigt gesetzt.', - '%s Personen wurden auf leseberechtigt gesetzt.', $read), $read); - } - if ($write) { - $details[] = sprintf(ngettext('Eine Person wurde auf schreibberechtigt gesetzt.', - '%s Personen wurden auf schreibberechtigt gesetzt.', $write), $write); - } - PageLayout::postSuccess(sprintf( - ngettext('Die Berechtigungen von einer Person wurde geändert.', - 'Die Berechtigungen von %s Personen wurden geändert.', - $sum), $sum), - $details - ); - // no message if the group was changed - } else if (!Request::submitted('calendar_group_submit')) { - PageLayout::postSuccess(_('Es wurden keine Berechtigungen geändert.')); - } - $this->redirect($this->url_for( - 'calendar/single/manage_access/' . $this->calendar->getRangeId(), - ['group_filter' => Request::option('group_filter', 'list')]) - ); - } - - public function seminar_events_action($order_by = null, $order = 'asc') - { - $config_sem = $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE; - if (!Config::get()->MY_COURSES_ENABLE_ALL_SEMESTERS && $config_sem == 'all') { - $config_sem = 'future'; - } - $this->sem_data = Semester::findAllVisible(); - - $sem = $config_sem ?: Config::get()->MY_COURSES_DEFAULT_CYCLE; - if (Request::option('sem_select')) { - $sem = Request::get('sem_select', $sem); - } - if (!in_array($sem, words('future all last current')) && isset($sem)) { - Request::set('sem_select', $sem); - } - $this->group_field = 'sem_number'; - $this->order_by = $order_by; - $this->config_sem_number = Config::get()->IMPORTANT_SEMNUMBER; - // Needed parameters for selecting courses - $params = [ - 'group_field' => $this->group_field, - 'order_by' => $order_by, - 'order' => $order, - 'studygroups_enabled' => false, - 'deputies_enabled' => false - ]; - - $this->sem_courses = MyRealmModel::getPreparedCourses($sem, $params); - $semesters = new SimpleCollection(Semester::getAll()); - $this->sem = $sem; - $this->semesters = $semesters->orderBy('beginn desc'); - - $this->bind_calendar = SimpleCollection::createFromArray( - CourseMember::findBySQL('user_id = ? AND bind_calendar = 1', [$GLOBALS['user']->id]) - )->pluck('seminar_id'); - - } - - public function store_selected_sem_action() - { - CSRFProtection::verifySecurityToken(); - if (Request::submitted('store')) { - $selected_sems = Request::intArray('selected_sem'); - $courses = SimpleORMapCollection::createFromArray( - CourseMember::findBySQL('user_id = ? AND Seminar_id IN (?)', - [$GLOBALS['user']->id, array_keys($selected_sems)])); - $courses->each(function ($a) use ($selected_sems) { - $a->bind_calendar = $selected_sems[$a->seminar_id]; - $a->store(); - }); - PageLayout::postSuccess(_('Die Auswahl der Veranstaltungen wurde gespeichert.')); - } - $this->redirect($this->url_for('calendar/single/' . $this->last_view)); - } - - /** - * Retrieve the title of the calendar depending on calendar owner (range). - * - * @param SingleCalendar $calendar The calendar - * @param string $title_end Additional text - * @return string The complete title for the headline - */ - protected function getTitle(SingleCalendar $calendar, $title_end) - { - $status = ''; - if ($calendar->getRangeId() == $GLOBALS['user']->id) { - $title = _('Mein persönlicher Terminkalender'); - } else { - if ($calendar->getRange() == Calendar::RANGE_USER) { - $title = sprintf(_('Terminkalender von %s'), - $calendar->range_object->getFullname()); - } else { - $title = Context::getHeaderLine(); - } - if ($calendar->havePermission(Calendar::PERMISSION_WRITABLE)) { - $status = ' (' . _('schreibberechtigt') . ')'; - } else { - $status = ' (' . _('leseberechtigt') . ')'; - } - } - return $title . ' - ' . $title_end . $status ; - } -} diff --git a/app/controllers/contact.php b/app/controllers/contact.php index 0225149..7dd2b05 100644 --- a/app/controllers/contact.php +++ b/app/controllers/contact.php @@ -20,15 +20,15 @@ class ContactController extends AuthenticatedController parent::before_filter($action, $args); // Load statusgroups - $this->groups = SimpleCollection::createFromArray(Statusgruppen::findByRange_id(User::findCurrent()->id)); + $this->groups = SimpleCollection::createFromArray(ContactGroup::findByOwner_id(User::findCurrent()->id)); // Load requested group if (!empty($args[0])) { - $this->group = $this->groups->findOneBy('statusgruppe_id', $args[0]); + $this->group = $this->groups->findOneBy('id', $args[0]); //Check for cheaters - if ($this->group->range_id != User::findCurrent()->id) { - throw new AccessDeniedException; + if ($this->group->owner_id !== User::findCurrent()->id) { + throw new AccessDeniedException(); } } @@ -43,16 +43,17 @@ class ContactController extends AuthenticatedController // Check if we need to add contacts $mps = MultiPersonSearch::load('contacts'); $imported = 0; - foreach ($mps->getAddedUsers() as $userId) { - $user_to_add = User::find($userId); + foreach ($mps->getAddedUsers() as $user_id) { + $user_to_add = User::find($user_id); if ($user_to_add) { $new_contact = [ 'owner_id' => User::findCurrent()->id, - 'user_id' => $user_to_add->id]; + 'user_id' => $user_to_add->id, + ]; if ($filter && $this->group) { - $new_contact['group_assignments'][] = [ - 'statusgruppe_id' => $this->group->id, - 'user_id' => $user_to_add->id + $new_contact['groups'][] = [ + 'group_id' => $this->group->id, + 'user_id' => $user_to_add->id, ]; } $imported += (bool)Contact::import($new_contact)->store(); @@ -74,7 +75,7 @@ class ContactController extends AuthenticatedController if ($filter) { $selected = $this->group; - $contacts = SimpleCollection::createFromArray(User::findMany($selected->members->pluck('user_id'))); + $contacts = SimpleCollection::createFromArray(User::findMany($selected->items->pluck('user_id'))); } else { $selected = false; $contacts = User::findCurrent()->contacts; @@ -125,7 +126,7 @@ class ContactController extends AuthenticatedController $contact = Contact::find([User::findCurrent()->id, User::findByUsername($contact_username)->id]); if ($contact) { if ($group) { - $contact->group_assignments->unsetBy('statusgruppe_id', $group); + $contact->groups->unsetBy('group_id', $group); if ($contact->store()) { $removed_group_number++; } @@ -144,7 +145,7 @@ class ContactController extends AuthenticatedController $contact = Contact::find([User::findCurrent()->id, User::findByUsername(Request::username('user'))->id]); if ($contact) { if ($group) { - $contact->group_assignments->unsetBy('statusgruppe_id', $group); + $contact->group_assignments->unsetBy('group_id', $group); if ($contact->store()) { PageLayout::postSuccess(_('Der Kontakt wurde aus der Gruppe entfernt.')); } @@ -161,8 +162,8 @@ class ContactController extends AuthenticatedController public function editGroup_action() { if (!$this->group) { - $this->group = new Statusgruppen(); - $this->group->range_id = User::findCurrent()->id; + $this->group = new ContactGroup(); + $this->group->owner_id = User::findCurrent()->id; } if (Request::submitted('store')) { CSRFProtection::verifyRequest(); diff --git a/app/controllers/ical.php b/app/controllers/ical.php index 4ecdc13..44ff2a1 100644 --- a/app/controllers/ical.php +++ b/app/controllers/ical.php @@ -51,17 +51,14 @@ class iCalController extends StudipController $GLOBALS['user'] = new Seminar_User($user_id); $GLOBALS['perm'] = new Seminar_Perm(); - $extype = 'ALL_EVENTS'; - $calender_writer = new CalendarWriterICalendar(); - $export = new CalendarExport($calender_writer); - $export->exportFromDatabase($user_id, strtotime('-4 week'), 2114377200, 'ALL_EVENTS'); - - if ($GLOBALS['_calendar_error']->getMaxStatus(ErrorHandler::ERROR_CRITICAL)) { - $this->set_status(500); - $this->render_nothing(); - return; - } - $content = join($export->getExport()); + $end = DateTime::createFromFormat('U', '2114377200'); + $start = new DateTime(); + $start->modify('-4 week'); + $ical_export = new ICalendarExport(); + $ical = $ical_export->exportCalendarDates($user_id, $start, $end) + . $ical_export->exportCourseDates($user_id, $start, $end) + . $ical_export->exportCourseExDates($user_id, $start, $end); + $content = $ical_export->writeHeader() . $ical . $ical_export->writeFooter(); if (mb_stripos($_SERVER['HTTP_USER_AGENT'], 'google-calendar') !== false) { $content = str_replace(['CLASS:PRIVATE','CLASS:CONFIDENTIAL'], 'CLASS:PUBLIC', $content); } diff --git a/app/controllers/institute/overview.php b/app/controllers/institute/overview.php index f511bd4..66d55e1 100644 --- a/app/controllers/institute/overview.php +++ b/app/controllers/institute/overview.php @@ -144,10 +144,6 @@ class Institute_OverviewController extends AuthenticatedController $response = $this->relay('questionnaire/widget/' . $this->institute_id . '/institute'); $this->questionnaires = $response->body; } - - // Fetch dates - $response = $this->relay("calendar/contentbox/display/$this->institute_id/1210000"); - $this->dates = $response->body; } } diff --git a/app/controllers/institute/schedule.php b/app/controllers/institute/schedule.php new file mode 100644 index 0000000..1c5d091 --- /dev/null +++ b/app/controllers/institute/schedule.php @@ -0,0 +1,179 @@ +have_studip_perm('autor', Context::getId())) { + throw new AccessDeniedException(); + } + } + + public function index_action($institute_id) + { + PageLayout::setTitle(_('Veranstaltungs-Stundenplan')); + + if (Navigation::hasItem('/course/main/schedule')) { + Navigation::activateItem('/course/main/schedule'); + } + + $semester = null; + if (Request::submitted('semester_id')) { + $semester = Semester::find(Request::option('semester_id')); + } else { + $semester = Semester::findCurrent(); + } + + $extra_params = []; + if ($semester) { + $extra_params['semester_id'] = $semester->id; + } + + $sidebar = Sidebar::get(); + $semester_widget = new SemesterSelectorWidget($this->url_for('institute/schedule/index/' . $institute_id)); + if ($semester) { + $semester_widget->setSelection($semester->id); + } + $sidebar->addWidget($semester_widget); + + $calendar_settings = $GLOBALS['user']->cfg->CALENDAR_SETTINGS ?? []; + $week_slot_duration = \Studip\Calendar\Helper::getCalendarSlotDuration('week'); + + $this->fullcalendar = \Studip\Fullcalendar::create( + _('Veranstaltungs-Stundenplan'), + [ + 'minTime' => '08:00', + 'maxTime' => '20:00', + 'allDaySlot' => false, + 'header' => [ + 'left' => '', + 'right' => '' + ], + 'views' => [ + 'timeGridWeek' => [ + 'columnHeaderFormat' => ['weekday' => 'long'], + 'weekends' => $calendar_settings['type_week'] === 'LONG', + 'slotDuration' => $week_slot_duration + ] + ], + 'defaultView' => 'timeGridWeek', + 'defaultDate' => date('Y-m-d'), + 'timeGridEventMinHeight' => 20, + 'eventSources' => [ + [ + 'url' => $this->url_for('institute/schedule/data/' . $institute_id), + 'method' => 'GET', + 'extraParams' => $extra_params + ] + ] + ] + ); + } + + + public function data_action($institute_id) + { + //Fullcalendar sets the week time range in which to put the course dates + //of the semester. Therefore, start and end are handled in here. + $begin = Request::getDateTime('start', \DateTime::RFC3339); + $end = Request::getDateTime('end', \DateTime::RFC3339); + if (!($begin instanceof DateTime) || !($end instanceof DateTime)) { + //No time range specified. + throw new InvalidArgumentException('Invalid parameters!'); + } + + $semester_id = Request::option('semester_id'); + $semester = Semester::find($semester_id); + if (!$semester) { + $this->render_json([]); + return; + } + + //Get all regular course dates for that semester: + $cycle_dates = SeminarCycleDate::findBySql( + 'JOIN `termine` USING (`metadate_id`) + JOIN `seminare` USING (`seminar_id`) + JOIN `seminar_inst` USING (`seminar_id`) + WHERE `seminar_inst`.`institut_id` = :institute_id + AND ( + `termine`.`date` BETWEEN :begin AND :end + OR `termine`.`end_time` BETWEEN :begin AND :end + ) + GROUP BY `metadate_id`', + [ + 'institute_id' => $institute_id, + 'begin' => $semester->beginn, + 'end' => $semester->ende + ] + ); + + if (!$cycle_dates) { + $this->render_json([]); + return; + } + + foreach ($cycle_dates as $cycle_date) { + //Calculate a fake begin and end that lies in the week + //fullcalendar has specified. + $fake_begin = clone $begin; + $fake_end = clone $begin; + if ($cycle_date->weekday > 1) { + $fake_begin = $fake_begin->add(new DateInterval('P' . ($cycle_date->weekday - 1) . 'D')); + $fake_end = $fake_end->add(new DateInterval('P' . ($cycle_date->weekday - 1) . 'D')); + } + $start_time_parts = explode(':', $cycle_date->start_time); + $end_time_parts = explode(':', $cycle_date->end_time); + $fake_begin->setTime( + (int) $start_time_parts[0], + (int) $start_time_parts[1], + (int) $start_time_parts[2] + ); + $fake_end->setTime( + (int) $end_time_parts[0], + (int) $end_time_parts[1], + (int) $end_time_parts[2] + ); + + //Get the course colour: + $course_membership = CourseMember::findOneBySQL( + 'seminar_id = :course_id AND user_id = :user_id', + [ + 'course_id' => $cycle_date->seminar_id, + 'user_id' => $GLOBALS['user']->id + ] + ); + $event_classes = []; + if ($course_membership) { + $event_classes[] = sprintf('course-color-%u', $course_membership->gruppe); + } + + $event = new \Studip\Calendar\EventData( + $fake_begin, + $fake_end, + $cycle_date->course->getFullName(), + $event_classes, + '', + '', + false, + 'SeminarCycleDate', + $cycle_date->id, + '', + '', + 'course', + $cycle_date->seminar_id, + [ + 'show' => $this->url_for('course/details', ['cid' => $cycle_date->seminar_id, 'link_to_course' => '1']) + ] + ); + + $result[] = $event->toFullcalendarEvent(); + } + + $this->render_json($result); + } +} diff --git a/app/controllers/settings/calendar.php b/app/controllers/settings/calendar.php index 5704246..b2f4f1c 100644 --- a/app/controllers/settings/calendar.php +++ b/app/controllers/settings/calendar.php @@ -34,7 +34,7 @@ class Settings_CalendarController extends Settings_SettingsController parent::before_filter($action, $args); PageLayout::setHelpKeyword('Basis.MyStudIPTerminkalender'); - PageLayout::setTitle(_('Einstellungen des Terminkalenders anpassen')); + PageLayout::setTitle(_('Einstellungen des Kalenders anpassen')); Navigation::activateItem('/profile/settings/calendar_new'); } @@ -71,6 +71,11 @@ class Settings_CalendarController extends Settings_SettingsController ]); PageLayout::postSuccess(_('Ihre Einstellungen wurden gespeichert')); - $this->redirect('settings/calendar'); + if (Request::isDialog()) { + $this->response->add_header('X-Dialog-Close', '1'); + $this->render_nothing(); + } else { + $this->redirect('settings/calendar'); + } } } diff --git a/app/views/calendar/calendar/add_courses.php b/app/views/calendar/calendar/add_courses.php new file mode 100644 index 0000000..cf3d282 --- /dev/null +++ b/app/views/calendar/calendar/add_courses.php @@ -0,0 +1,15 @@ +
+ +
+ + +
+
+ + url_for('calendar/calendar')) ?> +
+
diff --git a/app/views/calendar/calendar/course.php b/app/views/calendar/calendar/course.php new file mode 100644 index 0000000..23602db --- /dev/null +++ b/app/views/calendar/calendar/course.php @@ -0,0 +1 @@ + diff --git a/app/views/calendar/calendar/export.php b/app/views/calendar/calendar/export.php new file mode 100644 index 0000000..3fa302e --- /dev/null +++ b/app/views/calendar/calendar/export.php @@ -0,0 +1,45 @@ + +
+ +
+ + + + +
+
+ + +
+
diff --git a/app/views/calendar/calendar/import.php b/app/views/calendar/calendar/import.php new file mode 100644 index 0000000..d91025a --- /dev/null +++ b/app/views/calendar/calendar/import.php @@ -0,0 +1,30 @@ + +
+ + +
+ + + + + +
+
+ + +
+
diff --git a/app/views/calendar/calendar/index.php b/app/views/calendar/calendar/index.php new file mode 100644 index 0000000..23602db --- /dev/null +++ b/app/views/calendar/calendar/index.php @@ -0,0 +1 @@ + diff --git a/app/views/calendar/calendar/publish.php b/app/views/calendar/calendar/publish.php new file mode 100644 index 0000000..71901b5 --- /dev/null +++ b/app/views/calendar/calendar/publish.php @@ -0,0 +1,55 @@ + +
+ + + +
+ + + + + +
+ + + +
+ +
+ +
+ + + _('Abschicken')]) ?> +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + + + _('Achtung: Die alte Adresse wird damit ungültig!') + ]) ?> + + _('Ein Zugriff auf Ihre Termine über diese Adresse ist dann nicht mehr möglich!') + ]) ?> + + + + url_for('calendar/calendar')) ?> + +
+
diff --git a/app/views/calendar/calendar/share.php b/app/views/calendar/calendar/share.php new file mode 100644 index 0000000..69663d2 --- /dev/null +++ b/app/views/calendar/calendar/share.php @@ -0,0 +1,13 @@ +
+ +
+ +
+
+ +
+
diff --git a/app/views/calendar/contentbox/_termin.php b/app/views/calendar/contentbox/_termin.php index 3c3fcff..ded7d0b 100644 --- a/app/views/calendar/contentbox/_termin.php +++ b/app/views/calendar/contentbox/_termin.php @@ -1,53 +1,88 @@ -
+

- - asImg(['class' => 'text-bottom']) ?> - + + asImg(['class' => 'text-bottom']) ?> + getObjectId()] ?? $termin->getTitle()) ?>

- - -

- - -

- asImg(20, ['class' => "text-bottom"]) ?> - -

-
- -
- - + topics->toArray('title description'); + } + $description = ''; + if ($termin instanceof CourseExDate) { + $description = $termin->content; + } elseif ($termin instanceof CourseDate && isset($termin->cycle)) { + $description = $termin->cycle->description; + } else { + $description = $termin->getDescription(); + } + ?> + 0) : ?> +

+ + +

+ asImg(20, ['class' => "text-bottom"]) ?> + +

+
+ +
+ +
    - $info): ?> - -
  • - - - : - - - -
  • - - + getAdditionalDescriptions() as $type => $info) : ?> + +
  • + + + : + + + +
  • + +
+ getObjectClass(), [CourseDate::class, CourseExDate::class])) : ?> +
+ + asImg(['class'=> 'text-bottom']) ?> + + +
+
diff --git a/app/views/calendar/contentbox/display.php b/app/views/calendar/contentbox/display.php index 41498f4..c0387fd 100644 --- a/app/views/calendar/contentbox/display.php +++ b/app/views/calendar/contentbox/display.php @@ -1,36 +1,35 @@ - -
-
-

- asImg() ?> - -

- -
- - - render_partial('calendar/contentbox/_termin.php', ['termin' => $termin]); ?> - - -
- - - - - -
- -
- +
+ +
+ +
+ diff --git a/app/views/calendar/date/_add_edit_form.php b/app/views/calendar/date/_add_edit_form.php new file mode 100644 index 0000000..7c922e4 --- /dev/null +++ b/app/views/calendar/date/_add_edit_form.php @@ -0,0 +1,154 @@ +
+ + + + + + + + + + + + + +
+
+

+ asImg(['class' => 'text-bottom validation_notes_icon']) ?> + +

+
+
+ +
+ +
+
+ +
+ +
    + $error) : ?> +
  • :
  • + +
+
+ +
+ +
+ + +
+ + +
+ + + + + + +
+
+ + getRepetitionInputHtml('repetition') ?> +
+
+ + +
+ CALENDAR_GROUP_ENABLE && $user_quick_search_type) : ?> +
+ + +
+ + + + + + + + +
+ isNew()) : ?> + + + + + isNew()) : ?> + url_for('calendar/date/delete/' . $date->id), + ['data-dialog' => 'reload-on-close'] + ) ?> + + url_for('calendar/calendar')) ?> +
+
diff --git a/app/views/calendar/date/add.php b/app/views/calendar/date/add.php new file mode 100644 index 0000000..29c94c2 --- /dev/null +++ b/app/views/calendar/date/add.php @@ -0,0 +1,4 @@ +render_partial('calendar/date/_add_edit_form', [ + 'action_link' => $controller->link_for('calendar/date/add'), + 'event' => null +]) ?> diff --git a/app/views/calendar/date/delete.php b/app/views/calendar/date/delete.php new file mode 100644 index 0000000..ccb9829 --- /dev/null +++ b/app/views/calendar/date/delete.php @@ -0,0 +1,51 @@ +
+ + + +
+ + + +
+ + + +
+ +
+
+
+ begin)) ?> + - + end)) ?> +
+
+
title) ?>
+ description) : ?> +
+
description) ?>
+ +
+
getAccessAsString()) ?>
+ repetition_type) : ?> +
+
getRepetitionAsString()) ?>
+ +
+
+
+ +
+
diff --git a/app/views/calendar/date/edit.php b/app/views/calendar/date/edit.php new file mode 100644 index 0000000..6090dfc --- /dev/null +++ b/app/views/calendar/date/edit.php @@ -0,0 +1,4 @@ +render_partial('calendar/date/_add_edit_form', [ + 'action_link' => $controller->link_for('calendar/date/edit/' . $date->id), + 'date' => $date +]) ?> diff --git a/app/views/calendar/date/index.php b/app/views/calendar/date/index.php new file mode 100644 index 0000000..faec7c7 --- /dev/null +++ b/app/views/calendar/date/index.php @@ -0,0 +1,135 @@ + + +
+

+
+
+ +
+ + + + + + + + + + + + +
+
+ +
+
+
+
+ +description) : ?> +
+

+
description) ?>
+
+ +
+

+
+
+
+
begin) ?> - end) ?>
+
+
getCategoryAsString()) ?>
+
+
getAccessAsString()) ?>
+ repetition_type) : ?> +
+
getRepetitionAsString()) ?>
+ + author && $date->editor + && ( + ($date->author_id !== User::findCurrent()->id) + || ($date->editor_id !== User::findCurrent()->id) + ) + ) : ?> +
+
+ author->id === $date->editor->id) : ?> + mkdate === $date->chdate) : ?> + author->getFullName()) + ) ?> + + author->getFullName()) + ) ?> + + + author->getFullName()), + htmlReady($date->editor->getFullName()) + ) ?> + +
+ +
+
+
+ +
+

+
+ + + + + + + + + +
getRangeName()) ?>getParticipationAsString()) ?>
+
+
+ +
+ isWritable(User::findCurrent()->id) && $all_assignments_writable) : ?> + + url_for('calendar/date/edit/' . $date->id, array_merge($button_params, ['return_path' => '/calendar/calendar'])), + ['data-dialog' => 'size=auto;reload-on-close'] + ) ?> + url_for('calendar/date/delete/' . $date->id, $button_params), + ['data-dialog' => 'reload-on-close'] + ) ?> + +
diff --git a/app/views/calendar/date/move.php b/app/views/calendar/date/move.php new file mode 100644 index 0000000..3ea2b89 --- /dev/null +++ b/app/views/calendar/date/move.php @@ -0,0 +1,22 @@ + +
+ + + + + + +
+ +
+
diff --git a/app/views/calendar/group/_attendees.php b/app/views/calendar/group/_attendees.php deleted file mode 100644 index 3fbe27e..0000000 --- a/app/views/calendar/group/_attendees.php +++ /dev/null @@ -1,82 +0,0 @@ - diff --git a/app/views/calendar/group/_tooltip.php b/app/views/calendar/group/_tooltip.php deleted file mode 100644 index aa8a9b1..0000000 --- a/app/views/calendar/group/_tooltip.php +++ /dev/null @@ -1,27 +0,0 @@ -events ?> - -
-

range_object->getFullname('no_title')) ?>

- -
- getStart()) == date('Ymd', $event->getEnd())) : ?> - isDayEvent()) : ?> - getStart()) . '(' . _('ganztägig') . ')' ?> - - getStart()) . strftime(' - %X', $event->getEnd()) ?> - - - isDayEvent()) : ?> - getStart()) . strftime(' - %x', $event->getEnd()) . '(' . _('ganztägig') . ')' ?> - - getStart()) . strftime(' - %x %X', $event->getEnd()) ?> - - -
-
- getTitle()) ?> -
-
- -
- diff --git a/app/views/calendar/group/_tooltip_year.php b/app/views/calendar/group/_tooltip_year.php deleted file mode 100644 index edd81ca..0000000 --- a/app/views/calendar/group/_tooltip_year.php +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - ' - . sprintf(ngettext('%s hat 1 Termin', '%s hat %s Termine', - count($count_lists[$i][$list_day])), - $calendar->range_object->getFullname('no_title'), - count($count_lists[$i][$list_day])) - . ''; - ?> - - - - -
- -
- diff --git a/app/views/calendar/group/day.php b/app/views/calendar/group/day.php deleted file mode 100644 index a452320..0000000 --- a/app/views/calendar/group/day.php +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - - - - -
- -
- -
-
- -
-
- - - - - - - - - class="precol1w" style="text-align: center;"> - - - - - - - - - adapt_events($start, $end, $settings['step_day_group']); ?> - - - - events[$adapted['day_map'][$i]]; - endfor; ?> - havePermission(Calendar::PERMISSION_WRITABLE)) : ?> - - getStart(); $i < $end + $calendar->getStart(); $i += $step_day) : - if (!($i % 3600)) { - $k++; - } - $js_events = []; - ?> - - getStart() <= $i && $adapted['events'][$j]->getEnd() > $i) || ($adapted['events'][$j]->getStart() > $i && $adapted['events'][$j]->getStart() < $i + $step_day)) : - $js_events[] = $calendar->events[$adapted['map'][$j]]; - endif; ?> - - havePermission(Calendar::PERMISSION_WRITABLE)) : ?> - - - - - - - - - - class="precol1w" style="text-align: center;"> - - - - - - - -
- - - - asImg() ?> - -
- - havePermission(Calendar::PERMISSION_OWN) ? _('Eigener Kalender') : get_fullname($calendar->getRangeId(), 'no_title_short'))) ?> - - style="width: %; text-align: right;" class="calendar-day-edit"> - render_partial('calendar/group/_tooltip', ['calendar' => $calendar, 'events' => $js_events]) ?> - + - - style="width: %;" class=""> - render_partial('calendar/group/_tooltip', ['calendar' => $calendar, 'events' => $js_events]) ?> - - style="width: %; text-align: right;" class="calendar-day-edit"> - render_partial('calendar/group/_tooltip', ['calendar' => $calendar, 'events' => $js_events]) ?> - + - - style="width: %; text-align: right;" class=""> - render_partial('calendar/group/_tooltip', ['calendar' => $calendar, 'events' => $js_events]) ?> - -
- - asImg() ?> - -
-
-
diff --git a/app/views/calendar/group/month.php b/app/views/calendar/group/month.php deleted file mode 100644 index 67212dd..0000000 --- a/app/views/calendar/group/month.php +++ /dev/null @@ -1,110 +0,0 @@ -view; ?> - - - - - - - - - - - - - - - - - 0) || ($j - $aday > 6)) { - $class_cell = 'lightmonth'; - $class_day = 'light'; - } elseif (date('Ymd', $i) == date('Ymd')) { - $class_cell = 'celltoday'; - } else { - $class_cell = 'month'; - } - $hday = holiday($i); - - if ($j % 7 == 0) { - ?> - - - - - - - - - -
- - - - - - - events) ?> - -
- range_object->getFullname('no_title')) ?> - render_partial('calendar/group/_tooltip', ['calendar' => $user_calendars[$j]]) ?> -
- - - - - - - -
- -
- getStart())) .' '. date('Y', $calendars[0][15]->getStart()); ?> -
- -
- - - -
- - - - - -
- - - events) ?> - -
- range_object->getFullname('no_title')) ?> - render_partial('calendar/group/_tooltip', ['calendar' => $user_calendars[$j]]) ?> -
- - -
- -
diff --git a/app/views/calendar/group/week.php b/app/views/calendar/group/week.php deleted file mode 100644 index cdac0b5..0000000 --- a/app/views/calendar/group/week.php +++ /dev/null @@ -1,144 +0,0 @@ - - - - - -
- -
- getStart()), strftime("%x", $calendars[0][0]->getStart()), strftime("%x", $calendars[0][$wlength]->getEnd())) ?> -
- -
-
- - - - - getStart(); ?> - - - - - - - - - - getStart() + $start; $i < $day->getStart() + $end; $i += 3600 * ceil($settings['step_week_group'] / 3600)) : ?> - - - - - - - - - - - - getStart()) == date('Ymd')) { - $css_class = 'celltoday'; - } else { - if ($k % 2) { - $css_class = 'lightmonth'; - } else { - $css_class = 'month'; - } - } - $k++; - $adapted = $day->adapt_events($start, $end, $settings['step_week_group']); - - // display day events - $js_events = []; ?> - - events[$adapted['day_map'][$i]]; ?> - - havePermission(Calendar::PERMISSION_WRITABLE)) : ?> - - - getStart(); $i < $end + $day->getStart(); $i += $settings['step_week_group']) : ?> - - - getStart() <= $i && $adapted['events'][$j]->getEnd() > $i) || ($adapted['events'][$j]->getStart() > $i && $adapted['events'][$j]->getStart() < $i + $settings['step_week_group'])) : ?> - events[$adapted['map'][$j]]; ?> - - - havePermission(Calendar::PERMISSION_WRITABLE)) : ?> - - - - - - - - - - - - getStart() + $start; $i < $day->getStart() + $end; $i += 3600 * ceil($settings['step_week_group'] / 3600)) : ?> - - - - - -
- - - -
- - - - asImg() ?> - - - - - -
- - havePermission(Calendar::PERMISSION_OWN) ? _('Eigener Kalender') : get_fullname($user_calendar[0]->getRangeId(), 'no_title_short')) ?> - - style="text-align: right; width: %" class="calendar-day-edit "> - render_partial('calendar/group/_tooltip', ['calendar' => $day, 'events' => $js_events]) ?> - + - - style="text-align: right; width: %" class=""> - render_partial('calendar/group/_tooltip', ['calendar' => $day, 'events' => $js_events]) ?> - - style="text-align: right; width: %" class="calendar-day-edit "> - render_partial('calendar/group/_tooltip', ['calendar' => $day, 'events' => $js_events]) ?> - + - - style="text-align: right; width: %" class=""> - render_partial('calendar/group/_tooltip', ['calendar' => $day, 'events' => $js_events]) ?> - -
- - asImg() ?> - - - - - -
-
diff --git a/app/views/calendar/group/year.php b/app/views/calendar/group/year.php deleted file mode 100644 index b07f749..0000000 --- a/app/views/calendar/group/year.php +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - - - - - - - -
- -
- getStart()) ?> -
- -
- - getStart())) { - $days_per_month[2]++; - } - ?> - - - - - - - - - - - getStart())); ?> - - - - height="25"> - - > - - render_partial('calendar/group/_tooltip_year', - ['aday' => $aday, 'calendars' => $calendars, 'count_lists' => $count_lists]) ?> - -
- - - -
- - > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - style="text-align: right;" data-tooltip=""> - $event_count_txt])->asImg(16, ["alt" => $event_count_txt]); ?> - - - -
- -
- - - -
- - - diff --git a/app/views/calendar/instschedule/_entry_details.php b/app/views/calendar/instschedule/_entry_details.php deleted file mode 100644 index 7fd4a38..0000000 --- a/app/views/calendar/instschedule/_entry_details.php +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -
- -
getNumber()) ?> - - asImg() ?> - getName()) ?> - -
-
diff --git a/app/views/calendar/instschedule/index.php b/app/views/calendar/instschedule/index.php deleted file mode 100644 index bcfa72e..0000000 --- a/app/views/calendar/instschedule/index.php +++ /dev/null @@ -1,15 +0,0 @@ -

- - -

- - -
- render_partial('calendar/schedule/_dialog', [ - 'content_for_layout' => $this->render_partial('calendar/schedule/settings', [ - 'settings' => $my_schedule_settings]), - 'title' => _('Darstellung ändern') - ]) ?> - - -render() ?> diff --git a/app/views/calendar/schedule/stylesheet.php b/app/views/calendar/schedule/stylesheet.php new file mode 100644 index 0000000..aaf7334 --- /dev/null +++ b/app/views/calendar/schedule/stylesheet.php @@ -0,0 +1,13 @@ +div.schedule_day { + height: px; +} + +div.schedule_marker { + height: px; + line-height: px; + margin-bottom: px; +} + +div.schedule_hours { + height: px; +} diff --git a/app/views/calendar/single/_attendees.php b/app/views/calendar/single/_attendees.php deleted file mode 100644 index c2ee568..0000000 --- a/app/views/calendar/single/_attendees.php +++ /dev/null @@ -1,31 +0,0 @@ - -attendees->findOneBy('range_id', - $calendar->getRangeId(), '!=') ?> - -attendees->findOneBy('range_id', $GLOBALS['user']->id) ?> - - - - _('Abwartend'), - CalendarEvent::PARTSTAT_ACCEPTED => _('Angenommen'), - CalendarEvent::PARTSTAT_DECLINED => _('Abgelehnt'), - CalendarEvent::PARTSTAT_DELEGATED => _('Angenommen (keine Teilnahme)'), - CalendarEvent::PARTSTAT_NEEDS_ACTION => ''] ?> -
- - attendees->map( - function ($att) use ($event, $group_status) { - $profil_link = ObjectdisplayHelper::link($att->user); - if ($event->havePermission(Event::PERMISSION_OWN, $att->user->getId())) { - $profil_link .= ' (' . _('Organisator') . ')'; - } else { - if ($group_status[$att->group_status]) { - $profil_link .= ' (' . $group_status[$att->group_status] . ')'; - } - } - return $profil_link; - })); ?> -
- diff --git a/app/views/calendar/single/_calhead.php b/app/views/calendar/single/_calhead.php deleted file mode 100644 index 0d3e777..0000000 --- a/app/views/calendar/single/_calhead.php +++ /dev/null @@ -1,16 +0,0 @@ -
- - - -
diff --git a/app/views/calendar/single/_calhead_label_day.php b/app/views/calendar/single/_calhead_label_day.php deleted file mode 100644 index df4f8bc..0000000 --- a/app/views/calendar/single/_calhead_label_day.php +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/views/calendar/single/_calhead_label_week.php b/app/views/calendar/single/_calhead_label_week.php deleted file mode 100644 index fc0a544..0000000 --- a/app/views/calendar/single/_calhead_label_week.php +++ /dev/null @@ -1,3 +0,0 @@ -getStart())) ?> -getStart()) ?> -getStart()), strftime("%x", $calendars[$week_type - 1]->getStart())) ?> diff --git a/app/views/calendar/single/_day.php b/app/views/calendar/single/_day.php deleted file mode 100644 index 8b70fe9..0000000 --- a/app/views/calendar/single/_day.php +++ /dev/null @@ -1,90 +0,0 @@ -= $settings['start'] - && $at <= $settings['end'] || !$atime) { - $start = $settings['start'] * 3600; - $end = $settings['end'] * 3600; -} elseif ($at < $settings['start']) { - $start = 0; - $end = ($settings['start'] + 2) * 3600; -} else { - $start = ($settings['end'] - 2) * 3600; - $end = 23 * 3600; -} -$em = $calendar->createEventMatrix($start, $end, $settings['step_day']); -$max_columns = $em['max_cols'] ?: 1; -?> - - - - - - - - - - - - - - - - - 0) : ?> - - - - - - - - render_partial('calendar/single/_day_table', ['start' => $start, 'end' => $end, 'em' => $em]) ?> - - - - - - - - - - - - - -
> - - _('Früher')])->asImg() ?> - - -
> - - _('Später')])->asImg() ?> - - -
 
diff --git a/app/views/calendar/single/_day_cell.php b/app/views/calendar/single/_day_cell.php deleted file mode 100644 index 31845ef..0000000 --- a/app/views/calendar/single/_day_cell.php +++ /dev/null @@ -1,54 +0,0 @@ - -getStart() + $i * $step ?> - - havePermission(Calendar::PERMISSION_WRITABLE)) : ?> - 0 ? ' colspan="' . ($em['max_cols'] + 1) . '"' : '') ?>> - + - - - 0 ? ' colspan="' . ($em['max_cols'] + 1) . '"' : '') ?>> - - - - - - - events[$em['mapping'][$row][$j]]; ?> - - 1 ? ' colspan="' . $em['cspan'][$row][$j] . '"' : '') ?> 1 ? ' rowspan="' . $em['rows'][$row][$j] . '"' : '') ?> class="getCategory() ?> calendar-day-event"> - 1) : ?> -
- getStart()) . date('H.i', $mapped_event->getEnd()) ?> -
- -
- getTitle()) ?> - render_partial('calendar/single/_tooltip', ['event' => $mapped_event]) ?> -
- - - 1 ? ' colspan="' . $em['cspan'][$row][$j] . '"' : '') ?>> -   - - - havePermission(Calendar::PERMISSION_WRITABLE)) : ?> - 1 ? ' colspan="' . $em['cspan'][$row][$j] . '"' : '') ?>> - + - - - 1 ? ' colspan="' . $em['cspan'][$row][$j] . '"' : '') ?>> - - - - - - - - havePermission(Calendar::PERMISSION_WRITABLE)) : ?> - - + - - - - - diff --git a/app/views/calendar/single/_day_dayevents.php b/app/views/calendar/single/_day_dayevents.php deleted file mode 100644 index 397f0bf..0000000 --- a/app/views/calendar/single/_day_dayevents.php +++ /dev/null @@ -1,33 +0,0 @@ - 0) : ?> - 0) ? ' colspan="' . ($em['max_cols']) . '"' : '') ?>> - - - - - getPermission() == Event::PERMISSION_CONFIDENTIAL) : ?> - - - - - - - -
- getTitle()) ?> - - render_partial('calendar/single/_tooltip', ['event' => $calendar->events[$em['day_map'][$i]]]) ?> - getTitle()) ?> -
- - - havePermission(Calendar::PERMISSION_WRITABLE)) : ?> - + - - - - 0) ? ' colspan="' . ($em['max_cols'] + 1) . '"' : '') ?>> - havePermission(Calendar::PERMISSION_WRITABLE)) : ?> - + - - - diff --git a/app/views/calendar/single/_day_table.php b/app/views/calendar/single/_day_table.php deleted file mode 100644 index 1b0ebcd..0000000 --- a/app/views/calendar/single/_day_table.php +++ /dev/null @@ -1,28 +0,0 @@ -= 3600) { - $rowspan_precol = ''; -} else { - $rowspan_precol = ' rowspan="' . 3600 / $settings['step_day'] . '"'; -} -?> - - > - render_partial('calendar/single/_day_dayevents', ['em' => $em]); ?> - - - - - - > - - - - - - - - - - render_partial('calendar/single/_day_cell', ['events' => $calendar->events, 'start' => $start, 'em' => $em, 'row' => $row, 'i' => $i, 'step' => $settings['step_day']]); ?> - - \ No newline at end of file diff --git a/app/views/calendar/single/_edit_status.php b/app/views/calendar/single/_edit_status.php deleted file mode 100644 index b5f41aa..0000000 --- a/app/views/calendar/single/_edit_status.php +++ /dev/null @@ -1,48 +0,0 @@ - -
-
- : - _('Abwartend'), - CalendarEvent::PARTSTAT_ACCEPTED => _('Angenommen'), - CalendarEvent::PARTSTAT_DECLINED => _('Abgelehnt'), - CalendarEvent::PARTSTAT_DELEGATED => _('Angenommen (keine Teilnahme)')] ?> -
    - $name) : ?> -
      - - - -
    -
-
- getAuthor() ?> - - mkdate), - htmlReady($author->getFullName('no_title'))) ?> - -
- event->mkdate < $event->event->chdate) : ?> - getEditor() ?> - -
- chdate), - htmlReady($editor->getFullName('no_title'))) ?> -
- - -
- _('Termin speichern')]) ?> - havePermission(Event::PERMISSION_DELETABLE)) : ?> - url_for('calendar/single/delete/' . implode('/', $event->getId()))) ?> - - - url_for('calendar/single/' . $last_view, [$event->getStart()])) ?> - -
-
diff --git a/app/views/calendar/single/_event_data.php b/app/views/calendar/single/_event_data.php deleted file mode 100644 index da9a5fe..0000000 --- a/app/views/calendar/single/_event_data.php +++ /dev/null @@ -1,100 +0,0 @@ - -
-

getTitle()) ?>

-
- : getStart()) ?> -
-
- : getEnd()) ?> -
- havePermission(Event::PERMISSION_READABLE)) : ?> - -
- : - have_studip_perm('user', $event->range_id)) : ?> - - - - - course->getFullname()) ?> - -
- - getDescription()) : ?> -
- : -
- - toStringCategories()) : ?> -
- : -
- - getLocation()) : ?> -
- : -
- - toStringPriority()) : ?> -
- : -
- - toStringAccessibility()) : ?> -
- : -
- - toStringRecurrence()) : ?> -
- : -
- - - CALENDAR_GROUP_ENABLE) : ?> - render_partial('calendar/single/_attendees.php') ?> - havePermission(Calendar::PERMISSION_OWN) - && $event->toStringGroupStatus()) : ?> - render_partial('calendar/single/_edit_status') ?> - -
- havePermission(Event::PERMISSION_DELETABLE)) : ?> - url_for('calendar/single/delete/' . implode('/', $event->getId()))) ?> - - - url_for('calendar/single/' . $last_view, [$event->getStart()])) ?> - -
- - - - - dozenten; ?> - -
- : -
    - -
  • - -
  • - -
-
- - - getRelatedGroups(); ?> - -
- : - pluck('name'))) ?> -
- - -
- url_for('calendar/single/' . $last_view, [$event->getStart()])) ?> -
- - - -
diff --git a/app/views/calendar/single/_include_month.php b/app/views/calendar/single/_include_month.php deleted file mode 100644 index 6942d2f..0000000 --- a/app/views/calendar/single/_include_month.php +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - -
- - - - -
- -
- -
- -
-
- - - - - - - - - - - - - - - - - - - - - - 0) || ($j - $aday > 6)) { - $style = 'light'; - } - $hday = holiday($i); - ?> - - - - - - - > - - > - - > - - - - - > - - - - - - -
- -
- - - - - - - - - - - '.$aday.'' ?> - - - > - - - - - - -
-
diff --git a/app/views/calendar/single/_jump_to.php b/app/views/calendar/single/_jump_to.php deleted file mode 100644 index b5e96bb..0000000 --- a/app/views/calendar/single/_jump_to.php +++ /dev/null @@ -1,13 +0,0 @@ -
- - -
- - - asInput(['class' => 'text-top']) ?> -
-
- - diff --git a/app/views/calendar/single/_select_calendar.php b/app/views/calendar/single/_select_calendar.php deleted file mode 100644 index 7b2b942..0000000 --- a/app/views/calendar/single/_select_calendar.php +++ /dev/null @@ -1,80 +0,0 @@ -
- -
- - - - - asInput(['class' => "text-top"]) ?> -
-
diff --git a/app/views/calendar/single/_select_category.php b/app/views/calendar/single/_select_category.php deleted file mode 100644 index fe86bdc..0000000 --- a/app/views/calendar/single/_select_category.php +++ /dev/null @@ -1,16 +0,0 @@ -
- -
- - - - asInput(['class' => "text-top"]) ?> -
-
diff --git a/app/views/calendar/single/_semester_filter.php b/app/views/calendar/single/_semester_filter.php deleted file mode 100644 index 016f1e5..0000000 --- a/app/views/calendar/single/_semester_filter.php +++ /dev/null @@ -1,30 +0,0 @@ -
-
- -
- - -
diff --git a/app/views/calendar/single/_tooltip.php b/app/views/calendar/single/_tooltip.php deleted file mode 100644 index 0979ec2..0000000 --- a/app/views/calendar/single/_tooltip.php +++ /dev/null @@ -1,113 +0,0 @@ -
-

getTitle()) ?>

-
- : getStart()) ?> -
-
- : getEnd()) ?> -
- havePermission(Event::PERMISSION_READABLE)) : ?> - -
- : course->getFullname()) ?> -
- - getDescription()) : ?> -
- : -
- - toStringCategories()) : ?> -
- : -
- - getLocation()) : ?> -
- : -
- - toStringPriority()) : ?> -
- : -
- - toStringAccessibility()) : ?> -
- : -
- - toStringRecurrence()) : ?> -
- : -
- - - havePermission(Event::PERMISSION_READABLE)) : ?> - CALENDAR_GROUP_ENABLE - && $calendar->getRange() == Calendar::RANGE_USER) : ?> - _('Abwartend'), - CalendarEvent::PARTSTAT_ACCEPTED => _('Angenommen'), - CalendarEvent::PARTSTAT_DECLINED => _('Abgelehnt'), - CalendarEvent::PARTSTAT_DELEGATED => _('Angenommen (keine Teilnahme)'), - CalendarEvent::PARTSTAT_NEEDS_ACTION => ''] ?> - attendees->findOneBy('range_id', - $calendar->getRangeId(), '!=') ?> - - attendees->findOneBy('range_id', $GLOBALS['user']->id) ?> - - - -
- - attendees->map( - function ($att) use ($event, $group_status) { - if ($event->havePermission(Event::PERMISSION_OWN, $att->owner->id)) { - $ret = htmlReady($att->owner->getFullname()) - . ' (' . _('Organisator') . ')'; - } else { - $ret = htmlReady($att->owner->getFullname()); - if ($group_status[$att->group_status]) { - $ret .= ' (' . $group_status[$att->group_status] . ')'; - } - } - return $ret; - })); ?> -
- - - - - dozenten; ?> - -
- : -
    - -
  • - getFullName()) ?> -
  • - -
-
- - - getRelatedGroups(); ?> - -
- : -
    - -
  • - name) ?> -
  • - -
-
- - - -
diff --git a/app/views/calendar/single/day.php b/app/views/calendar/single/day.php deleted file mode 100644 index 448509c..0000000 --- a/app/views/calendar/single/day.php +++ /dev/null @@ -1,13 +0,0 @@ -
-
- render_partial('calendar/single/_day'); ?> -
-
- - render_partial('calendar/single/_include_month', ['imt' => $imt, 'href' => '', 'mod' => '']) ?> - - render_partial('calendar/single/_include_month', ['imt' => $imt, 'href' => '', 'mod' => 'NONAVARROWS']) ?> - - render_partial('calendar/single/_include_month', ['imt' => $imt, 'href' => '', 'mod' => 'NONAVARROWS']) ?> -
-
diff --git a/app/views/calendar/single/edit.php b/app/views/calendar/single/edit.php deleted file mode 100644 index c22f7a6..0000000 --- a/app/views/calendar/single/edit.php +++ /dev/null @@ -1,454 +0,0 @@ - - - - - - -
- - -
- - isNew()) : ?> - - - - - - - - -
- -
- - - - - -
- -
- - - - - - - - - - - - - - - - getPermissionByUser($GLOBALS['user']->id) == Calendar::PERMISSION_OWN) : ?> - - - - * - */ ?> - - getRange() == Calendar::RANGE_SEM) : ?> - - getRange() == Calendar::RANGE_INST) : ?> - - - - - - - - - - isNew() && Config::get()->CALENDAR_GROUP_ENABLE) : ?> -
- getAuthor() ?> - - mkdate), - htmlReady($author->getFullName('no_title'))) ?> - -
- event->mkdate < $event->event->chdate) : ?> - getEditor() ?> - -
- chdate), - htmlReady($editor->getFullName('no_title'))) ?> -
- - - -
- - - - - CALENDAR_GROUP_ENABLE && $calendar->getRange() == Calendar::RANGE_USER) : ?> - render_partial('calendar/group/_attendees') ?> - - -
- _('Termin speichern')]) ?> - - isNew()) : ?> - getRecurrence('rtype') != 'SINGLE') : ?> - url_for('calendar/single/delete_recurrence/' . implode('/', $event->getId()) . '/' . $atime)) ?> - - url_for('calendar/single/delete/' . implode('/', $event->getId()))) ?> - - - url_for('calendar/single/' . $last_view, [$event->getStart()])) ?> - -
-
- diff --git a/app/views/calendar/single/edit_status.php b/app/views/calendar/single/edit_status.php deleted file mode 100644 index 3e3bfc3..0000000 --- a/app/views/calendar/single/edit_status.php +++ /dev/null @@ -1,3 +0,0 @@ -
- render_partial('calendar/single/_event_data') ?> -
diff --git a/app/views/calendar/single/event.php b/app/views/calendar/single/event.php deleted file mode 100644 index e9d544f..0000000 --- a/app/views/calendar/single/event.php +++ /dev/null @@ -1 +0,0 @@ -render_partial('calendar/single/_event_data') ?> diff --git a/app/views/calendar/single/export_calendar.php b/app/views/calendar/single/export_calendar.php deleted file mode 100644 index 66f86c5..0000000 --- a/app/views/calendar/single/export_calendar.php +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - -
-
- - - - - - - - - - -
- - - - -
-
- -
- _('Termine exportieren')]) ?> - - - url_for('calendar/single/' . $last_view)) ?> - -
-
- diff --git a/app/views/calendar/single/import.php b/app/views/calendar/single/import.php deleted file mode 100644 index db1feb3..0000000 --- a/app/views/calendar/single/import.php +++ /dev/null @@ -1,27 +0,0 @@ - -
- - -
- - - - - - - -
- -
- - url_for('calendar/single/' . $last_view)) ?> -
-
diff --git a/app/views/calendar/single/manage_access.php b/app/views/calendar/single/manage_access.php deleted file mode 100644 index 4f7be8f..0000000 --- a/app/views/calendar/single/manage_access.php +++ /dev/null @@ -1,99 +0,0 @@ - - - - - -
- - _('Keine'), 2 => _('Lesen'), 4 => _('Schreiben')] ?> - - - - - - - - - - - - $usergroup): ?> - - - - - - - - - - - - - -
- - - - asInput([ - 'id' => "calendar-group-submit", - 'name' => "calendar_group_submit", - 'class' => "text-top"]) ?> - - render() ?> - - - -
- - - - - - - -
- -
- user) ?> - - - - - - user_id]] ?> - - - asImg() ?> - -
-
- - - url_for('calendar/single/' . $last_view)) ?> - -
-
diff --git a/app/views/calendar/single/month.php b/app/views/calendar/single/month.php deleted file mode 100644 index da715f5..0000000 --- a/app/views/calendar/single/month.php +++ /dev/null @@ -1,109 +0,0 @@ - - -
- - - - - - - - - - - - - - 0) || ($j - $aday > 6)) { - $class_cell = 'lightmonth'; - $class_day = 'light'; - } elseif (date('Ymd', $i) == date('Ymd')) { - $class_cell = 'celltoday'; - } else { - $class_cell = 'month'; - } - $hday = holiday($i); - - if ($j % 7 == 0) { - ?> - - - - - - - - - -
- - - - - - events as $event) : ?> -
- getTitle()) ?> - render_partial('calendar/single/_tooltip', ['event' => $event, 'calendar' => $calendars[$j]]) ?> -
- - - - - - -
- - - -
- - - - - -
- - events as $event) : ?> -
- getTitle()) ?> - render_partial('calendar/single/_tooltip', ['event' => $event, 'calendar' => $calendars[$j]]) ?> -
- -
- -
-
diff --git a/app/views/calendar/single/seminar_events.php b/app/views/calendar/single/seminar_events.php deleted file mode 100644 index 06ed5fa..0000000 --- a/app/views/calendar/single/seminar_events.php +++ /dev/null @@ -1,104 +0,0 @@ - - render_partial('calendar/single/_semester_filter') ?> - -
- -
- $course_group) : ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
> - - - - > - - - -
- - getImageTag(Avatar::SMALL, ['title' => $course['name']]) - ?> - - getImageTag(Avatar::SMALL, ['title' => $course['name']]) - ?> - - - > - - - - - - - - - - - - asImg(20, $params) ?> - - - 20, 'height' => 20]); ?> - - - - > -
- -
-
- - - url_for('calendar/single/' . $last_view)) ?> - -
-
- - Bitte nutzen Sie %sVeranstaltung suchen / hinzufügen%s um sich für Veranstaltungen anzumelden.'),'', ''), - _('In dem ausgewählten Semester wurden keine Veranstaltungen belegt.').'
'._('Wählen Sie links im Semesterfilter ein anderes Semester aus') - ]))?> - - - render_partial('my_courses/_deputy_bosses'); ?> - diff --git a/app/views/calendar/single/share.php b/app/views/calendar/single/share.php deleted file mode 100644 index eef5ca3..0000000 --- a/app/views/calendar/single/share.php +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - -
- - - -
- - - - - -
- - - -
- -
- -
- - - _('Abschicken')]) ?> -
- -
- - -
- -
- - -
- -
- -
- - - - _('Achtung: Die alte Adresse wird damit ungültig!') - ]) ?> - - _('Ein Zugriff auf Ihre Termine über diese Adresse ist dann nicht mehr möglich!') - ]) ?> - - - - url_for('calendar/single/' . $last_view)) ?> - -
-
diff --git a/app/views/calendar/single/week.php b/app/views/calendar/single/week.php deleted file mode 100644 index 98f8317..0000000 --- a/app/views/calendar/single/week.php +++ /dev/null @@ -1,199 +0,0 @@ -= $settings['start'] - && $at <= $settings['end'] || !$atime) { - $start = $settings['start']; - $end = $settings['end']; -} elseif ($at < $settings['start']) { - $start = 0; - $end = $settings['start'] + 2; -} else { - $start = $settings['end'] - 2; - $end = 23; -} -$tab_arr = []; -$max_columns = 0; -$week_type = $settings['type_week'] == 'SHORT' ? 5 : 7; -$rows = ($end - $start + 1) * 3600 / $settings['step_week']; - -for ($i = 0; $i < $week_type; $i++) { - $tab_arr[$i] = $calendars[$i]->createEventMatrix($start * 3600, $end * 3600, $settings['step_week']); - if ($tab_arr[$i]['max_cols']) { - $max_columns += ($tab_arr[$i]['max_cols'] + 1); - } else { - $max_columns++; - } -} - -$rowspan = ceil(3600 / $settings['step_week']); -$height = ' height="20"'; - -if ($rowspan > 1) { - $colspan_1 = ' colspan="2"'; - $colspan_2 = $max_columns + 4; - $width_daycols = 100 - (4 + $week_type) * 0.1; -} else { - $colspan_1 = ''; - $colspan_2 = $max_columns + 2; - $width_daycols = 100 - (2 + $week_type) * 0.1; -} -?> - - - - - - - 1) : ?> - - - - 0) : ?> - - - - - - - - - 1) : ?> - - - - - - - - - - - - - - - - - - - getStart()) == date('Ymd')) { - $class_cell = 'celltoday'; - } else { - $class_cell = ''; - } - ?> - render_partial('calendar/single/_day_dayevents', ['em' => $tab_arr[$i], 'calendar' => $calendars[$i], 'class_cell' => $class_cell]) ?> - - - - - - - - - - - - - - 1) : ?> - - - - - - - - - getStart()) == date('Ymd')) { - $class_cell = 'celltoday'; - } else { - $class_cell = ''; - } - ?> - render_partial('calendar/single/_day_cell', ['calendar' => $calendars[$y], 'em' => $tab_arr[$y], 'row' => $i, 'start' => $start * 3600, 'i' => $i + ($start * 3600 / $settings['step_week']), 'step' => $settings['step_week'], 'class_cell' => $class_cell]); ?> - - 1) : ?> - - - - - - - - - - - - - - - - - - - - style="text-align:center;"> - - - _('Später')])->asImg() ?> - - - - - style="text-align:center;"> - - - _('Später')])->asImg() ?> - - - - - -
> - 0) : ?> - - _('Früher')])->asImg() ?> - - - 0 ? ' colspan="' . ($tab_arr[$i]['max_cols'] + 1) . '"' : '' ) ?>> - - getStart()) ?> getStart()) ?> - - getStart())) : ?> -
- -
> - 0) : ?> - - _('Früher')])->asImg() ?> - - -
height="20"> - - > - -
>>00>>00>>
 
diff --git a/app/views/calendar/single/year.php b/app/views/calendar/single/year.php deleted file mode 100644 index 0ebafe1..0000000 --- a/app/views/calendar/single/year.php +++ /dev/null @@ -1,145 +0,0 @@ -
- - - -
- - getStart())) { - $days_per_month[2]++; - } - ?> - - - - - - - - - - - - - - - - - - getStart())); ?> - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
> - - - - - > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - align="right"> - $event_count_txt])->asImg(16, ["alt" => $event_count_txt]); ?> - - -
- - -
- - - -
-
-
diff --git a/app/views/calendar/stylesheet.php b/app/views/calendar/stylesheet.php deleted file mode 100644 index aaf7334..0000000 --- a/app/views/calendar/stylesheet.php +++ /dev/null @@ -1,13 +0,0 @@ -div.schedule_day { - height: px; -} - -div.schedule_marker { - height: px; - line-height: px; - margin-bottom: px; -} - -div.schedule_hours { - height: px; -} diff --git a/app/views/course/cancel_dates/index.php b/app/views/course/cancel_dates/index.php index e427849..3c092bc 100644 --- a/app/views/course/cancel_dates/index.php +++ b/app/views/course/cancel_dates/index.php @@ -12,7 +12,7 @@