diff --git a/components/ILIAS/BookingManager/BookingProcess/class.SlotGUI.php b/components/ILIAS/BookingManager/BookingProcess/class.SlotGUI.php index ba5bb4e6738c..8b99ef1fa5da 100755 --- a/components/ILIAS/BookingManager/BookingProcess/class.SlotGUI.php +++ b/components/ILIAS/BookingManager/BookingProcess/class.SlotGUI.php @@ -20,58 +20,44 @@ namespace ILIAS\BookingManager\BookingProcess; -use ILIAS\BookingManager\getObjectSettingsCommand; +use ILIAS\UI\Factory as UIFactory; +use ILIAS\UI\Renderer; +use ilTemplate; -/** - * @author Alexander Killing - */ class SlotGUI { - protected int $color_nr; - protected $from; - protected string $to; - protected int $from_ts; - protected int $to_ts; - protected string $title; - protected int $available; - protected string $link; + protected UIFactory $ui_factory; + protected Renderer $ui_renderer; public function __construct( - string $link, - string $from, - string $to, - int $from_ts, - int $to_ts, - string $title, - int $available, - int $color_nr + protected string $link, + protected string $from, + protected string $to, + protected int $from_ts, + protected int $to_ts, + protected string $title, + protected int $available, + protected int $color_nr ) { - $this->from = $from; - $this->to = $to; - $this->from_ts = $from_ts; - $this->to_ts = $to_ts; - $this->title = $title; - $this->available = $available; - $this->link = $link; - $this->color_nr = $color_nr; + + global $DIC; + $this->ui_factory = $DIC->ui()->factory(); + $this->ui_renderer = $DIC->ui()->renderer(); } public function render(): string { - global $DIC; - $ui = $DIC->ui(); - $tpl = new \ilTemplate("tpl.slot.html", true, true, "components/ILIAS/BookingManager/BookingProcess"); + $tpl = new ilTemplate('tpl.slot.html', true, true, 'components/ILIAS/BookingManager/BookingProcess'); - $modal = $ui->factory()->modal()->roundtrip("", $ui->factory()->legacy()->content("")); - $url = $this->link . '&replaceSignal=' . $modal->getReplaceSignal()->getId(); + $modal = $this->ui_factory->modal()->roundtrip('', $this->ui_factory->legacy()->content('')); + $url = "{$this->link}&replaceSignal={$modal->getReplaceSignal()->getId()}"; $modal = $modal->withAsyncRenderUrl($url); - $button = $ui->factory()->button()->shy($this->title, "#") - ->withOnClick($modal->getShowSignal()); + $button = $this->ui_factory->button()->shy($this->title, '#')->withOnClick($modal->getShowSignal()); - $tpl->setVariable("OBJECT_LINK", $ui->renderer()->render([$button, $modal])); - $tpl->setVariable("TIME", $this->from . "-" . $this->to); - $tpl->setVariable("COLOR_NR", $this->color_nr); - $tpl->setVariable("AVAILABILITY", "(" . $this->available . ") "); + $tpl->setVariable('OBJECT_LINK', $this->ui_renderer->render([$button, $modal])); + $tpl->setVariable('TIME', ($this->to_ts - $this->from_ts) !== 86400 ? "{$this->from}-{$this->to}" : ''); + $tpl->setVariable('COLOR_NR', $this->color_nr); + $tpl->setVariable('AVAILABILITY', "({$this->available}) "); return $tpl->get(); } diff --git a/components/ILIAS/BookingManager/BookingProcess/class.WeekGridEntry.php b/components/ILIAS/BookingManager/BookingProcess/class.WeekGridEntry.php index 4be68e2be876..669d4a932fc4 100755 --- a/components/ILIAS/BookingManager/BookingProcess/class.WeekGridEntry.php +++ b/components/ILIAS/BookingManager/BookingProcess/class.WeekGridEntry.php @@ -20,24 +20,13 @@ namespace ILIAS\BookingManager\BookingProcess; -/** - * - * @author Alexander Killing - */ class WeekGridEntry { - protected int $start; - protected int $end; - protected string $html; - public function __construct( - int $start, - int $end, - string $html + protected int $start, + protected int $end, + protected string $html ) { - $this->start = $start; - $this->end = $end; - $this->html = $html; } public function getStart(): int @@ -54,4 +43,9 @@ public function getHTML(): string { return $this->html; } + + public function isAllDay(): bool + { + return ($this->getEnd() - $this->getStart()) === 86400; + } } diff --git a/components/ILIAS/BookingManager/BookingProcess/class.WeekGridGUI.php b/components/ILIAS/BookingManager/BookingProcess/class.WeekGridGUI.php index c63957c16364..bee050479296 100755 --- a/components/ILIAS/BookingManager/BookingProcess/class.WeekGridGUI.php +++ b/components/ILIAS/BookingManager/BookingProcess/class.WeekGridGUI.php @@ -63,7 +63,7 @@ public function __construct( protected function getHoursOfDay(): array { - $hours = array(); + $hours = [0 => ['caption' => $this->lng->txt('book_all_day'), 'start' => '00:00', 'end' => '24:00']]; $sep = "
-
"; for ($i = $this->day_start;$i <= $this->day_end;$i++) { $caption = ""; @@ -178,7 +178,7 @@ public function render(): string foreach ($hours as $hour => $days) { $caption = $days["caption"]; $day_of_week = 0; - foreach (\ilCalendarUtil::_buildWeekDayList($this->seed, $this->week_start)->get() as $date) { + foreach ($weekday_list as $date) { $data = $cells[$day_of_week][$hour]; $total_tds = $cells[$day_of_week]["col_span"]; foreach ($data["entries"] as $e) { @@ -216,10 +216,13 @@ public function render(): string */ protected function getEntriesForCell(int $start_ts, int $end_ts): array { - return array_filter($this->entries, function ($e) use ($start_ts, $end_ts) { - /** @var WeekGridEntry $e */ - return ($e->getStart() < $end_ts && $e->getEnd() > $start_ts); - }); + $all_day_cell = ($end_ts - $start_ts) === 86400; + return array_filter( + $this->entries, + static fn(WeekGridEntry $e) => + ($all_day_cell && $e->isAllDay() && $e->getStart() === $start_ts && $e->getEnd() === $end_ts) + || (!$all_day_cell && !$e->isAllDay() && $e->getStart() < $end_ts && $e->getEnd() > $start_ts) + ); } protected function renderCell(array $data) diff --git a/components/ILIAS/BookingManager/FORMS.md b/components/ILIAS/BookingManager/FORMS.md index 1cc0a7855767..2f9dfc22e20a 100755 --- a/components/ILIAS/BookingManager/FORMS.md +++ b/components/ILIAS/BookingManager/FORMS.md @@ -12,4 +12,5 @@ This is an overview of forms that need to be migrated to the KS and missing inpu 1. Reservation, cancel aggregation - none 1. Schedule - - ilScheduleInputGUI \ No newline at end of file + - ilScheduleInputGUI + - ilScheduleDaysInputGUI (weekday checkboxes only) \ No newline at end of file diff --git a/components/ILIAS/BookingManager/Reservations/class.ilBookingReservationsTableGUI.php b/components/ILIAS/BookingManager/Reservations/class.ilBookingReservationsTableGUI.php index d557655ecb92..be0d3b3e2ec4 100755 --- a/components/ILIAS/BookingManager/Reservations/class.ilBookingReservationsTableGUI.php +++ b/components/ILIAS/BookingManager/Reservations/class.ilBookingReservationsTableGUI.php @@ -325,8 +325,11 @@ public function initFilter( $day_caption = ilCalendarUtil::_numericDayToString((int) $map[$day], false); foreach ($slots as $slot) { - $idx = $map[$day] . "_" . $slot; - $options[$idx] = $day_caption . ", " . $slot; + $slot = $slot === ilBookingSchedule::ALL_DAY_SLOT + ? $this->lng->txt('book_all_day') + : $slot; + + $options["{$map[$day]}_{$slot}"] = "{$day_caption}, {$slot}"; } } } @@ -643,7 +646,12 @@ protected function fillRow(array $a_set): void ilCalendarUtil::_numericDayToString((int) $a_set["weekday"], false) ); } - $this->tpl->setVariable("VALUE_SLOT", $a_set["slot"]); + $this->tpl->setVariable( + "VALUE_SLOT", + in_array($a_set["slot"], [ilBookingSchedule::ALL_DAY_SLOT, "00:00 - 00:00"], true) + ? $this->lng->txt('book_all_day') + : $a_set["slot"] + ); $this->tpl->setVariable("VALUE_COUNTER", $a_set["counter"]); } elseif (in_array( $a_set['status'], diff --git a/components/ILIAS/BookingManager/Schedule/class.ilBookingSchedule.php b/components/ILIAS/BookingManager/Schedule/class.ilBookingSchedule.php index be3ad69ad9c0..be1812d5d7dc 100755 --- a/components/ILIAS/BookingManager/Schedule/class.ilBookingSchedule.php +++ b/components/ILIAS/BookingManager/Schedule/class.ilBookingSchedule.php @@ -22,6 +22,10 @@ */ class ilBookingSchedule { + public const SCHEDULE_TYPE_TIME_PERIOD = 'time_period'; + public const SCHEDULE_TYPE_ALL_DAY = 'all_day'; + public const ALL_DAY_SLOT = '00:00-24:00'; + protected ilDBInterface $db; protected int $id = 0; protected string $title = ""; @@ -34,6 +38,7 @@ class ilBookingSchedule protected array $definition; protected ?ilDateTime $av_from = null; protected ?ilDateTime $av_to = null; + protected string $schedule_type = self::SCHEDULE_TYPE_TIME_PERIOD; public function __construct( ?int $a_id = null @@ -169,13 +174,23 @@ public function getAvailabilityTo(): ?ilDateTime return $this->av_to; } + public function getScheduleType(): string + { + return $this->schedule_type; + } + + public function setScheduleType(string $a_schedule_type): void + { + $this->schedule_type = $a_schedule_type; + } + protected function read(): void { $ilDB = $this->db; if ($this->id) { $set = $ilDB->query('SELECT title,raster,rent_min,rent_max,auto_break,' . - 'deadline,av_from,av_to' . + 'deadline,av_from,av_to,schedule_type' . ' FROM booking_schedule' . ' WHERE booking_schedule_id = ' . $ilDB->quote($this->id, 'integer')); $row = $ilDB->fetchAssoc($set); @@ -189,6 +204,7 @@ protected function read(): void $this->setMaxRental($row['rent_max']); $this->setAutoBreak($row['auto_break']); } + $this->setScheduleType($row['schedule_type'] ?? self::SCHEDULE_TYPE_TIME_PERIOD); // load definition $definition = array(); @@ -221,12 +237,13 @@ public function save(): ?int $ilDB->manipulate('INSERT INTO booking_schedule' . ' (booking_schedule_id,title,pool_id,raster,rent_min,rent_max,auto_break,' . - 'deadline,av_from,av_to)' . + 'deadline,av_from,av_to,schedule_type)' . ' VALUES (' . $ilDB->quote($this->id, 'integer') . ',' . $ilDB->quote($this->getTitle(), 'text') . ',' . $ilDB->quote($this->getPoolId(), 'integer') . ',' . $ilDB->quote($this->getRaster(), 'integer') . ',' . $ilDB->quote($this->getMinRental(), 'integer') . ',' . $ilDB->quote($this->getMaxRental(), 'integer') . ',' . $ilDB->quote($this->getAutoBreak(), 'integer') . ',' . $ilDB->quote($this->getDeadline(), 'integer') . - ',' . $ilDB->quote($av_from, 'integer') . ',' . $ilDB->quote($av_to, 'integer') . ')'); + ',' . $ilDB->quote($av_from, 'integer') . ',' . $ilDB->quote($av_to, 'integer') . + ',' . $ilDB->quote($this->getScheduleType(), 'text') . ')'); $this->saveDefinition(); @@ -258,6 +275,7 @@ public function update(): bool ', deadline = ' . $ilDB->quote($this->getDeadline(), 'integer') . ', av_from = ' . $ilDB->quote($av_from, 'integer') . ', av_to = ' . $ilDB->quote($av_to, 'integer') . + ', schedule_type = ' . $ilDB->quote($this->getScheduleType(), 'text') . ' WHERE booking_schedule_id = ' . $ilDB->quote($this->id, 'integer')); $this->saveDefinition(); @@ -277,6 +295,7 @@ public function doClone(int $a_pool_id): int $new_obj->setDefinition($this->getDefinition()); $new_obj->setAvailabilityFrom($this->getAvailabilityFrom()); $new_obj->setAvailabilityTo($this->getAvailabilityTo()); + $new_obj->setScheduleType($this->getScheduleType()); return $new_obj->save(); } diff --git a/components/ILIAS/BookingManager/Schedule/class.ilBookingScheduleGUI.php b/components/ILIAS/BookingManager/Schedule/class.ilBookingScheduleGUI.php index e9897081371e..6635abf22eac 100755 --- a/components/ILIAS/BookingManager/Schedule/class.ilBookingScheduleGUI.php +++ b/components/ILIAS/BookingManager/Schedule/class.ilBookingScheduleGUI.php @@ -155,10 +155,27 @@ public function initForm(string $a_mode = 'create', ?int $id = null): ilProperty $title->setMaxLength(120); $form_gui->addItem($title); - $definition = new ilScheduleInputGUI($this->lng->txt('book_schedule_days'), 'days'); - $definition->setInfo($this->lng->txt('book_schedule_days_info')); - $definition->setRequired(true); - $form_gui->addItem($definition); + $schedule = $a_mode === 'edit' && $id !== null ? new ilBookingSchedule($id) : null; + $schedule_type = $schedule?->getScheduleType() ?? ilBookingSchedule::SCHEDULE_TYPE_TIME_PERIOD; + + $radio = new ilRadioGroupInputGUI($this->lng->txt('schedule_type'), 'schedule_type'); + $radio->setValue($schedule_type); + $radio->setRequired(true); + + $opt_time_period = new ilRadioOption($this->lng->txt('book_schedule_days'), ilBookingSchedule::SCHEDULE_TYPE_TIME_PERIOD); + $definition_slots = new ilScheduleInputGUI('', 'days'); + $definition_slots->setInfo($this->lng->txt('book_schedule_days_info')); + $definition_slots->setRequired(true); + $opt_time_period->addSubItem($definition_slots); + $radio->addOption($opt_time_period); + + $opt_all_days = new ilRadioOption($this->lng->txt('book_schedule_all_day'), ilBookingSchedule::SCHEDULE_TYPE_ALL_DAY); + $definition_all_day = new ilScheduleDaysInputGUI('', 'days_all_day'); + $definition_all_day->setRequired(true); + $opt_all_days->addSubItem($definition_all_day); + $radio->addOption($opt_all_days); + + $form_gui->addItem($radio); $deadline_opts = new ilRadioGroupInputGUI($this->lng->txt('book_deadline_options'), 'deadline_opts'); $deadline_opts->setRequired(true); @@ -182,10 +199,6 @@ public function initForm(string $a_mode = 'create', ?int $id = null): ilProperty $deadline_slot = new ilRadioOption($this->lng->txt('book_deadline_slot_end'), 'slot_end'); $deadline_opts->addOption($deadline_slot); - if ($a_mode === 'edit') { - $schedule = new ilBookingSchedule($id); - } - $av = new ilFormSectionHeaderGUI(); $av->setTitle($this->lng->txt('obj_activation_list_gui')); $form_gui->addItem($av); @@ -201,14 +214,13 @@ public function initForm(string $a_mode = 'create', ?int $id = null): ilProperty $to->setShowTime(true); $form_gui->addItem($to); - if ($a_mode === 'edit') { + if ($schedule instanceof ilBookingSchedule) { $form_gui->setTitle($this->lng->txt('book_edit_schedule')); $item = new ilHiddenInputGUI('schedule_id'); $item->setValue($id); $form_gui->addItem($item); - $schedule = new ilBookingSchedule($id); $title->setValue($schedule->getTitle()); $from->setDate($schedule->getAvailabilityFrom()); $to->setDate($schedule->getAvailabilityTo()); @@ -223,7 +235,12 @@ public function initForm(string $a_mode = 'create', ?int $id = null): ilProperty $deadline_opts->setValue('slot_end'); } - $definition->setValue($schedule->getDefinitionBySlots()); + match ($schedule_type) { + ilBookingSchedule::SCHEDULE_TYPE_ALL_DAY => $definition_all_day->setValue( + $schedule->getDefinitionBySlots()[ilBookingSchedule::ALL_DAY_SLOT] + ), + default => $definition_slots->setValue($schedule->getDefinitionBySlots()), + }; $form_gui->addCommandButton('update', $this->lng->txt('save')); } else { @@ -284,6 +301,7 @@ protected function formToObject(ilPropertyFormGUI $form, ilBookingSchedule $sche { $schedule->setTitle($form->getInput('title')); $schedule->setPoolId($this->obj_data_cache->lookupObjId($this->ref_id)); + $schedule->setScheduleType($form->getInput('schedule_type')); $from = $form->getItemByPostVar('from'); if ($from !== null) { @@ -301,7 +319,11 @@ protected function formToObject(ilPropertyFormGUI $form, ilBookingSchedule $sche 'slot_end' => $schedule->setDeadline(-1), }; - $schedule->setDefinitionBySlots($form->getInput('days')); + $days = match ($schedule->getScheduleType()) { + ilBookingSchedule::SCHEDULE_TYPE_TIME_PERIOD => $form->getInput("days"), + ilBookingSchedule::SCHEDULE_TYPE_ALL_DAY => [ilBookingSchedule::ALL_DAY_SLOT => $form->getInput("days_all_day")], + }; + $schedule->setDefinitionBySlots($days); } private function configureScheduleTable(ScheduleManager $schedule_manager): ScheduleTable diff --git a/components/ILIAS/BookingManager/classes/Setup/class.ilBookingManagerDBUpdateSteps.php b/components/ILIAS/BookingManager/classes/Setup/class.ilBookingManagerDBUpdateSteps.php index 5d30594c4bc7..075d393c2f62 100755 --- a/components/ILIAS/BookingManager/classes/Setup/class.ilBookingManagerDBUpdateSteps.php +++ b/components/ILIAS/BookingManager/classes/Setup/class.ilBookingManagerDBUpdateSteps.php @@ -20,6 +20,8 @@ namespace ILIAS\BookingManager\Setup; +use ilBookingSchedule; + class ilBookingManagerDBUpdateSteps implements \ilDatabaseUpdateSteps { protected \ilDBInterface $db; @@ -144,4 +146,19 @@ public function step_9(): void } } + public function step_10(): void + { + if (!$this->db->tableColumnExists('booking_schedule', 'schedule_type')) { + $this->db->addTableColumn( + 'booking_schedule', + 'schedule_type', + [ + 'type' => 'text', + 'notnull' => true, + 'length' => 64, + 'default' => ilBookingSchedule::SCHEDULE_TYPE_TIME_PERIOD + ] + ); + } + } } diff --git a/components/ILIAS/BookingManager/classes/class.ilScheduleDaysInputGUI.php b/components/ILIAS/BookingManager/classes/class.ilScheduleDaysInputGUI.php new file mode 100644 index 000000000000..70a940c4c833 --- /dev/null +++ b/components/ILIAS/BookingManager/classes/class.ilScheduleDaysInputGUI.php @@ -0,0 +1,82 @@ +value = $a_value; + } + + public function getValue(): array + { + return $this->value; + } + + public function checkInput(): bool + { + $data = $this->getPostData(); + foreach ($data as $day) { + if (!in_array($day, self::DAY_KEYS, true)) { + $this->setAlert($this->lng->txt('msg_input_does_not_match_regexp')); + return false; + } + } + return true; + } + + public function getPostData(): array + { + return $this->strArray($this->getPostVar()); + } + + public function setValueByArray(array $a_values): void + { + $this->setValue($this->getPostData()); + } + + protected function render(): string + { + $tpl = new ilTemplate('tpl.schedule_days_input.html', true, true, 'components/ILIAS/BookingManager'); + + foreach (self::DAY_KEYS as $offset => $day_value) { + $tpl->setCurrentBlock('day'); + $tpl->setVariable('ID', $this->getFieldId()); + $tpl->setVariable('POST_VAR', $this->getPostVar()); + $tpl->setVariable('DAY', $day_value); + $tpl->setVariable('TXT_DAY', ilCalendarUtil::_numericDayToString(($offset + 1) % 7, false, $this->lng)); + $tpl->setVariable('DAY_STATUS', in_array($day_value, $this->getValue(), true) ? ' checked="checked"' : ''); + $tpl->parseCurrentBlock(); + } + + return $tpl->get(); + } + + public function insert(ilTemplate $a_tpl): void + { + $a_tpl->setCurrentBlock('prop_generic'); + $a_tpl->setVariable('PROP_GENERIC', $this->render()); + $a_tpl->parseCurrentBlock(); + } +} diff --git a/components/ILIAS/BookingManager/src/BookableItem/Table/BookableItemWithScheduleTable.php b/components/ILIAS/BookingManager/src/BookableItem/Table/BookableItemWithScheduleTable.php index 2f3b1524b9fb..5660dfe241de 100644 --- a/components/ILIAS/BookingManager/src/BookableItem/Table/BookableItemWithScheduleTable.php +++ b/components/ILIAS/BookingManager/src/BookableItem/Table/BookableItemWithScheduleTable.php @@ -90,6 +90,7 @@ protected function loadRecords(mixed $filter_data): array $filter_data = is_array($filter_data) ? $filter_data : []; $period_bounds = $this->resolvePeriod($filter_data); + $time_slot_filter = $this->stringFilter($filter_data, 'time_slot'); $booking_objects = $this->loadFilteredBookingObjects( $this->stringFilter($filter_data, 'title'), @@ -107,13 +108,21 @@ protected function loadRecords(mixed $filter_data): array $object_id = (int) $item['booking_object_id']; $schedule = new ilBookingSchedule($schedule_id); + $is_all_day_schedule = $this->isAllDaySchedule($schedule); $slots = $this->enumerateSlots( $schedule, $period_bounds[0] ?? null, $period_bounds[1] ?? null ); foreach ($slots as $slot) { - $rows[] = $this->composeRow($item, $object_id, $slot['from'], $slot['to']); + if ( + $time_slot_filter !== null + && $this->getTimeSlotLabel($slot['from'], $slot['to'], $is_all_day_schedule) !== $time_slot_filter + ) { + continue; + } + + $rows[] = $this->composeRow($item, $object_id, $slot['from'], $slot['to'], $is_all_day_schedule); } } @@ -127,7 +136,11 @@ protected function buildRowCells(array $record): array { return [ 'availability' => $this->buildAvailabilityCell((int) $record['available'], (int) $record['nr_items']), - 'date_time' => $this->formatDateTime((int) $record['slot_from'], (int) $record['slot_to']), + 'date_time' => $this->formatDateTime( + (int) $record['slot_from'], + (int) $record['slot_to'], + (bool) $record['is_all_day'] + ), 'title' => (string) $record['title'], 'description' => nl2br((string) $record['description']), ]; @@ -211,8 +224,13 @@ private function enumerateSlots(ilBookingSchedule $schedule, ?int $period_start * @param array $item * @return array */ - private function composeRow(array $item, int $object_id, int $slot_from, int $slot_to): array - { + private function composeRow( + array $item, + int $object_id, + int $slot_from, + int $slot_to, + bool $is_all_day = false + ): array { $nr_items = (int) $item['nr_items']; $available = max($nr_items - $this->countActiveReservations($object_id, $slot_from, $slot_to), 0); @@ -229,6 +247,7 @@ private function composeRow(array $item, int $object_id, int $slot_from, int $sl 'has_reservations' => $this->hasActiveReservation($object_id, $slot_from, $slot_to), 'slot_from' => $slot_from, 'slot_to' => $slot_to, + 'is_all_day' => $is_all_day, 'schedule_id' => (int) $item['schedule_id'], 'post_text' => (string) ($item['post_text'] ?? ''), ]; @@ -282,16 +301,38 @@ private function countActiveReservations(int $object_id, int $slot_from, int $sl ); } - public function formatDateTime(int $slot_from, int $slot_to): string + public function formatDateTime(int $slot_from, int $slot_to, bool $is_all_day = false): string { $this->lng->loadLanguageModule('dateplaner'); + if ($is_all_day) { + return ilDatePresentation::formatDate(new ilDateTime($slot_from, IL_CAL_UNIX, 'UTC')) + . ", {$this->lng->txt('book_all_day')}"; + } + return ilDatePresentation::formatPeriod( new ilDateTime($slot_from, IL_CAL_UNIX, 'UTC'), new ilDateTime($slot_to, IL_CAL_UNIX, 'UTC') ); } + private function isAllDaySchedule(ilBookingSchedule $schedule): bool + { + return $schedule->getScheduleType() === ilBookingSchedule::SCHEDULE_TYPE_ALL_DAY; + } + + private function getTimeSlotLabel(int $slot_from, int $slot_to, bool $is_all_day): string + { + if ($is_all_day) { + return $this->lng->txt('book_all_day'); + } + + $from = new DateTimeImmutable("@{$slot_from}"); + $to = new DateTimeImmutable("@{$slot_to}"); + + return "{$from->format('H:i')} - {$to->format('H:i')}"; + } + private function stringFilter(array $filter_data, string $key): ?string { return trim((string) ($filter_data[$key] ?? '')) ?: null; diff --git a/components/ILIAS/BookingManager/src/Booking/Table/BookingsTable.php b/components/ILIAS/BookingManager/src/Booking/Table/BookingsTable.php index 98f605528707..cc30d6caae1c 100644 --- a/components/ILIAS/BookingManager/src/Booking/Table/BookingsTable.php +++ b/components/ILIAS/BookingManager/src/Booking/Table/BookingsTable.php @@ -192,14 +192,9 @@ static function (array $booking) use ($filter_data_value): bool { $booking['date_from'] >= $period_from && $booking['date_to'] <= $period_to ); }, - 'time_slot' => static fn(array $bookings): array => array_filter( + 'time_slot' => fn(array $bookings): array => array_filter( $bookings, - static function (array $booking) use ($filter_data_value): bool { - $date_from = new DateTimeImmutable("@{$booking['date_from']}"); - $booking['date_to']++; - $date_to = new DateTimeImmutable("@{$booking['date_to']}"); - return "{$date_from->format('H:i')} - {$date_to->format('H:i')}" === $filter_data_value; - } + fn(array $booking): bool => $this->getBookingTimeSlotFilterLabel($booking) === $filter_data_value ), 'past_bookings' => static function (array $bookings) use ($filter_data_value): array { $now = new DateTimeImmutable(); @@ -260,18 +255,11 @@ protected function sortRecords(Order $order, array $records): array $week_b = (int) (new DateTimeImmutable("@{$record_b['date_from']}"))->format('N'); return ($week_a <=> $week_b) * $order_direction; }, - 'time_slot' => static function (array $record_a, array $record_b) use ($order_direction): int { - $date_from_a = new DateTimeImmutable("@{$record_a['date_from']}"); - $record_a['date_to']++; - $date_to_a = new DateTimeImmutable("@{$record_a['date_to']}"); - $time_slot_a = "{$date_from_a->format('H:i')} - {$date_to_a->format('H:i')}"; - - $date_from_b = new DateTimeImmutable("@{$record_b['date_from']}"); - $record_b['date_to']++; - $date_to_b = new DateTimeImmutable("@{$record_b['date_to']}"); - $time_slot_b = "{$date_from_b->format('H:i')} - {$date_to_b->format('H:i')}"; - - return strcasecmp($time_slot_a, $time_slot_b) * $order_direction; + 'time_slot' => function (array $record_a, array $record_b) use ($order_direction): int { + return strcasecmp( + $this->getBookingTimeSlotFilterLabel($record_a), + $this->getBookingTimeSlotFilterLabel($record_b) + ) * $order_direction; }, 'unit_count' => static fn(array $record_a, array $record_b): int => 0 * $order_direction, 'message' => static fn(array $record_a, array $record_b): int => strcasecmp( @@ -525,4 +513,12 @@ private function parseBookingPeriodValue(mixed $value): ?int return null; } } + + protected function getBookingTimeSlotFilterLabel(array $booking): string + { + $date_from = new DateTimeImmutable("@{$booking['date_from']}"); + $date_to = new DateTimeImmutable('@' . ((int) $booking['date_to'] + 1)); + + return "{$date_from->format('H:i')} - {$date_to->format('H:i')}"; + } } diff --git a/components/ILIAS/BookingManager/src/Booking/Table/BookingsWithScheduleTable.php b/components/ILIAS/BookingManager/src/Booking/Table/BookingsWithScheduleTable.php index 6acef4234ea8..db0272bd5327 100644 --- a/components/ILIAS/BookingManager/src/Booking/Table/BookingsWithScheduleTable.php +++ b/components/ILIAS/BookingManager/src/Booking/Table/BookingsWithScheduleTable.php @@ -23,6 +23,7 @@ use DateTimeImmutable; use Generator; use ilBookingReservation; +use ilBookingSchedule; use ILIAS\Data\DateFormat\DateFormat; use ILIAS\Data\Order; use ILIAS\Data\Range; @@ -34,6 +35,11 @@ class BookingsWithScheduleTable extends BookingsTable { public const string ID = 'bkbws'; + /** + * @var array + */ + private array $schedule_cache = []; + public function getRows( DataRowBuilder $row_builder, array $visible_column_ids, @@ -51,11 +57,13 @@ public function getRows( $record['date_to']++; $date_from = new DateTimeImmutable("@{$record['date_from']}"); - $time_slot = ilDatePresentation::formatPeriod( - new ilDateTime($record['date_from'], IL_CAL_UNIX), - new ilDateTime($record['date_to'], IL_CAL_UNIX), - true - ); + $time_slot = $this->isAllDayBooking($record) + ? $this->lng->txt('book_all_day') + : ilDatePresentation::formatPeriod( + new ilDateTime($record['date_from'], IL_CAL_UNIX), + new ilDateTime($record['date_to'], IL_CAL_UNIX), + true + ); yield $this->getTableActions()->onDataRow( $row_builder->buildDataRow( @@ -154,13 +162,32 @@ protected function getTimeSlots(): array $time_slots = []; foreach ($this->bookings as $booking) { - $date_from = new DateTimeImmutable("@{$booking['date_from']}"); - $booking['date_to']++; - $date_to = new DateTimeImmutable("@{$booking['date_to']}"); - $time_slot = "{$date_from->format('H:i')} - {$date_to->format('H:i')}"; + $time_slot = $this->getBookingTimeSlotFilterLabel($booking); $time_slots[$time_slot] ??= $time_slot; } return $time_slots; } + + protected function getBookingTimeSlotFilterLabel(array $booking): string + { + return $this->isAllDayBooking($booking) + ? $this->lng->txt('book_all_day') + : parent::getBookingTimeSlotFilterLabel($booking); + } + + private function isAllDayBooking(array $booking): bool + { + $object_id = (int) $booking['object_id']; + $schedule_id = (int) ($this->booking_items[$object_id]['schedule_id'] ?? 0); + + return + $schedule_id !== 0 + && $this->getSchedule($schedule_id)->getScheduleType() === ilBookingSchedule::SCHEDULE_TYPE_ALL_DAY; + } + + private function getSchedule(int $schedule_id): ilBookingSchedule + { + return $this->schedule_cache[$schedule_id] ??= new ilBookingSchedule($schedule_id); + } } diff --git a/components/ILIAS/BookingManager/templates/default/tpl.schedule_days_input.html b/components/ILIAS/BookingManager/templates/default/tpl.schedule_days_input.html new file mode 100644 index 000000000000..46c597f2d295 --- /dev/null +++ b/components/ILIAS/BookingManager/templates/default/tpl.schedule_days_input.html @@ -0,0 +1,8 @@ +
+ + + +
diff --git a/lang/ilias_de.lang b/lang/ilias_de.lang index 3e8e5c54d5f3..6dc03d2d7716 100644 --- a/lang/ilias_de.lang +++ b/lang/ilias_de.lang @@ -2543,6 +2543,7 @@ book#:#book_add_participant#:#Teilnehmer hinzufügen book#:#book_add_schedule#:#Zeitplan hinzufügen book#:#book_additional_info_file#:#Zusätzliche Beschreibung book#:#book_all#:#Alle anzeigen +book#:#book_all_day#:#Ganztägig book#:#book_all_pools_need_schedules#:#Die Änderungen wurden nicht gespeichert. Alle ausgewählten Buchungspools benötigen mindestens einen Zeitplan. book#:#book_all_users#:#Alle Personen book#:#book_assign#:#Buchen @@ -2693,6 +2694,7 @@ book#:#book_reservation_title#:#Buchungsanfrage für book#:#book_reservations_list#:#Buchungen book#:#book_schedule#:#Zeitplan book#:#book_schedule_added#:#Ein Zeitplan wurde hinzugefügt. +book#:#book_schedule_all_day#:#Ganztägiger Zeitplan book#:#book_schedule_days#:#Zeitfenster book#:#book_schedule_days_info#:#Gültige Zeiten für jeden Tag (HH:MM-HH:MM) book#:#book_schedule_deleted#:#Der Zeitplan wurde gelöscht. @@ -2738,6 +2740,7 @@ book#:#no_valid_selection#:#Keine gültige Auswahl book#:#obj_book_duplicate#:#Buchungspool kopieren book#:#participants#:#Personen book#:#reservation_deleted#:#Buchung gelöscht +book#:#schedule_type#:#Zeitplantyp buddysystem#:#buddy_allow_to_contact_me#:#Kontaktanfragen erlauben buddysystem#:#buddy_allow_to_contact_me_default_info#:#Bestimmt ob standardmässig Kontaktanfragen verschickt werden können. buddysystem#:#buddy_allow_to_contact_me_info#:#Wenn aktiviert, können andere Benutzer mir Kontaktanfragen senden. diff --git a/lang/ilias_en.lang b/lang/ilias_en.lang index 7993b3bae105..b8dc112793ac 100644 --- a/lang/ilias_en.lang +++ b/lang/ilias_en.lang @@ -2544,6 +2544,7 @@ book#:#book_add_participant#:#Add Participant book#:#book_add_schedule#:#Add Schedule book#:#book_additional_info_file#:#Additional Description book#:#book_all#:#Show all +book#:#book_all_day#:#All-Day book#:#book_all_pools_need_schedules#:#Settings not saved. All selected booking pools need to have at least one schedule. book#:#book_all_users#:#All Participants book#:#book_assign#:#Book @@ -2694,6 +2695,7 @@ book#:#book_reservation_title#:#Booking Reservation for book#:#book_reservations_list#:#Bookings book#:#book_schedule#:#Schedule book#:#book_schedule_added#:#Booking schedule added. +book#:#book_schedule_all_day#:#All-Day Schedule book#:#book_schedule_days#:#Time Slots book#:#book_schedule_days_info#:#Valid booking times for each day (HH:MM-HH:MM) book#:#book_schedule_deleted#:#Booking schedule deleted. @@ -2739,6 +2741,7 @@ book#:#no_valid_selection#:#No valid selection book#:#obj_book_duplicate#:#Copy Booking Pool book#:#participants#:#Participants book#:#reservation_deleted#:#Booking deleted. +book#:#schedule_type#:#Schedule Type buddysystem#:#buddy_allow_to_contact_me#:#Allow Contact Requests buddysystem#:#buddy_allow_to_contact_me_default_info#:#Defines if users can send each other requests for getting into contact by default. buddysystem#:#buddy_allow_to_contact_me_info#:#Allow other users to send you contact requests.