friendica-addons/dav/SabreDAV/lib/Sabre/VObject/FreeBusyGenerator.php

298 lines
9.6 KiB
PHP

<?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;
}
}