4 * Parses the calendar-query report request body.
6 * Whoever designed this format, and the CalDAV equivalent even more so,
7 * has no feel for design.
11 * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
12 * @author Evert Pot (http://www.rooftopsolutions.nl/)
13 * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
15 class Sabre_CalDAV_CalendarQueryParser {
18 * List of requested properties the client wanted
22 public $requestedProperties;
25 * List of property/component filters.
32 * This property will contain null if CALDAV:expand was not specified,
33 * otherwise it will contain an array with 2 elements (start, end). Each
34 * contain a DateTime object.
36 * If expand is specified, recurring calendar objects are to be expanded
37 * into their individual components, and only the components that fall
38 * within the specified time-range are to be returned.
40 * For more details, see rfc4791, section 9.6.5.
63 * @param DOMDocument $dom
65 public function __construct(DOMDocument $dom) {
69 $this->xpath = new DOMXPath($dom);
70 $this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
71 $this->xpath->registerNameSpace('dav','urn:DAV');
80 public function parse() {
84 $filter = $this->xpath->query('/cal:calendar-query/cal:filter');
85 if ($filter->length !== 1) {
86 throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
89 $compFilters = $this->parseCompFilters($filter->item(0));
90 if (count($compFilters)!==1) {
91 throw new Sabre_DAV_Exception_BadRequest('There must be exactly 1 top-level comp-filter.');
94 $this->filters = $compFilters[0];
95 $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild));
97 $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand');
98 if ($expand->length>0) {
99 $this->expand = $this->parseExpand($expand->item(0));
106 * Parses all the 'comp-filter' elements from a node
108 * @param DOMElement $parentNode
111 protected function parseCompFilters(DOMElement $parentNode) {
113 $compFilterNodes = $this->xpath->query('cal:comp-filter', $parentNode);
116 for($ii=0; $ii < $compFilterNodes->length; $ii++) {
118 $compFilterNode = $compFilterNodes->item($ii);
120 $compFilter = array();
121 $compFilter['name'] = $compFilterNode->getAttribute('name');
122 $compFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $compFilterNode)->length>0;
123 $compFilter['comp-filters'] = $this->parseCompFilters($compFilterNode);
124 $compFilter['prop-filters'] = $this->parsePropFilters($compFilterNode);
125 $compFilter['time-range'] = $this->parseTimeRange($compFilterNode);
127 if ($compFilter['time-range'] && !in_array($compFilter['name'],array(
134 throw new Sabre_DAV_Exception_BadRequest('The time-range filter is not defined for the ' . $compFilter['name'] . ' component');
137 $result[] = $compFilter;
146 * Parses all the prop-filter elements from a node
148 * @param DOMElement $parentNode
151 protected function parsePropFilters(DOMElement $parentNode) {
153 $propFilterNodes = $this->xpath->query('cal:prop-filter', $parentNode);
156 for ($ii=0; $ii < $propFilterNodes->length; $ii++) {
158 $propFilterNode = $propFilterNodes->item($ii);
159 $propFilter = array();
160 $propFilter['name'] = $propFilterNode->getAttribute('name');
161 $propFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $propFilterNode)->length>0;
162 $propFilter['param-filters'] = $this->parseParamFilters($propFilterNode);
163 $propFilter['text-match'] = $this->parseTextMatch($propFilterNode);
164 $propFilter['time-range'] = $this->parseTimeRange($propFilterNode);
166 $result[] = $propFilter;
175 * Parses the param-filter element
177 * @param DOMElement $parentNode
180 protected function parseParamFilters(DOMElement $parentNode) {
182 $paramFilterNodes = $this->xpath->query('cal:param-filter', $parentNode);
185 for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
187 $paramFilterNode = $paramFilterNodes->item($ii);
188 $paramFilter = array();
189 $paramFilter['name'] = $paramFilterNode->getAttribute('name');
190 $paramFilter['is-not-defined'] = $this->xpath->query('cal:is-not-defined', $paramFilterNode)->length>0;
191 $paramFilter['text-match'] = $this->parseTextMatch($paramFilterNode);
193 $result[] = $paramFilter;
202 * Parses the text-match element
204 * @param DOMElement $parentNode
207 protected function parseTextMatch(DOMElement $parentNode) {
209 $textMatchNodes = $this->xpath->query('cal:text-match', $parentNode);
211 if ($textMatchNodes->length === 0)
214 $textMatchNode = $textMatchNodes->item(0);
215 $negateCondition = $textMatchNode->getAttribute('negate-condition');
216 $negateCondition = $negateCondition==='yes';
217 $collation = $textMatchNode->getAttribute('collation');
218 if (!$collation) $collation = 'i;ascii-casemap';
221 'negate-condition' => $negateCondition,
222 'collation' => $collation,
223 'value' => $textMatchNode->nodeValue
229 * Parses the time-range element
231 * @param DOMElement $parentNode
234 protected function parseTimeRange(DOMElement $parentNode) {
236 $timeRangeNodes = $this->xpath->query('cal:time-range', $parentNode);
237 if ($timeRangeNodes->length === 0) {
241 $timeRangeNode = $timeRangeNodes->item(0);
243 if ($start = $timeRangeNode->getAttribute('start')) {
244 $start = Sabre_VObject_DateTimeParser::parseDateTime($start);
248 if ($end = $timeRangeNode->getAttribute('end')) {
249 $end = Sabre_VObject_DateTimeParser::parseDateTime($end);
254 if (!is_null($start) && !is_null($end) && $end <= $start) {
255 throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter');
266 * Parses the CALDAV:expand element
268 * @param DOMElement $parentNode
271 protected function parseExpand(DOMElement $parentNode) {
273 $start = $parentNode->getAttribute('start');
275 throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element');
277 $start = Sabre_VObject_DateTimeParser::parseDateTime($start);
279 $end = $parentNode->getAttribute('end');
281 throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element');
283 $end = Sabre_VObject_DateTimeParser::parseDateTime($end);
285 if ($end <= $start) {
286 throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');