6 * Parses the calendar-query report request body.
8 * Whoever designed this format, and the CalDAV equivalent even more so,
9 * has no feel for design.
13 * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
14 * @author Evert Pot (http://www.rooftopsolutions.nl/)
15 * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
17 class Sabre_CalDAV_CalendarQueryParser {
20 * List of requested properties the client wanted
24 public $requestedProperties;
27 * List of property/component filters.
34 * This property will contain null if CALDAV:expand was not specified,
35 * otherwise it will contain an array with 2 elements (start, end). Each
36 * contain a DateTime object.
38 * If expand is specified, recurring calendar objects are to be expanded
39 * into their individual components, and only the components that fall
40 * within the specified time-range are to be returned.
42 * For more details, see rfc4791, section 9.6.5.
65 * @param DOMDocument $dom
67 public function __construct(DOMDocument $dom) {
71 $this->xpath = new DOMXPath($dom);
72 $this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
73 $this->xpath->registerNameSpace('dav','DAV:');
82 public function parse() {
86 $filter = $this->xpath->query('/cal:calendar-query/cal:filter');
87 if ($filter->length !== 1) {
88 throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
91 $compFilters = $this->parseCompFilters($filter->item(0));
92 if (count($compFilters)!==1) {
93 throw new Sabre_DAV_Exception_BadRequest('There must be exactly 1 top-level comp-filter.');
96 $this->filters = $compFilters[0];
97 $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild));
99 $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand');
100 if ($expand->length>0) {
101 $this->expand = $this->parseExpand($expand->item(0));
108 * Parses all the 'comp-filter' elements from a node
110 * @param DOMElement $parentNode
113 protected function parseCompFilters(DOMElement $parentNode) {
115 $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode);
118 for($ii=0; $ii < $compFilterNodes->length; $ii++) {
120 $compFilterNode = $compFilterNodes->item($ii);
122 $compFilter = array();
123 $compFilter['name'] = $compFilterNode->getAttribute('name');
124 $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0;
125 $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode);
126 $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode);
127 $compFilter['time-range'] = $this->parseTimeRange($compFilterNode);
129 if ($compFilter['time-range'] && !in_array($compFilter['name'],array(
136 throw new Sabre_DAV_Exception_BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component');
139 $result[] = $compFilter;
148 * Parses all the prop-filter elements from a node
150 * @param DOMElement $parentNode
153 protected function parsePropFilters(DOMElement $parentNode) {
155 $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode);
158 for ($ii=0; $ii < $propFilterNodes->length; $ii++) {
160 $propFilterNode = $propFilterNodes->item($ii);
161 $propFilter = array();
162 $propFilter['name'] = $propFilterNode->getAttribute('name');
163 $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0;
164 $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode);
165 $propFilter['text-match'] = $this->parseTextMatch($propFilterNode);
166 $propFilter['time-range'] = $this->parseTimeRange($propFilterNode);
168 $result[] = $propFilter;
177 * Parses the param-filter element
179 * @param DOMElement $parentNode
182 protected function parseParamFilters(DOMElement $parentNode) {
184 $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode);
187 for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
189 $paramFilterNode = $paramFilterNodes->item($ii);
190 $paramFilter = array();
191 $paramFilter['name'] = $paramFilterNode->getAttribute('name');
192 $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0;
193 $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode);
195 $result[] = $paramFilter;
204 * Parses the text-match element
206 * @param DOMElement $parentNode
209 protected function parseTextMatch(DOMElement $parentNode) {
211 $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode);
213 if ($textMatchNodes->length === 0)
216 $textMatchNode = $textMatchNodes->item(0);
217 $negateCondition = $textMatchNode->getAttribute('negate-condition');
218 $negateCondition = $negateCondition==='yes';
219 $collation = $textMatchNode->getAttribute('collation');
220 if (!$collation) $collation = 'i;ascii-casemap';
223 'negate-condition' => $negateCondition,
224 'collation' => $collation,
225 'value' => $textMatchNode->nodeValue
231 * Parses the time-range element
233 * @param DOMElement $parentNode
236 protected function parseTimeRange(DOMElement $parentNode) {
238 $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode);
239 if ($timeRangeNodes->length === 0) {
243 $timeRangeNode = $timeRangeNodes->item(0);
245 if ($start = $timeRangeNode->getAttribute('start')) {
246 $start = VObject\DateTimeParser::parseDateTime($start);
250 if ($end = $timeRangeNode->getAttribute('end')) {
251 $end = VObject\DateTimeParser::parseDateTime($end);
256 if (!is_null($start) && !is_null($end) && $end <= $start) {
257 throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter');
268 * Parses the CALDAV:expand element
270 * @param DOMElement $parentNode
273 protected function parseExpand(DOMElement $parentNode) {
275 $start = $parentNode->getAttribute('start');
277 throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element');
279 $start = VObject\DateTimeParser::parseDateTime($start);
281 $end = $parentNode->getAttribute('end');
283 throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element');
285 $end = VObject\DateTimeParser::parseDateTime($end);
287 if ($end <= $start) {
288 throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');