ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Sabre\VObject\FreeBusyGenerator Class Reference

This class helps with generating FREEBUSY reports based on existing sets of objects. More...

+ Collaboration diagram for Sabre\VObject\FreeBusyGenerator:

Public Member Functions

 __construct (DateTimeInterface $start=null, DateTimeInterface $end=null, $objects=null, DateTimeZone $timeZone=null)
 Creates the generator. More...
 
 setBaseObject (Document $vcalendar)
 Sets the VCALENDAR object. More...
 
 setVAvailability (Document $vcalendar)
 Sets a VAVAILABILITY document. More...
 
 setObjects ($objects)
 Sets the input objects. More...
 
 setTimeRange (DateTimeInterface $start=null, DateTimeInterface $end=null)
 Sets the time range. More...
 
 setTimeZone (DateTimeZone $timeZone)
 Sets the reference timezone for floating times. More...
 
 getResult ()
 Parses the input data and returns a correct VFREEBUSY object, wrapped in a VCALENDAR. More...
 

Protected Member Functions

 calculateAvailability (FreeBusyData $fbData, VCalendar $vavailability)
 This method takes a VAVAILABILITY component and figures out all the available times. More...
 
 calculateBusy (FreeBusyData $fbData, array $objects)
 This method takes an array of iCalendar objects and applies its busy times on fbData. More...
 
 generateFreeBusyCalendar (FreeBusyData $fbData)
 This method takes a FreeBusyData object and generates the VCALENDAR object associated with it. More...
 

Protected Attributes

 $objects = []
 
 $start
 
 $end
 
 $baseObject
 
 $timeZone
 
 $vavailability
 

Detailed Description

This class helps with generating FREEBUSY reports based on existing sets of objects.

It only looks at VEVENT and VFREEBUSY objects from the sourcedata, and generates a single VFREEBUSY object.

VFREEBUSY components are described in RFC5545, The rules for what should go in a single freebusy report is taken from RFC4791, section 7.10.

