dom = $dom; $this->xpath = new \DOMXPath($dom); $this->xpath->registerNameSpace('cal',Plugin::NS_CALDAV); $this->xpath->registerNameSpace('dav','urn:DAV'); } /** * Parses the request. * * @return void */ public function parse() { $filterNode = null; $filter = $this->xpath->query('/cal:calendar-query/cal:filter'); if ($filter->length !== 1) { throw new \OldSabre\DAV\Exception\BadRequest('Only one filter element is allowed'); } $compFilters = $this->parseCompFilters($filter->item(0)); if (count($compFilters)!==1) { throw new \OldSabre\DAV\Exception\BadRequest('There must be exactly 1 top-level comp-filter.'); } $this->filters = $compFilters[0]; $this->requestedProperties = array_keys(\OldSabre\DAV\XMLUtil::parseProperties($this->dom->firstChild)); $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand'); if ($expand->length>0) { $this->expand = $this->parseExpand($expand->item(0)); } } /** * Parses all the 'comp-filter' elements from a node * * @param \DOMElement $parentNode * @return array */ protected function parseCompFilters(\DOMElement $parentNode) { $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode); $result = array(); for($ii=0; $ii < $compFilterNodes->length; $ii++) { $compFilterNode = $compFilterNodes->item($ii); $compFilter = array(); $compFilter['name'] = $compFilterNode->getAttribute('name'); $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0; $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode); $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode); $compFilter['time-range'] = $this->parseTimeRange($compFilterNode); if ($compFilter['time-range'] && !in_array($compFilter['name'],array( 'VEVENT', 'VTODO', 'VJOURNAL', 'VFREEBUSY', 'VALARM', ))) { throw new \OldSabre\DAV\Exception\BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component'); }; $result[] = $compFilter; } return $result; } /** * Parses all the prop-filter elements from a node * * @param \DOMElement $parentNode * @return array */ protected function parsePropFilters(\DOMElement $parentNode) { $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode); $result = array(); for ($ii=0; $ii < $propFilterNodes->length; $ii++) { $propFilterNode = $propFilterNodes->item($ii); $propFilter = array(); $propFilter['name'] = $propFilterNode->getAttribute('name'); $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0; $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode); $propFilter['text-match'] = $this->parseTextMatch($propFilterNode); $propFilter['time-range'] = $this->parseTimeRange($propFilterNode); $result[] = $propFilter; } return $result; } /** * Parses the param-filter element * * @param \DOMElement $parentNode * @return array */ protected function parseParamFilters(\DOMElement $parentNode) { $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode); $result = array(); for($ii=0;$ii<$paramFilterNodes->length;$ii++) { $paramFilterNode = $paramFilterNodes->item($ii); $paramFilter = array(); $paramFilter['name'] = $paramFilterNode->getAttribute('name'); $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0; $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode); $result[] = $paramFilter; } return $result; } /** * Parses the text-match element * * @param \DOMElement $parentNode * @return array|null */ protected function parseTextMatch(\DOMElement $parentNode) { $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode); if ($textMatchNodes->length === 0) return null; $textMatchNode = $textMatchNodes->item(0); $negateCondition = $textMatchNode->getAttribute('negate-condition'); $negateCondition = $negateCondition==='yes'; $collation = $textMatchNode->getAttribute('collation'); if (!$collation) $collation = 'i;ascii-casemap'; return array( 'negate-condition' => $negateCondition, 'collation' => $collation, 'value' => $textMatchNode->nodeValue ); } /** * Parses the time-range element * * @param \DOMElement $parentNode * @return array|null */ protected function parseTimeRange(\DOMElement $parentNode) { $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode); if ($timeRangeNodes->length === 0) { return null; } $timeRangeNode = $timeRangeNodes->item(0); if ($start = $timeRangeNode->getAttribute('start')) { $start = VObject\DateTimeParser::parseDateTime($start); } else { $start = null; } if ($end = $timeRangeNode->getAttribute('end')) { $end = VObject\DateTimeParser::parseDateTime($end); } else { $end = null; } if (!is_null($start) && !is_null($end) && $end <= $start) { throw new \OldSabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the time-range filter'); } return array( 'start' => $start, 'end' => $end, ); } /** * Parses the CALDAV:expand element * * @param \DOMElement $parentNode * @return void */ protected function parseExpand(\DOMElement $parentNode) { $start = $parentNode->getAttribute('start'); if(!$start) { throw new \OldSabre\DAV\Exception\BadRequest('The "start" attribute is required for the CALDAV:expand element'); } $start = VObject\DateTimeParser::parseDateTime($start); $end = $parentNode->getAttribute('end'); if(!$end) { throw new \OldSabre\DAV\Exception\BadRequest('The "end" attribute is required for the CALDAV:expand element'); } $end = VObject\DateTimeParser::parseDateTime($end); if ($end <= $start) { throw new \OldSabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.'); } return array( 'start' => $start, 'end' => $end, ); } }