aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMoritz Strohm <strohm@data-quest.de>2026-01-16 09:36:16 +0000
committerMoritz Strohm <strohm@data-quest.de>2026-01-16 09:36:16 +0000
commitb58142fe5fa1ba1a99d850baa1465df6fa6e0d3b (patch)
treed73956252dc17dd054d0db8e0f167a4103b7ba83 /lib
parentc3e07e221b0bef64d3ad4da48c6371c75ca12cc3 (diff)
updated Fullcalendar to version 6, closes #4887
Closes #4887 Merge request studip/studip!4438
Diffstat (limited to 'lib')
-rw-r--r--lib/classes/Fullcalendar.php88
-rw-r--r--lib/classes/InstituteCalendarHelper.php45
-rw-r--r--lib/classes/calendar/EventData.php2
-rw-r--r--lib/classes/calendar/Helper.php60
-rw-r--r--lib/models/calendar/CalendarDateAssignment.php8
-rw-r--r--lib/models/calendar/ScheduleEntry.php5
6 files changed, 153 insertions, 55 deletions
diff --git a/lib/classes/Fullcalendar.php b/lib/classes/Fullcalendar.php
index 8674601..d3bc823 100644
--- a/lib/classes/Fullcalendar.php
+++ b/lib/classes/Fullcalendar.php
@@ -3,6 +3,32 @@ namespace Studip;
class Fullcalendar
{
+ /**
+ * The standard month view for Stud.IP calendars.
+ */
+ const VIEW_MONTH = 'dayGridMonth';
+
+ /**
+ * The standard week view for Stud.IP calendars.
+ */
+ const VIEW_WEEK = 'timeGridWeek';
+
+ /**
+ * The standard day view for Stud.IP calendars.
+ */
+ const VIEW_DAY = 'timeGridDay';
+
+ /**
+ * The standard week view for Stud.IP group calendars.
+ */
+ const GROUP_WEEK = 'resourceTimelineWeek';
+
+ /**
+ * The standard day view for Stud.IP group calendars.
+ */
+ const GROUP_DAY = 'resourceTimelineDay';
+
+
protected $title;
/**
@@ -58,9 +84,9 @@ class Fullcalendar
public function setDefaultView(?string $view): void
{
if ($view === null) {
- unset($this->config['defaultView']);
+ unset($this->config['initialView']);
} else {
- $this->config['defaultView'] = $view;
+ $this->config['initialView'] = $view;
}
}
@@ -78,16 +104,56 @@ class Fullcalendar
$factory = new \Flexi\Factory($GLOBALS['STUDIP_BASE_PATH'] . '/templates');
$template = $factory->open('studip-fullcalendar.php');
$real_data_name = sprintf('data-%s', $this->data_name);
- return $template->render(
- [
- 'title' => $this->title,
- 'config' => $this->config,
- 'attributes' => array_merge(
- $this->attributes,
- [$real_data_name => '1']
- )
- ]
+
+ //Move the Stud.IP parts of the configuration out of the
+ //fullcalendar configuration:
+ $fullcalendar_config = $this->config;
+ $template_params = [
+ 'title' => $this->title,
+ 'dialogSize' => 'auto',
+ 'actionUrls' => [],
+ 'displayHolidays' => true,
+ 'displayVacations' => true,
+ 'externalDroppableContainerId' => '',
+ 'externalDroppableEventSelector' => '',
+ 'eventColourPicker' => false
+ ];
+ if (!empty($this->attributes['class'])) {
+ $template_params['extraClasses'] = $this->attributes['class'];
+ unset($this->attributes['class']);
+ } else {
+ $template_params['extraClasses'] = '';
+ }
+ $template_params['attributes'] = array_merge(
+ $this->attributes,
+ [$real_data_name => '1']
);
+ if (array_key_exists('studip_urls', $fullcalendar_config)
+ && is_array($fullcalendar_config['studip_urls'])
+ ) {
+ $template_params['actionUrls'] = $fullcalendar_config['studip_urls'];
+ unset($fullcalendar_config['studip_urls']);
+ }
+
+ $studip_config_map = [
+ 'dialog_size' => 'dialogSize',
+ 'display_holidays' => 'displayHolidays',
+ 'display_vacations' => 'displayVacations',
+ 'external_droppable_container_id' => 'externalDroppableContainerId',
+ 'external_droppable_event_selector' => 'externalDroppableEventSelector',
+ 'event_colour_picker' => 'eventColourPicker',
+ ];
+
+ foreach ($studip_config_map as $config_key => $param_key) {
+ if (array_key_exists($config_key, $fullcalendar_config)) {
+ $template_params[$param_key] = $fullcalendar_config[$config_key];
+ unset($fullcalendar_config[$config_key]);
+ }
+ }
+
+ $template_params['config'] = $fullcalendar_config;
+
+ return $template->render($template_params);
}
/**
diff --git a/lib/classes/InstituteCalendarHelper.php b/lib/classes/InstituteCalendarHelper.php
index 54393fa..404ab6a 100644
--- a/lib/classes/InstituteCalendarHelper.php
+++ b/lib/classes/InstituteCalendarHelper.php
@@ -190,7 +190,7 @@ class InstituteCalendarHelper
}
$event_colors[$event_id][$institut_id] = $color;
$df[0]->content = serialize($event_colors);
- return $df[0]->store();
+ return $df[0]->store() !== false;
}
return false;
}
@@ -395,7 +395,7 @@ class InstituteCalendarHelper
$end_time[0] += 2;
$end_time = implode(':', $end_time);
- $move_url = URLHelper::getURL('dispatch.php/admin/courseplanning/move_event');
+ $move_url = URLHelper::getURL('dispatch.php/admin/courseplanning/move_event/' . $cycle_date->id);
$name = $course->getFullName('number-name');
if ($start_time != $cycle_date['start_time'] && $end_time != $cycle_date['end_time']) {
@@ -451,7 +451,7 @@ class InstituteCalendarHelper
$events[] = [
'resourceId' => $resource_column,
'id' => $cycle_date->id,
- 'title' => empty($fields) ? $name : '',
+ 'title' => '',
'start' => $start,
'end' => $end,
'textColor' => $textcolor,
@@ -462,14 +462,23 @@ class InstituteCalendarHelper
'durationEditable' => $is_duration_editable,
'resourceEditable' => true,
'studip_api_urls' => ['move' => $move_url],
- 'studip_view_urls' => ['edit' => URLHelper::getURL('dispatch.php/course/details/index/' . $cycle_date->seminar_id)],
+ 'studip_view_urls' => [
+ 'show' =>URLHelper::getURL('dispatch.php/course/details/index/' . $cycle_date->seminar_id)
+ ],
'metadate_id' => $cycle_date->metadate_id,
'course_id' => $cycle_date->seminar_id,
'tooltip' => self::getCycleInfos($course, $cycle_date),
'icon' => $is_start_editable ? '' : 'lock-locked',
'conform' => $conform,
// custom props (event.extendedProps)
- 'content_fields' => $fields,
+ 'title-lines' => $fields,
+ 'action-icons' => [
+ [
+ 'url' => URLHelper::getURL('dispatch.php/admin/courseplanning/pick_color/' . $cycle_date->id . '/index'),
+ 'icon_name' => 'group4',
+ 'label' => _('Farbe wählen')
+ ]
+ ]
];
}
}, array_keys($courses));
@@ -535,7 +544,7 @@ class InstituteCalendarHelper
$event_columns = self::getCourseEventcolumns($course);
$event_colors = self::getCourseEventcolors($course);
- $move_url = URLHelper::getURL('dispatch.php/admin/courseplanning/move_event');
+ $move_url = URLHelper::getURL('dispatch.php/admin/courseplanning/move_event/' . $cycle_date->id);
$name = $course->getFullName('number-name');
if ($start_time != $cycle_date['start_time'] && $end_time != $cycle_date['end_time']) {
@@ -574,6 +583,13 @@ class InstituteCalendarHelper
}
}
+ $fields = [
+ 'course_number' => UserConfig::get($GLOBALS['user']->id)->TIMETABLE_COURSE_NUMBER_VISIBLE ? ($course->veranstaltungsnummer ?: _('(keine VA-Nummer)')) : null,
+ 'course_name' => UserConfig::get($GLOBALS['user']->id)->TIMETABLE_COURSE_NAME_VISIBLE ? $course->getFullName('name') : null,
+ 'lecturers' => UserConfig::get($GLOBALS['user']->id)->TIMETABLE_LECTURERS_VISIBLE ? self::getLecturers($course) : null,
+ 'room' => UserConfig::get($GLOBALS['user']->id)->TIMETABLE_ROOMS_VISIBLE ? '' : null
+ ];
+
return [
'resourceId' => $resource_column,
'id' => $cycle_date->id,
@@ -592,7 +608,16 @@ class InstituteCalendarHelper
'metadate_id' => $cycle_date->metadate_id,
'course_id' => $cycle_date->seminar_id,
'tooltip' => self::getCycleInfos($course, $cycle_date),
- 'icon' => $is_start_editable ? '' : 'lock-locked'
+ 'icon' => $is_start_editable ? '' : 'lock-locked',
+ // custom props (event.extendedProps)
+ 'title-lines' => $fields,
+ 'action-icons' => [
+ [
+ 'url' => URLHelper::getURL('dispatch.php/admin/courseplanning/pick_color/' . $cycle_date->id . '/index'),
+ 'icon_name' => 'group4',
+ 'label' => _('Farbe wählen')
+ ]
+ ]
];
}
@@ -638,8 +663,8 @@ class InstituteCalendarHelper
if (!$GLOBALS['MVV_MODUL']['STATUS']['values'][$modul->stat]['public']) {
return false;
}
- $modul_start = Semester::find($modul->start)->beginn ?: 0;
- $modul_end = Semester::find($modul->end)->ende ?: PHP_INT_MAX;
+ $modul_start = Semester::find($modul->start)->beginn ?? 0;
+ $modul_end = Semester::find($modul->end)->ende ?? PHP_INT_MAX;
return $modul_end >= $course->start_semester->beginn
&& (
$course->isOpenEnded()
@@ -718,7 +743,7 @@ class InstituteCalendarHelper
return $info_string;
}
- private static function getLecturers(Course $course): array
+ private static function getLecturers(Course $course): string
{
$dozenten = [];
$lecturers = '';
diff --git a/lib/classes/calendar/EventData.php b/lib/classes/calendar/EventData.php
index db1e472..233f131 100644
--- a/lib/classes/calendar/EventData.php
+++ b/lib/classes/calendar/EventData.php
@@ -116,7 +116,7 @@ class EventData
'studip_range_id' => $this->range_id,
'studip_view_urls' => $this->view_urls,
'studip_api_urls' => $this->api_urls,
- 'icon' => $this->icon
+ 'icons' => $this->icon ? explode(',', $this->icon) : [] //Backwards compatibility in case only one icon is set.
] + ($this->group_id ? ['groupId' => $this->group_id] : []);
}
}
diff --git a/lib/classes/calendar/Helper.php b/lib/classes/calendar/Helper.php
index 4bcd4f6..afcfbfd 100644
--- a/lib/classes/calendar/Helper.php
+++ b/lib/classes/calendar/Helper.php
@@ -159,15 +159,15 @@ class Helper
}
$available_views = [
- 'timeGridWeek' => [
- 'columnHeaderFormat' => ['weekday' => 'short'],
+ \Studip\Fullcalendar::VIEW_WEEK => [
+ 'dayHeaderFormat' => ['weekday' => 'short'],
'slotDuration' => $slot_duration
]
];
if (!in_array(date('N'), $hidden_days)) {
//The current day is visible: Allow a day view:
- $available_views['timeGridDay'] = [
- 'columnHeaderFormat' => ['weekday' => 'short'],
+ $available_views[\Studip\Fullcalendar::VIEW_DAY] = [
+ 'dayHeaderFormat' => ['weekday' => 'short'],
'slotDuration' => $slot_duration
];
}
@@ -175,27 +175,28 @@ class Helper
return new \Studip\Fullcalendar(
_('Stundenplan'),
[
- 'editable' => true,
- 'selectable' => true,
- 'dialog_size' => 'auto',
- 'minTime' => $schedule_settings['start_time'] ?? '08:00',
- 'maxTime' => $schedule_settings['end_time'] ?? '20:00',
- 'allDaySlot' => false,
- 'header' => [
- 'left' => count($available_views) > 1 ? implode(',', array_keys($available_views)) : '',
- 'right' => 'prev,today,next'
+ 'editable' => true,
+ 'selectable' => true,
+ 'dialog_size' => 'auto',
+ 'slotMinTime' => $schedule_settings['start_time'] ?? '08:00',
+ 'slotMaxTime' => $schedule_settings['end_time'] ?? '20:00',
+ 'allDaySlot' => false,
+ 'headerToolbar' => [
+ 'start' => count($available_views) > 1 ? implode(',', array_keys($available_views)) : '',
+ 'end' => 'prev,today,next'
],
'views' => $available_views,
- 'columnHeaderFormat' => ['weekday' => 'short'],
- 'defaultView' => 'timeGridWeek',
- 'defaultDate' => date('Y-m-d'),
+ 'dayHeaderFormat' => ['weekday' => 'short'],
+ 'initialView' => \Studip\Fullcalendar::VIEW_WEEK,
+ 'initialDate' => date('Y-m-d'),
'slotLabelFormat' => [
'hour' => 'numeric',
'minute' => '2-digit',
'omitZeroMinute' => false
],
- 'weekends' => true,
- 'hiddenDays' => $fullcalendar_hidden_days,
+ 'weekends' => true,
+ 'weekNumbers' => false,
+ 'hiddenDays' => $fullcalendar_hidden_days,
'timeGridEventMinHeight' => 20,
'eventSources' => [
[
@@ -230,12 +231,12 @@ class Helper
$calendar_settings = $calendar_owner->getConfiguration()->CALENDAR_SETTINGS ?? [];
//Map calendar settings to fullcalendar settings:
- $default_view = 'timeGridWeek';
+ $default_view = \Studip\Fullcalendar::VIEW_WEEK;
if (!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;
}
}
@@ -259,29 +260,32 @@ class Helper
'allDaySlot' => true,
'allDayText' => '',
'header' => [
- 'left' => 'dayGridYear,dayGridMonth,timeGridWeek,timeGridDay',
+ 'left' => implode(
+ ',',
+ [\Studip\Fullcalendar::VIEW_MONTH, \Studip\Fullcalendar::VIEW_WEEK, \Studip\Fullcalendar::VIEW_DAY]
+ ),
'right' => '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_durations['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_durations['day']
]
],
- 'defaultView' => $default_view,
+ 'initialView' => $default_view,
'eventSources' => [
[
'url' => \URLHelper::getURL(
diff --git a/lib/models/calendar/CalendarDateAssignment.php b/lib/models/calendar/CalendarDateAssignment.php
index 515f26b..0e2f1e8 100644
--- a/lib/models/calendar/CalendarDateAssignment.php
+++ b/lib/models/calendar/CalendarDateAssignment.php
@@ -30,8 +30,8 @@
- "ACKNOWLEDGED": The calendar owner only acknowledged that the date exists
but doesn't necessarily participate in it.
* @property CalendarDate $calendar_date The associated calendar date object.
- * @property User $user
- * @property Course $course
+ * @property User $user
+ * @property Course $course
*/
class CalendarDateAssignment extends SimpleORMap implements Event
{
@@ -663,8 +663,8 @@ class CalendarDateAssignment extends SimpleORMap implements Event
$studip_urls['show'] = URLHelper::getURL('dispatch.php/calendar/date/index/' . $this->calendar_date_id, $show_url_params);
if ($this->isWritable($user_id)) {
- $action_urls['resize_dialog'] = URLHelper::getURL('dispatch.php/calendar/date/move/' . $this->calendar_date_id);
- $action_urls['move_dialog'] = URLHelper::getURL('dispatch.php/calendar/date/move/' . $this->calendar_date_id, ['original_date' => $begin->format('Y-m-d')]);
+ $action_urls['resize'] = URLHelper::getURL('dispatch.php/calendar/date/move/' . $this->calendar_date_id);
+ $action_urls['move'] = URLHelper::getURL('dispatch.php/calendar/date/move/' . $this->calendar_date_id, ['original_date' => $begin->format('Y-m-d')]);
}
}
diff --git a/lib/models/calendar/ScheduleEntry.php b/lib/models/calendar/ScheduleEntry.php
index ca72e99..c9eb13c 100644
--- a/lib/models/calendar/ScheduleEntry.php
+++ b/lib/models/calendar/ScheduleEntry.php
@@ -312,7 +312,10 @@ class ScheduleEntry extends SimpleORMap implements Event
[
'show' => URLHelper::getURL('dispatch.php/calendar/schedule/entry/' . $this->id)
],
- [],
+ [
+ 'resize' => URLHelper::getURL('dispatch.php/calendar/schedule/move_entry/' . $this->id),
+ 'move' => URLHelper::getURL('dispatch.php/calendar/schedule/move_entry/' . $this->id)
+ ],
'',
$GLOBALS['PERS_TERMIN_KAT'][$this->colour_id]['border_color'] ?? '#000000',
$this->isAllDayEvent()