ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
CalendarQueryValidator.php
Go to the documentation of this file.
1<?php
2
3namespace Sabre\CalDAV;
4
5use DateTime;
7
22
32 function validate(VObject\Component\VCalendar $vObject, array $filters) {
33
34 // The top level object is always a component filter.
35 // We'll parse it manually, as it's pretty simple.
36 if ($vObject->name !== $filters['name']) {
37 return false;
38 }
39
40 return
41 $this->validateCompFilters($vObject, $filters['comp-filters']) &&
42 $this->validatePropFilters($vObject, $filters['prop-filters']);
43
44
45 }
46
58 protected function validateCompFilters(VObject\Component $parent, array $filters) {
59
60 foreach ($filters as $filter) {
61
62 $isDefined = isset($parent->{$filter['name']});
63
64 if ($filter['is-not-defined']) {
65
66 if ($isDefined) {
67 return false;
68 } else {
69 continue;
70 }
71
72 }
73 if (!$isDefined) {
74 return false;
75 }
76
77 if ($filter['time-range']) {
78 foreach ($parent->{$filter['name']} as $subComponent) {
79 if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) {
80 continue 2;
81 }
82 }
83 return false;
84 }
85
86 if (!$filter['comp-filters'] && !$filter['prop-filters']) {
87 continue;
88 }
89
90 // If there are sub-filters, we need to find at least one component
91 // for which the subfilters hold true.
92 foreach ($parent->{$filter['name']} as $subComponent) {
93
94 if (
95 $this->validateCompFilters($subComponent, $filter['comp-filters']) &&
96 $this->validatePropFilters($subComponent, $filter['prop-filters'])) {
97 // We had a match, so this comp-filter succeeds
98 continue 2;
99 }
100
101 }
102
103 // If we got here it means there were sub-comp-filters or
104 // sub-prop-filters and there was no match. This means this filter
105 // needs to return false.
106 return false;
107
108 }
109
110 // If we got here it means we got through all comp-filters alive so the
111 // filters were all true.
112 return true;
113
114 }
115
127 protected function validatePropFilters(VObject\Component $parent, array $filters) {
128
129 foreach ($filters as $filter) {
130
131 $isDefined = isset($parent->{$filter['name']});
132
133 if ($filter['is-not-defined']) {
134
135 if ($isDefined) {
136 return false;
137 } else {
138 continue;
139 }
140
141 }
142 if (!$isDefined) {
143 return false;
144 }
145
146 if ($filter['time-range']) {
147 foreach ($parent->{$filter['name']} as $subComponent) {
148 if ($this->validateTimeRange($subComponent, $filter['time-range']['start'], $filter['time-range']['end'])) {
149 continue 2;
150 }
151 }
152 return false;
153 }
154
155 if (!$filter['param-filters'] && !$filter['text-match']) {
156 continue;
157 }
158
159 // If there are sub-filters, we need to find at least one property
160 // for which the subfilters hold true.
161 foreach ($parent->{$filter['name']} as $subComponent) {
162
163 if (
164 $this->validateParamFilters($subComponent, $filter['param-filters']) &&
165 (!$filter['text-match'] || $this->validateTextMatch($subComponent, $filter['text-match']))
166 ) {
167 // We had a match, so this prop-filter succeeds
168 continue 2;
169 }
170
171 }
172
173 // If we got here it means there were sub-param-filters or
174 // text-match filters and there was no match. This means the
175 // filter needs to return false.
176 return false;
177
178 }
179
180 // If we got here it means we got through all prop-filters alive so the
181 // filters were all true.
182 return true;
183
184 }
185
197 protected function validateParamFilters(VObject\Property $parent, array $filters) {
198
199 foreach ($filters as $filter) {
200
201 $isDefined = isset($parent[$filter['name']]);
202
203 if ($filter['is-not-defined']) {
204
205 if ($isDefined) {
206 return false;
207 } else {
208 continue;
209 }
210
211 }
212 if (!$isDefined) {
213 return false;
214 }
215
216 if (!$filter['text-match']) {
217 continue;
218 }
219
220 // If there are sub-filters, we need to find at least one parameter
221 // for which the subfilters hold true.
222 foreach ($parent[$filter['name']]->getParts() as $paramPart) {
223
224 if ($this->validateTextMatch($paramPart, $filter['text-match'])) {
225 // We had a match, so this param-filter succeeds
226 continue 2;
227 }
228
229 }
230
231 // If we got here it means there was a text-match filter and there
232 // were no matches. This means the filter needs to return false.
233 return false;
234
235 }
236
237 // If we got here it means we got through all param-filters alive so the
238 // filters were all true.
239 return true;
240
241 }
242
253 protected function validateTextMatch($check, array $textMatch) {
254
255 if ($check instanceof VObject\Node) {
256 $check = $check->getValue();
257 }
258
259 $isMatching = \Sabre\DAV\StringUtil::textMatch($check, $textMatch['value'], $textMatch['collation']);
260
261 return ($textMatch['negate-condition'] xor $isMatching);
262
263 }
264
276 protected function validateTimeRange(VObject\Node $component, $start, $end) {
277
278 if (is_null($start)) {
279 $start = new DateTime('1900-01-01');
280 }
281 if (is_null($end)) {
282 $end = new DateTime('3000-01-01');
283 }
284
285 switch ($component->name) {
286
287 case 'VEVENT' :
288 case 'VTODO' :
289 case 'VJOURNAL' :
290
291 return $component->isInTimeRange($start, $end);
292
293 case 'VALARM' :
294
295 // If the valarm is wrapped in a recurring event, we need to
296 // expand the recursions, and validate each.
297 //
298 // Our datamodel doesn't easily allow us to do this straight
299 // in the VALARM component code, so this is a hack, and an
300 // expensive one too.
301 if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
302
303 // Fire up the iterator!
304 $it = new VObject\Recur\EventIterator($component->parent->parent, (string)$component->parent->UID);
305 while ($it->valid()) {
306 $expandedEvent = $it->getEventObject();
307
308 // We need to check from these expanded alarms, which
309 // one is the first to trigger. Based on this, we can
310 // determine if we can 'give up' expanding events.
311 $firstAlarm = null;
312 if ($expandedEvent->VALARM !== null) {
313 foreach ($expandedEvent->VALARM as $expandedAlarm) {
314
315 $effectiveTrigger = $expandedAlarm->getEffectiveTriggerTime();
316 if ($expandedAlarm->isInTimeRange($start, $end)) {
317 return true;
318 }
319
320 if ((string)$expandedAlarm->TRIGGER['VALUE'] === 'DATE-TIME') {
321 // This is an alarm with a non-relative trigger
322 // time, likely created by a buggy client. The
323 // implication is that every alarm in this
324 // recurring event trigger at the exact same
325 // time. It doesn't make sense to traverse
326 // further.
327 } else {
328 // We store the first alarm as a means to
329 // figure out when we can stop traversing.
330 if (!$firstAlarm || $effectiveTrigger < $firstAlarm) {
331 $firstAlarm = $effectiveTrigger;
332 }
333 }
334 }
335 }
336 if (is_null($firstAlarm)) {
337 // No alarm was found.
338 //
339 // Or technically: No alarm that will change for
340 // every instance of the recurrence was found,
341 // which means we can assume there was no match.
342 return false;
343 }
344 if ($firstAlarm > $end) {
345 return false;
346 }
347 $it->next();
348 }
349 return false;
350 } else {
351 return $component->isInTimeRange($start, $end);
352 }
353
354 case 'VFREEBUSY' :
355 throw new \Sabre\DAV\Exception\NotImplemented('time-range filters are currently not supported on ' . $component->name . ' components');
356
357 case 'COMPLETED' :
358 case 'CREATED' :
359 case 'DTEND' :
360 case 'DTSTAMP' :
361 case 'DTSTART' :
362 case 'DUE' :
363 case 'LAST-MODIFIED' :
364 return ($start <= $component->getDateTime() && $end >= $component->getDateTime());
365
366
367
368 default :
369 throw new \Sabre\DAV\Exception\BadRequest('You cannot create a time-range filter on a ' . $component->name . ' component');
370
371 }
372
373 }
374
375}
An exception for terminatinating execution or to throw for unit testing.
validateTimeRange(VObject\Node $component, $start, $end)
Validates if a component matches the given time range.
validatePropFilters(VObject\Component $parent, array $filters)
This method checks the validity of prop-filters.
validateParamFilters(VObject\Property $parent, array $filters)
This method checks the validity of param-filters.
validateCompFilters(VObject\Component $parent, array $filters)
This method checks the validity of comp-filters.
validate(VObject\Component\VCalendar $vObject, array $filters)
Verify if a list of filters applies to the calendar data object.
validateTextMatch($check, array $textMatch)
This method checks the validity of a text-match.
Node class.
Definition: Node.php:14
static textMatch($haystack, $needle, $collation, $matchType='contains')
Checks if a needle occurs in a haystack ;)
Definition: StringUtil.php:27
The VCalendar component.
Definition: VCalendar.php:23
This class is used to determine new for a recurring event, when the next events occur.
$start
Definition: bench.php:8