ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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
45  const TYPE_PD_UPCOMING = 5;
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  // portfolio does custom filter handling (booking group ids)
115  // consultation hour calendar views do not mind calendar category visibility
117  // this is the "default" filter which handles currently hidden categories for the user
118  $this->addFilter(new ilCalendarScheduleFilterHidden($this->user->getId()));
119  } else {
120  // handle booking visibility (target object, booked out)
121  //this filter deals with consultation hours
122  $this->addFilter(new ilCalendarScheduleFilterBookings($this->user->getId()));
123  }
124 
126  //this filter deals with booking pool reservations
127  $this->addFilter(new ilCalendarScheduleFilterBookingPool($this->user->getId()));
128  }
129 
130  $this->addFilter(new ilCalendarScheduleFilterExercise($this->user->getId()));
131  $this->addFilter(new ilCalendarScheduleFilterTimings($this->user->getId()));
132  }
133  }
134 
139  protected function areEventsLimited()
140  {
141  return $this->limit_events != -1;
142  }
143 
148  public function getEventsLimit()
149  {
150  return $this->limit_events;
151  }
152 
157  public function setEventsLimit($a_limit)
158  {
159  $this->limit_events = $a_limit;
160  }
161 
167  public function addSubitemCalendars($a_status)
168  {
169  $this->subitems_enabled = $a_status;
170  }
171 
176  public function enabledSubitemCalendars()
177  {
178  return (bool) $this->subitems_enabled;
179  }
180 
186  public function addFilter(ilCalendarScheduleFilter $a_filter)
187  {
188  $this->filters[] = $a_filter;
189  }
190 
198  public function getByDay(ilDate $a_start, $a_timezone)
199  {
201  $fstart = new ilDate($a_start->get(IL_CAL_UNIX), IL_CAL_UNIX);
202  $fend = clone $fstart;
203 
204  $f_unix_start = $fstart->get(IL_CAL_UNIX);
205  $fend->increment(ilDateTime::DAY, 1);
206  $f_unix_end = $fend->get(IL_CAL_UNIX);
207 
208  $unix_start = $start->get(IL_CAL_UNIX);
209  $start->increment(ilDateTime::DAY, 1);
210  $unix_end = $start->get(IL_CAL_UNIX);
211 
212  $counter = 0;
213 
214  $tmp_date = new ilDateTime($unix_start, IL_CAL_UNIX, $this->timezone);
215  $tmp_schedule = array();
216  $tmp_schedule_fullday = array();
217  foreach ($this->schedule as $schedule) {
218  if ($schedule['fullday']) {
219  if (($f_unix_start == $schedule['dstart']) or
220  $f_unix_start == $schedule['dend'] or
221  ($f_unix_start > $schedule['dstart'] and $f_unix_end <= $schedule['dend'])) {
222  $tmp_schedule_fullday[] = $schedule;
223  }
224  } elseif (($schedule['dstart'] == $unix_start) or
225  (($schedule['dstart'] <= $unix_start) and ($schedule['dend'] > $unix_start)) or
226  (($schedule['dstart'] >= $unix_start) and ($schedule['dstart'] < $unix_end))) {
227  $tmp_schedule[] = $schedule;
228  }
229  }
230 
231  //order non full day events by starting date;
232  usort($tmp_schedule, function ($a, $b) {
233  return $a['dstart'] <=> $b['dstart'];
234  });
235 
236  //merge both arrays keeping the full day events first and then rest ordered by starting date.
237  $schedules = array_merge($tmp_schedule_fullday, $tmp_schedule);
238 
239  return $schedules;
240  }
241 
242 
248  public function calculate()
249  {
250  $events = $this->getEvents();
251 
252  // we need category type for booking handling
253  $ids = array();
254  foreach ($events as $event) {
255  $ids[] = $event->getEntryId();
256  }
257 
258  include_once('Services/Calendar/classes/class.ilCalendarCategoryAssignments.php');
260  include_once('Services/Calendar/classes/class.ilCalendarCategory.php');
261  $cat_types = array();
262  foreach (array_unique($cat_map) as $cat_id) {
263  $cat = new ilCalendarCategory($cat_id);
264  $cat_types[$cat_id] = $cat->getType();
265  }
266 
267  $counter = 0;
268  foreach ($events as $event) {
269  // Calculdate recurring events
270  include_once('Services/Calendar/classes/class.ilCalendarRecurrences.php');
271  if ($recs = ilCalendarRecurrences::_getRecurrences($event->getEntryId())) {
272  $duration = $event->getEnd()->get(IL_CAL_UNIX) - $event->getStart()->get(IL_CAL_UNIX);
273  foreach ($recs as $rec) {
274  $calc = new ilCalendarRecurrenceCalculator($event, $rec);
275  foreach ($calc->calculateDateList($this->start, $this->end)->get() as $rec_date) {
276  if ($this->type == self::TYPE_PD_UPCOMING &&
277  $rec_date->get(IL_CAL_UNIX) < time()) {
278  continue;
279  }
280 
281  $this->schedule[$counter]['event'] = $event;
282  $this->schedule[$counter]['dstart'] = $rec_date->get(IL_CAL_UNIX);
283  $this->schedule[$counter]['dend'] = $this->schedule[$counter]['dstart'] + $duration;
284  $this->schedule[$counter]['fullday'] = $event->isFullday();
285  $this->schedule[$counter]['category_id'] = $cat_map[$event->getEntryId()];
286  $this->schedule[$counter]['category_type'] = $cat_types[$cat_map[$event->getEntryId()]];
287 
288  switch ($this->type) {
289  case self::TYPE_DAY:
290  case self::TYPE_WEEK:
291  // store date info (used for calculation of overlapping events)
292  $tmp_date = new ilDateTime($this->schedule[$counter]['dstart'], IL_CAL_UNIX, $this->timezone);
293  $this->schedule[$counter]['start_info'] = $tmp_date->get(IL_CAL_FKT_GETDATE, '', $this->timezone);
294 
295  $tmp_date = new ilDateTime($this->schedule[$counter]['dend'], IL_CAL_UNIX, $this->timezone);
296  $this->schedule[$counter]['end_info'] = $tmp_date->get(IL_CAL_FKT_GETDATE, '', $this->timezone);
297  break;
298 
299  default:
300  break;
301  }
302  $counter++;
303  if ($this->type != self::TYPE_PD_UPCOMING &&
304  $this->areEventsLimited() && $counter >= $this->getEventsLimit()) {
305  break;
306  }
307  }
308  }
309  } else {
310  $this->schedule[$counter]['event'] = $event;
311  $this->schedule[$counter]['dstart'] = $event->getStart()->get(IL_CAL_UNIX);
312  $this->schedule[$counter]['dend'] = $event->getEnd()->get(IL_CAL_UNIX);
313  $this->schedule[$counter]['fullday'] = $event->isFullday();
314  $this->schedule[$counter]['category_id'] = $cat_map[$event->getEntryId()];
315  $this->schedule[$counter]['category_type'] = $cat_types[$cat_map[$event->getEntryId()]];
316 
317  if (!$event->isFullday()) {
318  switch ($this->type) {
319  case self::TYPE_DAY:
320  case self::TYPE_WEEK:
321  // store date info (used for calculation of overlapping events)
322  $tmp_date = new ilDateTime($this->schedule[$counter]['dstart'], IL_CAL_UNIX, $this->timezone);
323  $this->schedule[$counter]['start_info'] = $tmp_date->get(IL_CAL_FKT_GETDATE, '', $this->timezone);
324 
325  $tmp_date = new ilDateTime($this->schedule[$counter]['dend'], IL_CAL_UNIX, $this->timezone);
326  $this->schedule[$counter]['end_info'] = $tmp_date->get(IL_CAL_FKT_GETDATE, '', $this->timezone);
327  break;
328 
329  default:
330  break;
331  }
332  }
333  $counter++;
334  if ($this->type != self::TYPE_PD_UPCOMING &&
335  $this->areEventsLimited() && $counter >= $this->getEventsLimit()) {
336  break;
337  }
338  }
339  }
340 
341  if ($this->type == self::TYPE_PD_UPCOMING) {
342  $this->schedule = ilUtil::sortArray($this->schedule, "dstart", "asc", true);
343  if ($this->areEventsLimited() && sizeof($this->schedule) >= $this->getEventsLimit()) {
344  $this->schedule = array_slice($this->schedule, 0, $this->getEventsLimit());
345  }
346  }
347  }
348 
349  public function getScheduledEvents()
350  {
351  return (array) $this->schedule;
352  }
353 
354  protected function filterCategories(array $a_cats)
355  {
356  if (!sizeof($a_cats)) {
357  return $a_cats;
358  }
359 
360  foreach ($this->filters as $filter) {
361  if (sizeof($a_cats)) {
362  $a_cats = $filter->filterCategories($a_cats);
363  }
364  }
365 
366  return $a_cats;
367  }
368 
369  protected function modifyEventByFilters(ilCalendarEntry $event)
370  {
371  foreach ($this->filters as $filter) {
372  $res = $filter->modifyEvent($event);
373  if (!$res) {
374  $this->logger->info('filtering failed for ' . get_class($filter));
375  return false;
376  }
377  $event = $res;
378  }
379  return $event;
380  }
381 
382  protected function addCustomEvents(ilDate $start, ilDate $end, array $categories)
383  {
384  $new_events = array();
385  foreach ($this->filters as $filter) {
386  $events_by_filter = $filter->addCustomEvents($start, $end, $categories);
387  if ($events_by_filter) {
388  $new_events = array_merge($new_events, $events_by_filter);
389  }
390  }
391  return $new_events;
392  }
393 
402  public function getChangedEvents($a_include_subitem_calendars = false)
403  {
404  global $DIC;
405 
406  $ilDB = $DIC['ilDB'];
407 
408  include_once('./Services/Calendar/classes/class.ilCalendarCategories.php');
409  $cats = ilCalendarCategories::_getInstance($this->user->getId())->getCategories($a_include_subitem_calendars);
410  $cats = $this->filterCategories($cats);
411 
412  if (!count($cats)) {
413  return array();
414  }
415 
416  $start = new ilDate(date('Y-m-d', time()), IL_CAL_DATE);
417  $start->increment(IL_CAL_MONTH, -1);
418 
419  $query = "SELECT ce.cal_id cal_id FROM cal_entries ce " .
420  "JOIN cal_cat_assignments ca ON ca.cal_id = ce.cal_id " .
421  "WHERE last_update > " . $ilDB->quote($start->get(IL_CAL_DATETIME), 'timestamp') . " " .
422  "AND " . $ilDB->in('ca.cat_id', $cats, false, 'integer') . ' ' .
423  "ORDER BY last_update";
424  $res = $this->db->query($query);
425 
426  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
427  $event = new ilCalendarEntry($row->cal_id);
428  $valid_event = $this->modifyEventByFilters($event);
429  if ($valid_event) {
430  $events[] = $valid_event;
431  }
432  }
433 
434  foreach ($this->addCustomEvents($this->start, $this->end, $cats) as $event) {
435  $events[] = $event;
436  }
437 
438  return $events ? $events : array();
439  }
440 
441 
447  public function getEvents()
448  {
449  global $DIC;
450 
451  $ilDB = $DIC['ilDB'];
452 
453  include_once('./Services/Calendar/classes/class.ilCalendarCategories.php');
454  $cats = ilCalendarCategories::_getInstance($this->user->getId())->getCategories($this->enabledSubitemCalendars());
455  $cats = $this->filterCategories($cats);
456 
457  if (!count($cats)) {
458  return array();
459  }
460 
461  // TODO: optimize
462  $query = "SELECT ce.cal_id cal_id" .
463  " FROM cal_entries ce" .
464  " LEFT JOIN cal_recurrence_rules crr ON (ce.cal_id = crr.cal_id)" .
465  " JOIN cal_cat_assignments ca ON (ca.cal_id = ce.cal_id)";
466 
467  if ($this->type != self::TYPE_INBOX) {
468  $query .= " WHERE ((starta <= " . $this->db->quote($this->end->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp') .
469  " AND enda >= " . $this->db->quote($this->start->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp') . ")" .
470  " OR (starta <= " . $this->db->quote($this->end->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp') .
471  " AND NOT rule_id IS NULL))";
472  } else {
473  $date = new ilDateTime(mktime(0, 0, 0), IL_CAL_UNIX);
474  $query .= " WHERE starta >= " . $this->db->quote($date->get(IL_CAL_DATETIME, '', 'UTC'), 'timestamp');
475  }
476 
477  $query .= " AND " . $ilDB->in('ca.cat_id', $cats, false, 'integer') .
478  " ORDER BY starta";
479 
480  $res = $this->db->query($query);
481 
482  $events = array();
483  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
484  $event = new ilCalendarEntry($row->cal_id);
485  $valid_event = $this->modifyEventByFilters($event);
486  if ($valid_event) {
487  $events[] = $valid_event;
488  }
489  }
490 
491  foreach ($this->addCustomEvents($this->start, $this->end, $cats) as $event) {
492  $events[] = $event;
493  }
494 
495  return $events;
496  }
497 
505  protected function initPeriod(ilDate $seed)
506  {
507  switch ($this->type) {
508  case self::TYPE_DAY:
509  $this->start = clone $seed;
510  $this->end = clone $seed;
511  //this strict period is just to avoid possible side effects.
512  if (!$this->strict_period) {
513  $this->start->increment(IL_CAL_DAY, -2);
514  $this->end->increment(IL_CAL_DAY, 2);
515  } else {
516  $this->end->increment(IL_CAL_DAY, 1);
517  $this->end->increment(IL_CAL_SECOND, -1);
518  }
519  break;
520 
521  case self::TYPE_WEEK:
522  $this->start = clone $seed;
523  $start_info = $this->start->get(IL_CAL_FKT_GETDATE, '', 'UTC');
524  $day_diff = $this->weekstart - $start_info['isoday'];
525 
526  if ($day_diff == 7) {
527  $day_diff = 0;
528  }
529 
530  //this strict period is just to avoid possible side effects.
531  if ($this->strict_period) {
532  $this->start->increment(IL_CAL_DAY, $day_diff);
533  $this->end = clone $this->start;
534  $this->end->increment(IL_CAL_WEEK); #22173
535  } else {
536  $this->start->increment(IL_CAL_DAY, $day_diff);
537  $this->start->increment(IL_CAL_DAY, -1);
538  $this->end = clone $this->start;
539  $this->end->increment(IL_CAL_DAY, 9);
540  }
541  break;
542 
543  case self::TYPE_MONTH:
544  if ($this->strict_period) {
545  $this->start = clone $seed;
546  $this->end = clone $seed;
547  $this->end->increment(IL_CAL_MONTH, 1);
548  } else {
549  $year_month = $seed->get(IL_CAL_FKT_DATE, 'Y-m', 'UTC');
550  list($year, $month) = explode('-', $year_month);
551 
552  #21716
553  $this->start = new ilDate($year_month . '-01', IL_CAL_DATE);
554 
555  $start_unix_time = $this->start->getUnixTime();
556 
557  $start_day_of_week = (int) date('w', $start_unix_time);
558 
559  $number_days_previous_month = 0;
560 
561  if ($start_day_of_week === 0 && $this->weekstart === ilCalendarSettings::WEEK_START_MONDAY) {
562  $number_days_previous_month = 6;
563  } elseif ($start_day_of_week > 0) {
564  $number_days_previous_month = $start_day_of_week;
565 
566  if ($this->weekstart === ilCalendarSettings::WEEK_START_MONDAY) {
567  $number_days_previous_month = $start_day_of_week - 1;
568  }
569  }
570 
571  $this->start->increment(IL_CAL_DAY, -$number_days_previous_month);
572 
573  #21716
574  $this->end = new ilDate($year_month . '-' . ilCalendarUtil::_getMaxDayOfMonth($year, $month), IL_CAL_DATE);
575 
576  $end_unix_time = $this->end->getUnixTime();
577 
578  $end_day_of_week = (int) date('w', $end_unix_time);
579 
580  if ($end_day_of_week > 0) {
581  $number_days_next_month = 7 - $end_day_of_week;
582 
583  if ($this->weekstart == ilCalendarSettings::WEEK_START_SUNDAY) {
584  $number_days_next_month = $number_days_next_month - 1;
585  }
586 
587  $this->end->increment(IL_CAL_DAY, $number_days_next_month);
588  }
589  }
590 
591  break;
592 
593  case self::TYPE_HALF_YEAR:
594  $this->start = clone $seed;
595  $this->end = clone $this->start;
596  $this->end->increment(IL_CAL_MONTH, 6);
597  break;
598 
599  case self::TYPE_PD_UPCOMING:
600  case self::TYPE_INBOX:
601  $this->start = $seed;
602  $this->end = clone $this->start;
603  $this->end->increment(IL_CAL_MONTH, 12);
604  break;
605  }
606 
607  return true;
608  }
609 
616  public function setPeriod(ilDate $a_start, ilDate $a_end)
617  {
618  $this->start = $a_start;
619  $this->end = $a_end;
620  }
621 }
static sortArray( $array, $a_array_sortby, $a_array_sortorder=0, $a_numeric=false, $a_keep_keys=false)
sortArray
Calendar schedule filter for individual timings.
static _getRecurrences($a_cal_id)
get all recurrences of an appointment
Model for a calendar entry.
__construct(ilDate $seed, $a_type, $a_user_id=0, $a_strict_period=false)
Constructor.
areEventsLimited()
Check if events are limited.
modifyEventByFilters(ilCalendarEntry $event)
getEventsLimit()
get current limit of events
Calendar schedule filter for consultation hour bookings.
const IL_CAL_DATETIME
setPeriod(ilDate $a_start, ilDate $a_end)
Set period.
global $DIC
Definition: saml.php:7
static _getInstanceByUserId($a_user_id)
get singleton instance
enabledSubitemCalendars()
Are subitem calendars enabled.
addSubitemCalendars($a_status)
Enable subitem calendars (session calendars for courses)
if($argc< 2) $events
const IL_CAL_MONTH
Stores calendar categories.
setEventsLimit($a_limit)
Set events limit.
const IL_CAL_UNIX
const IL_CAL_WEEK
getByDay(ilDate $a_start, $a_timezone)
get byday
user()
Definition: user.php:4
get($a_format, $a_format_str='', $a_tz='')
get formatted date
static _getMaxDayOfMonth($a_year, $a_month)
get max day of month 2008,2 => 29
$a_type
Definition: workflow.php:92
const IL_CAL_DAY
Calendar schedule filter for hidden categories.
Calendar schedule filter interface.
Class for single dates.
foreach($_POST as $key=> $value) $res
Calculates an ilDateList for a given calendar entry and recurrence rule.
const IL_CAL_FKT_DATE
const IL_CAL_SECOND
Date and time handling
$ilUser
Definition: imgupload.php:18
Calendar schedule filter for exercises.
$query
get($a_format, $a_format_str='', $a_tz='')
get formatted date
increment($a_type, $a_count=1)
increment
static _getInstance($a_usr_id=0)
get singleton instance
getEvents()
Read events (will be moved to another class, since only active and/or visible calendars are shown) ...
$row
initPeriod(ilDate $seed)
init period of events
const IL_CAL_FKT_GETDATE
Calendar schedule filter for booking pool reservations.
const IL_CAL_DATE
addFilter(ilCalendarScheduleFilter $a_filter)
Add filter.
getChangedEvents($a_include_subitem_calendars=false)
get new/changed events
global $ilDB
addCustomEvents(ilDate $start, ilDate $end, array $categories)
Represents a list of calendar appointments (including recurring events) for a specific user in a give...
static _getAppointmentCalendars($a_cal_ids)
lookup calendars for appointment ids