ILIAS  trunk Revision v12.0_alpha-1613-gae4c99ebb18
BookingsTable.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
23use DateTimeImmutable;
24use DateTimeZone;
25use Generator;
26use Throwable;
43use ILIAS\Refinery\Factory as Refinery;
44use ILIAS\UI\Component\Dropdown\Standard as StandardDropdown;
50use ILIAS\UI\Factory as UIFactory;
51use ILIAS\UI\Renderer as UIRenderer;
54use ilObjUser;
55use ilTable2GUI;
56use ilUIService;
57use ilUserUtil;
62
63abstract class BookingsTable implements Table
64{
66
67 public const ROW_ID_PARAMETER = 'booking_id';
68
69 public const ACTION_PARAMETER = 'action';
70
71 public const ACTION_TYPE_PARAMETER = 'action_type';
72
73 protected readonly array $booking_items;
74
75 protected readonly array $bookings;
76
77 protected readonly array $participants;
78
79 protected readonly ColumnFactory $column_factory;
80
81 protected readonly InputFactory $input_factory;
82
83 public function __construct(
84 protected readonly UIFactory $ui_factory,
85 protected readonly UIRenderer $ui_renderer,
86 protected readonly AccessManager $access,
87 protected readonly ilGlobalTemplateInterface $tpl,
88 protected readonly Refinery $refinery,
89 protected readonly Language $lng,
90 protected readonly HttpService $http,
91 protected readonly ilObjUser $user,
92 protected readonly ReservationDBRepository $reservation_repository,
93 protected readonly ilCtrlInterface $ctrl,
94 protected readonly ilUIService $ui_service,
95 protected readonly Settings $settings,
96 protected readonly ilObjBookingPool $booking_pool
97 ) {
98 $this->booking_items = array_column(
99 ilBookingObject::getList($this->booking_pool->getId()),
100 null,
101 'booking_object_id'
102 );
103
104 $filter = [];
105 if (
106 !$this->access->canManageAllReservations($this->booking_pool->getRefId())
107 && !$this->access->canReadPublicLog($this->booking_pool->getRefId())
108 ) {
109 $filter['user_id'] = $this->user->getId();
110 }
111 $this->bookings = array_column(
112 ilBookingReservation::getList(array_keys($this->booking_items), 1000, 0, $filter)['data'],
113 null,
114 'booking_reservation_id'
115 );
116
117 $this->participants = array_column(
118 ilBookingParticipant::getList($this->booking_pool->getId()),
119 null,
120 'user_id'
121 );
122
123 foreach (['dateplaner', 'tbl'] as $module) {
124 $this->lng->loadLanguageModule($module);
125 }
126
127 $this->column_factory = $this->ui_factory->table()->column();
128 $this->input_factory = $this->ui_factory->input()->field();
129 }
130
131 public function getTableId(): string
132 {
133 return static::ID;
134 }
135
136 protected function getUserPresentationName(int $user_id): string
137 {
138 return str_replace('&', '&amp;', htmlentities(ilUserUtil::getNamePresentation($user_id)));
139 }
140
141 abstract public function getRows(
142 DataRowBuilder $row_builder,
143 array $visible_column_ids,
145 Order $order,
146 mixed $additional_viewcontrol_data,
147 mixed $filter_data,
148 mixed $additional_parameters
149 ): Generator;
150
151 abstract protected function getColumns(): array;
152
153 abstract protected function getFilterInputs();
154
155 protected function loadRecords(mixed $filter_data): array
156 {
157 if (!$filter_data instanceof Filter) {
158 return $this->bookings;
159 }
160
162
163 foreach ($this->ui_service->filter()->getData($filter_data) ?? [] as $key => $filter_data_value) {
164 if ($filter_data_value === null || $filter_data_value === '') {
165 continue;
166 }
167
168 $bookings = (match ($key) {
169 'object' => static fn(array $bookings): array => array_filter(
170 $bookings,
171 static fn(array $booking): bool => (int) $booking['object_id'] === (int) $filter_data_value
172 ),
173 'object_title_or_description' => static fn(array $bookings): array => array_filter(
174 $bookings,
175 static function (array $booking) use ($filter_data_value): bool {
176 $filter_data_value = strtolower($filter_data_value);
177 return
178 str_contains(strtolower($booking['title']), $filter_data_value)
179 || str_contains(strtolower($booking['message']), $filter_data_value);
180 }
181 ),
182 'period' => function (array $bookings) use ($filter_data_value): array {
183 $bounds = $this->resolveBookingPeriodBounds($filter_data_value);
184 if ($bounds === null) {
185 return $bookings;
186 }
187
188 [$period_from, $period_to] = $bounds;
189 return array_filter(
190 $bookings,
191 static fn(array $booking): bool =>
192 $booking['date_from'] >= $period_from && $booking['date_to'] <= $period_to
193 );
194 },
195 'time_slot' => fn(array $bookings): array => array_filter(
196 $bookings,
197 fn(array $booking): bool => $this->getBookingTimeSlotFilterLabel($booking) === $filter_data_value
198 ),
199 'past_bookings' => static function (array $bookings) use ($filter_data_value): array {
200 $now = new DateTimeImmutable();
201 return array_filter(
202 $bookings,
203 static fn(array $booking): bool => (int) $filter_data_value === 1
204 ? new DateTimeImmutable("@{$booking['date_to']}") < $now
205 : new DateTimeImmutable("@{$booking['date_to']}") >= $now
206 );
207 },
208 'status' => static fn(array $bookings): array => array_filter(
209 $bookings,
210 static fn(array $booking): bool => match ((int) $filter_data_value) {
212 $booking['status'] === ilBookingReservation::STATUS_IN_USE || $booking['status'] === 0,
214 $booking['status'] === ilBookingReservation::STATUS_CANCELLED,
215 default => true
216 }
217 ),
218 'user' => static fn(array $bookings): array => array_filter(
219 $bookings,
220 static fn(array $booking): bool => (int) $booking['user_id'] === (int) $filter_data_value
221 ),
222 default => static fn(array $bookings): array => $bookings
223 })($bookings);
224 }
225
226 return $bookings;
227 }
228
229 protected function sortRecords(Order $order, array $records): array
230 {
231 $order_data = $order->get();
232 if ($order_data === []) {
233 return $records;
234 }
235
236 $users = [];
237 foreach ($order_data as $key => $value) {
238 $order_direction = $value === Order::DESC ? -1 : 1;
239 $callable = match ($key) {
240 'title' => static fn(array $record_a, array $record_b): int => strcasecmp(
241 $record_a['title'],
242 $record_b['title']
243 ) * $order_direction,
244 'status' => static fn(array $record_a, array $record_b): int =>
245 ($record_a['status'] <=> $record_b['status']) * $order_direction,
246 'date' => static fn(array $record_a, array $record_b): int =>
247 ($record_a['date_from'] <=> $record_b['date_from']) * $order_direction,
248 'week' => static function (array $record_a, array $record_b) use ($order_direction): int {
249 $week_a = (int) (new DateTimeImmutable("@{$record_a['date_from']}"))->format('W');
250 $week_b = (int) (new DateTimeImmutable("@{$record_b['date_from']}"))->format('W');
251 return ($week_a <=> $week_b) * $order_direction;
252 },
253 'weekday' => static function (array $record_a, array $record_b) use ($order_direction): int {
254 $week_a = (int) (new DateTimeImmutable("@{$record_a['date_from']}"))->format('N');
255 $week_b = (int) (new DateTimeImmutable("@{$record_b['date_from']}"))->format('N');
256 return ($week_a <=> $week_b) * $order_direction;
257 },
258 'time_slot' => function (array $record_a, array $record_b) use ($order_direction): int {
259 return strcasecmp(
260 $this->getBookingTimeSlotFilterLabel($record_a),
261 $this->getBookingTimeSlotFilterLabel($record_b)
262 ) * $order_direction;
263 },
264 'unit_count' => static fn(array $record_a, array $record_b): int => 0 * $order_direction,
265 'message' => static fn(array $record_a, array $record_b): int => strcasecmp(
266 (string) ($record_a['message'] ?? ''),
267 (string) ($record_b['message'] ?? '')
268 ) * $order_direction,
269 'user' => function (array $record_a, array $record_b) use (&$users, $order_direction): int {
270 $user_a = $record_a['user_id'];
271 $user_b = $record_b['user_id'];
272 $user_a = $users[$user_a] ??= $this->getUserPresentationName($user_a);
273 $user_b = $users[$user_b] ??= $this->getUserPresentationName($user_b);
274 return strcasecmp($user_a, $user_b) * $order_direction;
275 } ,
276 default => null
277 };
278
279 if ($callable === null) {
280 continue;
281 }
282
283 usort($records, $callable);
284 }
285
286 return $records;
287 }
288
289 protected function limitRecords(Range $range, array $records): array
290 {
291 return array_slice($records, $range->getStart(), $range->getLength());
292 }
293
294 public function getFilter(): ?Filter
295 {
296 $filter_inputs = $this->getFilterInputs();
297
298 $filter_inputs = $this->presetFilterInputs($filter_inputs);
299
300 if ($filter_inputs === []) {
301 return null;
302 }
303
304 return $this->ui_service->filter()->standard(
305 "{$this->getTableId()}_filter",
306 $this->ctrl->getLinkTargetByClass(ilBookingReservationsGUI::class),
307 $filter_inputs,
308 array_fill(0, count($filter_inputs), true),
309 true,
310 true
311 );
312 }
313
314 private function presetFilterInputs(array $filter_inputs): array
315 {
316 $query_params = $this->http->getRequest()->getQueryParams();
317
318 if (
319 $this->access->canManageAllReservations($this->booking_pool->getRefId())
320 || $this->access->canReadPublicLog($this->booking_pool->getRefId())
321 ) {
322 $user_id = $query_params['user_id'] ?? null;
323 if (is_numeric($user_id)) {
324 $filter_inputs['user'] = $filter_inputs['user']->withValue((int) $user_id);
325 }
326 } elseif ($this->access->canManageOwnReservations($this->booking_pool->getRefId())) {
327 $filter_inputs['user'] = $filter_inputs['user']->withValue($this->user->getId());
328 }
329
330 $object_id = $query_params['object_id'] ?? null;
331 if (is_numeric($object_id)) {
332 $filter_inputs['object'] = $filter_inputs['object']->withValue((int) $object_id);
333 }
334
335 if ($this->settings->getReservationPeriod() > 0) {
336 $filter_inputs['period'] = $filter_inputs['period']->withValue(
337 [
338 new DateTimeImmutable('today 00:00:00'),
339 new DateTimeImmutable("today +{$this->settings->getReservationPeriod()} days 23:59:59")
340 ]
341 );
342 }
343
344 $period_from = $query_params['period_from'] ?? null;
345 $period_to = $query_params['period_to'] ?? null;
346 if (is_numeric($period_from) && is_numeric($period_to)) {
347 $filter_inputs['period'] = $filter_inputs['period']->withValue(
348 [
349 new DateTimeImmutable("@{$period_from}"),
350 new DateTimeImmutable("@{$period_to}")
351 ]
352 );
353
354 }
355
356 (new ilUIFilterServiceSessionGateway())->reset(static::ID . '_filter');
357
358 return $filter_inputs;
359 }
360
361 public function getTotalRowCount(
362 mixed $additional_viewcontrol_data,
363 mixed $filter_data,
364 mixed $additional_parameters
365 ): ?int {
366 return count($this->bookings);
367 }
368
369 public function getComponents(URLBuilder $url_builder): array
370 {
371 $filter = $this->getFilter();
372 $table = $this->getTable($url_builder)->withFilter($filter);
373 return array_filter([$filter, $table, $this->ui_factory->divider()->horizontal(), $this->getExportDropdown()]);
374 }
375
376 protected function acquireParameters(URLBuilder $url_builder): array
377 {
378 return $url_builder->acquireParameters(
379 [$this->getTableId()],
380 self::ROW_ID_PARAMETER,
381 self::ACTION_PARAMETER,
382 self::ACTION_TYPE_PARAMETER
383 );
384 }
385
386 private function getTable(URLBuilder $url_builder): Data
387 {
388 return
389 $this->ui_factory->table()->data(
390 $this,
391 $this->lng->txt('bookings'),
392 $this->getColumns()
393 )
394 ->withActions($this->getTableActions()->getEnabledActions(...$this->acquireParameters($url_builder)))
395 ->withRequest($this->http->getRequest())
396 ->withId($this->getTableId());
397 }
398
399 private function getExportDropdown(): StandardDropdown
400 {
401 $parameter = "bkrsv{$this->booking_pool->getRefId()}_xpt";
402 $export_formats = [ilTable2GUI::EXPORT_EXCEL => 'tbl_export_excel' , ilTable2GUI::EXPORT_CSV => 'tbl_export_csv'];
403 $actions = [];
404
405 foreach ($export_formats as $format => $caption_lng_id) {
406 $this->ctrl->setParameterByClass(ilBookingReservationsGUI::class, $parameter, $format);
407 $actions[] = $this->ui_factory->link()->standard(
408 $this->lng->txt($caption_lng_id),
409 $this->ctrl->getLinkTargetByClass(
410 ilBookingReservationsGUI::class,
412 )
413 );
414 $this->ctrl->setParameterByClass(ilBookingReservationsGUI::class, $parameter, null);
415 }
416
417 return $this->ui_factory->dropdown()->standard($actions)->withLabel($this->lng->txt('export'));
418 }
419
423 protected function getUsers(?int $user_id = null): array
424 {
425 if (is_int($user_id)) {
426 return [$user_id => $this->getUserPresentationName($user_id)];
427 }
428
429 return array_map(
430 fn(array $participant): string => $this->getUserPresentationName($participant['user_id']),
431 $this->participants
432 );
433 }
434
438 protected function getStatuses(): array
439 {
440 return [
441 ilBookingReservation::STATUS_IN_USE => $this->lng->txt('book_not_cancelled'),
442 ilBookingReservation::STATUS_CANCELLED => $this->lng->txt('book_reservation_status_5')
443 ];
444 }
445
446 protected function getTableActions(): TableActions
447 {
448 return (new BookingTableActionsFactory(
449 $this->ctrl,
450 $this->lng,
451 $this->tpl,
452 $this->ui_factory,
453 $this->ui_renderer,
454 $this->refinery,
455 $this->http,
456 $this->access,
457 $this->reservation_repository,
458 $this->booking_pool,
459 $this->bookings
460 ))->getTableActions();
461 }
462
463 protected function getUserComponent(int $user_id)
464 {
465 $user_name = $this->getUserPresentationName($user_id);
467 return $this->ui_factory->link()->standard($user_name, '')->withDisabled(true);
468 }
469
470 $this->ctrl->setParameterByClass(PublicProfileGUI::class, 'user_id', $user_id);
471
472 return $this->ui_factory->link()->standard(
473 $user_name,
474 $this->ctrl->getLinkTargetByClass(
475 [ilPublicProfileBaseClassGUI::class, PublicProfileGUI::class],
476 'getHTML'
477 )
478 );
479 }
480
481 private function resolveBookingPeriodBounds(mixed $period): ?array
482 {
483 if (!$period) {
484 return null;
485 }
486
487 $from = $this->parseBookingPeriodValue($period[0] ?? null);
488 $to = $this->parseBookingPeriodValue($period[1] ?? null);
489
490 if ($from === null && $to === null) {
491 return null;
492 }
493
494 return [
495 $from ?? PHP_INT_MIN,
496 $to ?? PHP_INT_MAX,
497 ];
498 }
499
500 private function parseBookingPeriodValue(mixed $value): ?int
501 {
502 if (!$value) {
503 return null;
504 }
505
506 if ($value instanceof DateTimeImmutable) {
507 return $value->getTimestamp();
508 }
509
510 try {
511 return (new DateTimeImmutable((string) $value))->getTimestamp();
512 } catch (Throwable) {
513 return null;
514 }
515 }
516
517 protected function getBookingTimeSlotFilterLabel(array $booking): string
518 {
519 $date_from = new DateTimeImmutable("@{$booking['date_from']}");
520 $date_to = new DateTimeImmutable('@' . ((int) $booking['date_to'] + 1));
521
522 return "{$date_from->format('H:i')} - {$date_to->format('H:i')}";
523 }
524}
getTotalRowCount(mixed $additional_viewcontrol_data, mixed $filter_data, mixed $additional_parameters)
Mainly for the purpose of pagination-support, it is important to know about the total number of recor...
getRows(DataRowBuilder $row_builder, array $visible_column_ids, Range $range, Order $order, mixed $additional_viewcontrol_data, mixed $filter_data, mixed $additional_parameters)
This is called by the table to retrieve rows; map data-records to rows using the $row_builder e....
__construct(protected readonly UIFactory $ui_factory, protected readonly UIRenderer $ui_renderer, protected readonly AccessManager $access, protected readonly ilGlobalTemplateInterface $tpl, protected readonly Refinery $refinery, protected readonly Language $lng, protected readonly HttpService $http, protected readonly ilObjUser $user, protected readonly ReservationDBRepository $reservation_repository, protected readonly ilCtrlInterface $ctrl, protected readonly ilUIService $ui_service, protected readonly Settings $settings, protected readonly ilObjBookingPool $booking_pool)
Repo class for reservations Acts on tables booking_reservation (rw), booking_reservation_group (rw) a...
Builds a Color from either hex- or rgb values.
Definition: Factory.php:31
Builds data types.
Definition: Factory.php:36
Both the subject and the direction need to be specified when expressing an order.
Definition: Order.php:29
const DESC
Definition: Order.php:31
A simple class to express a naive range of whole positive numbers.
Definition: Range.php:29
acquireParameters(array $namespace, string ... $names)
Definition: URLBuilder.php:138
GUI class for public user profile presentation.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getList(int $a_pool_id, ?string $a_title=null)
Get list of booking objects.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getList(int $a_booking_pool, ?array $a_filter=null, ?int $a_object_id=null)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getList(array $a_object_ids, int $a_limit=10, int $a_offset=0, array $filter=[])
List all reservations.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
User class.
This class is just a connector as base classes cannot be namespaced.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Filter service.
Class ilUserUtil.
static getNamePresentation( $a_user_id, bool $a_user_image=false, bool $a_profile_link=false, string $a_profile_back_link='', bool $a_force_first_lastname=false, bool $a_omit_login=false, bool $a_sortable=true, bool $a_return_data_array=false, $a_ctrl_path=null)
Default behaviour is:
static hasPublicProfile(int $a_user_id)
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This describes a Standard Dropdown.
Definition: Standard.php:27
This describes a standard filter.
Definition: Standard.php:27
This is what a factory for input fields looks like.
Definition: Factory.php:31
This describes a Data Table.
Definition: Data.php:33
An entity that renders components to a string output.
Definition: Renderer.php:31
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static http()
Fetches the global http state from ILIAS.
global $lng
Definition: privfeed.php:26