<?php /** * 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. * * @package Sabre * @subpackage VObject * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved. * @author Evert Pot (http://www.rooftopsolutions.nl/) * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License */ class Sabre_VObject_FreeBusyGenerator { /** * Input objects * * @var array */ protected $objects; /** * Start of range * * @var DateTime|null */ protected $start; /** * End of range * * @var DateTime|null */ protected $end; /** * VCALENDAR object * * @var Sabre_VObject_Component */ protected $baseObject; /** * 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. * * @param Sabre_VObject_Component $vcalendar * @return void */ public function setBaseObject(Sabre_VObject_Component $vcalendar) { $this->baseObject = $vcalendar; } /** * Sets the input objects * * Every object must either be a string or a Sabre_VObject_Component. * * @param array $objects * @return void */ public function setObjects(array $objects) { $this->objects = array(); foreach($objects as $object) { if (is_string($object)) { $this->objects[] = Sabre_VObject_Reader::read($object); } elseif ($object instanceof Sabre_VObject_Component) { $this->objects[] = $object; } else { throw new InvalidArgumentException('You can only pass strings or Sabre_VObject_Component arguments to setObjects'); } } } /** * Sets the time range * * Any freebusy object falling outside of this time range will be ignored. * * @param DateTime $start * @param DateTime $end * @return void */ public function setTimeRange(DateTime $start = null, DateTime $end = null) { $this->start = $start; $this->end = $end; } /** * Parses the input data and returns a correct VFREEBUSY object, wrapped in * a VCALENDAR. * * @return Sabre_VObject_Component */ public function getResult() { $busyTimes = array(); foreach($this->objects as $object) { foreach($object->getBaseComponents() as $component) { switch($component->name) { case 'VEVENT' : $FBTYPE = 'BUSY'; if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) { break; } if (isset($component->STATUS)) { $status = strtoupper($component->STATUS); if ($status==='CANCELLED') { break; } if ($status==='TENTATIVE') { $FBTYPE = 'BUSY-TENTATIVE'; } } $times = array(); if ($component->RRULE) { $iterator = new Sabre_VObject_RecurrenceIterator($object, (string)$component->uid); if ($this->start) { $iterator->fastForward($this->start); } $maxRecurrences = 200; while($iterator->valid() && --$maxRecurrences) { $startTime = $iterator->getDTStart(); if ($this->end && $startTime > $this->end) { break; } $times[] = array( $iterator->getDTStart(), $iterator->getDTEnd(), ); $iterator->next(); } } else { $startTime = $component->DTSTART->getDateTime(); if ($this->end && $startTime > $this->end) { break; } $endTime = null; if (isset($component->DTEND)) { $endTime = $component->DTEND->getDateTime(); } elseif (isset($component->DURATION)) { $duration = Sabre_VObject_DateTimeParser::parseDuration((string)$component->DURATION); $endTime = clone $startTime; $endTime->add($duration); } elseif ($component->DTSTART->getDateType() === Sabre_VObject_Property_DateTime::DATE) { $endTime = clone $startTime; $endTime->modify('+1 day'); } else { // The event had no duration (0 seconds) break; } $times[] = array($startTime, $endTime); } foreach($times as $time) { if ($this->end && $time[0] > $this->end) break; if ($this->start && $time[1] < $this->start) break; $busyTimes[] = array( $time[0], $time[1], $FBTYPE, ); } break; case 'VFREEBUSY' : foreach($component->FREEBUSY as $freebusy) { $fbType = isset($freebusy['FBTYPE'])?strtoupper($freebusy['FBTYPE']):'BUSY'; // Skipping intervals marked as 'free' if ($fbType==='FREE') continue; $values = explode(',', $freebusy); foreach($values as $value) { list($startTime, $endTime) = explode('/', $value); $startTime = Sabre_VObject_DateTimeParser::parseDateTime($startTime); if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') { $duration = Sabre_VObject_DateTimeParser::parseDuration($endTime); $endTime = clone $startTime; $endTime->add($duration); } else { $endTime = Sabre_VObject_DateTimeParser::parseDateTime($endTime); } if($this->start && $this->start > $endTime) continue; if($this->end && $this->end < $startTime) continue; $busyTimes[] = array( $startTime, $endTime, $fbType ); } } break; } } } if ($this->baseObject) { $calendar = $this->baseObject; } else { $calendar = new Sabre_VObject_Component('VCALENDAR'); $calendar->version = '2.0'; if (Sabre_DAV_Server::$exposeVersion) { $calendar->prodid = '-//SabreDAV//Sabre VObject ' . Sabre_VObject_Version::VERSION . '//EN'; } else { $calendar->prodid = '-//SabreDAV//Sabre VObject//EN'; } $calendar->calscale = 'GREGORIAN'; } $vfreebusy = new Sabre_VObject_Component('VFREEBUSY'); $calendar->add($vfreebusy); if ($this->start) { $dtstart = new Sabre_VObject_Property_DateTime('DTSTART'); $dtstart->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC); $vfreebusy->add($dtstart); } if ($this->end) { $dtend = new Sabre_VObject_Property_DateTime('DTEND'); $dtend->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC); $vfreebusy->add($dtend); } $dtstamp = new Sabre_VObject_Property_DateTime('DTSTAMP'); $dtstamp->setDateTime(new DateTime('now'), Sabre_VObject_Property_DateTime::UTC); $vfreebusy->add($dtstamp); foreach($busyTimes as $busyTime) { $busyTime[0]->setTimeZone(new DateTimeZone('UTC')); $busyTime[1]->setTimeZone(new DateTimeZone('UTC')); $prop = new Sabre_VObject_Property( 'FREEBUSY', $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z') ); $prop['FBTYPE'] = $busyTime[2]; $vfreebusy->add($prop); } return $calendar; } }