Author
Evert Pot (http://evertpot.com/) http://sabre.io/license/ Modified BSD License

Definition at line 26 of file FreeBusyGenerator.php.

Constructor & Destructor Documentation

◆ __construct()

Sabre\VObject\FreeBusyGenerator::__construct ( DateTimeInterface  $start = null,
DateTimeInterface  $end = null,
  $objects = null,
DateTimeZone  $timeZone = null 
)

Creates the generator.

Check the setTimeRange and setObjects methods for details about the arguments.

Parameters
DateTimeInterface$start
DateTimeInterface$end
mixed$objects
DateTimeZone$timeZone

Definition at line 92 of file FreeBusyGenerator.php.

References Sabre\VObject\FreeBusyGenerator\$end, Sabre\VObject\FreeBusyGenerator\$objects, Sabre\VObject\FreeBusyGenerator\$start, Sabre\VObject\FreeBusyGenerator\$timeZone, Sabre\VObject\FreeBusyGenerator\setObjects(), Sabre\VObject\FreeBusyGenerator\setTimeRange(), and Sabre\VObject\FreeBusyGenerator\setTimeZone().

92  {
93 
94  $this->setTimeRange($start, $end);
95 
96  if ($objects) {
97  $this->setObjects($objects);
98  }
99  if (is_null($timeZone)) {
100  $timeZone = new DateTimeZone('UTC');
101  }
102  $this->setTimeZone($timeZone);
103 
104  }
setTimeRange(DateTimeInterface $start=null, DateTimeInterface $end=null)
Sets the time range.
setObjects($objects)
Sets the input objects.
setTimeZone(DateTimeZone $timeZone)
Sets the reference timezone for floating times.
+ Here is the call graph for this function:

Member Function Documentation

◆ calculateAvailability()

Sabre\VObject\FreeBusyGenerator::calculateAvailability ( FreeBusyData  $fbData,
VCalendar  $vavailability 
)
protected

This method takes a VAVAILABILITY component and figures out all the available times.

Parameters
FreeBusyData$fbData
VCalendar$vavailability
Returns
void

Definition at line 236 of file FreeBusyGenerator.php.

References Sabre\VObject\FreeBusyGenerator\$end, Sabre\VObject\FreeBusyGenerator\$start, Sabre\VObject\FreeBusyData\add(), and Sabre\VObject\Recur\RRuleIterator\fastForward().

Referenced by Sabre\VObject\FreeBusyGenerator\getResult().

236  {
237 
238  $vavailComps = iterator_to_array($vavailability->VAVAILABILITY);
239  usort(
240  $vavailComps,
241  function($a, $b) {
242 
243  // We need to order the components by priority. Priority 1
244  // comes first, up until priority 9. Priority 0 comes after
245  // priority 9. No priority implies priority 0.
246  //
247  // Yes, I'm serious.
248  $priorityA = isset($a->PRIORITY) ? (int)$a->PRIORITY->getValue() : 0;
249  $priorityB = isset($b->PRIORITY) ? (int)$b->PRIORITY->getValue() : 0;
250 
251  if ($priorityA === 0) $priorityA = 10;
252  if ($priorityB === 0) $priorityB = 10;
253 
254  return $priorityA - $priorityB;
255 
256  }
257  );
258 
259  // Now we go over all the VAVAILABILITY components and figure if
260  // there's any we don't need to consider.
261  //
262  // This is can be because of one of two reasons: either the
263  // VAVAILABILITY component falls outside the time we are interested in,
264  // or a different VAVAILABILITY component with a higher priority has
265  // already completely covered the time-range.
266  $old = $vavailComps;
267  $new = [];
268 
269  foreach ($old as $vavail) {
270 
271  list($compStart, $compEnd) = $vavail->getEffectiveStartEnd();
272 
273  // We don't care about datetimes that are earlier or later than the
274  // start and end of the freebusy report, so this gets normalized
275  // first.
276  if (is_null($compStart) || $compStart < $this->start) {
277  $compStart = $this->start;
278  }
279  if (is_null($compEnd) || $compEnd > $this->end) {
280  $compEnd = $this->end;
281  }
282 
283  // If the item fell out of the timerange, we can just skip it.
284  if ($compStart > $this->end || $compEnd < $this->start) {
285  continue;
286  }
287 
288  // Going through our existing list of components to see if there's
289  // a higher priority component that already fully covers this one.
290  foreach ($new as $higherVavail) {
291 
292  list($higherStart, $higherEnd) = $higherVavail->getEffectiveStartEnd();
293  if (
294  (is_null($higherStart) || $higherStart < $compStart) &&
295  (is_null($higherEnd) || $higherEnd > $compEnd)
296  ) {
297 
298  // Component is fully covered by a higher priority
299  // component. We can skip this component.
300  continue 2;
301 
302  }
303 
304  }
305 
306  // We're keeping it!
307  $new[] = $vavail;
308 
309  }
310 
311  // Lastly, we need to traverse the remaining components and fill in the
312  // freebusydata slots.
313  //
314  // We traverse the components in reverse, because we want the higher
315  // priority components to override the lower ones.
316  foreach (array_reverse($new) as $vavail) {
317 
318  $busyType = isset($vavail->BUSYTYPE) ? strtoupper($vavail->BUSYTYPE) : 'BUSY-UNAVAILABLE';
319  list($vavailStart, $vavailEnd) = $vavail->getEffectiveStartEnd();
320 
321  // Making the component size no larger than the requested free-busy
322  // report range.
323  if (!$vavailStart || $vavailStart < $this->start) {
324  $vavailStart = $this->start;
325  }
326  if (!$vavailEnd || $vavailEnd > $this->end) {
327  $vavailEnd = $this->end;
328  }
329 
330  // Marking the entire time range of the VAVAILABILITY component as
331  // busy.
332  $fbData->add(
333  $vavailStart->getTimeStamp(),
334  $vavailEnd->getTimeStamp(),
335  $busyType
336  );
337 
338  // Looping over the AVAILABLE components.
339  if (isset($vavail->AVAILABLE)) foreach ($vavail->AVAILABLE as $available) {
340 
341  list($availStart, $availEnd) = $available->getEffectiveStartEnd();
342  $fbData->add(
343  $availStart->getTimeStamp(),
344  $availEnd->getTimeStamp(),
345  'FREE'
346  );
347 
348  if ($available->RRULE) {
349  // Our favourite thing: recurrence!!
350 
351  $rruleIterator = new Recur\RRuleIterator(
352  $available->RRULE->getValue(),
353  $availStart
354  );
355  $rruleIterator->fastForward($vavailStart);
356 
357  $startEndDiff = $availStart->diff($availEnd);
358 
359  while ($rruleIterator->valid()) {
360 
361  $recurStart = $rruleIterator->current();
362  $recurEnd = $recurStart->add($startEndDiff);
363 
364  if ($recurStart > $vavailEnd) {
365  // We're beyond the legal timerange.
366  break;
367  }
368 
369  if ($recurEnd > $vavailEnd) {
370  // Truncating the end if it exceeds the
371  // VAVAILABILITY end.
372  $recurEnd = $vavailEnd;
373  }
374 
375  $fbData->add(
376  $recurStart->getTimeStamp(),
377  $recurEnd->getTimeStamp(),
378  'FREE'
379  );
380 
381  $rruleIterator->next();
382 
383  }
384  }
385 
386  }
387 
388  }
389 
390  }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ calculateBusy()

Sabre\VObject\FreeBusyGenerator::calculateBusy ( FreeBusyData  $fbData,
array  $objects 
)
protected

This method takes an array of iCalendar objects and applies its busy times on fbData.

Parameters
FreeBusyData$fbData
VCalendar[]$objects

Definition at line 399 of file FreeBusyGenerator.php.

References $key, Sabre\VObject\Settings\$maxRecurrences, $time, $values, Sabre\VObject\FreeBusyData\add(), Sabre\VObject\DateTimeParser\parseDateTime(), and Sabre\VObject\DateTimeParser\parseDuration().

Referenced by Sabre\VObject\FreeBusyGenerator\getResult().

399  {
400 
401  foreach ($objects as $key => $object) {
402 
403  foreach ($object->getBaseComponents() as $component) {
404 
405  switch ($component->name) {
406 
407  case 'VEVENT' :
408 
409  $FBTYPE = 'BUSY';
410  if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) {
411  break;
412  }
413  if (isset($component->STATUS)) {
414  $status = strtoupper($component->STATUS);
415  if ($status === 'CANCELLED') {
416  break;
417  }
418  if ($status === 'TENTATIVE') {
419  $FBTYPE = 'BUSY-TENTATIVE';
420  }
421  }
422 
423  $times = [];
424 
425  if ($component->RRULE) {
426  try {
427  $iterator = new EventIterator($object, (string)$component->UID, $this->timeZone);
428  } catch (NoInstancesException $e) {
429  // This event is recurring, but it doesn't have a single
430  // instance. We are skipping this event from the output
431  // entirely.
432  unset($this->objects[$key]);
433  continue;
434  }
435 
436  if ($this->start) {
437  $iterator->fastForward($this->start);
438  }
439 
440  $maxRecurrences = Settings::$maxRecurrences;
441 
442  while ($iterator->valid() && --$maxRecurrences) {
443 
444  $startTime = $iterator->getDTStart();
445  if ($this->end && $startTime > $this->end) {
446  break;
447  }
448  $times[] = [
449  $iterator->getDTStart(),
450  $iterator->getDTEnd(),
451  ];
452 
453  $iterator->next();
454 
455  }
456 
457  } else {
458 
459  $startTime = $component->DTSTART->getDateTime($this->timeZone);
460  if ($this->end && $startTime > $this->end) {
461  break;
462  }
463  $endTime = null;
464  if (isset($component->DTEND)) {
465  $endTime = $component->DTEND->getDateTime($this->timeZone);
466  } elseif (isset($component->DURATION)) {
467  $duration = DateTimeParser::parseDuration((string)$component->DURATION);
468  $endTime = clone $startTime;
469  $endTime = $endTime->add($duration);
470  } elseif (!$component->DTSTART->hasTime()) {
471  $endTime = clone $startTime;
472  $endTime = $endTime->modify('+1 day');
473  } else {
474  // The event had no duration (0 seconds)
475  break;
476  }
477 
478  $times[] = [$startTime, $endTime];
479 
480  }
481 
482  foreach ($times as $time) {
483 
484  if ($this->end && $time[0] > $this->end) break;
485  if ($this->start && $time[1] < $this->start) break;
486 
487  $fbData->add(
488  $time[0]->getTimeStamp(),
489  $time[1]->getTimeStamp(),
490  $FBTYPE
491  );
492  }
493  break;
494 
495  case 'VFREEBUSY' :
496  foreach ($component->FREEBUSY as $freebusy) {
497 
498  $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY';
499 
500  // Skipping intervals marked as 'free'
501  if ($fbType === 'FREE')
502  continue;
503 
504  $values = explode(',', $freebusy);
505  foreach ($values as $value) {
506  list($startTime, $endTime) = explode('/', $value);
507  $startTime = DateTimeParser::parseDateTime($startTime);
508 
509  if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') {
510  $duration = DateTimeParser::parseDuration($endTime);
511  $endTime = clone $startTime;
512  $endTime = $endTime->add($duration);
513  } else {
514  $endTime = DateTimeParser::parseDateTime($endTime);
515  }
516 
517  if ($this->start && $this->start > $endTime) continue;
518  if ($this->end && $this->end < $startTime) continue;
519  $fbData->add(
520  $startTime->getTimeStamp(),
521  $endTime->getTimeStamp(),
522  $fbType
523  );
524 
525  }
526 
527 
528  }
529  break;
530 
531  }
532 
533 
534  }
535 
536  }
537 
538  }
static parseDateTime($dt, DateTimeZone $tz=null)
Parses an iCalendar (rfc5545) formatted datetime and returns a DateTimeImmutable object.
static $maxRecurrences
The maximum number of recurrences that will be generated.
Definition: Settings.php:54
$time
Definition: cron.php:21
$values
static parseDuration($duration, $asString=false)
Parses an iCalendar (RFC5545) formatted duration value.
$key
Definition: croninfo.php:18
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ generateFreeBusyCalendar()

