diff options
| author | Moritz Strohm <strohm@data-quest.de> | 2026-01-16 09:36:16 +0000 |
|---|---|---|
| committer | Moritz Strohm <strohm@data-quest.de> | 2026-01-16 09:36:16 +0000 |
| commit | b58142fe5fa1ba1a99d850baa1465df6fa6e0d3b (patch) | |
| tree | d73956252dc17dd054d0db8e0f167a4103b7ba83 /app/controllers | |
| parent | c3e07e221b0bef64d3ad4da48c6371c75ca12cc3 (diff) | |
updated Fullcalendar to version 6, closes #4887
Closes #4887
Merge request studip/studip!4438
Diffstat (limited to 'app/controllers')
| -rw-r--r-- | app/controllers/admin/courseplanning.php | 89 | ||||
| -rw-r--r-- | app/controllers/admin/overlapping.php | 16 | ||||
| -rw-r--r-- | app/controllers/calendar/calendar.php | 127 | ||||
| -rw-r--r-- | app/controllers/calendar/date.php | 48 | ||||
| -rw-r--r-- | app/controllers/calendar/schedule.php | 85 | ||||
| -rw-r--r-- | app/controllers/institute/schedule.php | 14 | ||||
| -rw-r--r-- | app/controllers/resources/ajax.php | 6 | ||||
| -rw-r--r-- | app/controllers/resources/booking.php | 20 | ||||
| -rw-r--r-- | app/controllers/resources/print.php | 13 | ||||
| -rw-r--r-- | app/controllers/resources/room_planning.php | 12 | ||||
| -rw-r--r-- | app/controllers/room_management/planning.php | 12 |
11 files changed, 284 insertions, 158 deletions
diff --git a/app/controllers/admin/courseplanning.php b/app/controllers/admin/courseplanning.php index 132b35a..d86f8cd 100644 --- a/app/controllers/admin/courseplanning.php +++ b/app/controllers/admin/courseplanning.php @@ -67,9 +67,9 @@ class Admin_CourseplanningController extends AuthenticatedController $sidebar = Sidebar::get(); $actions = $sidebar->addWidget(new ActionsWidget()); $actions->addLink( - _('Veranstaltungs-Stundenplan PDF'), - 'javascript:STUDIP.Fullcalendar.downloadPDF();', - Icon::create('file-pdf') + _('Drucken'), + 'javascript:void(window.print());', + Icon::create('print') ); Sidebar::get()->getWidget('actions')->addLink( @@ -341,29 +341,23 @@ class Admin_CourseplanningController extends AuthenticatedController $this->non_conform_dates = $non_rasters; } - public function add_event_action() + public function add_event_action($course_id) { - $course_id = Request::option('course_id'); - $begin = Request::get('begin'); - $end = Request::get('end'); - $success = false; + $start = Request::getDateTime('start', DateTimeInterface::RFC3339_EXTENDED); + $end = Request::getDateTime('end', DateTimeInterface::RFC3339_EXTENDED); + $success = false; $cycle_start = null; - $cycle_end = null; - - if ($course_id && $this->semester) { - $begin_date = new DateTime($begin); - $end_date = new DateTime($end); - $begin_date->setTimezone(new DateTimeZone('UTC')); - $end_date->setTimezone(new DateTimeZone('UTC')); + $cycle_end = null; + $course = Course::find($course_id); - $course = Course::find($course_id); - - if (count($course->semesters) > 1) { // course over more than one semester + if ($course && $this->semester && $start && $end) { + if (count($course->semesters) > 1) { + //The course spans over more than one semester. $start_weeks = $course->start_semester->getStartWeeks($course->end_semester); - $sem_weeks = $this->semester->getStartWeeks(); + $sem_weeks = $this->semester->getStartWeeks(); $sem_weeks_start = explode(' Semesterwoche ', $sem_weeks[0]); - $sem_weeks_end = explode(' Semesterwoche ', end($sem_weeks)); + $sem_weeks_end = explode(' Semesterwoche ', end($sem_weeks)); foreach ($start_weeks as $week_num => $week_text) { if ($cycle_start && $cycle_end) break; @@ -378,14 +372,14 @@ class Admin_CourseplanningController extends AuthenticatedController $cycle = new SeminarCycleDate(); $cycle->seminar_id = $course_id; - $cycle->weekday = $begin_date->format('w'); + $cycle->weekday = $start->format('w'); $cycle->description = ''; $cycle->sws = floatVal(0.0); $cycle->cycle = 0; - $cycle->week_offset = $cycle_start?$cycle_start:0; - $cycle->end_offset = $cycle_end?$cycle_end:intVal(count($this->semester->getStartWeeks()) -1); - $cycle->start_time = $begin_date->format('H:i:s'); - $cycle->end_time = $end_date->format('H:i:s'); + $cycle->week_offset = $cycle_start ? $cycle_start : 0; + $cycle->end_offset = $cycle_end ? $cycle_end : intval(count($this->semester->getStartWeeks()) -1); + $cycle->start_time = $start->format('H:i:s'); + $cycle->end_time = $end->format('H:i:s'); $success = $cycle->store(); } @@ -402,24 +396,36 @@ class Admin_CourseplanningController extends AuthenticatedController } $this->render_nothing(); - return; } public function pick_color_action($metadate_id, $from_action, $weekday = null) { PageLayout::setTitle(_('Farbwähler')); + $this->available_colours = [ + 'yellow' => '#ffbd33', + 'orange' => '#f26e00', + 'red' => '#d60000', + 'violet' => '#b02e7c', + 'dark-violet' => '#682c8b', + 'green' => '#6ead10', + 'dark-green' => '#008512', + 'petrol' => '#0E817B', + 'brown' => '#a85d45' + ]; + $cdate = SeminarCycleDate::find($metadate_id); if ($cdate) { $course = Course::find($cdate->Seminar_id); $semtype = $course->getSemType(); - if (Request::submitted('save') && Request::submitted('event_color')) { - if (Request::get('event_color_semtype')) { + if (Request::submitted('save') && Request::submitted('event_colour')) { + $selected_colour_index = Request::get('event_colour'); + if (Request::get('event_colour_semtype')) { if (InstituteCalendarHelper::setSemtypeEventcolor( Course::find($cdate->seminar_id), $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT, - Request::get('event_color') + $this->available_colours[$selected_colour_index] ?? '', )) { PageLayout::postSuccess(sprintf( _('Die Farbe wurde allen VA des Typs %s zugewiesen.'), @@ -433,7 +439,7 @@ class Admin_CourseplanningController extends AuthenticatedController Course::find($cdate->seminar_id), $metadate_id, $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT, - Request::get('event_color') + $this->available_colours[$selected_colour_index] ?? '' )) { PageLayout::postSuccess(_('Die Farbe wurde der VA zugewiesen.')); } else { @@ -447,7 +453,7 @@ class Admin_CourseplanningController extends AuthenticatedController $course_colors = InstituteCalendarHelper::getCourseEventcolors($course); if (array_key_exists($metadate_id, $course_colors)) { - $this->color = $course_colors[$metadate_id][$GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT]; + $this->color = $course_colors[$metadate_id][$GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT] ?? ''; } else { $this->color = '#28497c'; } @@ -459,31 +465,26 @@ class Admin_CourseplanningController extends AuthenticatedController $this->weekday = $weekday; } - public function move_event_action() + public function move_event_action($cycle_date_id) { - $metadate_id = Request::option('cycle_id'); - $begin = Request::get('begin'); - $end = Request::get('end'); + $start = Request::getDateTime('start', DateTimeInterface::RFC3339_EXTENDED); + $end = Request::getDateTime('end', DateTimeInterface::RFC3339_EXTENDED); $success = false; - $cdate = SeminarCycleDate::find($metadate_id); + $cdate = SeminarCycleDate::find($cycle_date_id); if ($cdate) { if (Request::submitted('resource_id')) { InstituteCalendarHelper::setCourseEventcolumn( Course::find($cdate->seminar_id), - $metadate_id, + $cycle_date_id, $GLOBALS['user']->cfg->MY_INSTITUTES_DEFAULT, Request::get('resource_id', '0') ); } - $begin_date = new DateTime($begin); - $end_date = new DateTime($end); - $begin_date->setTimezone(new DateTimeZone('UTC')); - $end_date->setTimezone(new DateTimeZone('UTC')); - $weekday = $begin_date->format('w'); - $cdate->start_time = $begin_date->format('H:i:s'); - $cdate->end_time = $end_date->format('H:i:s'); + $weekday = $start->format('w'); + $cdate->start_time = $start->format('H:i:s'); + $cdate->end_time = $end->format('H:i:s'); $cdate->weekday = $weekday; $success = $cdate->store(); } diff --git a/app/controllers/admin/overlapping.php b/app/controllers/admin/overlapping.php index b339e83..69a93fc 100644 --- a/app/controllers/admin/overlapping.php +++ b/app/controllers/admin/overlapping.php @@ -362,7 +362,7 @@ class Admin_OverlappingController extends AuthenticatedController $selection_id = $selection_id ?: $_SESSION['MVV_OVL_SELECTION_ID'] ?? null; - $this->fullcalendar = Studip\Fullcalendar::create( + $this->fullcalendar = \Studip\Fullcalendar::create( _('Kalender'), [ 'editable' => false, @@ -374,20 +374,20 @@ class Admin_OverlappingController extends AuthenticatedController 'defaultDate' => date('Y-m-d', $this->selected_semester->vorles_beginn), 'allDaySlot' => false, 'allDayText' => '', - 'header' => [ - 'left' => false, - 'center' => $this->selected_semester->name, - 'right' => false, + 'headerToolbar' => [ + 'start' => false, + 'center' => $this->selected_semester->name, + 'end' => false, ], 'weekNumbers' => false, 'views' => [ - 'timeGridWeek' => [ - 'columnHeaderFormat' => ['weekday' => 'short', 'omitCommas' => true], + \Studip\Fullcalendar::VIEW_WEEK => [ + 'dayHeaderFormat' => ['weekday' => 'short', 'omitCommas' => true], 'weekends' => true, 'slotDuration' => '00:30:00' ], ], - 'defaultView' => 'timeGridWeek', + 'initialView' => \Studip\Fullcalendar::VIEW_WEEK, 'timeGridEventMinHeight' => 20, 'eventSources' => [ [ diff --git a/app/controllers/calendar/calendar.php b/app/controllers/calendar/calendar.php index 6e79d3a..1f9a4a6 100644 --- a/app/controllers/calendar/calendar.php +++ b/app/controllers/calendar/calendar.php @@ -347,17 +347,17 @@ class Calendar_CalendarController extends AuthenticatedController //Map calendar settings to fullcalendar settings: - $default_view = 'timeGridWeek'; + $default_view = \Studip\Fullcalendar::VIEW_WEEK; if ($timeline_view) { - $default_view = 'resourceTimelineWeek'; + $default_view = \Studip\Fullcalendar::GROUP_WEEK; if ($calendar_settings['view'] === 'day') { - $default_view = 'resourceTimelineDay'; + $default_view = \Studip\Fullcalendar::GROUP_DAY; } } elseif (!empty($calendar_settings['view'])) { if ($calendar_settings['view'] === 'day') { - $default_view = 'timeGridDay'; + $default_view = \Studip\Fullcalendar::VIEW_DAY; } elseif ($calendar_settings['view'] === 'month') { - $default_view = 'dayGridMonth'; + $default_view = \Studip\Fullcalendar::VIEW_MONTH; } } @@ -373,59 +373,68 @@ class Calendar_CalendarController extends AuthenticatedController $data_url_params['timeline_view'] = '1'; } + $available_views = []; + if ($timeline_view) { + $available_views = [ + \Studip\Fullcalendar::GROUP_WEEK, + \Studip\Fullcalendar::GROUP_DAY + ]; + } else { + $available_views = [ + \Studip\Fullcalendar::VIEW_MONTH, + \Studip\Fullcalendar::VIEW_WEEK, + \Studip\Fullcalendar::VIEW_DAY + ]; + } + $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' => true, - 'allDayText' => '', - 'header' => [ - 'left' => ( - $timeline_view - ? 'resourceTimelineWeek,resourceTimelineDay' - : 'dayGridYear,dayGridMonth,timeGridWeek,timeGridDay' - ), + 'editable' => $write_permissions, + 'selectable' => $write_permissions, + 'studip_urls' => $fullcalendar_studip_urls, + 'slotMinTime' => sprintf('%02u:00', $calendar_settings['start'] ?? 8), + 'slotMaxTime' => sprintf('%02u:00', $calendar_settings['end'] ?? 20), + 'initialDate' => $default_date->format('Y-m-d'), + 'allDaySlot' => true, + 'allDayText' => '', + 'headerToolbar' => [ + 'start' => implode(',', $available_views), 'center' => 'title', - 'right' => 'prev,today,next' + 'end' => 'prev,today,next' ], 'weekNumbers' => true, 'views' => [ - 'dayGridMonth' => [ + \Studip\Fullcalendar::VIEW_MONTH => [ 'eventTimeFormat' => ['hour' => 'numeric', 'minute' => '2-digit'], 'titleFormat' => ['year' => 'numeric', 'month' => 'long'], 'displayEventEnd' => true ], - 'timeGridWeek' => [ - 'columnHeaderFormat' => ['weekday' => 'short', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true], - 'weekends' => $calendar_settings['type_week'] === 'LONG', - 'titleFormat' => ['year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit'], - 'slotDuration' => $slot_durations['week'] + \Studip\Fullcalendar::VIEW_WEEK => [ + 'dayHeaderFormat' => ['weekday' => 'short', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true], + 'weekends' => $calendar_settings['type_week'] === 'LONG', + 'titleFormat' => ['year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit'], + 'slotDuration' => $slot_durations['week'] ], - 'timeGridDay' => [ - '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'] + \Studip\Fullcalendar::VIEW_DAY => [ + 'dayHeaderFormat' => ['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'] ], - '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'] + \Studip\Fullcalendar::GROUP_WEEK => [ + 'dayHeaderFormat' => ['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'] + \Studip\Fullcalendar::GROUP_DAY => [ + 'dayHeaderFormat' => ['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, + 'initialView' => $default_view, + 'eventMinHeight' => 20, 'eventSources' => [ [ 'url' => $this->url_for( @@ -436,12 +445,11 @@ class Calendar_CalendarController extends AuthenticatedController ), $data_url_params ), - 'method' => 'GET', - 'extraParams' => [] + 'method' => 'GET' ] ], - 'resources' => $calendar_resources, - 'resourceLabelText' => $calendar_group_title + 'resources' => $calendar_resources, + 'resourceAreaHeaderContent' => $calendar_group_title ] ); } @@ -504,6 +512,13 @@ class Calendar_CalendarController extends AuthenticatedController $fullcalendar_studip_urls['add'] = $this->url_for('calendar/date/add/course_' . $course->id); } + $available_views = [ + 'dayGridYear', + \Studip\Fullcalendar::VIEW_MONTH, + \Studip\Fullcalendar::VIEW_WEEK, + \Studip\Fullcalendar::VIEW_DAY + ]; + $this->fullcalendar = Studip\Fullcalendar::create( _('Veranstaltungskalender'), [ @@ -514,31 +529,31 @@ class Calendar_CalendarController extends AuthenticatedController 'maxTime' => sprintf('%02u:00', $calendar_settings['end'] ?? 20), 'allDaySlot' => true, 'allDayText' => '', - 'header' => [ - 'left' => 'dayGridYear,dayGridMonth,timeGridWeek,timeGridDay', - 'center' => 'title', - 'right' => 'prev,today,next' + 'headerToolbar' => [ + 'start' => implode(',', $available_views), + 'center' => 'title', + 'end' => 'prev,today,next' ], 'weekNumbers' => true, 'views' => [ - 'dayGridMonth' => [ + \Studip\Fullcalendar::VIEW_MONTH => [ 'eventTimeFormat' => ['hour' => 'numeric', 'minute' => '2-digit'], 'titleFormat' => ['year' => 'numeric', 'month' => 'long'], 'displayEventEnd' => true ], - 'timeGridWeek' => [ - 'columnHeaderFormat' => [ 'weekday' => 'short', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true ], + \Studip\Fullcalendar::VIEW_WEEK => [ + 'dayHeaderFormat' => [ 'weekday' => 'short', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true ], 'weekends' => $calendar_settings['type_week'] === 'LONG', 'titleFormat' => ['year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit'], 'slotDuration' => $slot_settings['week'] ], - 'timeGridDay' => [ - 'columnHeaderFormat' => [ 'weekday' => 'long', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true ], + \Studip\Fullcalendar::VIEW_DAY => [ + 'dayHeaderFormat' => [ 'weekday' => 'long', 'year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit', 'omitCommas' => true ], 'titleFormat' => ['year' => 'numeric', 'month' => '2-digit', 'day' => '2-digit'], 'slotDuration' => $slot_settings['day'] ] ], - 'defaultView' => 'timeGridWeek', + 'initialView' => \Studip\Fullcalendar::VIEW_WEEK, 'timeGridEventMinHeight' => 20, 'eventSources' => [ [ diff --git a/app/controllers/calendar/date.php b/app/controllers/calendar/date.php index 07a77b9..a33ec42 100644 --- a/app/controllers/calendar/date.php +++ b/app/controllers/calendar/date.php @@ -223,9 +223,26 @@ class Calendar_DateController extends AuthenticatedController $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'); + if (Request::submitted('start') && Request::submitted('end')) { + $tz_date = new DateTime(); + $start = Request::getDateTime('start', DateTimeInterface::RFC3339_EXTENDED); + $end = Request::getDateTime('end', DateTimeInterface::RFC3339_EXTENDED); + //The date might be in UTC instead of the default time zone, so it has to be converted: + $start->setTimezone($tz_date->getTimezone()); + $end->setTimezone($tz_date->getTimezone()); + if ($start === false || $end === false) { + throw new InvalidArgumentException(_('Ungültiges Datumsformat')); + } + if (Request::get('all_day')) { + $start->setTime(0, 0, 0); + //Special case for the end timestamp: + //In Fullcalendar, all-day events end on the next day while in Stud.IP + //they end on 23:59:59 on the same day. + $end->setTime(0, 0, 0); + $end = $end->sub(new DateInterval('PT1S')); + } + $this->date->begin = $start->getTimestamp(); + $this->date->end = $end->getTimestamp(); $this->date->repetition_end = $this->date->end; } elseif (Request::submitted('begin_str') && Request::submitted('end_str')) { //Assume the textual format d.m.Y H:i: @@ -646,11 +663,32 @@ class Calendar_DateController extends AuthenticatedController ); } - $this->begin = Request::getDateTime('begin', \DateTime::RFC3339); - $this->end = Request::getDateTime('end', \DateTime::RFC3339); + $tz_date = new DateTime(); + $this->begin = Request::getDateTime('start', \DateTime::RFC3339_EXTENDED); + $this->end = Request::getDateTime('end', \DateTime::RFC3339_EXTENDED); + if ($this->begin) { + $this->begin->setTimezone($tz_date->getTimezone()); + } + if (Request::get('all_day') && $this->begin) { + if (!$this->end) { + //Set the end to the end of the day the event starts: + $this->end = clone $this->begin; + $this->end = $this->end->add(new DateInterval('P1D')); + $this->end->setTime(0,0,0); + } + //At this point, there should be an end date. + //If it is on 0:00:00 in the local time zone, we need to get to + //the last second of the day before so that the event is + //detected as all-day event. + $this->end->setTimezone($tz_date->getTimezone()); + if ($this->end->format('His') == 0) { + $this->end = $this->end->sub(new DateInterval('PT1S')); + } + } if (!$this->begin || !$this->end) { throw new InvalidArgumentException(); } + $this->end->setTimezone($tz_date->getTimezone()); //In case the moved event is a repetition event, we must know the original date from where //it was moved from to correctly move the whole date series: diff --git a/app/controllers/calendar/schedule.php b/app/controllers/calendar/schedule.php index 6abe4b1..2f9e628 100644 --- a/app/controllers/calendar/schedule.php +++ b/app/controllers/calendar/schedule.php @@ -160,8 +160,8 @@ class Calendar_ScheduleController extends AuthenticatedController //The range is not necessary a full week: If fullcalendar starts in the day //view (like in the mobile view), the start of the range may lie in the middle //of the week. - $begin = Request::getDateTime('start', \DateTime::RFC3339); - $end = Request::getDateTime('end', \DateTime::RFC3339); + $begin = Request::getDateTime('start', \DateTimeInterface::RFC3339); + $end = Request::getDateTime('end', \DateTimeInterface::RFC3339); if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) { //No time range specified. throw new InvalidArgumentException('Invalid parameters!'); @@ -335,8 +335,6 @@ class Calendar_ScheduleController extends AuthenticatedController $weekly_dates = ScheduleEntry::findByUser_id($GLOBALS['user']->id); foreach ($weekly_dates as $date) { $event_data = $date->toEventData($GLOBALS['user']->id); - //Disable fullcalendar drag & drop actions: - $event_data->editable = false; $result[] = $event_data->toFullcalendarEvent(); } @@ -344,6 +342,56 @@ class Calendar_ScheduleController extends AuthenticatedController } /** + * Handles moving an entry in the calendar. + * + * @param string $entry_id + * + * @return void + */ + public function move_entry_action(string $entry_id) + { + if (!$entry_id) { + $this->response->set_status(400, 'No entry-ID provided.'); + $this->render_nothing(); + return; + } + $entry = ScheduleEntry::find($entry_id); + if (!$entry) { + $this->response->set_status(404, 'Entry not found.'); + $this->render_nothing(); + return; + } + //Check if the current user owns this entry: + if ($entry->user_id !== $GLOBALS['user']->id) { + throw new AccessDeniedException(); + } + + $start = Request::getDateTime('start', DateTimeInterface::RFC3339_EXTENDED); + $end = Request::getDateTime('end', DateTimeInterface::RFC3339_EXTENDED); + + if (!$start || !$end) { + $this->response->set_status(400, 'Invalid date format.'); + $this->render_nothing(); + return; + } + + $start->setTimezone(new DateTimeZone('Europe/Berlin')); + $end->setTimezone(new DateTimeZone('Europe/Berlin')); + + $entry->start_time = $start->format('Hi'); + $entry->end_time = $end->format('Hi'); + $entry->dow = $start->format('N'); + if (!$entry->store()) { + $this->response->set_status(500, 'Cannot store entry.'); + $this->render_nothing(); + return; + } + + $entry_event = $entry->toEventData($GLOBALS['user']->id); + $this->render_json($entry_event->toFullcalendarEvent()); + } + + /** * This action handles adding and editing schedule entries. * * @param string $entry_id The ID of the entry to be modified. In case the ID is set to "add", a new entry @@ -360,18 +408,23 @@ class Calendar_ScheduleController extends AuthenticatedController //Provide good default values: $this->entry->colour_id = 1; if (Request::submitted('start')) { - //String format - $this->entry->dow = Request::int('dow',date('N')); - $this->entry->setFormattedStart(Request::get('start', date('H:00', strtotime('+1 hour')))); - $this->entry->setFormattedEnd(Request::get('end', date('H:00', strtotime('+2 hours')))); - } elseif (Request::submitted('begin')) { - //Fullcalendar: Timestamps - $begin = Request::get('begin'); - $end = Request::get('end'); - if ($begin && $end) { - $this->entry->dow = intval(date('N', $begin)); - $this->entry->setFormattedStart(date('H:i', $begin)); - $this->entry->setFormattedEnd(date('H:i', $end)); + //Fullcalendar provides date and time in RFC3339_EXTENDED format. + $start = Request::getDateTime('start', \DateTimeInterface::RFC3339_EXTENDED); + $end = Request::getDateTime('end', \DateTimeInterface::RFC3339_EXTENDED); + if ($start) { + //Correct the timezone first before setting start and end time. + $local_datetime = new DateTime(); + $start->setTimezone($local_datetime->getTimezone()); + $this->entry->dow = $start->format('N'); + $this->entry->start_time = $start->format('Hi'); + if ($end) { + $end->setTimezone($local_datetime->getTimezone()); + $this->entry->end_time = $end->format('Hi'); + } else { + $end = clone $start; + $end = $end->add(new DateInterval('PT1H')); + $this->entry->end_time = $end->format('Hi'); + } } } else { $begin = time() + 3600; diff --git a/app/controllers/institute/schedule.php b/app/controllers/institute/schedule.php index 1c5d091..931bdb4 100644 --- a/app/controllers/institute/schedule.php +++ b/app/controllers/institute/schedule.php @@ -50,19 +50,19 @@ class Institute_ScheduleController extends AuthenticatedController 'minTime' => '08:00', 'maxTime' => '20:00', 'allDaySlot' => false, - 'header' => [ - 'left' => '', - 'right' => '' + 'headerToolbar' => [ + 'start' => '', + 'end' => '' ], 'views' => [ - 'timeGridWeek' => [ - 'columnHeaderFormat' => ['weekday' => 'long'], + \Studip\Fullcalendar::VIEW_WEEK => [ + 'dayHeaderFormat' => ['weekday' => 'long'], 'weekends' => $calendar_settings['type_week'] === 'LONG', 'slotDuration' => $week_slot_duration ] ], - 'defaultView' => 'timeGridWeek', - 'defaultDate' => date('Y-m-d'), + 'initialView' => \Studip\Fullcalendar::VIEW_WEEK, + 'initialDate' => date('Y-m-d'), 'timeGridEventMinHeight' => 20, 'eventSources' => [ [ diff --git a/app/controllers/resources/ajax.php b/app/controllers/resources/ajax.php index 2d7c27b..8b771b3 100644 --- a/app/controllers/resources/ajax.php +++ b/app/controllers/resources/ajax.php @@ -756,7 +756,7 @@ class Resources_AjaxController extends AuthenticatedController $resource_id = Request::get('resource_id'); $interval_id = Request::get('interval_id'); - $begin = $this->convertDatetime(Request::get('begin')); + $begin = $this->convertDatetime(Request::get('start')); $end = $this->convertDatetime(Request::get('end')); //Check if a specific interval has been moved: @@ -842,7 +842,7 @@ class Resources_AjaxController extends AuthenticatedController throw new AccessDeniedException(); } - $request->begin = $this->convertDatetime(Request::get('begin')); + $request->begin = $this->convertDatetime(Request::get('start')); $request->end = $this->convertDatetime(Request::get('end')); try { @@ -913,7 +913,7 @@ class Resources_AjaxController extends AuthenticatedController return null; } - return DateTime::createFromFormat(DateTime::RFC3339, $input) + return DateTime::createFromFormat(DateTime::RFC3339_EXTENDED, $input) ?? DateTime::createFromFormat( 'Y-m-d\TH:i:s', $input, diff --git a/app/controllers/resources/booking.php b/app/controllers/resources/booking.php index f5dec25..8bc2d78 100644 --- a/app/controllers/resources/booking.php +++ b/app/controllers/resources/booking.php @@ -902,11 +902,21 @@ class Resources_BookingController extends AuthenticatedController $this->max_preparation_time = Config::get()->RESOURCES_MAX_PREPARATION_TIME; if ($mode == 'add') { - //In case a begin and end time are already given + //In case a start and end time are already given //use those values instead: - if (Request::submitted('begin') && Request::submitted('end')) { - $this->begin->setTimestamp(Request::get('begin')); - $this->end->setTimestamp(Request::get('end')); + if ((Request::submitted('begin') || Request::submitted('start')) && Request::submitted('end')) { + if (Request::submitted('start')) { + $this->begin = Request::getDateTime('start', DateTimeInterface::RFC3339_EXTENDED); + $this->end = Request::getDateTime('end', DateTimeInterface::RFC3339_EXTENDED); + //The date might be in UTC instead of the default time zone, so it has to be converted: + $this->begin->setTimezone(new DateTimeZone('Europe/Berlin')); + $this->end->setTimezone(new DateTimeZone('Europe/Berlin')); + } else { + //For backwards compatibility: Handle the timestamp in the old "begin" parameter. + //To be removed with Stud.IP 7.0. + $this->begin->setTimestamp(Request::get('begin')); + $this->end->setTimestamp(Request::get('end')); + } if (Request::get('semester_id')) { $this->booking_style = 'repeat'; $this->repetition_style = 'weekly'; @@ -1493,7 +1503,7 @@ class Resources_BookingController extends AuthenticatedController public function add_action($resource_id = null, $booking_type = null) { if (!$resource_id) { - $resource_id = Request::option('ressource_id'); + $resource_id = Request::option('resource_id'); } $resource_ids = Request::getArray('resource_ids'); diff --git a/app/controllers/resources/print.php b/app/controllers/resources/print.php index ea7da07..b8abb4d 100644 --- a/app/controllers/resources/print.php +++ b/app/controllers/resources/print.php @@ -52,12 +52,17 @@ class Resources_PrintController extends AuthenticatedController throw new AccessDeniedException(); } - $this->timestamp = Request::get('timestamp', time()); - - $this->date = new DateTime(); - $this->date->setTimestamp($this->timestamp); + $this->semester = Semester::find(Request::get('semester_id')); + if (!$this->semester) { + $this->semester = Semester::findCurrent(); + } $sidebar = Sidebar::get(); + $sidebar->addWidget( + new SemesterSelectorWidget( + $this->url_for('resources/print/individual_booking_plan/' . $resource_id), + ) + ); $views = new ViewsWidget(); if ($GLOBALS['user']->id && ($GLOBALS['user']->id != 'nobody')) { diff --git a/app/controllers/resources/room_planning.php b/app/controllers/resources/room_planning.php index b06e166..acd52ca 100644 --- a/app/controllers/resources/room_planning.php +++ b/app/controllers/resources/room_planning.php @@ -265,11 +265,13 @@ class Resources_RoomPlanningController extends AuthenticatedController $sidebar->addWidget($options); } - $sidebar->addWidget(new TemplateWidget( - _('Datum'), - $this->get_template_factory()->open('resources/room_planning/_sidebar_date_selection.php'), - ['date' => $this->date] - )); + $date_selector = new DateSelectWidget(); + $date_selector->setCalendarControl(true); + $default_date = Request::getDateTime('defaultDate'); + if ($default_date) { + $date_selector->setDate($default_date); + } + $sidebar->addWidget($date_selector); $actions = new ActionsWidget(); if ($GLOBALS['user']->id && $GLOBALS['user']->id !== 'nobody') { diff --git a/app/controllers/room_management/planning.php b/app/controllers/room_management/planning.php index c7fda25..312146c 100644 --- a/app/controllers/room_management/planning.php +++ b/app/controllers/room_management/planning.php @@ -83,10 +83,13 @@ class RoomManagement_PlanningController extends AuthenticatedController } $sidebar->addWidget($views); - $sidebar->addWidget(new TemplateWidget( - _('Datum'), - $this->get_template_factory()->open('resources/room_planning/_sidebar_date_selection.php') - )); + $date_selector = new DateSelectWidget(); + $date_selector->setCalendarControl(true); + $default_date = Request::getDateTime('defaultDate'); + if ($default_date) { + $date_selector->setDate($default_date); + } + $sidebar->addWidget($date_selector); $clipboards = Clipboard::getClipboardsForUser($GLOBALS['user']->id); if (!empty($clipboards)) { @@ -133,7 +136,6 @@ class RoomManagement_PlanningController extends AuthenticatedController foreach ($rooms as $room) { $this->scheduler_resources[] = [ 'id' => $room->id, - 'parent_name' => $room->building->name, 'title' => $room->name ]; } |
