ILIAS  release_7 Revision v7.30-3-g800a261c036
class.ilCalendarSchedule.php
Go to the documentation of this file.
1<?php
2/*
3 +-----------------------------------------------------------------------------+
4 | ILIAS open source |
5 +-----------------------------------------------------------------------------+
6 | Copyright (c) 1998-2006 ILIAS open source, University of Cologne |
7 | |
8 | This program is free software; you can redistribute it and/or |
9 | modify it under the terms of the GNU General Public License |
10 | as published by the Free Software Foundation; either version 2 |
11 | of the License, or (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software |
20 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21 +-----------------------------------------------------------------------------+
22*/
23
24
37{
38 const TYPE_DAY = 1;
39 const TYPE_WEEK = 2;
40 const TYPE_MONTH = 3;
41 const TYPE_INBOX = 4;
42 const TYPE_HALF_YEAR = 6;
43
44 // @deprecated
46
47 protected $limit_events = -1;
48 protected $schedule = array();
49 protected $timezone;
50 protected $weekstart;
51 protected $type = 0;
52
53 protected $subitems_enabled = false;
54
55 protected $start = null;
56 protected $end = null;
57 protected $user = null;
58 protected $user_settings = null;
59 protected $db = null;
60 protected $filters = array();
61
65 protected $strict_period;
66
70 protected $logger;
71
82 public function __construct(ilDate $seed, $a_type, $a_user_id = 0, $a_strict_period = false)
83 {
84 global $DIC;
85
86 $ilUser = $DIC['ilUser'];
87 $ilDB = $DIC['ilDB'];
88
89 $this->logger = $DIC->logger()->cal();
90
91 $this->db = $ilDB;
92
93 $this->type = $a_type;
94
95 //this strict period is just to avoid possible side effects.
96 //I there are none, we can get rid of this strict period control and remove it from the constructor
97 //and from the calls in ilCalendarView getEvents.
98 $this->strict_period = $a_strict_period;
99
100 if (!$a_user_id || $a_user_id == $ilUser->getId()) {
101 $this->user = $ilUser;
102 } else {
103 $this->user = new ilObjUser($a_user_id);
104 }
105 $this->user_settings = ilCalendarUserSettings::_getInstanceByUserId($this->user->getId());
106 $this->weekstart = $this->user_settings->getWeekStart();
107 $this->timezone = $this->user->getTimeZone();
108
109 $this->initPeriod($seed);
110
111
112 // category / event filters
113
114 // portfolio does custom filter handling (booking group ids)
116 // consultation hour calendar views do not mind calendar category visibility
118 // this is the "default" filter which handles currently hidden categories for the user
119 $this->addFilter(new ilCalendarScheduleFilterHidden($this->user->getId()));
120 } else {
121 // handle booking visibility (target object, booked out)
122 //this filter deals with consultation hours
123 $this->addFilter(new ilCalendarScheduleFilterBookings($this->user->getId()));
124 }
125
127 //this filter deals with booking pool reservations
128 $this->addFilter(new ilCalendarScheduleFilterBookingPool($this->user->getId()));
129 }
130
133 }
134
135 $this->addFilter(new ilCalendarScheduleFilterExercise($this->user->getId()));
136 $this->addFilter(new ilCalendarScheduleFilterTimings($this->user->getId()));
137 }
138 }
139
144 protected function areEventsLimited()
145 {
146 return $this->limit_events != -1;
147 }
148
153 public function getEventsLimit()
154 {
155 return $this->limit_events;
156 }
157
162 public function setEventsLimit($a_limit)
163 {
164 $this->limit_events = $a_limit;
165 }
166
172 public function addSubitemCalendars($a_status)
173 {
174 $this->subitems_enabled = $a_status;
175 }
176
181 public function enabledSubitemCalendars()
182 {
183 return (bool) $this->subitems_enabled;
184 }
185
191 public function addFilter(ilCalendarScheduleFilter $a_filter)
192 {
193 $this->filters[] = $a_filter;
194 }
195
203 public function getByDay(ilDate $a_start, $a_timezone)
204 {
205 $start = new ilDateTime($a_start->get(IL_CAL_DATETIME), IL_CAL_DATETIME, $this->timezone);
206 $fstart = new ilDate($a_start->get(IL_CAL_UNIX), IL_CAL_UNIX);
207 $fend = clone $fstart;
208
209 $f_unix_start = $fstart->get(IL_CAL_UNIX);
210 $fend->increment(ilDateTime::DAY, 1);
211 $f_unix_end = $fend->get(IL_CAL_UNIX);
212
213 $unix_start = $start->get(IL_CAL_UNIX);
214 $start->increment(ilDateTime::DAY, 1);
215 $unix_end = $start->get(IL_CAL_UNIX);
216
217 $counter = 0;
218
219 $tmp_date = new ilDateTime($unix_start, IL_CAL_UNIX, $this->timezone);
220 $tmp_schedule = array();
221 $tmp_schedule_fullday = array();
222 foreach ($this->schedule as $schedule) {
223 if ($schedule['fullday']) {
224 if (($f_unix_start == $schedule['dstart']) or
225 $f_unix_start == $schedule['dend'] or
226 ($f_unix_start > $schedule['dstart'] and $f_unix_end <= $schedule['dend'])) {
227 $tmp_schedule_fullday[] = $schedule;
228 }
229 } elseif (($schedule['dstart'] == $unix_start) or
230 (($schedule['dstart'] <= $unix_start) and ($schedule['dend'] > $unix_start)) or
231 (($schedule['dstart'] >= $unix_start) and ($schedule['dstart'] < $unix_end))) {
232 $tmp_schedule[] = $schedule;
233 }
234 }
235
236 //order non full day events by starting date;
237 usort($tmp_schedule, function ($a, $b) {
238 return $a['dstart'] <=> $b['dstart'];
239 });
240
241 //merge both arrays keeping the full day events first and then rest ordered by starting date.
242 $schedules = array_merge($tmp_schedule_fullday, $tmp_schedule);
243
244 return $schedules;
245 }
246
247
253 public function calculate()
254 {
255 $events = $this->getEvents();
256
257 // we need category type for booking handling
258 $ids = array();
259 foreach ($events as $event) {
260 $ids[] = $event->getEntryId();
261 }
262
263 include_once('Services/Calendar/classes/class.ilCalendarCategoryAssignments.php');
265 include_once('Services/Calendar/classes/class.ilCalendarCategory.php');
266 $cat_types = array();
267 foreach (array_unique($cat_map) as $cat_id) {
268 $cat = new ilCalendarCategory($cat_id);
269 $cat_types[$cat_id] = $cat->getType();
270 }
271
272 $counter = 0;
273 foreach ($events as $event) {
274 // Calculdate recurring events
275 include_once('Services/Calendar/classes/class.ilCalendarRecurrences.php');
276 if ($recs = ilCalendarRecurrences::_getRecurrences($event->getEntryId())) {
277 $duration = $event->getEnd()->get(IL_CAL_UNIX) - $event->getStart()->get(IL_CAL_UNIX);
278 foreach ($recs as $rec) {
279 $calc = new ilCalendarRecurrenceCalculator($event, $rec);
280 foreach ($calc->calculateDateList($this->start, $this->end)->get() as $rec_date) {
281 if ($this->type == self::TYPE_PD_UPCOMING &&
282 $rec_date->get(IL_CAL_UNIX) < time()) {
283 continue;
284 }
285
286 $this->schedule[$counter]['event'] = $event;
287 $this->schedule[$counter]['dstart'] = $rec_date->get(IL_CAL_UNIX);
288 $this->schedule[$counter]['dend'] = $this->schedule[$counter]['dstart'] + $duration;
289 $this->schedule[$counter]['fullday'] = $event->isFullday();
290 $this->schedule[$counter]['category_id'] = $cat_map[$event->getEntryId()];
291 $this->schedule[$counter]['category_type'] = $cat_types[$cat_map[$event->getEntryId()]];
292
293 switch ($this->type) {
294 case self::TYPE_DAY:
295 case self::TYPE_WEEK:
296 // store date info (used for calculation of overlapping events)
297 $tmp_date = new ilDateTime($this->schedule[$counter]['dstart'], IL_CAL_UNIX, $this->timezone);
298 $this->schedule[$counter]['start_info'] = $tmp_date->get(IL_CAL_FKT_GETDATE, '', $this->timezone);
299
300 $tmp_date = new ilDateTime($this->schedule[$counter]['dend'], IL_CAL_UNIX, $this->timezone);
301 $this->schedule[$counter]['end_info'] = $tmp_date->get(IL_CAL_FKT_GETDATE, '', $this->timezone);
302 break;
303
304 default:
305 break;
306 }
307 $counter++;
308 if ($this->type != self::TYPE_PD_UPCOMING &&
309 $this->areEventsLimited() && $counter >= $this->getEventsLimit()) {
310 break;
311 }
312 }
313 }
314 } else {
315 $this->schedule[$counter]['event'] = $event;
316 $this->schedule[$counter]['dstart'] = $event->getStart()->get(IL_CAL_UNIX);
317 $this->schedule[$counter]['dend'] = $event->getEnd()->get(IL_CAL_UNIX);
318 $this->schedule[$counter]['fullday'] = $event->isFullday();
319 $this->schedule[$counter]['category_id'] = $cat_map[$event->getEntryId()];
320 $this->schedule[$counter]['category_type'] = $cat_types[$cat_map[$event->getEntryId()]];
321
322 if (!$event->isFullday()) {
323 switch ($this->type) {
324 case self::TYPE_DAY:
325 case self::TYPE_WEEK:
326 // store date info (used for calculation of overlapping events)
327 $tmp_date = new ilDateTime($this->schedule[$counter]['dstart'], IL_CAL_UNIX, $this->timezone);
328 $this->schedule[$counter]['start_info'] = $tmp_date->get(IL_CAL_FKT_GETDATE, '', $this->timezone);
329
330 $tmp_date = new ilDateTime($this->schedule[$counter]['dend'], IL_CAL_UNIX, $this->timezone);
331 $this->schedule[$counter]['end_info'] = $tmp_date->get(IL_CAL_FKT_GETDATE, '', $this->timezone);
332 break;
333
334 default:
335 break;
336 }
337 }
338 $counter++;
339 if ($this->type != self::TYPE_PD_UPCOMING &&
340 $this->areEventsLimited() && $counter >= $this->getEventsLimit()) {
341 break;
342 }
343 }
344 }
345
346 if ($this->type == self::TYPE_PD_UPCOMING) {
347 $this->schedule = ilUtil::sortArray($this->schedule, "dstart", "asc", true);
348 if ($this->areEventsLimited() && sizeof($this->schedule) >= $this->getEventsLimit()) {
349 $this->schedule = array_slice($this->schedule, 0, $this->getEventsLimit());
350 }
351 }
352 }
353
354 public function getScheduledEvents()
355 {
356 return (array) $this->schedule;
357 }
358
359 protected function filterCategories(array $a_cats)
360 {
361 if (!sizeof($a_cats)) {
362 return $a_cats;
363 }
364
365 foreach ($this->filters as $filter) {
366 if (sizeof($a_cats)) {
367 $a_cats = $filter->filterCategories($a_cats);
368 }
369 }
370
371 return $a_cats;
372 }
373
374 protected function modifyEventByFilters(ilCalendarEntry $event)
375 {
376 foreach ($this->filters as $filter) {
377 $res = $filter->modifyEvent($event);
378 if (!$res) {
379 $this->logger->info('filtering failed for ' . get_class($filter));
380 return false;
381 }
382 $event = $res;
383 }
384 return $event;
385 }
386
387 protected function addCustomEvents(ilDate $start, ilDate $end, array $categories)
388 {
389 $new_events = array();
390 foreach ($this->filters as $filter) {
391 $events_by_filter = $filter->addCustomEvents($start, $end, $categories);
392 if ($events_by_filter) {
393 $new_events = array_merge($new_events, $events_by_filter);
394 }
395 }
396 return $new_events;
397 }
398
407 public function getChangedEvents($a_include_subitem_calendars = false)
408 {
409 global $DIC;
410
411 $ilDB = $DIC['ilDB'];
412
413 include_once('./Services/Calendar/classes/class.ilCalendarCategories.php');
414 $cats = ilCalendarCategories::_getInstance($this->user->getId())->getCategories($a_include_subitem_calendars);
415 $cats = $this->filterCategories($cats);
416
417 if (!count($cats)) {
418 return array();
419 }
420
421 $start = new ilDate(date('Y-m-d', time()), IL_CAL_DATE);
422 $start->increment(IL_CAL_MONTH, -1);
423
424 $query = "SELECT ce.cal_id cal_id FROM cal_entries ce " .
425 "JOIN cal_cat_assignments ca ON ca.cal_id = ce.cal_id " .
426 "WHERE last_update > " . $ilDB->quote($start->get(IL_CAL_DATETIME), 'timestamp') . " " .
427 "AND " . $ilDB->in('ca.cat_id', $cats, false, 'integer') . ' ' .
428 "ORDER BY last_update";
429 $res = $this->db->query($query);
430
431 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
432 $event = new ilCalendarEntry($row->cal_id);
433 $valid_event = $this->modifyEventByFilters($event);
434 if ($valid_event) {
435 $events[] = $valid_event;
436 }
437 }
438
439 foreach ($this->addCustomEvents($this->start, $this->end, $cats) as $event) {
440 $events[] = $event;
441 }
442
443 return $events ? $events : array();
444 }
445
446
452 public function getEvents()
453 {
454 global $DIC;
455
456 $ilDB = $DIC['ilDB'];
457
458 include_once('./Services/Calendar/classes/class.ilCalendarCategories.php');
459 $cats = ilCalendarCategories::_getInstance($this->user->getId())->getCategories($this->enabledSubitemCalendars());
460 $cats = $this->filterCategories($cats);
461
462 if (!count($cats)) {
463 return array();
464 }
465
466 // TODO: optimize
467 $query = "SELECT ce.cal_id cal_id" .
468 " FROM cal_entries ce" .
469 " LEFT JOIN cal_recurrence_rules crr ON (ce.cal_id = crr.cal_id)" .
470 " JOIN cal_cat_assignments ca ON (ca.cal_id = ce.cal_id)";
471
472 if ($this->type != self::TYPE_INBOX) {
473 $query .= " WHERE ((starta <= " . $this->db->quote($this->end->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp') .
474 " AND enda >= " . $this->db->quote($this->start->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp') . ")" .
475 " OR (starta <= " . $this->db->quote($this->end->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp') .
476 " AND NOT rule_id IS NULL))";
477 } else {
478 $date = new ilDateTime(mktime(0, 0, 0), IL_CAL_UNIX);
479 $query .= " WHERE starta >= " . $this->db->quote($date->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp');
480 }
481
482 $query .= " AND " . $ilDB->in('ca.cat_id', $cats, false, 'integer') .
483 " ORDER BY starta";
484
485 $res = $this->db->query($query);
486
487 $events = array();
488 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
489 $event = new ilCalendarEntry($row->cal_id);
490 $valid_event = $this->modifyEventByFilters($event);
491 if ($valid_event) {
492 $events[] = $valid_event;
493 }
494 }
495
496 foreach ($this->addCustomEvents($this->start, $this->end, $cats) as $event) {
497 $events[] = $event;
498 }
499
500 return $events;
501 }
502
510 protected function initPeriod(ilDate $seed)
511 {
512 switch ($this->type) {
513 case self::TYPE_DAY:
514 $this->start = clone $seed;
515 $this->end = clone $seed;
516 //this strict period is just to avoid possible side effects.
517 if (!$this->strict_period) {
518 $this->start->increment(IL_CAL_DAY, -2);
519 $this->end->increment(IL_CAL_DAY, 2);
520 } else {
521 $this->end->increment(IL_CAL_DAY, 1);
522 $this->end->increment(IL_CAL_SECOND, -1);
523 }
524 break;
525
526 case self::TYPE_WEEK:
527 $this->start = clone $seed;
528 $start_info = $this->start->get(IL_CAL_FKT_GETDATE, '', 'UTC');
529 $day_diff = $this->weekstart - $start_info['isoday'];
530
531 if ($day_diff == 7) {
532 $day_diff = 0;
533 }
534
535 //this strict period is just to avoid possible side effects.
536 if ($this->strict_period) {
537 $this->start->increment(IL_CAL_DAY, $day_diff);
538 $this->end = clone $this->start;
539 $this->end->increment(IL_CAL_WEEK); #22173
540 } else {
541 $this->start->increment(IL_CAL_DAY, $day_diff);
542 $this->start->increment(IL_CAL_DAY, -1);
543 $this->end = clone $this->start;
544 $this->end->increment(IL_CAL_DAY, 9);
545 }
546 break;
547
548 case self::TYPE_MONTH:
549 if ($this->strict_period) {
550 $this->start = clone $seed;
551 $this->end = clone $seed;
552 $this->end->increment(IL_CAL_MONTH, 1);
553 } else {
554 $year_month = $seed->get(IL_CAL_FKT_DATE, 'Y-m', 'UTC');
555 list($year, $month) = explode('-', $year_month);
556
557 #21716
558 $this->start = new ilDate($year_month . '-01', IL_CAL_DATE);
559
560 $start_unix_time = $this->start->getUnixTime();
561
562 $start_day_of_week = (int) date('w', $start_unix_time);
563
564 $number_days_previous_month = 0;
565
566 if ($start_day_of_week === 0 && $this->weekstart === ilCalendarSettings::WEEK_START_MONDAY) {
567 $number_days_previous_month = 6;
568 } elseif ($start_day_of_week > 0) {
569 $number_days_previous_month = $start_day_of_week;
570
571 if ($this->weekstart === ilCalendarSettings::WEEK_START_MONDAY) {
572 $number_days_previous_month = $start_day_of_week - 1;
573 }
574 }
575
576 $this->start->increment(IL_CAL_DAY, -$number_days_previous_month);
577
578 #21716
579 $this->end = new ilDate($year_month . '-' . ilCalendarUtil::_getMaxDayOfMonth($year, $month), IL_CAL_DATE);
580
581 $end_unix_time = $this->end->getUnixTime();
582
583 $end_day_of_week = (int) date('w', $end_unix_time);
584
585 if ($end_day_of_week > 0) {
586 $number_days_next_month = 7 - $end_day_of_week;
587
588 if ($this->weekstart == ilCalendarSettings::WEEK_START_SUNDAY) {
589 $number_days_next_month = $number_days_next_month - 1;
590 }
591
592 $this->end->increment(IL_CAL_DAY, $number_days_next_month);
593 }
594 }
595
596 break;
597
599 $this->start = clone $seed;
600 $this->end = clone $this->start;
601 $this->end->increment(IL_CAL_MONTH, 6);
602 break;
603
605 case self::TYPE_INBOX:
606 $this->start = $seed;
607 $this->end = clone $this->start;
608 $this->end->increment(IL_CAL_MONTH, 12);
609 break;
610 }
611
612 return true;
613 }
614
621 public function setPeriod(ilDate $a_start, ilDate $a_end)
622 {
623 $this->start = $a_start;
624 $this->end = $a_end;
625 }
626}
user()
Definition: user.php:4
An exception for terminatinating execution or to throw for unit testing.
const IL_CAL_FKT_GETDATE
const IL_CAL_DATE
const IL_CAL_WEEK
const IL_CAL_UNIX
const IL_CAL_DATETIME
const IL_CAL_MONTH
const IL_CAL_FKT_DATE
const IL_CAL_DAY
const IL_CAL_SECOND
static _getInstance($a_usr_id=0)
get singleton instance
static _getAppointmentCalendars($a_cal_ids)
lookup calendars for appointment ids
Stores calendar categories.
Model for a calendar entry.
Calculates an ilDateList for a given calendar entry and recurrence rule.
static _getRecurrences($a_cal_id)
get all recurrences of an appointment
Calendar schedule filter for booking pool reservations.
Calendar schedule filter for consultation hour bookings.
Calendar schedule filter for hidden categories.
Calendar schedule filter for individual timings.
Represents a list of calendar appointments (including recurring events) for a specific user in a give...
enabledSubitemCalendars()
Are subitem calendars enabled.
areEventsLimited()
Check if events are limited.
setEventsLimit($a_limit)
Set events limit.
__construct(ilDate $seed, $a_type, $a_user_id=0, $a_strict_period=false)
Constructor.
modifyEventByFilters(ilCalendarEntry $event)
getChangedEvents($a_include_subitem_calendars=false)
get new/changed events
getEvents()
Read events (will be moved to another class, since only active and/or visible calendars are shown)
addCustomEvents(ilDate $start, ilDate $end, array $categories)
addFilter(ilCalendarScheduleFilter $a_filter)
Add filter.
setPeriod(ilDate $a_start, ilDate $a_end)
Set period.
addSubitemCalendars($a_status)
Enable subitem calendars (session calendars for courses)
getByDay(ilDate $a_start, $a_timezone)
get byday
getEventsLimit()
get current limit of events
initPeriod(ilDate $seed)
init period of events
static _getInstanceByUserId($a_user_id)
get singleton instance
static _getMaxDayOfMonth($a_year, $a_month)
get max day of month 2008,2 => 29
@classDescription Date and time handling
increment($a_type, $a_count=1)
increment
Class for single dates.
get($a_format, $a_format_str='', $a_tz='')
get formatted date
static sortArray( $array, $a_array_sortby, $a_array_sortorder=0, $a_numeric=false, $a_keep_keys=false)
sortArray
global $DIC
Definition: goto.php:24
$ilUser
Definition: imgupload.php:18
Calendar schedule filter interface.
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
$query
foreach($_POST as $key=> $value) $res
global $ilDB