Sabre\VObject\FreeBusyGenerator::generateFreeBusyCalendar ( FreeBusyData  $fbData)
protected

This method takes a FreeBusyData object and generates the VCALENDAR object associated with it.

Returns
VCalendar

Definition at line 546 of file FreeBusyGenerator.php.

References Sabre\VObject\FreeBusyGenerator\$baseObject, $calendar, $tz, and Sabre\VObject\FreeBusyData\getData().

Referenced by Sabre\VObject\FreeBusyGenerator\getResult().

546  {
547 
548  if ($this->baseObject) {
550  } else {
551  $calendar = new VCalendar();
552  }
553 
554  $vfreebusy = $calendar->createComponent('VFREEBUSY');
555  $calendar->add($vfreebusy);
556 
557  if ($this->start) {
558  $dtstart = $calendar->createProperty('DTSTART');
559  $dtstart->setDateTime($this->start);
560  $vfreebusy->add($dtstart);
561  }
562  if ($this->end) {
563  $dtend = $calendar->createProperty('DTEND');
564  $dtend->setDateTime($this->end);
565  $vfreebusy->add($dtend);
566  }
567 
568  $tz = new \DateTimeZone('UTC');
569  $dtstamp = $calendar->createProperty('DTSTAMP');
570  $dtstamp->setDateTime(new DateTimeImmutable('now', $tz));
571  $vfreebusy->add($dtstamp);
572 
573  foreach ($fbData->getData() as $busyTime) {
574 
575  $busyType = strtoupper($busyTime['type']);
576 
577  // Ignoring all the FREE parts, because those are already assumed.
578  if ($busyType === 'FREE') {
579  continue;
580  }
581 
582  $busyTime[0] = new \DateTimeImmutable('@' . $busyTime['start'], $tz);
583  $busyTime[1] = new \DateTimeImmutable('@' . $busyTime['end'], $tz);
584 
585  $prop = $calendar->createProperty(
586  'FREEBUSY',
587  $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')
588  );
589 
590  // Only setting FBTYPE if it's not BUSY, because BUSY is the
591  // default anyway.
592  if ($busyType !== 'BUSY') {
593  $prop['FBTYPE'] = $busyType;
594  }
595  $vfreebusy->add($prop);
596 
597  }
598 
599  return $calendar;
600 
601 
602  }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ getResult()

