Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F1821997
CalendarQueryValidator.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Size
12 KB
Subscribers
None
CalendarQueryValidator.php
View Options
<?php
namespace
Sabre\CalDAV
;
use
Sabre\VObject
;
use
DateTime
;
/**
* CalendarQuery Validator
*
* This class is responsible for checking if an iCalendar object matches a set
* of filters. The main function to do this is 'validate'.
*
* This is used to determine which icalendar objects should be returned for a
* calendar-query REPORT request.
*
* @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/).
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
class
CalendarQueryValidator
{
/**
* Verify if a list of filters applies to the calendar data object
*
* The list of filters must be formatted as parsed by \Sabre\CalDAV\CalendarQueryParser
*
* @param VObject\Component $vObject
* @param array $filters
* @return bool
*/
function
validate
(
VObject\Component\VCalendar
$vObject
,
array
$filters
)
{
// The top level object is always a component filter.
// We'll parse it manually, as it's pretty simple.
if
(
$vObject
->
name
!==
$filters
[
'name'
])
{
return
false
;
}
return
$this
->
validateCompFilters
(
$vObject
,
$filters
[
'comp-filters'
])
&&
$this
->
validatePropFilters
(
$vObject
,
$filters
[
'prop-filters'
]);
}
/**
* This method checks the validity of comp-filters.
*
* A list of comp-filters needs to be specified. Also the parent of the
* component we're checking should be specified, not the component to check
* itself.
*
* @param VObject\Component $parent
* @param array $filters
* @return bool
*/
protected
function
validateCompFilters
(
VObject\Component
$parent
,
array
$filters
)
{
foreach
(
$filters
as
$filter
)
{
$isDefined
=
isset
(
$parent
->
$filter
[
'name'
]);
if
(
$filter
[
'is-not-defined'
])
{
if
(
$isDefined
)
{
return
false
;
}
else
{
continue
;
}
}
if
(!
$isDefined
)
{
return
false
;
}
if
(
$filter
[
'time-range'
])
{
foreach
(
$parent
->
$filter
[
'name'
]
as
$subComponent
)
{
if
(
$this
->
validateTimeRange
(
$subComponent
,
$filter
[
'time-range'
][
'start'
],
$filter
[
'time-range'
][
'end'
]))
{
continue
2
;
}
}
return
false
;
}
if
(!
$filter
[
'comp-filters'
]
&&
!
$filter
[
'prop-filters'
])
{
continue
;
}
// If there are sub-filters, we need to find at least one component
// for which the subfilters hold true.
foreach
(
$parent
->
$filter
[
'name'
]
as
$subComponent
)
{
if
(
$this
->
validateCompFilters
(
$subComponent
,
$filter
[
'comp-filters'
])
&&
$this
->
validatePropFilters
(
$subComponent
,
$filter
[
'prop-filters'
]))
{
// We had a match, so this comp-filter succeeds
continue
2
;
}
}
// If we got here it means there were sub-comp-filters or
// sub-prop-filters and there was no match. This means this filter
// needs to return false.
return
false
;
}
// If we got here it means we got through all comp-filters alive so the
// filters were all true.
return
true
;
}
/**
* This method checks the validity of prop-filters.
*
* A list of prop-filters needs to be specified. Also the parent of the
* property we're checking should be specified, not the property to check
* itself.
*
* @param VObject\Component $parent
* @param array $filters
* @return bool
*/
protected
function
validatePropFilters
(
VObject\Component
$parent
,
array
$filters
)
{
foreach
(
$filters
as
$filter
)
{
$isDefined
=
isset
(
$parent
->
$filter
[
'name'
]);
if
(
$filter
[
'is-not-defined'
])
{
if
(
$isDefined
)
{
return
false
;
}
else
{
continue
;
}
}
if
(!
$isDefined
)
{
return
false
;
}
if
(
$filter
[
'time-range'
])
{
foreach
(
$parent
->
$filter
[
'name'
]
as
$subComponent
)
{
if
(
$this
->
validateTimeRange
(
$subComponent
,
$filter
[
'time-range'
][
'start'
],
$filter
[
'time-range'
][
'end'
]))
{
continue
2
;
}
}
return
false
;
}
if
(!
$filter
[
'param-filters'
]
&&
!
$filter
[
'text-match'
])
{
continue
;
}
// If there are sub-filters, we need to find at least one property
// for which the subfilters hold true.
foreach
(
$parent
->
$filter
[
'name'
]
as
$subComponent
)
{
if
(
$this
->
validateParamFilters
(
$subComponent
,
$filter
[
'param-filters'
])
&&
(!
$filter
[
'text-match'
]
||
$this
->
validateTextMatch
(
$subComponent
,
$filter
[
'text-match'
]))
)
{
// We had a match, so this prop-filter succeeds
continue
2
;
}
}
// If we got here it means there were sub-param-filters or
// text-match filters and there was no match. This means the
// filter needs to return false.
return
false
;
}
// If we got here it means we got through all prop-filters alive so the
// filters were all true.
return
true
;
}
/**
* This method checks the validity of param-filters.
*
* A list of param-filters needs to be specified. Also the parent of the
* parameter we're checking should be specified, not the parameter to check
* itself.
*
* @param VObject\Property $parent
* @param array $filters
* @return bool
*/
protected
function
validateParamFilters
(
VObject\Property
$parent
,
array
$filters
)
{
foreach
(
$filters
as
$filter
)
{
$isDefined
=
isset
(
$parent
[
$filter
[
'name'
]]);
if
(
$filter
[
'is-not-defined'
])
{
if
(
$isDefined
)
{
return
false
;
}
else
{
continue
;
}
}
if
(!
$isDefined
)
{
return
false
;
}
if
(!
$filter
[
'text-match'
])
{
continue
;
}
// If there are sub-filters, we need to find at least one parameter
// for which the subfilters hold true.
foreach
(
$parent
[
$filter
[
'name'
]]->
getParts
()
as
$paramPart
)
{
if
(
$this
->
validateTextMatch
(
$paramPart
,
$filter
[
'text-match'
]))
{
// We had a match, so this param-filter succeeds
continue
2
;
}
}
// If we got here it means there was a text-match filter and there
// were no matches. This means the filter needs to return false.
return
false
;
}
// If we got here it means we got through all param-filters alive so the
// filters were all true.
return
true
;
}
/**
* This method checks the validity of a text-match.
*
* A single text-match should be specified as well as the specific property
* or parameter we need to validate.
*
* @param VObject\Node|string $check Value to check against.
* @param array $textMatch
* @return bool
*/
protected
function
validateTextMatch
(
$check
,
array
$textMatch
)
{
if
(
$check
instanceof
VObject\Node
)
{
$check
=
$check
->
getValue
();
}
$isMatching
=
\Sabre\DAV\StringUtil
::
textMatch
(
$check
,
$textMatch
[
'value'
],
$textMatch
[
'collation'
]);
return
(
$textMatch
[
'negate-condition'
]
xor
$isMatching
);
}
/**
* Validates if a component matches the given time range.
*
* This is all based on the rules specified in rfc4791, which are quite
* complex.
*
* @param VObject\Node $component
* @param DateTime $start
* @param DateTime $end
* @return bool
*/
protected
function
validateTimeRange
(
VObject\Node
$component
,
$start
,
$end
)
{
if
(
is_null
(
$start
))
{
$start
=
new
DateTime
(
'1900-01-01'
);
}
if
(
is_null
(
$end
))
{
$end
=
new
DateTime
(
'3000-01-01'
);
}
switch
(
$component
->
name
)
{
case
'VEVENT'
:
case
'VTODO'
:
case
'VJOURNAL'
:
return
$component
->
isInTimeRange
(
$start
,
$end
);
case
'VALARM'
:
// If the valarm is wrapped in a recurring event, we need to
// expand the recursions, and validate each.
//
// Our datamodel doesn't easily allow us to do this straight
// in the VALARM component code, so this is a hack, and an
// expensive one too.
if
(
$component
->
parent
->
name
===
'VEVENT'
&&
$component
->
parent
->
RRULE
)
{
// Fire up the iterator!
$it
=
new
VObject\Recur\EventIterator
(
$component
->
parent
->
parent
,
(
string
)
$component
->
parent
->
UID
);
while
(
$it
->
valid
())
{
$expandedEvent
=
$it
->
getEventObject
();
// We need to check from these expanded alarms, which
// one is the first to trigger. Based on this, we can
// determine if we can 'give up' expanding events.
$firstAlarm
=
null
;
if
(
$expandedEvent
->
VALARM
!==
null
)
{
foreach
(
$expandedEvent
->
VALARM
as
$expandedAlarm
)
{
$effectiveTrigger
=
$expandedAlarm
->
getEffectiveTriggerTime
();
if
(
$expandedAlarm
->
isInTimeRange
(
$start
,
$end
))
{
return
true
;
}
if
((
string
)
$expandedAlarm
->
TRIGGER
[
'VALUE'
]
===
'DATE-TIME'
)
{
// This is an alarm with a non-relative trigger
// time, likely created by a buggy client. The
// implication is that every alarm in this
// recurring event trigger at the exact same
// time. It doesn't make sense to traverse
// further.
}
else
{
// We store the first alarm as a means to
// figure out when we can stop traversing.
if
(!
$firstAlarm
||
$effectiveTrigger
<
$firstAlarm
)
{
$firstAlarm
=
$effectiveTrigger
;
}
}
}
}
if
(
is_null
(
$firstAlarm
))
{
// No alarm was found.
//
// Or technically: No alarm that will change for
// every instance of the recurrence was found,
// which means we can assume there was no match.
return
false
;
}
if
(
$firstAlarm
>
$end
)
{
return
false
;
}
$it
->
next
();
}
return
false
;
}
else
{
return
$component
->
isInTimeRange
(
$start
,
$end
);
}
case
'VFREEBUSY'
:
throw
new
\Sabre\DAV\Exception\NotImplemented
(
'time-range filters are currently not supported on '
.
$component
->
name
.
' components'
);
case
'COMPLETED'
:
case
'CREATED'
:
case
'DTEND'
:
case
'DTSTAMP'
:
case
'DTSTART'
:
case
'DUE'
:
case
'LAST-MODIFIED'
:
return
(
$start
<=
$component
->
getDateTime
()
&&
$end
>=
$component
->
getDateTime
());
default
:
throw
new
\Sabre\DAV\Exception\BadRequest
(
'You cannot create a time-range filter on a '
.
$component
->
name
.
' component'
);
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Tue, Dec 24, 12:43 PM (18 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
914682
Default Alt Text
CalendarQueryValidator.php (12 KB)
Attached To
rDAVCAL DokuWiki DAVCal PlugIn
Event Timeline
Log In to Comment