childNodes as $child) { switch(Sabre_DAV_XMLUtil::toClarkNotation($child)) { case '{urn:ietf:params:xml:ns:caldav}comp-filter' : case '{urn:ietf:params:xml:ns:caldav}prop-filter' : $filterName = $basePath . '/' . 'c:' . strtolower($child->getAttribute('name')); $filters[$filterName] = array(); self::parseCalendarQueryFilters($child, $filterName,$filters); break; case '{urn:ietf:params:xml:ns:caldav}time-range' : if ($start = $child->getAttribute('start')) { $start = self::parseICalendarDateTime($start); } else { $start = null; } if ($end = $child->getAttribute('end')) { $end = self::parseICalendarDateTime($end); } else { $end = null; } if (!is_null($start) && !is_null($end) && $end <= $start) { throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter'); } $filters[$basePath]['time-range'] = array( 'start' => $start, 'end' => $end ); break; case '{urn:ietf:params:xml:ns:caldav}is-not-defined' : $filters[$basePath]['is-not-defined'] = true; break; case '{urn:ietf:params:xml:ns:caldav}param-filter' : $filterName = $basePath . '/@' . strtolower($child->getAttribute('name')); $filters[$filterName] = array(); self::parseCalendarQueryFilters($child, $filterName, $filters); break; case '{urn:ietf:params:xml:ns:caldav}text-match' : $collation = $child->getAttribute('collation'); if (!$collation) $collation = 'i;ascii-casemap'; $filters[$basePath]['text-match'] = array( 'collation' => ($collation == 'default'?'i;ascii-casemap':$collation), 'negate-condition' => $child->getAttribute('negate-condition')==='yes', 'value' => $child->nodeValue, ); break; } } return $filters; } /** * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object * * Specifying a reference timezone is optional. It will only be used * if the non-UTC format is used. The argument is used as a reference, the * returned DateTime object will still be in the UTC timezone. * * @param string $dt * @param DateTimeZone $tz * @return DateTime */ static public function parseICalendarDateTime($dt,DateTimeZone $tz = null) { // Format is YYYYMMDD + "T" + hhmmss $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches); if (!$result) { throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar datetime value is incorrect: ' . $dt); } if ($matches[7]==='Z' || is_null($tz)) { $tz = new DateTimeZone('UTC'); } $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz); // Still resetting the timezone, to normalize everything to UTC $date->setTimeZone(new DateTimeZone('UTC')); return $date; } /** * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object * * @param string $date * @param DateTimeZone $tz * @return DateTime */ static public function parseICalendarDate($date) { // Format is YYYYMMDD $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches); if (!$result) { throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date); } $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC')); return $date; } /** * Parses an iCalendar (RFC5545) formatted duration and returns a string suitable * for strtotime or DateTime::modify. * * NOTE: When we require PHP 5.3 this can be replaced by the DateTimeInterval object, which * supports ISO 8601 Intervals, which is a superset of ICalendar durations. * * For now though, we're just gonna live with this messy system * * @param string $duration * @return string */ static public function parseICalendarDuration($duration) { $result = preg_match('/^(?P\+|-)?P((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$/', $duration, $matches); if (!$result) { throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar duration value is incorrect: ' . $duration); } $parts = array( 'week', 'day', 'hour', 'minute', 'second', ); $newDur = ''; foreach($parts as $part) { if (isset($matches[$part]) && $matches[$part]) { $newDur.=' '.$matches[$part] . ' ' . $part . 's'; } } $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur); return $newDur; } }