Sabre\VObject\FreeBusyGenerator::getResult ( )

Parses the input data and returns a correct VFREEBUSY object, wrapped in a VCALENDAR.

Returns
Component

Definition at line 209 of file FreeBusyGenerator.php.

References Sabre\VObject\FreeBusyGenerator\calculateAvailability(), Sabre\VObject\FreeBusyGenerator\calculateBusy(), and Sabre\VObject\FreeBusyGenerator\generateFreeBusyCalendar().

209  {
210 
211  $fbData = new FreeBusyData(
212  $this->start->getTimeStamp(),
213  $this->end->getTimeStamp()
214  );
215  if ($this->vavailability) {
216 
217  $this->calculateAvailability($fbData, $this->vavailability);
218 
219  }
220 
221  $this->calculateBusy($fbData, $this->objects);
222 
223  return $this->generateFreeBusyCalendar($fbData);
224 
225 
226  }
calculateAvailability(FreeBusyData $fbData, VCalendar $vavailability)
This method takes a VAVAILABILITY component and figures out all the available times.
generateFreeBusyCalendar(FreeBusyData $fbData)
This method takes a FreeBusyData object and generates the VCALENDAR object associated with it...
calculateBusy(FreeBusyData $fbData, array $objects)
This method takes an array of iCalendar objects and applies its busy times on fbData.
+ Here is the call graph for this function:

