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/) @license 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.

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 }
setObjects($objects)
Sets the input objects.
setTimeZone(DateTimeZone $timeZone)
Sets the reference timezone for floating times.
setTimeRange(DateTimeInterface $start=null, DateTimeInterface $end=null)
Sets the time range.

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().

+ 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.

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 }

References Sabre\VObject\FreeBusyGenerator\$end, Sabre\VObject\FreeBusyGenerator\$start, Sabre\VObject\FreeBusyGenerator\$vavailability, and Sabre\VObject\FreeBusyData\add().

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

+ 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.

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 parseDuration($duration, $asString=false)
Parses an iCalendar (RFC5545) formatted duration value.
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
$key
Definition: croninfo.php:18
$time
Definition: cron.php:21
$values

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

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

+ 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.

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 }

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

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

+ 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.

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 }
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.
calculateAvailability(FreeBusyData $fbData, VCalendar $vavailability)
This method takes a VAVAILABILITY component and figures out all the available times.

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

+ 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.

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

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

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

+ 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.

177 {
178
179 if (!$start) {
180 $start = new DateTimeImmutable(Settings::$minDate);
181 }
182 if (!$end) {
183 $end = new DateTimeImmutable(Settings::$maxDate);
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

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

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

+ 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.

197 {
198
199 $this->timeZone = $timeZone;
200
201 }

References Sabre\VObject\FreeBusyGenerator\$timeZone.

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

+ 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

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