1<?php
2
3/* Copyright (c) 1998-2019 ILIAS open source, Extended GPL, see docs/LICENSE */
4
5/**
6 * Booking process ui class
7 *
8 * @author killing@leifos.de
9 * @ingroup ModulesBookingManager
10 */
11class ilBookingProcessGUI
12{
13    /**
14     * @var ilObjBookingPool
15     */
16    protected $pool;
17
18    /**
19     * @var
20     */
21    protected $booking_object_id;
22
23    /**
24     * user to be booked: user being assigned or deassigned to/from a reservation
25     * @var int
26     */
27    protected $user_id_to_book;
28
29    /**
30     * @var int
31     */
32    protected $user_id_assigner;
33
34    /**
35     * @var string
36     */
37    protected $seed;
38
39    /**
40     * @var ilBookingHelpAdapter
41     */
42    protected $help;
43
44    /**
45     * @var int
46     */
47    protected $context_obj_id;
48
49    /**
50     * Constructor
51     */
52    public function __construct(
53        ilObjBookingPool $pool,
54        int $booking_object_id,
55        ilBookingHelpAdapter $help,
56        string $seed = "",
57        string $sseed = "",
58        int $context_obj_id = 0
59    ) {
60        global $DIC;
61
62        $this->ctrl = $DIC->ctrl();
63        $this->tpl = $DIC["tpl"];
64        $this->lng = $DIC->language();
65        $this->access = $DIC->access();
66        $this->tabs_gui = $DIC->tabs();
67        $this->user = $DIC->user();
68        $this->help = $help;
69
70        $this->context_obj_id = $context_obj_id;
71
72        $this->book_obj_id = $booking_object_id;
73
74        $this->pool = $pool;
75
76        $this->seed = $seed;
77        $this->sseed = $sseed;
78
79        $this->rsv_ids = explode(";", $_REQUEST["rsv_ids"]);
80
81
82        $this->user_id_assigner = $this->user->getId();
83        if ($_GET['bkusr']) {
84            $this->user_id_to_book = (int) $_GET['bkusr'];
85        } else {
86            $this->user_id_to_book = $this->user_id_assigner; // by default user books his own booking objects.
87        }
88        $this->ctrl->saveParameter($this, ["bkusr"]);
89    }
90
91    /**
92     * Execute command
93     */
94    public function executeCommand()
95    {
96        $ctrl = $this->ctrl;
97
98        $next_class = $ctrl->getNextClass($this);
99        $cmd = $ctrl->getCmd("show");
100        switch ($next_class) {
101            default:
102                if (in_array($cmd, array("book", "back",
103                    "assignParticipants",
104                    "bookMultipleParticipants",
105                    "saveMultipleBookings",
106                    "confirmedBooking",
107                    "confirmBookingNumbers",
108                    "confirmedBookingNumbers",
109                    "displayPostInfo",
110                    "deliverPostFile"
111            ))) {
112                    $this->$cmd();
113                }
114        }
115    }
116
117    /**
118     * Back to parent
119     */
120    protected function back()   // ok
121    {
122        $this->ctrl->returnToParent($this);
123    }
124
125    /**
126     * @param string $a_id
127     */
128    protected function setHelpId(string $a_id)
129    {
130        $this->help->setHelpId($a_id);
131    }
132
133    /**
134     * Check permission
135     *
136     * @param $a_perm
137     */
138    protected function checkPermissionBool($a_perm)
139    {
140        $ilAccess = $this->access;
141
142        if (!$ilAccess->checkAccess($a_perm, "", $this->pool->getRefId())) {
143            return false;
144        }
145        return true;
146    }
147
148    /**
149     * Check permission
150     *
151     * @param $a_perm
152     */
153    protected function checkPermission($a_perm)
154    {
155        if (!$this->checkPermissionBool($a_perm)) {
156            ilUtil::sendFailure($this->lng->txt("no_permission"), true);
157            $this->back();
158        }
159    }
160
161    //
162    // Step 1
163    //
164
165
166    /**
167     * First step in booking process
168     */
169    public function book() // ok
170    {
171        $tpl = $this->tpl;
172
173        $this->tabs_gui->clearTargets();
174        $this->tabs_gui->setBackTarget($this->lng->txt('book_back_to_list'), $this->ctrl->getLinkTarget($this, 'back'));
175
176        $this->setHelpId("book");
177
178        $obj = new ilBookingObject($this->book_obj_id);
179
180        $this->lng->loadLanguageModule("dateplaner");
181        $this->ctrl->setParameter($this, 'object_id', $obj->getId());
182
183        if ($this->user_id_to_book != $this->user_id_assigner) {
184            $this->ctrl->setParameter($this, 'bkusr', $this->user_id_to_book);
185        }
186
187        if ($this->pool->getScheduleType() == ilObjBookingPool::TYPE_FIX_SCHEDULE) {
188            $schedule = new ilBookingSchedule($obj->getScheduleId());
189
190            $tpl->setContent($this->renderSlots($schedule, array($obj->getId()), $obj->getTitle()));
191        } else {
192            $cgui = new ilConfirmationGUI();
193            $cgui->setHeaderText($this->lng->txt("book_confirm_booking_no_schedule"));
194
195            $cgui->setFormAction($this->ctrl->getFormAction($this));
196            $cgui->setCancel($this->lng->txt("cancel"), "back");
197            $cgui->setConfirm($this->lng->txt("confirm"), "confirmedBooking");
198
199            $cgui->addItem("object_id", $obj->getId(), $obj->getTitle());
200
201            $tpl->setContent($cgui->getHTML());
202        }
203    }
204
205    /**
206     * @param ilBookingSchedule $schedule
207     * @param array $object_ids
208     * @param string $title
209     * @return string
210     */
211    protected function renderSlots(ilBookingSchedule $schedule, array $object_ids, string $title) // ok
212    {
213        $ilUser = $this->user;
214
215        // fix
216        if (!$schedule->getRaster()) {
217            $mytpl = new ilTemplate('tpl.booking_reservation_fix.html', true, true, 'Modules/BookingManager');
218
219            $mytpl->setVariable('FORM_ACTION', $this->ctrl->getFormAction($this));
220            $mytpl->setVariable('TXT_TITLE', $this->lng->txt('book_reservation_title'));
221            $mytpl->setVariable('TXT_INFO', $this->lng->txt('book_reservation_fix_info'));
222            $mytpl->setVariable('TXT_OBJECT', $title);
223            $mytpl->setVariable('TXT_CMD_BOOK', $this->lng->txt('book_confirm_booking'));
224            $mytpl->setVariable('TXT_CMD_CANCEL', $this->lng->txt('cancel'));
225
226            $user_settings = ilCalendarUserSettings::_getInstanceByUserId($ilUser->getId());
227
228            $morning_aggr = $user_settings->getDayStart();
229            $evening_aggr = $user_settings->getDayEnd();
230            $hours = array();
231            for ($i = $morning_aggr;$i <= $evening_aggr;$i++) {
232                switch ($user_settings->getTimeFormat()) {
233                    case ilCalendarSettings::TIME_FORMAT_24:
234                        if ($morning_aggr > 0 && $i == $morning_aggr) {
235                            $hours[$i] = sprintf('%02d:00', 0) . "-";
236                        }
237                        $hours[$i] .= sprintf('%02d:00', $i);
238                        if ($evening_aggr < 23 && $i == $evening_aggr) {
239                            $hours[$i] .= "-" . sprintf('%02d:00', 23);
240                        }
241                        break;
242
243                    case ilCalendarSettings::TIME_FORMAT_12:
244                        if ($morning_aggr > 0 && $i == $morning_aggr) {
245                            $hours[$i] = date('h a', mktime(0, 0, 0, 1, 1, 2000)) . "-";
246                        }
247                        $hours[$i] .= date('h a', mktime($i, 0, 0, 1, 1, 2000));
248                        if ($evening_aggr < 23 && $i == $evening_aggr) {
249                            $hours[$i] .= "-" . date('h a', mktime(23, 0, 0, 1, 1, 2000));
250                        }
251                        break;
252                }
253            }
254
255            if ($this->seed != "") {
256                $find_first_open = false;
257                $seed = new ilDate($this->seed, IL_CAL_DATE);
258            } else {
259                $find_first_open = true;
260                $seed = ($this->sseed != "")
261                    ? new ilDate($this->sseed, IL_CAL_DATE)
262                    : new ilDate(time(), IL_CAL_UNIX);
263            }
264
265            $week_start = $user_settings->getWeekStart();
266
267            if (!$find_first_open) {
268                $dates = array();
269                $this->buildDatesBySchedule($week_start, $hours, $schedule, $object_ids, $seed, $dates);
270            } else {
271                $dates = array();
272
273                //loop for 1 week
274                $has_open_slot = $this->buildDatesBySchedule($week_start, $hours, $schedule, $object_ids, $seed, $dates);
275
276                // find first open slot
277                if (!$has_open_slot) {
278                    // 1 year is limit for search
279                    $limit = clone($seed);
280                    $limit->increment(ilDate::YEAR, 1);
281                    $limit = $limit->get(IL_CAL_UNIX);
282
283                    while (!$has_open_slot && $seed->get(IL_CAL_UNIX) < $limit) {
284                        $seed->increment(ilDate::WEEK, 1);
285
286                        $dates = array();
287                        $has_open_slot = $this->buildDatesBySchedule($week_start, $hours, $schedule, $object_ids, $seed, $dates);
288                    }
289                }
290            }
291
292            $navigation = new ilCalendarHeaderNavigationGUI($this, $seed, ilDateTime::WEEK, 'book');
293            $mytpl->setVariable('NAVIGATION', $navigation->getHTML());
294
295            /** @var ilDateTime $date */
296            foreach (ilCalendarUtil::_buildWeekDayList($seed, $week_start)->get() as $date) {
297                $date_info = $date->get(IL_CAL_FKT_GETDATE, '', 'UTC');
298
299                $mytpl->setCurrentBlock('weekdays');
300                $mytpl->setVariable('TXT_WEEKDAY', ilCalendarUtil:: _numericDayToString($date_info['wday']));
301                $mytpl->setVariable('TXT_DATE', $date_info['mday'] . ' ' . ilCalendarUtil:: _numericMonthToString($date_info['mon']));
302                $mytpl->parseCurrentBlock();
303            }
304
305            $color = array();
306            $all = ilCalendarAppointmentColors::_getColorsByType('crs');
307            for ($loop = 0; $loop < 7; $loop++) {
308                $col = $all[$loop];
309                $fnt = ilCalendarUtil::calculateFontColor($col);
310                $color[$loop + 1] = 'border-bottom: 1px solid ' . $col . '; background-color: ' . $col . '; color: ' . $fnt;
311            }
312
313            $counter = 0;
314            foreach ($dates as $hour => $days) {
315                $caption = $days;
316                $caption = array_shift($caption);
317
318                for ($loop = 1; $loop < 8; $loop++) {
319                    if (!isset($days[$loop])) {
320                        $mytpl->setCurrentBlock('dates');
321                        $mytpl->setVariable('DUMMY', '&nbsp;');
322                        $mytpl->parseCurrentBlock();
323                    } else {
324                        if (isset($days[$loop]['captions'])) {
325                            foreach ($days[$loop]['captions'] as $slot_id => $slot_caption) {
326                                $mytpl->setCurrentBlock('choice');
327                                $mytpl->setVariable('TXT_DATE', $slot_caption);
328                                $mytpl->setVariable('VALUE_DATE', $slot_id);
329                                $mytpl->setVariable('DATE_COLOR', $color[$loop]);
330                                $mytpl->setVariable(
331                                    'TXT_AVAILABLE',
332                                    sprintf(
333                                        $this->lng->txt('book_reservation_available'),
334                                        $days[$loop]['available'][$slot_id]
335                                    )
336                                );
337                                $mytpl->parseCurrentBlock();
338                            }
339
340                            $mytpl->setCurrentBlock('dates');
341                            $mytpl->setVariable('DUMMY', '');
342                            $mytpl->parseCurrentBlock();
343                        } elseif (isset($days[$loop]['in_slot'])) {
344                            $mytpl->setCurrentBlock('dates');
345                            $mytpl->setVariable('DATE_COLOR', $color[$loop]);
346                            $mytpl->parseCurrentBlock();
347                        } else {
348                            $mytpl->setCurrentBlock('dates');
349                            $mytpl->setVariable('DUMMY', '&nbsp;');
350                            $mytpl->parseCurrentBlock();
351                        }
352                    }
353                }
354
355                $mytpl->setCurrentBlock('slots');
356                $mytpl->setVariable('TXT_HOUR', $caption);
357                if ($counter % 2) {
358                    $mytpl->setVariable('CSS_ROW', 'tblrow1');
359                } else {
360                    $mytpl->setVariable('CSS_ROW', 'tblrow2');
361                }
362                $mytpl->parseCurrentBlock();
363
364                $counter++;
365            }
366        }
367
368        return $mytpl->get();
369    }
370
371    /**
372     * @param $week_start
373     * @param array $hours
374     * @param ilBookingSchedule $schedule
375     * @param array $object_ids
376     * @param $seed
377     * @param array $dates
378     * @return bool
379     */
380    protected function buildDatesBySchedule($week_start, array $hours, ilBookingSchedule $schedule, array $object_ids, $seed, array &$dates) // ok
381    {
382        $ilUser = $this->user;
383
384        $user_settings = ilCalendarUserSettings::_getInstanceByUserId($ilUser->getId());
385
386        $map = array('mo', 'tu', 'we', 'th', 'fr', 'sa', 'su');
387        $definition = $schedule->getDefinition();
388
389        $av_from = ($schedule->getAvailabilityFrom() && !$schedule->getAvailabilityFrom()->isNull())
390            ? $schedule->getAvailabilityFrom()->get(IL_CAL_DATE)
391            : null;
392        $av_to = ($schedule->getAvailabilityTo() && !$schedule->getAvailabilityTo()->isNull())
393            ? $schedule->getAvailabilityTo()->get(IL_CAL_DATE)
394            : null;
395
396        $has_open_slot = false;
397        /** @var ilDateTime $date */
398        foreach (ilCalendarUtil::_buildWeekDayList($seed, $week_start)->get() as $date) {
399            $date_info = $date->get(IL_CAL_FKT_GETDATE, '', 'UTC');
400
401            #24045 and #24936
402            if ($av_from || $av_to) {
403                $today = $date->get(IL_CAL_DATE);
404
405                if ($av_from && $av_from > $today) {
406                    continue;
407                }
408
409                if ($av_to && $av_to < $today) {
410                    continue;
411                }
412            }
413
414            $slots = array();
415            if (isset($definition[$map[$date_info['isoday'] - 1]])) {
416                $slots = array();
417                foreach ($definition[$map[$date_info['isoday'] - 1]] as $slot) {
418                    $slot = explode('-', $slot);
419                    $slots[] = array('from' => str_replace(':', '', $slot[0]),
420                        'to' => str_replace(':', '', $slot[1]));
421                }
422            }
423
424            $last = array_pop(array_keys($hours));
425            $slot_captions = array();
426            foreach ($hours as $hour => $period) {
427                $dates[$hour][0] = $period;
428
429                $period = explode("-", $period);
430
431                // #13738
432                if ($user_settings->getTimeFormat() == ilCalendarSettings::TIME_FORMAT_12) {
433                    $period[0] = date("H", strtotime($period[0]));
434                    if (sizeof($period) == 2) {
435                        $period[1] = date("H", strtotime($period[1]));
436                    }
437                }
438
439                if (sizeof($period) == 1) {
440                    $period_from = (int) substr($period[0], 0, 2) . "00";
441                    $period_to = (int) substr($period[0], 0, 2) . "59";
442                } else {
443                    $period_from = (int) substr($period[0], 0, 2) . "00";
444                    $period_to = (int) substr($period[1], 0, 2) . "59";
445                }
446
447                $column = $date_info['isoday'];
448                if (!$week_start) {
449                    if ($column < 7) {
450                        $column++;
451                    } else {
452                        $column = 1;
453                    }
454                }
455
456                if (sizeof($slots)) {
457                    $in = false;
458                    foreach ($slots as $slot) {
459                        $slot_from = mktime(substr($slot['from'], 0, 2), substr($slot['from'], 2, 2), 0, $date_info["mon"], $date_info["mday"], $date_info["year"]);
460                        $slot_to = mktime(substr($slot['to'], 0, 2), substr($slot['to'], 2, 2), 0, $date_info["mon"], $date_info["mday"], $date_info["year"]);
461
462                        // always single object, we can sum up
463                        $nr_available = (array) ilBookingReservation::getAvailableObject($object_ids, $slot_from, $slot_to - 1, false, true);
464
465                        // any objects available?
466                        if (!array_sum($nr_available)) {
467                            continue;
468                        }
469
470                        // check deadline
471                        if ($schedule->getDeadline() >= 0) {
472                            // 0-n hours before slots begins
473                            if ($slot_from < (time() + $schedule->getDeadline() * 60 * 60)) {
474                                continue;
475                            }
476                        } else {
477                            // running slots can be booked, only ended slots are invalid
478                            if ($slot_to < time()) {
479                                continue;
480                            }
481                        }
482
483                        // is slot active in current hour?
484                        if ((int) $slot['from'] < $period_to && (int) $slot['to'] > $period_from) {
485                            $from = ilDatePresentation::formatDate(new ilDateTime($slot_from, IL_CAL_UNIX));
486                            $from = array_pop(explode(' ', $from));
487                            $to = ilDatePresentation::formatDate(new ilDateTime($slot_to, IL_CAL_UNIX));
488                            $to = array_pop(explode(' ', $to));
489
490                            // show caption (first hour) of slot
491                            $id = $slot_from . '_' . $slot_to;
492                            if (!in_array($id, $slot_captions)) {
493                                $dates[$hour][$column]['captions'][$id] = $from . '-' . $to;
494                                $dates[$hour][$column]['available'][$id] = array_sum($nr_available);
495                                $slot_captions[] = $id;
496                            }
497
498                            $in = true;
499                        }
500                    }
501                    // (any) active slot
502                    if ($in) {
503                        $has_open_slot = true;
504                        $dates[$hour][$column]['in_slot'] = $in;
505                    }
506                }
507            }
508        }
509
510        return $has_open_slot;
511    }
512
513    //
514    // Step 1a)
515    //
516    // Assign multiple participants (starting from participants screen) (no, from object screen!)
517    //
518
519    //Table to assing participants to an object.
520    public function assignParticipants()
521    {
522        $this->tabs_gui->clearTargets();
523        $this->tabs_gui->setBackTarget($this->lng->txt('book_back_to_list'), $this->ctrl->getLinkTarget($this, 'back'));
524
525        $table = new ilBookingAssignParticipantsTableGUI($this, 'assignParticipants', $this->pool->getRefId(), $this->pool->getId(), $this->book_obj_id);
526
527        $this->tpl->setContent($table->getHTML());
528    }
529
530    /**
531     * Create reservations for a bunch of booking pool participants.
532     */
533    public function bookMultipleParticipants()
534    {
535        if ($_POST["mass"]) {
536            $participants = $_POST["mass"];
537        } else {
538            $this->back();
539        }
540
541        $this->tabs_gui->clearTargets();
542        $this->tabs_gui->setBackTarget($this->lng->txt("back"), $this->ctrl->getLinkTarget($this, 'assignparticipants'));
543
544        $conf = new ilConfirmationGUI();
545        $conf->setFormAction($this->ctrl->getFormAction($this));
546
547        //add user list as items.
548        foreach ($participants as $id) {
549            $name = ilObjUser::_lookupFullname($id);
550            $conf->addItem("participants[]", $id, $name);
551        }
552
553        $available = ilBookingReservation::numAvailableFromObjectNoSchedule($this->book_obj_id);
554        if (sizeof($participants) > $available) {
555            $obj = new ilBookingObject($this->book_obj_id);
556            $conf->setHeaderText(
557                sprintf(
558                    $this->lng->txt('book_limit_objects_available'),
559                    sizeof($participants),
560                    $obj->getTitle(),
561                    $available
562                )
563            );
564        } else {
565            $conf->setHeaderText($this->lng->txt('book_confirm_booking_no_schedule'));
566            $conf->addHiddenItem("object_id", $this->book_obj_id);
567            $conf->setConfirm($this->lng->txt("assign"), "saveMultipleBookings");
568        }
569
570        $conf->setCancel($this->lng->txt("cancel"), 'redirectToList');
571        $this->tpl->setContent($conf->getHTML());
572    }
573
574    public function redirectToList()
575    {
576        $this->ctrl->redirect($this, 'assignParticipants');
577    }
578
579    /**
580     * Save multiple users reservations for one booking pool object.
581     * //TODO check if object/user exist in the DB,
582     */
583    public function saveMultipleBookings()
584    {
585        if ($_POST["participants"] && $_POST['object_id']) {
586            $participants = $_POST["participants"];
587            $this->book_obj_id = $_POST['object_id'];
588        } else {
589            $this->back();
590        }
591        $rsv_ids = array();
592        foreach ($participants as $id) {
593            $this->user_id_to_book = $id;
594            $rsv_ids[] = $this->processBooking($this->book_obj_id);
595        }
596
597        if (sizeof($rsv_ids)) {
598            ilUtil::sendSuccess("booking_multiple_succesfully");
599            $this->back();
600        } else {
601            ilUtil::sendFailure($this->lng->txt('book_reservation_failed_overbooked'), true);
602            $this->back();
603        }
604    }
605
606
607    //
608    // Step 2: Confirmation
609    //
610
611    /**
612     * Book object - either of type or specific - for given dates
613     * @return bool
614     */
615    public function confirmedBooking() // ok
616    {
617        $success = false;
618        $rsv_ids = array();
619
620        if ($this->pool->getScheduleType() != ilObjBookingPool::TYPE_FIX_SCHEDULE) {
621            if ($this->book_obj_id > 0) {
622                $object_id = $this->book_obj_id;
623                if ($object_id) {
624                    if (ilBookingReservation::isObjectAvailableNoSchedule($object_id) &&
625                        !ilBookingReservation::getObjectReservationForUser($object_id, $this->user_id_to_book)) { // #18304
626                        $rsv_ids[] = $this->processBooking($object_id);
627                        $success = $object_id;
628                    } else {
629                        // #11852
630                        ilUtil::sendFailure($this->lng->txt('book_reservation_failed_overbooked'), true);
631                        $this->ctrl->redirect($this, 'back');
632                    }
633                }
634            }
635        } else {
636            if (!isset($_POST['date'])) {
637                ilUtil::sendFailure($this->lng->txt('select_one'));
638                $this->book();
639                return false;
640            }
641
642            // single object reservation(s)
643            if ($this->book_obj_id > 0) {
644                $confirm = array();
645
646                $object_id = $this->book_obj_id;
647                if ($object_id) {
648                    $group_id = null;
649                    $nr = ilBookingObject::getNrOfItemsForObjects(array($object_id));
650                    // needed for recurrence
651                    $f = new ilBookingReservationDBRepositoryFactory();
652                    $repo = $f->getRepo();
653                    $group_id = $repo->getNewGroupId();
654                    foreach ($_POST['date'] as $date) {
655                        $fromto = explode('_', $date);
656                        $fromto[1]--;
657
658                        $counter = ilBookingReservation::getAvailableObject(array($object_id), $fromto[0], $fromto[1], false, true);
659                        $counter = $counter[$object_id];
660                        if ($counter) {
661                            // needed for recurrence
662                            $confirm[$object_id . "_" . $fromto[0] . "_" . ($fromto[1] + 1)] = $counter;
663                        }
664                    }
665                }
666
667                if (sizeof($confirm)) {
668                    $this->confirmBookingNumbers($confirm, $group_id);
669                    return false;
670                }
671            }
672        }
673
674        if ($success) {
675            $this->saveParticipant();
676            $this->handleBookingSuccess($success, $rsv_ids);
677        } else {
678            ilUtil::sendFailure($this->lng->txt('book_reservation_failed'), true);
679            $this->ctrl->redirect($this, 'book');
680        }
681        return true;
682    }
683
684    /**
685     * save booking participant.
686     */
687    protected function saveParticipant()
688    {
689        $participant = new ilBookingParticipant($this->user_id_to_book, $this->pool->getId());
690    }
691
692    /**
693     * Book object for date
694     *
695     * @param int $a_object_id
696     * @param int $a_from timestamp
697     * @param int $a_to timestamp
698     * @param int $a_group_id
699     * @return int
700     */
701    public function processBooking($a_object_id, $a_from = null, $a_to = null, $a_group_id = null)
702    {
703        // #11995
704        $this->checkPermission('read');
705
706        $reservation = new ilBookingReservation();
707        $reservation->setObjectId($a_object_id);
708        $reservation->setUserId($this->user_id_to_book);
709        $reservation->setAssignerId($this->user_id_assigner);
710        $reservation->setFrom($a_from);
711        $reservation->setTo($a_to);
712        $reservation->setGroupId($a_group_id);
713        $reservation->setContextObjId($this->context_obj_id);
714        $reservation->save();
715
716        if ($a_from) {
717            $this->lng->loadLanguageModule('dateplaner');
718            $def_cat = ilCalendarUtil::initDefaultCalendarByType(ilCalendarCategory::TYPE_BOOK, $this->user_id_to_book, $this->lng->txt('cal_ch_personal_book'), true);
719
720            $object = new ilBookingObject($a_object_id);
721
722            $entry = new ilCalendarEntry;
723            $entry->setStart(new ilDateTime($a_from, IL_CAL_UNIX));
724            $entry->setEnd(new ilDateTime($a_to, IL_CAL_UNIX));
725            $entry->setTitle($this->lng->txt('book_cal_entry') . ' ' . $object->getTitle());
726            $entry->setContextId($reservation->getId());
727            $entry->save();
728
729            $assignment = new ilCalendarCategoryAssignments($entry->getEntryId());
730            $assignment->addAssignment($def_cat->getCategoryId());
731        }
732
733        return $reservation->getId();
734    }
735
736    //
737    // Confirm booking numbers
738    //
739
740    public function confirmBookingNumbers(array $a_objects_counter, $a_group_id, ilPropertyFormGUI $a_form = null)
741    {
742        $tpl = $this->tpl;
743
744        $this->tabs_gui->clearTargets();
745        $this->tabs_gui->setBackTarget($this->lng->txt('book_back_to_list'), $this->ctrl->getLinkTarget($this, 'back'));
746
747        if (!$a_form) {
748            $a_form = $this->initBookingNumbersForm($a_objects_counter, $a_group_id);
749        }
750
751        $tpl->setContent($a_form->getHTML());
752    }
753
754
755    protected function initBookingNumbersForm(array $a_objects_counter, $a_group_id, $a_reload = false)
756    {
757        $form = new ilPropertyFormGUI();
758        $form->setFormAction($this->ctrl->getFormAction($this, "confirmedBooking"));
759        $form->setTitle($this->lng->txt("book_confirm_booking_schedule_number_of_objects"));
760        $form->setDescription($this->lng->txt("book_confirm_booking_schedule_number_of_objects_info"));
761
762        $section = false;
763        $min_date = null;
764        foreach ($a_objects_counter as $id => $counter) {
765            $id = explode("_", $id);
766            $book_id = $id[0] . "_" . $id[1] . "_" . $id[2] . "_" . $counter;
767
768            $obj = new ilBookingObject($id[0]);
769
770            if (!$section) {
771                $section = new ilFormSectionHeaderGUI();
772                $section->setTitle($obj->getTitle());
773                $form->addItem($section);
774
775                $section = true;
776            }
777
778            $period = /* $this->lng->txt("book_period").": ". */
779                ilDatePresentation::formatPeriod(
780                    new ilDateTime($id[1], IL_CAL_UNIX),
781                    new ilDateTime($id[2], IL_CAL_UNIX)
782                );
783
784            $nr_field = new ilNumberInputGUI($period, "conf_nr__" . $book_id);
785            $nr_field->setValue(1);
786            $nr_field->setSize(3);
787            $nr_field->setMaxValue($counter);
788            $nr_field->setMinValue($counter ? 1 : 0);
789            $nr_field->setRequired(true);
790            $form->addItem($nr_field);
791
792            if (!$min_date || $id[1] < $min_date) {
793                $min_date = $id[1];
794            }
795        }
796
797        // recurrence
798        $this->lng->loadLanguageModule("dateplaner");
799        $rec_mode = new ilSelectInputGUI($this->lng->txt("cal_recurrences"), "recm");
800        $rec_mode->setRequired(true);
801        $rec_mode->setOptions(array(
802            "-1" => $this->lng->txt("cal_no_recurrence"),
803            1 => $this->lng->txt("cal_weekly"),
804            2 => $this->lng->txt("r_14"),
805            4 => $this->lng->txt("r_4_weeks")
806        ));
807        $form->addItem($rec_mode);
808
809        $rec_end = new ilDateTimeInputGUI($this->lng->txt("cal_repeat_until"), "rece");
810        $rec_end->setRequired(true);
811        $rec_mode->addSubItem($rec_end);
812
813        if (!$a_reload) {
814            // show date only if active recurrence
815            $rec_mode->setHideSubForm(true, '>= 1');
816
817            if ($min_date) {
818                $rec_end->setDate(new ilDateTime($min_date, IL_CAL_UNIX));
819            }
820        } else {
821            // recurrence may not be changed on reload
822            $rec_mode->setDisabled(true);
823            $rec_end->setDisabled(true);
824        }
825
826        if ($a_group_id) {
827            $grp = new ilHiddenInputGUI("grp_id");
828            $grp->setValue($a_group_id);
829            $form->addItem($grp);
830        }
831
832        if ($this->user_id_assigner != $this->user_id_to_book) {
833            $usr = new ilHiddenInputGUI("bkusr");
834            $usr->setValue($this->user_id_to_book);
835            $form->addItem($usr);
836        }
837
838        $form->addCommandButton("confirmedBookingNumbers", $this->lng->txt("confirm"));
839        $form->addCommandButton("back", $this->lng->txt("cancel"));
840
841        return $form;
842    }
843
844    public function confirmedBookingNumbers() // ok
845    {
846
847        //get the user who will get the booking.
848        if ($_POST['bkusr']) {
849            $this->user_id_to_book = (int) $_POST['bkusr'];
850        }
851
852        // convert post data to initial form config
853        $counter = array();
854        $current_first = $obj_id = null;
855        foreach (array_keys($_POST) as $id) {
856            if (substr($id, 0, 9) == "conf_nr__") {
857                $id = explode("_", substr($id, 9));
858                $counter[$id[0] . "_" . $id[1] . "_" . $id[2]] = (int) $id[3];
859                if (!$current_first) {
860                    $current_first = date("Y-m-d", $id[1]);
861                }
862            }
863        }
864
865        // recurrence
866
867        // checkInput() has not been called yet, so we have to improvise
868        $end = ilCalendarUtil::parseIncomingDate($_POST["rece"], null);
869
870        if ((int) $_POST["recm"] > 0 && $end && $current_first) {
871            ksort($counter);
872            $end = $end->get(IL_CAL_DATE);
873            $cycle = (int) $_POST["recm"] * 7;
874            $cut = 0;
875            $org = $counter;
876            while ($cut < 1000 && $this->addDaysDate($current_first, $cycle) <= $end) {
877                $cut++;
878                $current_first = null;
879                foreach ($org as $item_id => $max) {
880                    $parts = explode("_", $item_id);
881                    $obj_id = $parts[0];
882
883                    $from = $this->addDaysStamp($parts[1], $cycle * $cut);
884                    $to = $this->addDaysStamp($parts[2], $cycle * $cut);
885
886                    $new_item_id = $obj_id . "_" . $from . "_" . $to;
887
888                    // form reload because of validation errors
889                    if (!isset($counter[$new_item_id]) && date("Y-m-d", $to) <= $end) {
890                        // get max available for added dates
891                        $new_max = ilBookingReservation::getAvailableObject(array($obj_id), $from, $to - 1, false, true);
892                        $new_max = (int) $new_max[$obj_id];
893
894                        $counter[$new_item_id] = $new_max;
895
896                        if (!$current_first) {
897                            $current_first = date("Y-m-d", $from);
898                        }
899
900                        // clone input
901                        $_POST["conf_nr__" . $new_item_id . "_" . $new_max] = $_POST["conf_nr__" . $item_id . "_" . $max];
902                    }
903                }
904            }
905        }
906
907        $group_id = $_POST["grp_id"];
908
909        $form = $this->initBookingNumbersForm($counter, $group_id, true);
910        if ($form->checkInput()) {
911            $success = false;
912            $rsv_ids = array();
913            foreach ($counter as $id => $all_nr) {
914                $book_nr = $form->getInput("conf_nr__" . $id . "_" . $all_nr);
915                $parts = explode("_", $id);
916                $obj_id = $parts[0];
917                $from = $parts[1];
918                $to = $parts[2] - 1;
919
920                // get currently available slots
921                $counter = ilBookingReservation::getAvailableObject(array($obj_id), $from, $to, false, true);
922                $counter = $counter[$obj_id];
923                if ($counter) {
924                    // we can only book what is left
925                    $book_nr = min($book_nr, $counter);
926                    for ($loop = 0; $loop < $book_nr; $loop++) {
927                        $rsv_ids[] = $this->processBooking($obj_id, $from, $to, $group_id);
928                        $success = $obj_id;
929                    }
930                }
931            }
932            if ($success) {
933                $this->saveParticipant();
934                $this->handleBookingSuccess($success, $rsv_ids);
935            } else {
936                ilUtil::sendFailure($this->lng->txt('book_reservation_failed'), true);
937                $this->back();
938            }
939        } else {
940            // ilDateTimeInputGUI does NOT add hidden values on disabled!
941
942            $rece_array = explode(".", $_POST['rece']);
943
944            $rece_day = str_pad($rece_array[0], 2, "0", STR_PAD_LEFT);
945            $rece_month = str_pad($rece_array[1], 2, "0", STR_PAD_LEFT);
946            $rece_year = $rece_array[2];
947
948            // ilDateTimeInputGUI will choke on POST array format
949            $_POST["rece"] = null;
950
951            $form->setValuesByPost();
952
953            $rece_date = new ilDate($rece_year . "-" . $rece_month . "-" . $rece_day, IL_CAL_DATE);
954
955            $form->getItemByPostVar("rece")->setDate($rece_date);
956            $form->getItemByPostVar("recm")->setHideSubForm($_POST["recm"] < 1);
957
958            $hidden_date = new ilHiddenInputGUI("rece");
959            $hidden_date->setValue($rece_date);
960            $form->addItem($hidden_date);
961
962            $this->confirmBookingNumbers($counter, $group_id, $form);
963        }
964    }
965
966    protected function addDaysDate($a_date, $a_days)
967    {
968        $date = date_parse($a_date);
969        $stamp = mktime(0, 0, 1, $date["month"], $date["day"] + $a_days, $date["year"]);
970        return date("Y-m-d", $stamp);
971    }
972
973    protected function addDaysStamp($a_stamp, $a_days)
974    {
975        $date = getDate($a_stamp);
976        return mktime(
977            $date["hours"],
978            $date["minutes"],
979            $date["seconds"],
980            $date["mon"],
981            $date["mday"] + $a_days,
982            $date["year"]
983        );
984    }
985
986    //
987    // Step 3: Display post success info
988    //
989
990    protected function handleBookingSuccess($a_obj_id, array $a_rsv_ids = null)
991    {
992        ilUtil::sendSuccess($this->lng->txt('book_reservation_confirmed'), true);
993
994        // show post booking information?
995        $obj = new ilBookingObject($a_obj_id);
996        $pfile = $obj->getPostFile();
997        $ptext = $obj->getPostText();
998
999        if (trim($ptext) || $pfile) {
1000            if (sizeof($a_rsv_ids)) {
1001                $this->ctrl->setParameter($this, 'rsv_ids', implode(";", $a_rsv_ids));
1002            }
1003            $this->ctrl->redirect($this, 'displayPostInfo');
1004        } else {
1005            $this->back();
1006        }
1007    }
1008
1009    /**
1010     * Display post booking informatins
1011     */
1012    public function displayPostInfo()
1013    {
1014        $tpl = $this->tpl;
1015        $lng = $this->lng;
1016        $ilCtrl = $this->ctrl;
1017        $id = $this->book_obj_id;
1018        if (!$id) {
1019            return;
1020        }
1021
1022        // placeholder
1023
1024        $book_ids = ilBookingReservation::getObjectReservationForUser($id, $this->user_id_assigner, true);
1025        $tmp = array();
1026        foreach ($book_ids as $book_id) {
1027            if (in_array($book_id, $this->rsv_ids)) {
1028                $obj = new ilBookingReservation($book_id);
1029                $from = $obj->getFrom();
1030                $to = $obj->getTo();
1031                if ($from > time()) {
1032                    $tmp[$from . "-" . $to]++;
1033                }
1034            }
1035        }
1036
1037        $olddt = ilDatePresentation::useRelativeDates();
1038        ilDatePresentation::setUseRelativeDates(false);
1039
1040        $period = array();
1041        ksort($tmp);
1042        foreach ($tmp as $time => $counter) {
1043            $time = explode("-", $time);
1044            $time = ilDatePresentation::formatPeriod(
1045                new ilDateTime($time[0], IL_CAL_UNIX),
1046                new ilDateTime($time[1], IL_CAL_UNIX)
1047            );
1048            if ($counter > 1) {
1049                $time .= " (" . $counter . ")";
1050            }
1051            $period[] = $time;
1052        }
1053        $book_id = array_shift($book_ids);
1054
1055        ilDatePresentation::setUseRelativeDates($olddt);
1056
1057
1058        /*
1059        #23578 since Booking pool participants.
1060        $obj = new ilBookingReservation($book_id);
1061        if ($obj->getUserId() != $ilUser->getId())
1062        {
1063            return;
1064        }
1065        */
1066
1067        $obj = new ilBookingObject($id);
1068        $pfile = $obj->getPostFile();
1069        $ptext = $obj->getPostText();
1070
1071        $mytpl = new ilTemplate('tpl.booking_reservation_post.html', true, true, 'Modules/BookingManager/BookingProcess');
1072        $mytpl->setVariable("TITLE", $lng->txt('book_post_booking_information'));
1073
1074        if ($ptext) {
1075            // placeholder
1076            $ptext = str_replace("[OBJECT]", $obj->getTitle(), $ptext);
1077            $ptext = str_replace("[PERIOD]", implode("<br />", $period), $ptext);
1078
1079            $mytpl->setVariable("POST_TEXT", nl2br($ptext));
1080        }
1081
1082        if ($pfile) {
1083            $url = $ilCtrl->getLinkTarget($this, 'deliverPostFile');
1084
1085            $mytpl->setVariable("DOWNLOAD", $lng->txt('download'));
1086            $mytpl->setVariable("URL_FILE", $url);
1087            $mytpl->setVariable("TXT_FILE", $pfile);
1088        }
1089
1090        $mytpl->setVariable("TXT_SUBMIT", $lng->txt('ok'));
1091        $mytpl->setVariable("URL_SUBMIT", $ilCtrl->getLinkTarget($this, "back"));
1092
1093        $tpl->setContent($mytpl->get());
1094    }
1095
1096    /**
1097     * Deliver post booking file
1098     */
1099    public function deliverPostFile()
1100    {
1101        $id = $this->book_obj_id;
1102        if (!$id) {
1103            return;
1104        }
1105
1106        $book_id = ilBookingReservation::getObjectReservationForUser($id, $this->user_id_assigner);
1107        $obj = new ilBookingReservation($book_id);
1108        if ($obj->getUserId() != $this->user_id_assigner) {
1109            return;
1110        }
1111
1112        $obj = new ilBookingObject($id);
1113        $file = $obj->getPostFileFullPath();
1114        if ($file) {
1115            ilUtil::deliverFile($file, $obj->getPostFile());
1116        }
1117    }
1118}
1119