◆ setBaseObject()

Sabre\VObject\FreeBusyGenerator::setBaseObject ( Document  $vcalendar)

Sets the VCALENDAR object.

If this is set, it will not be generated for you. You are responsible for setting things like the METHOD, CALSCALE, VERSION, etc..

The VFREEBUSY object will be automatically added though.

Parameters
Document$vcalendar
Returns
void

Definition at line 117 of file FreeBusyGenerator.php.

117  {
118 
119  $this->baseObject = $vcalendar;
120 
121  }

◆ setObjects()

Sabre\VObject\FreeBusyGenerator::setObjects (   $objects)

Sets the input objects.

You must either specify a valendar object as a string, or as the parse Component. It's also possible to specify multiple objects as an array.

Parameters
mixed$objects
Returns
void

Definition at line 146 of file FreeBusyGenerator.php.

References Sabre\VObject\FreeBusyGenerator\$objects, and Sabre\VObject\Reader\read().

Referenced by Sabre\VObject\FreeBusyGenerator\__construct().

146  {
147 
148  if (!is_array($objects)) {
149  $objects = [$objects];
150  }
151 
152  $this->objects = [];
153  foreach ($objects as $object) {
154 
155  if (is_string($object) || is_resource($object)) {
156  $this->objects[] = Reader::read($object);
157  } elseif ($object instanceof Component) {
158  $this->objects[] = $object;
159  } else {
160  throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects');
161  }
162 
163  }
164 
165  }
static read($data, $options=0, $charset='UTF-8')
Parses a vCard or iCalendar object, and returns the top component.
Definition: Reader.php:42
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ setTimeRange()

Sabre\VObject\FreeBusyGenerator::setTimeRange ( DateTimeInterface  $start = null,
DateTimeInterface  $end = null 
)

Sets the time range.

Any freebusy object falling outside of this time range will be ignored.

Parameters
DateTimeInterface$start
DateTimeInterface$end
Returns
void

Definition at line 177 of file FreeBusyGenerator.php.

References Sabre\VObject\FreeBusyGenerator\$end, Sabre\VObject\Settings\$maxDate, Sabre\VObject\Settings\$minDate, and Sabre\VObject\FreeBusyGenerator\$start.

Referenced by Sabre\VObject\FreeBusyGenerator\__construct().

177  {
178 
179  if (!$start) {
181  }
182  if (!$end) {
184  }
185  $this->start = $start;
186  $this->end = $end;
187 
188  }
static $maxDate
The maximum date we accept for various calculations with dates, such as recurrences.
Definition: Settings.php:37
static $minDate
The minimum date we accept for various calculations with dates, such as recurrences.
Definition: Settings.php:28
+ Here is the caller graph for this function:

◆ setTimeZone()

Sabre\VObject\FreeBusyGenerator::setTimeZone ( DateTimeZone  $timeZone)

Sets the reference timezone for floating times.

Parameters
DateTimeZone$timeZone
Returns
void

Definition at line 197 of file FreeBusyGenerator.php.

References Sabre\VObject\FreeBusyGenerator\$timeZone.

Referenced by Sabre\VObject\FreeBusyGenerator\__construct().

197  {
198 
199  $this->timeZone = $timeZone;
200 
201  }
+ Here is the caller graph for this function:

◆ setVAvailability()

Sabre\VObject\FreeBusyGenerator::setVAvailability ( Document  $vcalendar)

Sets a VAVAILABILITY document.

Parameters
Document$vcalendar
Returns
void

Definition at line 129 of file FreeBusyGenerator.php.

129  {
130 
131  $this->vavailability = $vcalendar;
132 
133  }

Field Documentation

◆ $baseObject

Sabre\VObject\FreeBusyGenerator::$baseObject
protected

◆ $end

◆ $objects

Sabre\VObject\FreeBusyGenerator::$objects = []
protected

◆ $start

◆ $timeZone

Sabre\VObject\FreeBusyGenerator::$timeZone
protected

◆ $vavailability

Sabre\VObject\FreeBusyGenerator::$vavailability
protected

Definition at line 79 of file FreeBusyGenerator.php.


The documentation for this class was generated from the following file: