ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilBookingEntry.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
26{
27 protected ilDBInterface $db;
28 protected ilObjUser $user;
29
30 private int $id = 0;
31 private int $obj_id = 0;
32
33 private int $deadline = 0;
34 private int $num_bookings = 1;
35 private ?array $target_obj_ids = [];
36
40 public function __construct(int $a_booking_id = 0)
41 {
42 global $DIC;
43
44 $this->db = $DIC->database();
45 $this->user = $DIC->user();
46 $this->setId($a_booking_id);
47 if ($this->getId()) {
48 $this->read();
49 }
50 }
51
59 public static function lookupBookingsOfUser(array $a_app_ids, int $a_usr_id, ?ilDateTime $start = null): array
60 {
61 global $DIC;
62
63 $ilDB = $DIC->database();
64 $query = 'SELECT entry_id FROM booking_user ' .
65 'WHERE ' . $ilDB->in('entry_id', $a_app_ids, false, 'integer') . ' ' .
66 'AND user_id = ' . $ilDB->quote($a_usr_id, 'integer');
67
68 $res = $ilDB->query($query);
69
70 $booked_entries = array();
71 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
72 $booked_entries[] = (int) $row->entry_id;
73 }
74 return $booked_entries;
75 }
76
77 protected function setId(int $a_id): void
78 {
79 $this->id = $a_id;
80 }
81
82 public function getId(): int
83 {
84 return $this->id;
85 }
86
87
88 public function setObjId(int $a_id): void
89 {
90 $this->obj_id = $a_id;
91 }
92
93 public function getObjId(): int
94 {
95 return $this->obj_id;
96 }
97
98 public function setDeadlineHours(int $a_hours): void
99 {
100 $this->deadline = $a_hours;
101 }
102
103 public function getDeadlineHours(): int
104 {
105 return $this->deadline;
106 }
107
108 public function setNumberOfBookings(int $a_num): void
109 {
110 $this->num_bookings = $a_num;
111 }
112
113 public function getNumberOfBookings(): int
114 {
115 return $this->num_bookings;
116 }
117
121 public function setTargetObjIds(?array $a_obj_id): void
122 {
123 $this->target_obj_ids = $a_obj_id;
124 }
125
129 public function getTargetObjIds(): ?array
130 {
132 }
133
137 public function isTargetObjectVisible(int $a_ref_id): bool
138 {
139 // no course/group filter
140 if (!$this->getTargetObjIds()) {
141 return true;
142 }
143
144 $obj_id = ilObject::_lookupObjId($a_ref_id);
145 return in_array($obj_id, $this->getTargetObjIds());
146 }
147
148 public function save(): void
149 {
150 $this->setId($this->db->nextId('booking_entry'));
151 $query = 'INSERT INTO booking_entry (booking_id,obj_id,deadline,num_bookings) ' .
152 "VALUES ( " .
153 $this->db->quote($this->getId(), 'integer') . ', ' .
154 $this->db->quote($this->getObjId(), 'integer') . ', ' .
155 $this->db->quote($this->getDeadlineHours(), 'integer') . ', ' .
156 $this->db->quote($this->getNumberOfBookings(), 'integer') . ' ' .
157 ") ";
158 $this->db->manipulate($query);
159
160 foreach ((array) $this->target_obj_ids as $obj_id) {
161 $query = 'INSERT INTO booking_obj_assignment (booking_id, target_obj_id) ' .
162 'VALUES( ' .
163 $this->db->quote($this->getId(), 'integer') . ', ' .
164 $this->db->quote($obj_id, 'integer') . ' ' .
165 ')';
166 $this->db->manipulate($query);
167 }
168 }
169
170 public function update(): void
171 {
172 if (!$this->getId()) {
173 return;
174 }
175
176 $query = "UPDATE booking_entry SET " .
177 " obj_id = " . $this->db->quote($this->getObjId(), 'integer') . ", " .
178 " deadline = " . $this->db->quote($this->getDeadlineHours(), 'integer') . ", " .
179 " num_bookings = " . $this->db->quote($this->getNumberOfBookings(), 'integer') . ' ' .
180 'WHERE booking_id = ' . $this->db->quote($this->getId(), 'integer');
181 $this->db->manipulate($query);
182
183 // obj assignments
184 $query = 'DELETE FROM booking_obj_assignment ' .
185 'WHERE booking_id = ' . $this->db->quote($this->getId(), 'integer');
186 $this->db->manipulate($query);
187
188 foreach ((array) $this->target_obj_ids as $obj_id) {
189 $query = 'INSERT INTO booking_obj_assignment (booking_id, target_obj_id) ' .
190 'VALUES( ' .
191 $this->db->quote($this->getId(), 'integer') . ', ' .
192 $this->db->quote($obj_id, 'integer') . ' ' .
193 ')';
194 $this->db->manipulate($query);
195 }
196 }
197
198 public function delete(): void
199 {
200 $query = "DELETE FROM booking_entry " .
201 "WHERE booking_id = " . $this->db->quote($this->getId(), 'integer');
202 $this->db->manipulate($query);
203 $query = 'DELETE FROM booking_obj_assignment ' .
204 'WHERE booking_id = ' . $this->db->quote($this->getId(), 'integer');
205 $this->db->manipulate($query);
206 }
207
208 protected function read(): void
209 {
210 if (!$this->getId()) {
211 return;
212 }
213
214 $query = "SELECT * FROM booking_entry " .
215 "WHERE booking_id = " . $this->db->quote($this->getId(), 'integer');
216 $res = $this->db->query($query);
217 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
218 $this->setObjId((int) $row['obj_id']);
219 $this->setDeadlineHours((int) $row['deadline']);
220 $this->setNumberOfBookings((int) $row['num_bookings']);
221 }
222
223 $query = 'SELECT * FROM booking_obj_assignment ' .
224 'WHERE booking_id = ' . $this->db->quote($this->getId(), 'integer');
225 $res = $this->db->query($query);
226 $this->target_obj_ids = array();
227 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
228 $this->target_obj_ids[] = (int) $row->target_obj_id;
229 }
230 }
231
237 public function isOwner(?int $a_user_id = null): bool
238 {
239 if (!$a_user_id) {
240 $a_user_id = $this->user->getId();
241 }
242 if ($this->getObjId() == $a_user_id) {
243 return true;
244 }
245 return false;
246 }
247
251 public static function removeObsoleteEntries(): void
252 {
253 global $DIC;
254
255 $ilDB = $DIC->database();
256 $set = $ilDB->query('SELECT DISTINCT(context_id) FROM cal_entries e' .
257 ' JOIN cal_cat_assignments a ON (e.cal_id = a.cal_id)' .
258 ' JOIN cal_categories c ON (a.cat_id = c.cat_id) WHERE c.type = ' . $ilDB->quote(
260 'integer'
261 ));
262
263 $used = array();
264 while ($row = $ilDB->fetchAssoc($set)) {
265 $used[] = $row['context_id'];
266 }
267 $ilDB->query($q = 'DELETE FROM booking_entry WHERE ' . $ilDB->in('booking_id', $used, true, 'integer'));
268 $ilDB->query($q = 'DELETE FROM booking_obj_assignment WHERE ' . $ilDB->in(
269 'booking_id',
270 $used,
271 true,
272 'integer'
273 ));
274 }
275
281 public static function getInstanceByCalendarEntryId(int $a_id): ?ilBookingEntry
282 {
283 $cal_entry = new ilCalendarEntry($a_id);
284 $booking_id = $cal_entry->getContextId();
285 if ($booking_id) {
286 return new self($booking_id);
287 }
288 return null;
289 }
290
297 public static function isBookable(array $a_obj_ids, ?int $a_target_obj_id = null): array
298 {
299 global $DIC;
300
301 $ilDB = $DIC->database();
302 if ($a_target_obj_id) {
303 $query = 'SELECT DISTINCT(obj_id) FROM booking_entry be ' .
304 'JOIN booking_obj_assignment bo ON be.booking_id = bo.booking_id ' .
305 'WHERE ' . $ilDB->in('obj_id', $a_obj_ids, false, 'integer') . ' ' .
306 'AND bo.target_obj_id = ' . $ilDB->quote($a_target_obj_id, 'integer');
307 } else {
308 $query = 'SELECT DISTINCT(obj_id) FROM booking_entry be ' .
309 'WHERE ' . $ilDB->in('obj_id', $a_obj_ids, false, 'integer') . ' ';
310 }
311
312 $res = $ilDB->query($query);
313 $all = [];
314 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
315 $all[] = (int) $row->obj_id;
316 }
317 return $all;
318 }
319
328 public static function lookupBookableUsersForObject(array $a_obj_id, array $a_user_ids): array
329 {
330 global $DIC;
331
332 $ilDB = $DIC->database();
333 $query = 'SELECT be.obj_id bobj FROM booking_entry be ' .
334 'JOIN booking_obj_assignment bo ON be.booking_id = bo.booking_id ' .
335 'JOIN cal_entries ce on be.booking_id = ce.context_id ' .
336 'JOIN cal_cat_assignments cca on ce.cal_id = cca.cal_id ' .
337 'JOIN cal_categories cc on cca.cat_id = cc.cat_id ' .
338 'WHERE ' . $ilDB->in('be.obj_id', $a_user_ids, false, 'integer') . ' ' .
339 'AND ' . $ilDB->in('bo.target_obj_id', $a_obj_id, false, 'integer') . ' ' .
340 'AND cc.obj_id = be.obj_id ' .
341 'AND cc.type = ' . $ilDB->quote(ilCalendarCategory::TYPE_CH, 'integer') . ' ';
342
343 $res = $ilDB->query($query);
344
345 $objs = [];
346 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
347 if (!in_array($row->bobj, $objs)) {
348 $objs[] = (int) $row->bobj;
349 }
350 }
351
352 // non filtered booking entries
353 $query = 'SELECT be.obj_id bobj FROM booking_entry be ' .
354 'LEFT JOIN booking_obj_assignment bo ON be.booking_id = bo.booking_id ' .
355 'JOIN cal_entries ce on be.booking_id = ce.context_id ' .
356 'JOIN cal_cat_assignments cca on ce.cal_id = cca.cal_id ' .
357 'JOIN cal_categories cc on cca.cat_id = cc.cat_id ' .
358 'WHERE bo.booking_id IS NULL ' .
359 'AND ' . $ilDB->in('be.obj_id', $a_user_ids, false, 'integer') . ' ' .
360 'AND cc.obj_id = be.obj_id ' .
361 'AND cc.type = ' . $ilDB->quote(ilCalendarCategory::TYPE_CH, 'integer') . ' ';
362
363 $res = $ilDB->query($query);
364 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
365 if (!in_array($row->bobj, $objs)) {
366 $objs[] = (int) $row->bobj;
367 }
368 }
369 return $objs;
370 }
371
375 public static function hasObjectBookingEntries(int $a_obj_id, int $a_usr_id): bool
376 {
377 global $DIC;
378
379 $ilDB = $DIC->database();
380
381 $user_restriction = '';
382 if ($a_usr_id) {
383 $user_restriction = 'AND obj_id = ' . $ilDB->quote($a_usr_id, ilDBConstants::T_INTEGER) . ' ';
384 }
385
386 $query = 'SELECT be.booking_id FROM booking_entry be ' .
387 'JOIN booking_obj_assignment bo ON be.booking_id = bo.booking_id ' .
388 'WHERE bo.target_obj_id = ' . $ilDB->quote($a_obj_id, 'integer') . ' ' .
389 $user_restriction;
390
391 $res = $ilDB->query($query);
392 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
393 return true;
394 }
395 return false;
396 }
397
398 public static function lookupBookingMessage(int $a_entry_id, int $a_usr_id): string
399 {
400 global $DIC;
401
402 $ilDB = $DIC->database();
403
404 $query = 'SELECT * from booking_user ' .
405 'WHERE entry_id = ' . $ilDB->quote($a_entry_id, 'integer') . ' ' .
406 'AND user_id = ' . $ilDB->quote($a_usr_id, 'integer');
407 $res = $ilDB->query($query);
408 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
409 return (string) $row->booking_message;
410 }
411 return '';
412 }
413
417 public static function writeBookingMessage(int $a_entry_id, int $a_usr_id, string $a_message): void
418 {
419 global $DIC;
420
421 $ilDB = $DIC->database();
422 $query = 'UPDATE booking_user SET ' .
423 'booking_message = ' . $ilDB->quote($a_message, 'text') . ' ' .
424 'WHERE entry_id = ' . $ilDB->quote($a_entry_id, 'integer') . ' ' .
425 'AND user_id = ' . $ilDB->quote($a_usr_id, 'integer');
426
427 $ilDB->manipulate($query);
428 }
429
433 public function getCurrentNumberOfBookings(int $a_entry_id): int
434 {
435 $set = $this->db->query('SELECT COUNT(*) AS counter FROM booking_user' .
436 ' WHERE entry_id = ' . $this->db->quote($a_entry_id, 'integer'));
437 $row = $this->db->fetchAssoc($set);
438 if (is_array($row)) {
439 return (int) $row['counter'];
440 }
441 return 0;
442 }
443
449 public function getCurrentBookings(int $a_entry_id): array
450 {
451 $set = $this->db->query('SELECT user_id FROM booking_user' .
452 ' WHERE entry_id = ' . $this->db->quote($a_entry_id, 'integer'));
453 $res = array();
454 while ($row = $this->db->fetchAssoc($set)) {
455 $res[] = (int) $row['user_id'];
456 }
457 return $res;
458 }
459
465 public static function lookupBookingsForAppointment(int $a_app_id): array
466 {
467 global $DIC;
468
469 $ilDB = $DIC->database();
470 $query = 'SELECT user_id FROM booking_user ' .
471 'WHERE entry_id = ' . $ilDB->quote($a_app_id, 'integer');
472 $res = $ilDB->query($query);
473 $users = [];
474 while ($row = $ilDB->fetchObject($res)) {
475 $users[] = (int) $row->user_id;
476 }
477 return $users;
478 }
479
486 public static function lookupBookingsForObject(int $a_obj_id, int $a_usr_id): array
487 {
488 global $DIC;
489
490 $ilDB = $DIC->database();
491 $query = 'SELECT bu.user_id, starta, enda FROM booking_user bu ' .
492 'JOIN cal_entries ca ON entry_id = ca.cal_id ' .
493 'JOIN booking_entry be ON context_id = booking_id ' .
494 'JOIN booking_obj_assignment bo ON be.booking_id = bo.booking_id ' .
495 'WHERE bo.target_obj_id = ' . $ilDB->quote($a_obj_id, 'integer') . ' ' .
496 'AND be.obj_id = ' . $ilDB->quote($a_usr_id, ilDBConstants::T_INTEGER) . ' ' .
497 'ORDER BY starta';
498 $res = $ilDB->query($query);
499
500 $bookings = array();
501 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
502 $dt = new ilDateTime($row->starta, IL_CAL_DATETIME, ilTimeZone::UTC);
503 $dt_end = new ilDateTime($row->enda, IL_CAL_DATETIME, ilTimeZone::UTC);
504 $bookings[(int) $row->user_id][] = [
505 'dt' => $dt->get(IL_CAL_UNIX),
506 'dtend' => $dt_end->get(IL_CAL_UNIX),
507 'owner' => $a_usr_id
508 ];
509 }
510 return $bookings;
511 }
512
517 public static function lookupManagedBookingsForObject(int $a_obj_id, int $a_usr_id): array
518 {
519 $bookings = self::lookupBookingsForObject($a_obj_id, $a_usr_id);
520 foreach (ilConsultationHourUtils::lookupManagedUsers($a_usr_id) as $managed_user_id) {
521 foreach (self::lookupBookingsForObject($a_obj_id, $managed_user_id) as $booked_user => $booking) {
522 $fullname = ilObjUser::_lookupFullname($managed_user_id);
523 foreach ($booking as $booking_entry) {
524 $booking_entry['explanation'] = '(' . $fullname . ')';
525 $bookings[$booked_user][] = $booking_entry;
526 }
527 }
528 }
529 return $bookings;
530 }
531
535 public function hasBooked(int $a_entry_id, ?int $a_user_id = null): bool
536 {
537 if (!$a_user_id) {
538 $a_user_id = $this->user->getId();
539 }
540
541 $query = 'SELECT COUNT(*) AS counter FROM booking_user' .
542 ' WHERE entry_id = ' . $this->db->quote($a_entry_id, 'integer') .
543 ' AND user_id = ' . $this->db->quote($a_user_id, 'integer');
544 $set = $this->db->query($query);
545 $row = $this->db->fetchAssoc($set);
546 if (is_array($row)) {
547 return (bool) $row['counter'];
548 }
549 return false;
550 }
551
555 public function isBookedOut(int $a_entry_id, bool $a_check_current_user = false): bool
556 {
557 if ($this->getNumberOfBookings() == $this->getCurrentNumberOfBookings($a_entry_id)) {
558 // check against current user
559 if ($a_check_current_user) {
560 if ($this->hasBooked($a_entry_id)) {
561 return false;
562 }
563 if ($this->user->getId() == $this->getObjId()) {
564 return false;
565 }
566 }
567 return true;
568 }
569
570 $deadline = $this->getDeadlineHours();
571 if ($deadline) {
572 $entry = new ilCalendarEntry($a_entry_id);
573 if (time() + ($deadline * 60 * 60) > $entry->getStart()->get(IL_CAL_UNIX)) {
574 return true;
575 }
576 }
577 return false;
578 }
579
583 public function isAppointmentBookableForUser(int $a_app_id, int $a_user_id): bool
584 {
585 // #12025
586 if ($a_user_id == ANONYMOUS_USER_ID) {
587 return false;
588 }
589 // Check max bookings
590 if ($this->getNumberOfBookings() <= $this->getCurrentNumberOfBookings($a_app_id)) {
591 return false;
592 }
593
594 // Check deadline
595 $dead_limit = new ilDateTime(time(), IL_CAL_UNIX);
596 $dead_limit->increment(IL_CAL_HOUR, $this->getDeadlineHours());
597
598 $entry = new ilCalendarEntry($a_app_id);
599 if (ilDateTime::_after($dead_limit, $entry->getStart())) {
600 return false;
601 }
602 return true;
603 }
604
608 public function book(int $a_entry_id, ?int $a_user_id = null): bool
609 {
610 if (!$a_user_id) {
611 $a_user_id = $this->user->getId();
612 }
613
614 if (!$this->hasBooked($a_entry_id, $a_user_id)) {
615 $this->db->manipulate('INSERT INTO booking_user (entry_id, user_id, tstamp)' .
616 ' VALUES (' . $this->db->quote($a_entry_id, 'integer') . ',' .
617 $this->db->quote($a_user_id, 'integer') . ',' . $this->db->quote(time(), 'integer') . ')');
618 }
619 return true;
620 }
621
625 public function cancelBooking(int $a_entry_id, ?int $a_user_id = null): bool
626 {
627 if (!$a_user_id) {
628 $a_user_id = $this->user->getId();
629 }
630
631 // @todo do not send mails about past consultation hours
632 $entry = new ilCalendarEntry($a_entry_id);
633
634 $past = ilDateTime::_before($entry->getStart(), new ilDateTime(time(), IL_CAL_UNIX));
635 if ($this->hasBooked($a_entry_id, $a_user_id) && !$past) {
636 $mail = new ilCalendarMailNotification();
637 $mail->setAppointmentId($a_entry_id);
638 $mail->setRecipients(array($a_user_id));
640 $mail->send();
641 }
642 $this->deleteBooking($a_entry_id, $a_user_id);
643 return true;
644 }
645
649 public function deleteBooking(int $a_entry_id, int $a_user_id): bool
650 {
651 $query = 'DELETE FROM booking_user ' .
652 'WHERE entry_id = ' . $this->db->quote($a_entry_id, 'integer') . ' ' .
653 'AND user_id = ' . $this->db->quote($a_user_id, 'integer');
654 $this->db->manipulate($query);
655 return true;
656 }
657}
const IL_CAL_UNIX
const IL_CAL_DATETIME
const IL_CAL_HOUR
Booking definition.
book(int $a_entry_id, ?int $a_user_id=null)
book calendar entry for user
static getInstanceByCalendarEntryId(int $a_id)
Get instance by calendar entry.
getCurrentNumberOfBookings(int $a_entry_id)
get current number of bookings
static hasObjectBookingEntries(int $a_obj_id, int $a_usr_id)
Check if object has assigned consultation hour appointments.
isTargetObjectVisible(int $a_ref_id)
Check if target ref id is visible.
static lookupBookingsOfUser(array $a_app_ids, int $a_usr_id, ?ilDateTime $start=null)
Lookup bookings of user.
isOwner(?int $a_user_id=null)
check if current (or given) user is entry owner
static isBookable(array $a_obj_ids, ?int $a_target_obj_id=null)
Which objects are bookable?
static removeObsoleteEntries()
Remove unused booking entries.
setDeadlineHours(int $a_hours)
hasBooked(int $a_entry_id, ?int $a_user_id=null)
get current number of bookings
static lookupBookingsForAppointment(int $a_app_id)
Lookup booked users for appointment.
setTargetObjIds(?array $a_obj_id)
__construct(int $a_booking_id=0)
Constructor.
deleteBooking(int $a_entry_id, int $a_user_id)
Delete booking.
isAppointmentBookableForUser(int $a_app_id, int $a_user_id)
Check if a calendar appointment is bookable for a specific user.
static lookupBookingMessage(int $a_entry_id, int $a_usr_id)
cancelBooking(int $a_entry_id, ?int $a_user_id=null)
cancel calendar booking for user
isBookedOut(int $a_entry_id, bool $a_check_current_user=false)
get current number of bookings
static lookupManagedBookingsForObject(int $a_obj_id, int $a_usr_id)
Lookup bookings for own and managed consultation hours of an object.
getCurrentBookings(int $a_entry_id)
get current bookings
static writeBookingMessage(int $a_entry_id, int $a_usr_id, string $a_message)
Write booking message.
static lookupBookingsForObject(int $a_obj_id, int $a_usr_id)
Lookup booking for an object and user.
static lookupBookableUsersForObject(array $a_obj_id, array $a_user_ids)
Consultation hours are offered if 1) consultation hour owner is admin or tutor and no object assignme...
setNumberOfBookings(int $a_num)
Model for a calendar entry.
Distributes calendar mail notifications.
static lookupManagedUsers($a_usr_id)
Lookup managed users.
@classDescription Date and time handling
static _after(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
compare two dates and check start is after end This method does not consider tz offsets.
static _before(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
compare two dates and check start is before end This method does not consider tz offsets.
User class.
static _lookupFullname(int $a_user_id)
static _lookupObjId(int $ref_id)
const ANONYMOUS_USER_ID
Definition: constants.php:27
Interface ilDBInterface.
$res
Definition: ltiservices.php:69
global $DIC
Definition: shib_login.php:26
$q
Definition: shib_logout.php:23