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