/** * This method is responsible for parsing the request and generating the * response for the CALDAV:free-busy-query REPORT. * * @param DOMNode $dom * @return void */ protected function freeBusyQueryReport(DOMNode $dom) { $start = null; $end = null; foreach ($dom->firstChild->childNodes as $childNode) { $clark = Sabre_DAV_XMLUtil::toClarkNotation($childNode); if ($clark == '{' . self::NS_CALDAV . '}time-range') { $start = $childNode->getAttribute('start'); $end = $childNode->getAttribute('end'); break; } } if ($start) { $start = Sabre_VObject_DateTimeParser::parseDateTime($start); } if ($end) { $end = Sabre_VObject_DateTimeParser::parseDateTime($end); } if (!$start && !$end) { throw new Sabre_DAV_Exception_BadRequest('The freebusy report must have a time-range filter'); } $acl = $this->server->getPlugin('acl'); if (!$acl) { throw new Sabre_DAV_Exception('The ACL plugin must be loaded for free-busy queries to work'); } $uri = $this->server->getRequestUri(); $acl->checkPrivileges($uri, '{' . self::NS_CALDAV . '}read-free-busy'); $calendar = $this->server->tree->getNodeForPath($uri); if (!$calendar instanceof Sabre_CalDAV_ICalendar) { throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars'); } $objects = array_map(function ($child) { $obj = $child->get(); if (is_resource($obj)) { $obj = stream_get_contents($obj); } return $obj; }, $calendar->getChildren()); $generator = new Sabre_VObject_FreeBusyGenerator(); $generator->setObjects($objects); $generator->setTimeRange($start, $end); $result = $generator->getResult(); $result = $result->serialize(); $this->server->httpResponse->sendStatus(200); $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); $this->server->httpResponse->setHeader('Content-Length', strlen($result)); $this->server->httpResponse->sendBody($result); }
/** * 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'; $calendar->prodid = '-//SabreDAV//Sabre VObject ' . Sabre_VObject_Version::VERSION . '//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; }
/** * Parses the CALDAV:expand element * * @param DOMElement $parentNode * @return void */ protected function parseExpand(DOMElement $parentNode) { $start = $parentNode->getAttribute('start'); if (!$start) { throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element'); } $start = Sabre_VObject_DateTimeParser::parseDateTime($start); $end = $parentNode->getAttribute('end'); if (!$end) { throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element'); } $end = Sabre_VObject_DateTimeParser::parseDateTime($end); if ($end <= $start) { throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.'); } return array('start' => $start, 'end' => $end); }
/** * @depends testParseICalendarDateTime */ function testParseICalendarDateTimeCustomTimeZone() { $dateTime = Sabre_VObject_DateTimeParser::parseDateTime('20100316T141405', new DateTimeZone('Europe/Amsterdam')); $compare = new DateTime('2010-03-16 13:14:05', new DateTimeZone('UTC')); $this->assertEquals($compare, $dateTime); }