Update SabreDAV to 1.7.1
This commit is contained in:
parent
5713dcfd11
commit
183cc22501
|
@ -1,47 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract Calendaring backend. Extend this class to create your own backends.
|
* Abstract Calendaring backend. Extend this class to create your own backends.
|
||||||
*
|
*
|
||||||
|
* Checkout the BackendInterface for all the methods that must be implemented.
|
||||||
|
*
|
||||||
* @package Sabre
|
* @package Sabre
|
||||||
* @subpackage CalDAV
|
* @subpackage CalDAV
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
*/
|
*/
|
||||||
abstract class Sabre_CalDAV_Backend_Abstract {
|
abstract class Sabre_CalDAV_Backend_Abstract implements Sabre_CalDAV_Backend_BackendInterface {
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of calendars for a principal.
|
|
||||||
*
|
|
||||||
* Every project is an array with the following keys:
|
|
||||||
* * id, a unique id that will be used by other functions to modify the
|
|
||||||
* calendar. This can be the same as the uri or a database key.
|
|
||||||
* * uri, which the basename of the uri with which the calendar is
|
|
||||||
* accessed.
|
|
||||||
* * principaluri. The owner of the calendar. Almost always the same as
|
|
||||||
* principalUri passed to this method.
|
|
||||||
*
|
|
||||||
* Furthermore it can contain webdav properties in clark notation. A very
|
|
||||||
* common one is '{DAV:}displayname'.
|
|
||||||
*
|
|
||||||
* @param string $principalUri
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
abstract function getCalendarsForUser($principalUri);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new calendar for a principal.
|
|
||||||
*
|
|
||||||
* If the creation was a success, an id must be returned that can be used to reference
|
|
||||||
* this calendar in other methods, such as updateCalendar.
|
|
||||||
*
|
|
||||||
* @param string $principalUri
|
|
||||||
* @param string $calendarUri
|
|
||||||
* @param array $properties
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
abstract function createCalendar($principalUri,$calendarUri,array $properties);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates properties for a calendar.
|
* Updates properties for a calendar.
|
||||||
|
@ -75,7 +47,7 @@ abstract class Sabre_CalDAV_Backend_Abstract {
|
||||||
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||||
* (424 Failed Dependency) because the request needs to be atomic.
|
* (424 Failed Dependency) because the request needs to be atomic.
|
||||||
*
|
*
|
||||||
* @param string $calendarId
|
* @param mixed $calendarId
|
||||||
* @param array $mutations
|
* @param array $mutations
|
||||||
* @return bool|array
|
* @return bool|array
|
||||||
*/
|
*/
|
||||||
|
@ -86,83 +58,97 @@ abstract class Sabre_CalDAV_Backend_Abstract {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a calendar and all it's objects
|
* Performs a calendar-query on the contents of this calendar.
|
||||||
*
|
*
|
||||||
* @param string $calendarId
|
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||||
* @return void
|
* calendar-query it is possible for a client to request a specific set of
|
||||||
*/
|
* object, based on contents of iCalendar properties, date-ranges and
|
||||||
abstract function deleteCalendar($calendarId);
|
* iCalendar component types (VTODO, VEVENT).
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all calendar objects within a calendar.
|
|
||||||
*
|
*
|
||||||
* Every item contains an array with the following keys:
|
* This method should just return a list of (relative) urls that match this
|
||||||
* * id - unique identifier which will be used for subsequent updates
|
* query.
|
||||||
* * calendardata - The iCalendar-compatible calendar data
|
|
||||||
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
|
|
||||||
* * lastmodified - a timestamp of the last modification time
|
|
||||||
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
|
||||||
* ' "abcdef"')
|
|
||||||
* * calendarid - The calendarid as it was passed to this function.
|
|
||||||
* * size - The size of the calendar objects, in bytes.
|
|
||||||
*
|
*
|
||||||
* Note that the etag is optional, but it's highly encouraged to return for
|
* The list of filters are specified as an array. The exact array is
|
||||||
* speed reasons.
|
* documented by Sabre_CalDAV_CalendarQueryParser.
|
||||||
*
|
*
|
||||||
* The calendardata is also optional. If it's not returned
|
* Note that it is extremely likely that getCalendarObject for every path
|
||||||
* 'getCalendarObject' will be called later, which *is* expected to return
|
* returned from this method will be called almost immediately after. You
|
||||||
* calendardata.
|
* may want to anticipate this to speed up these requests.
|
||||||
*
|
*
|
||||||
* If neither etag or size are specified, the calendardata will be
|
* This method provides a default implementation, which parses *all* the
|
||||||
* used/fetched to determine these numbers. If both are specified the
|
* iCalendar objects in the specified calendar.
|
||||||
* amount of times this is needed is reduced by a great degree.
|
|
||||||
*
|
*
|
||||||
* @param string $calendarId
|
* This default may well be good enough for personal use, and calendars
|
||||||
|
* that aren't very large. But if you anticipate high usage, big calendars
|
||||||
|
* or high loads, you are strongly adviced to optimize certain paths.
|
||||||
|
*
|
||||||
|
* The best way to do so is override this method and to optimize
|
||||||
|
* specifically for 'common filters'.
|
||||||
|
*
|
||||||
|
* Requests that are extremely common are:
|
||||||
|
* * requests for just VEVENTS
|
||||||
|
* * requests for just VTODO
|
||||||
|
* * requests with a time-range-filter on either VEVENT or VTODO.
|
||||||
|
*
|
||||||
|
* ..and combinations of these requests. It may not be worth it to try to
|
||||||
|
* handle every possible situation and just rely on the (relatively
|
||||||
|
* easy to use) CalendarQueryValidator to handle the rest.
|
||||||
|
*
|
||||||
|
* Note that especially time-range-filters may be difficult to parse. A
|
||||||
|
* time-range filter specified on a VEVENT must for instance also handle
|
||||||
|
* recurrence rules correctly.
|
||||||
|
* A good example of how to interprete all these filters can also simply
|
||||||
|
* be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct
|
||||||
|
* as possible, so it gives you a good idea on what type of stuff you need
|
||||||
|
* to think of.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param array $filters
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
abstract function getCalendarObjects($calendarId);
|
public function calendarQuery($calendarId, array $filters) {
|
||||||
|
|
||||||
/**
|
$result = array();
|
||||||
* Returns information from a single calendar object, based on it's object
|
$objects = $this->getCalendarObjects($calendarId);
|
||||||
* uri.
|
|
||||||
*
|
|
||||||
* The returned array must have the same keys as getCalendarObjects. The
|
|
||||||
* 'calendardata' object is required here though, while it's not required
|
|
||||||
* for getCalendarObjects.
|
|
||||||
*
|
|
||||||
* @param string $calendarId
|
|
||||||
* @param string $objectUri
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
abstract function getCalendarObject($calendarId,$objectUri);
|
|
||||||
|
|
||||||
/**
|
$validator = new Sabre_CalDAV_CalendarQueryValidator();
|
||||||
* Creates a new calendar object.
|
|
||||||
*
|
|
||||||
* @param string $calendarId
|
|
||||||
* @param string $objectUri
|
|
||||||
* @param string $calendarData
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
abstract function createCalendarObject($calendarId,$objectUri,$calendarData);
|
|
||||||
|
|
||||||
/**
|
foreach($objects as $object) {
|
||||||
* Updates an existing calendarobject, based on it's uri.
|
|
||||||
*
|
if ($this->validateFilterForObject($object, $filters)) {
|
||||||
* @param string $calendarId
|
$result[] = $object['uri'];
|
||||||
* @param string $objectUri
|
}
|
||||||
* @param string $calendarData
|
|
||||||
* @return void
|
}
|
||||||
*/
|
|
||||||
abstract function updateCalendarObject($calendarId,$objectUri,$calendarData);
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method validates if a filters (as passed to calendarQuery) matches
|
||||||
|
* the given object.
|
||||||
|
*
|
||||||
|
* @param array $object
|
||||||
|
* @param array $filter
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function validateFilterForObject(array $object, array $filters) {
|
||||||
|
|
||||||
|
// Unfortunately, setting the 'calendardata' here is optional. If
|
||||||
|
// it was excluded, we actually need another call to get this as
|
||||||
|
// well.
|
||||||
|
if (!isset($object['calendardata'])) {
|
||||||
|
$object = $this->getCalendarObject($object['calendarid'], $object['uri']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = is_resource($object['calendardata'])?stream_get_contents($object['calendardata']):$object['calendardata'];
|
||||||
|
$vObject = VObject\Reader::read($data);
|
||||||
|
|
||||||
|
$validator = new Sabre_CalDAV_CalendarQueryValidator();
|
||||||
|
return $validator->validate($vObject, $filters);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes an existing calendar object.
|
|
||||||
*
|
|
||||||
* @param string $calendarId
|
|
||||||
* @param string $objectUri
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
abstract function deleteCalendarObject($calendarId,$objectUri);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,231 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every CalDAV backend must at least implement this interface.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
interface Sabre_CalDAV_Backend_BackendInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of calendars for a principal.
|
||||||
|
*
|
||||||
|
* Every project is an array with the following keys:
|
||||||
|
* * id, a unique id that will be used by other functions to modify the
|
||||||
|
* calendar. This can be the same as the uri or a database key.
|
||||||
|
* * uri, which the basename of the uri with which the calendar is
|
||||||
|
* accessed.
|
||||||
|
* * principaluri. The owner of the calendar. Almost always the same as
|
||||||
|
* principalUri passed to this method.
|
||||||
|
*
|
||||||
|
* Furthermore it can contain webdav properties in clark notation. A very
|
||||||
|
* common one is '{DAV:}displayname'.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCalendarsForUser($principalUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new calendar for a principal.
|
||||||
|
*
|
||||||
|
* If the creation was a success, an id must be returned that can be used to reference
|
||||||
|
* this calendar in other methods, such as updateCalendar.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param string $calendarUri
|
||||||
|
* @param array $properties
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function createCalendar($principalUri,$calendarUri,array $properties);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates properties for a calendar.
|
||||||
|
*
|
||||||
|
* The mutations array uses the propertyName in clark-notation as key,
|
||||||
|
* and the array value for the property value. In the case a property
|
||||||
|
* should be deleted, the property value will be null.
|
||||||
|
*
|
||||||
|
* This method must be atomic. If one property cannot be changed, the
|
||||||
|
* entire operation must fail.
|
||||||
|
*
|
||||||
|
* If the operation was successful, true can be returned.
|
||||||
|
* If the operation failed, false can be returned.
|
||||||
|
*
|
||||||
|
* Deletion of a non-existent property is always successful.
|
||||||
|
*
|
||||||
|
* Lastly, it is optional to return detailed information about any
|
||||||
|
* failures. In this case an array should be returned with the following
|
||||||
|
* structure:
|
||||||
|
*
|
||||||
|
* array(
|
||||||
|
* 403 => array(
|
||||||
|
* '{DAV:}displayname' => null,
|
||||||
|
* ),
|
||||||
|
* 424 => array(
|
||||||
|
* '{DAV:}owner' => null,
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* In this example it was forbidden to update {DAV:}displayname.
|
||||||
|
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||||
|
* (424 Failed Dependency) because the request needs to be atomic.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param array $mutations
|
||||||
|
* @return bool|array
|
||||||
|
*/
|
||||||
|
public function updateCalendar($calendarId, array $mutations);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a calendar and all it's objects
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deleteCalendar($calendarId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all calendar objects within a calendar.
|
||||||
|
*
|
||||||
|
* Every item contains an array with the following keys:
|
||||||
|
* * id - unique identifier which will be used for subsequent updates
|
||||||
|
* * calendardata - The iCalendar-compatible calendar data
|
||||||
|
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
|
||||||
|
* * lastmodified - a timestamp of the last modification time
|
||||||
|
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
||||||
|
* ' "abcdef"')
|
||||||
|
* * calendarid - The calendarid as it was passed to this function.
|
||||||
|
* * size - The size of the calendar objects, in bytes.
|
||||||
|
*
|
||||||
|
* Note that the etag is optional, but it's highly encouraged to return for
|
||||||
|
* speed reasons.
|
||||||
|
*
|
||||||
|
* The calendardata is also optional. If it's not returned
|
||||||
|
* 'getCalendarObject' will be called later, which *is* expected to return
|
||||||
|
* calendardata.
|
||||||
|
*
|
||||||
|
* If neither etag or size are specified, the calendardata will be
|
||||||
|
* used/fetched to determine these numbers. If both are specified the
|
||||||
|
* amount of times this is needed is reduced by a great degree.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCalendarObjects($calendarId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns information from a single calendar object, based on it's object
|
||||||
|
* uri.
|
||||||
|
*
|
||||||
|
* The returned array must have the same keys as getCalendarObjects. The
|
||||||
|
* 'calendardata' object is required here though, while it's not required
|
||||||
|
* for getCalendarObjects.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getCalendarObject($calendarId,$objectUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new calendar object.
|
||||||
|
*
|
||||||
|
* It is possible return an etag from this function, which will be used in
|
||||||
|
* the response to this PUT request. Note that the ETag must be surrounded
|
||||||
|
* by double-quotes.
|
||||||
|
*
|
||||||
|
* However, you should only really return this ETag if you don't mangle the
|
||||||
|
* calendar-data. If the result of a subsequent GET to this object is not
|
||||||
|
* the exact same as this request body, you should omit the ETag.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
* @param string $calendarData
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function createCalendarObject($calendarId,$objectUri,$calendarData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing calendarobject, based on it's uri.
|
||||||
|
*
|
||||||
|
* It is possible return an etag from this function, which will be used in
|
||||||
|
* the response to this PUT request. Note that the ETag must be surrounded
|
||||||
|
* by double-quotes.
|
||||||
|
*
|
||||||
|
* However, you should only really return this ETag if you don't mangle the
|
||||||
|
* calendar-data. If the result of a subsequent GET to this object is not
|
||||||
|
* the exact same as this request body, you should omit the ETag.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
* @param string $calendarData
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function updateCalendarObject($calendarId,$objectUri,$calendarData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an existing calendar object.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param string $objectUri
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deleteCalendarObject($calendarId,$objectUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a calendar-query on the contents of this calendar.
|
||||||
|
*
|
||||||
|
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||||
|
* calendar-query it is possible for a client to request a specific set of
|
||||||
|
* object, based on contents of iCalendar properties, date-ranges and
|
||||||
|
* iCalendar component types (VTODO, VEVENT).
|
||||||
|
*
|
||||||
|
* This method should just return a list of (relative) urls that match this
|
||||||
|
* query.
|
||||||
|
*
|
||||||
|
* The list of filters are specified as an array. The exact array is
|
||||||
|
* documented by Sabre_CalDAV_CalendarQueryParser.
|
||||||
|
*
|
||||||
|
* Note that it is extremely likely that getCalendarObject for every path
|
||||||
|
* returned from this method will be called almost immediately after. You
|
||||||
|
* may want to anticipate this to speed up these requests.
|
||||||
|
*
|
||||||
|
* This method provides a default implementation, which parses *all* the
|
||||||
|
* iCalendar objects in the specified calendar.
|
||||||
|
*
|
||||||
|
* This default may well be good enough for personal use, and calendars
|
||||||
|
* that aren't very large. But if you anticipate high usage, big calendars
|
||||||
|
* or high loads, you are strongly adviced to optimize certain paths.
|
||||||
|
*
|
||||||
|
* The best way to do so is override this method and to optimize
|
||||||
|
* specifically for 'common filters'.
|
||||||
|
*
|
||||||
|
* Requests that are extremely common are:
|
||||||
|
* * requests for just VEVENTS
|
||||||
|
* * requests for just VTODO
|
||||||
|
* * requests with a time-range-filter on either VEVENT or VTODO.
|
||||||
|
*
|
||||||
|
* ..and combinations of these requests. It may not be worth it to try to
|
||||||
|
* handle every possible situation and just rely on the (relatively
|
||||||
|
* easy to use) CalendarQueryValidator to handle the rest.
|
||||||
|
*
|
||||||
|
* Note that especially time-range-filters may be difficult to parse. A
|
||||||
|
* time-range filter specified on a VEVENT must for instance also handle
|
||||||
|
* recurrence rules correctly.
|
||||||
|
* A good example of how to interprete all these filters can also simply
|
||||||
|
* be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct
|
||||||
|
* as possible, so it gives you a good idea on what type of stuff you need
|
||||||
|
* to think of.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param array $filters
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function calendarQuery($calendarId, array $filters);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds caldav notification support to a backend.
|
||||||
|
*
|
||||||
|
* Note: This feature is experimental, and may change in between different
|
||||||
|
* SabreDAV versions.
|
||||||
|
*
|
||||||
|
* Notifications are defined at:
|
||||||
|
* http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-notifications.txt
|
||||||
|
*
|
||||||
|
* These notifications are basically a list of server-generated notifications
|
||||||
|
* displayed to the user. Users can dismiss notifications by deleting them.
|
||||||
|
*
|
||||||
|
* The primary usecase is to allow for calendar-sharing.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
interface Sabre_CalDAV_Backend_NotificationSupport extends Sabre_CalDAV_Backend_BackendInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of notifications for a given principal url.
|
||||||
|
*
|
||||||
|
* The returned array should only consist of implementations of
|
||||||
|
* Sabre_CalDAV_Notifications_INotificationType.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getNotificationsForPrincipal($principalUri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This deletes a specific notifcation.
|
||||||
|
*
|
||||||
|
* This may be called by a client once it deems a notification handled.
|
||||||
|
*
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param Sabre_CalDAV_Notifications_INotificationType $notification
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deleteNotification($principalUri, Sabre_CalDAV_Notifications_INotificationType $notification);
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PDO CalDAV backend
|
* PDO CalDAV backend
|
||||||
*
|
*
|
||||||
|
@ -14,6 +16,16 @@
|
||||||
*/
|
*/
|
||||||
class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need to specify a max date, because we need to stop *somewhere*
|
||||||
|
*
|
||||||
|
* On 32 bit system the maximum for a signed integer is 2147483647, so
|
||||||
|
* MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
|
||||||
|
* in 2038-01-19 to avoid problems when the date is converted
|
||||||
|
* to a unix timestamp.
|
||||||
|
*/
|
||||||
|
const MAX_DATE = '2038-01-01';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pdo
|
* pdo
|
||||||
*
|
*
|
||||||
|
@ -37,8 +49,9 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of CalDAV properties, and how they map to database fieldnames
|
* List of CalDAV properties, and how they map to database fieldnames
|
||||||
|
* Add your own properties by simply adding on to this array.
|
||||||
*
|
*
|
||||||
* Add your own properties by simply adding on to this array
|
* Note that only string-based properties are supported here.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
|
@ -90,6 +103,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
$fields[] = 'ctag';
|
$fields[] = 'ctag';
|
||||||
$fields[] = 'components';
|
$fields[] = 'components';
|
||||||
$fields[] = 'principaluri';
|
$fields[] = 'principaluri';
|
||||||
|
$fields[] = 'transparent';
|
||||||
|
|
||||||
// Making fields a comma-delimited list
|
// Making fields a comma-delimited list
|
||||||
$fields = implode(', ', $fields);
|
$fields = implode(', ', $fields);
|
||||||
|
@ -110,6 +124,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
'principaluri' => $row['principaluri'],
|
'principaluri' => $row['principaluri'],
|
||||||
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0',
|
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0',
|
||||||
'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
|
'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
|
||||||
|
'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-calendar-transp' => new Sabre_CalDAV_Property_ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,11 +157,13 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
'principaluri',
|
'principaluri',
|
||||||
'uri',
|
'uri',
|
||||||
'ctag',
|
'ctag',
|
||||||
|
'transparent',
|
||||||
);
|
);
|
||||||
$values = array(
|
$values = array(
|
||||||
':principaluri' => $principalUri,
|
':principaluri' => $principalUri,
|
||||||
':uri' => $calendarUri,
|
':uri' => $calendarUri,
|
||||||
':ctag' => 1,
|
':ctag' => 1,
|
||||||
|
':transparent' => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Default value
|
// Default value
|
||||||
|
@ -160,6 +177,10 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
}
|
}
|
||||||
$values[':components'] = implode(',',$properties[$sccs]->getValue());
|
$values[':components'] = implode(',',$properties[$sccs]->getValue());
|
||||||
}
|
}
|
||||||
|
$transp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-calendar-transp';
|
||||||
|
if (isset($properties[$transp])) {
|
||||||
|
$values[':transparent'] = $properties[$transp]->getValue()==='transparent';
|
||||||
|
}
|
||||||
|
|
||||||
foreach($this->propertyMap as $xmlName=>$dbName) {
|
foreach($this->propertyMap as $xmlName=>$dbName) {
|
||||||
if (isset($properties[$xmlName])) {
|
if (isset($properties[$xmlName])) {
|
||||||
|
@ -225,8 +246,15 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
|
|
||||||
foreach($mutations as $propertyName=>$propertyValue) {
|
foreach($mutations as $propertyName=>$propertyValue) {
|
||||||
|
|
||||||
// We don't know about this property.
|
switch($propertyName) {
|
||||||
|
case '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-calendar-transp' :
|
||||||
|
$fieldName = 'transparent';
|
||||||
|
$newValues[$fieldName] = $propertyValue->getValue()==='transparent';
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
// Checking the property map
|
||||||
if (!isset($this->propertyMap[$propertyName])) {
|
if (!isset($this->propertyMap[$propertyName])) {
|
||||||
|
// We don't know about this property.
|
||||||
$hasError = true;
|
$hasError = true;
|
||||||
$result[403][$propertyName] = null;
|
$result[403][$propertyName] = null;
|
||||||
unset($mutations[$propertyName]);
|
unset($mutations[$propertyName]);
|
||||||
|
@ -235,6 +263,7 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
|
|
||||||
$fieldName = $this->propertyMap[$propertyName];
|
$fieldName = $this->propertyMap[$propertyName];
|
||||||
$newValues[$fieldName] = $propertyValue;
|
$newValues[$fieldName] = $propertyValue;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,9 +345,22 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
*/
|
*/
|
||||||
public function getCalendarObjects($calendarId) {
|
public function getCalendarObjects($calendarId) {
|
||||||
|
|
||||||
$stmt = $this->pdo->prepare('SELECT * FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
|
$stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
|
||||||
$stmt->execute(array($calendarId));
|
$stmt->execute(array($calendarId));
|
||||||
return $stmt->fetchAll();
|
|
||||||
|
$result = array();
|
||||||
|
foreach($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
|
||||||
|
$result[] = array(
|
||||||
|
'id' => $row['id'],
|
||||||
|
'uri' => $row['uri'],
|
||||||
|
'lastmodified' => $row['lastmodified'],
|
||||||
|
'etag' => '"' . $row['etag'] . '"',
|
||||||
|
'calendarid' => $row['calendarid'],
|
||||||
|
'size' => (int)$row['size'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,44 +378,166 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
*/
|
*/
|
||||||
public function getCalendarObject($calendarId,$objectUri) {
|
public function getCalendarObject($calendarId,$objectUri) {
|
||||||
|
|
||||||
$stmt = $this->pdo->prepare('SELECT * FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
|
$stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
|
||||||
$stmt->execute(array($calendarId, $objectUri));
|
$stmt->execute(array($calendarId, $objectUri));
|
||||||
return $stmt->fetch();
|
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if(!$row) return null;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'id' => $row['id'],
|
||||||
|
'uri' => $row['uri'],
|
||||||
|
'lastmodified' => $row['lastmodified'],
|
||||||
|
'etag' => '"' . $row['etag'] . '"',
|
||||||
|
'calendarid' => $row['calendarid'],
|
||||||
|
'size' => (int)$row['size'],
|
||||||
|
'calendardata' => $row['calendardata'],
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new calendar object.
|
* Creates a new calendar object.
|
||||||
*
|
*
|
||||||
* @param string $calendarId
|
* It is possible return an etag from this function, which will be used in
|
||||||
|
* the response to this PUT request. Note that the ETag must be surrounded
|
||||||
|
* by double-quotes.
|
||||||
|
*
|
||||||
|
* However, you should only really return this ETag if you don't mangle the
|
||||||
|
* calendar-data. If the result of a subsequent GET to this object is not
|
||||||
|
* the exact same as this request body, you should omit the ETag.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
* @param string $objectUri
|
* @param string $objectUri
|
||||||
* @param string $calendarData
|
* @param string $calendarData
|
||||||
* @return void
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function createCalendarObject($calendarId,$objectUri,$calendarData) {
|
public function createCalendarObject($calendarId,$objectUri,$calendarData) {
|
||||||
|
|
||||||
$stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)');
|
$extraData = $this->getDenormalizedData($calendarData);
|
||||||
$stmt->execute(array($calendarId,$objectUri,$calendarData,time()));
|
|
||||||
|
$stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence) VALUES (?,?,?,?,?,?,?,?,?)');
|
||||||
|
$stmt->execute(array(
|
||||||
|
$calendarId,
|
||||||
|
$objectUri,
|
||||||
|
$calendarData,
|
||||||
|
time(),
|
||||||
|
$extraData['etag'],
|
||||||
|
$extraData['size'],
|
||||||
|
$extraData['componentType'],
|
||||||
|
$extraData['firstOccurence'],
|
||||||
|
$extraData['lastOccurence'],
|
||||||
|
));
|
||||||
$stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?');
|
$stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?');
|
||||||
$stmt->execute(array($calendarId));
|
$stmt->execute(array($calendarId));
|
||||||
|
|
||||||
|
return '"' . $extraData['etag'] . '"';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates an existing calendarobject, based on it's uri.
|
* Updates an existing calendarobject, based on it's uri.
|
||||||
*
|
*
|
||||||
* @param string $calendarId
|
* It is possible return an etag from this function, which will be used in
|
||||||
|
* the response to this PUT request. Note that the ETag must be surrounded
|
||||||
|
* by double-quotes.
|
||||||
|
*
|
||||||
|
* However, you should only really return this ETag if you don't mangle the
|
||||||
|
* calendar-data. If the result of a subsequent GET to this object is not
|
||||||
|
* the exact same as this request body, you should omit the ETag.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
* @param string $objectUri
|
* @param string $objectUri
|
||||||
* @param string $calendarData
|
* @param string $calendarData
|
||||||
* @return void
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
|
public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
|
||||||
|
|
||||||
$stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?');
|
$extraData = $this->getDenormalizedData($calendarData);
|
||||||
$stmt->execute(array($calendarData,time(),$calendarId,$objectUri));
|
|
||||||
|
$stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ? WHERE calendarid = ? AND uri = ?');
|
||||||
|
$stmt->execute(array($calendarData,time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'] ,$calendarId,$objectUri));
|
||||||
$stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?');
|
$stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET ctag = ctag + 1 WHERE id = ?');
|
||||||
$stmt->execute(array($calendarId));
|
$stmt->execute(array($calendarId));
|
||||||
|
|
||||||
|
return '"' . $extraData['etag'] . '"';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses some information from calendar objects, used for optimized
|
||||||
|
* calendar-queries.
|
||||||
|
*
|
||||||
|
* Returns an array with the following keys:
|
||||||
|
* * etag
|
||||||
|
* * size
|
||||||
|
* * componentType
|
||||||
|
* * firstOccurence
|
||||||
|
* * lastOccurence
|
||||||
|
*
|
||||||
|
* @param string $calendarData
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getDenormalizedData($calendarData) {
|
||||||
|
|
||||||
|
$vObject = VObject\Reader::read($calendarData);
|
||||||
|
$componentType = null;
|
||||||
|
$component = null;
|
||||||
|
$firstOccurence = null;
|
||||||
|
$lastOccurence = null;
|
||||||
|
foreach($vObject->getComponents() as $component) {
|
||||||
|
if ($component->name!=='VTIMEZONE') {
|
||||||
|
$componentType = $component->name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$componentType) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
|
||||||
|
}
|
||||||
|
if ($componentType === 'VEVENT') {
|
||||||
|
$firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
|
||||||
|
// Finding the last occurence is a bit harder
|
||||||
|
if (!isset($component->RRULE)) {
|
||||||
|
if (isset($component->DTEND)) {
|
||||||
|
$lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
|
||||||
|
} elseif (isset($component->DURATION)) {
|
||||||
|
$endDate = clone $component->DTSTART->getDateTime();
|
||||||
|
$endDate->add(VObject\DateTimeParser::parse($component->DURATION->value));
|
||||||
|
$lastOccurence = $endDate->getTimeStamp();
|
||||||
|
} elseif ($component->DTSTART->getDateType()===VObject\Property\DateTime::DATE) {
|
||||||
|
$endDate = clone $component->DTSTART->getDateTime();
|
||||||
|
$endDate->modify('+1 day');
|
||||||
|
$lastOccurence = $endDate->getTimeStamp();
|
||||||
|
} else {
|
||||||
|
$lastOccurence = $firstOccurence;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$it = new VObject\RecurrenceIterator($vObject, (string)$component->UID);
|
||||||
|
$maxDate = new DateTime(self::MAX_DATE);
|
||||||
|
if ($it->isInfinite()) {
|
||||||
|
$lastOccurence = $maxDate->getTimeStamp();
|
||||||
|
} else {
|
||||||
|
$end = $it->getDtEnd();
|
||||||
|
while($it->valid() && $end < $maxDate) {
|
||||||
|
$end = $it->getDtEnd();
|
||||||
|
$it->next();
|
||||||
|
|
||||||
|
}
|
||||||
|
$lastOccurence = $end->getTimeStamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'etag' => md5($calendarData),
|
||||||
|
'size' => strlen($calendarData),
|
||||||
|
'componentType' => $componentType,
|
||||||
|
'firstOccurence' => $firstOccurence,
|
||||||
|
'lastOccurence' => $lastOccurence,
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -392,5 +556,132 @@ class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a calendar-query on the contents of this calendar.
|
||||||
|
*
|
||||||
|
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||||
|
* calendar-query it is possible for a client to request a specific set of
|
||||||
|
* object, based on contents of iCalendar properties, date-ranges and
|
||||||
|
* iCalendar component types (VTODO, VEVENT).
|
||||||
|
*
|
||||||
|
* This method should just return a list of (relative) urls that match this
|
||||||
|
* query.
|
||||||
|
*
|
||||||
|
* The list of filters are specified as an array. The exact array is
|
||||||
|
* documented by Sabre_CalDAV_CalendarQueryParser.
|
||||||
|
*
|
||||||
|
* Note that it is extremely likely that getCalendarObject for every path
|
||||||
|
* returned from this method will be called almost immediately after. You
|
||||||
|
* may want to anticipate this to speed up these requests.
|
||||||
|
*
|
||||||
|
* This method provides a default implementation, which parses *all* the
|
||||||
|
* iCalendar objects in the specified calendar.
|
||||||
|
*
|
||||||
|
* This default may well be good enough for personal use, and calendars
|
||||||
|
* that aren't very large. But if you anticipate high usage, big calendars
|
||||||
|
* or high loads, you are strongly adviced to optimize certain paths.
|
||||||
|
*
|
||||||
|
* The best way to do so is override this method and to optimize
|
||||||
|
* specifically for 'common filters'.
|
||||||
|
*
|
||||||
|
* Requests that are extremely common are:
|
||||||
|
* * requests for just VEVENTS
|
||||||
|
* * requests for just VTODO
|
||||||
|
* * requests with a time-range-filter on a VEVENT.
|
||||||
|
*
|
||||||
|
* ..and combinations of these requests. It may not be worth it to try to
|
||||||
|
* handle every possible situation and just rely on the (relatively
|
||||||
|
* easy to use) CalendarQueryValidator to handle the rest.
|
||||||
|
*
|
||||||
|
* Note that especially time-range-filters may be difficult to parse. A
|
||||||
|
* time-range filter specified on a VEVENT must for instance also handle
|
||||||
|
* recurrence rules correctly.
|
||||||
|
* A good example of how to interprete all these filters can also simply
|
||||||
|
* be found in Sabre_CalDAV_CalendarQueryFilter. This class is as correct
|
||||||
|
* as possible, so it gives you a good idea on what type of stuff you need
|
||||||
|
* to think of.
|
||||||
|
*
|
||||||
|
* This specific implementation (for the PDO) backend optimizes filters on
|
||||||
|
* specific components, and VEVENT time-ranges.
|
||||||
|
*
|
||||||
|
* @param string $calendarId
|
||||||
|
* @param array $filters
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function calendarQuery($calendarId, array $filters) {
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
$validator = new Sabre_CalDAV_CalendarQueryValidator();
|
||||||
|
|
||||||
|
$componentType = null;
|
||||||
|
$requirePostFilter = true;
|
||||||
|
$timeRange = null;
|
||||||
|
|
||||||
|
// if no filters were specified, we don't need to filter after a query
|
||||||
|
if (!$filters['prop-filters'] && !$filters['comp-filters']) {
|
||||||
|
$requirePostFilter = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figuring out if there's a component filter
|
||||||
|
if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) {
|
||||||
|
$componentType = $filters['comp-filters'][0]['name'];
|
||||||
|
|
||||||
|
// Checking if we need post-filters
|
||||||
|
if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) {
|
||||||
|
$requirePostFilter = false;
|
||||||
|
}
|
||||||
|
// There was a time-range filter
|
||||||
|
if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) {
|
||||||
|
$timeRange = $filters['comp-filters'][0]['time-range'];
|
||||||
|
|
||||||
|
// If start time OR the end time is not specified, we can do a
|
||||||
|
// 100% accurate mysql query.
|
||||||
|
if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) {
|
||||||
|
$requirePostFilter = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($requirePostFilter) {
|
||||||
|
$query = "SELECT uri, calendardata FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid";
|
||||||
|
} else {
|
||||||
|
$query = "SELECT uri FROM ".$this->calendarObjectTableName." WHERE calendarid = :calendarid";
|
||||||
|
}
|
||||||
|
|
||||||
|
$values = array(
|
||||||
|
'calendarid' => $calendarId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($componentType) {
|
||||||
|
$query.=" AND componenttype = :componenttype";
|
||||||
|
$values['componenttype'] = $componentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($timeRange && $timeRange['start']) {
|
||||||
|
$query.=" AND lastoccurence > :startdate";
|
||||||
|
$values['startdate'] = $timeRange['start']->getTimeStamp();
|
||||||
|
}
|
||||||
|
if ($timeRange && $timeRange['end']) {
|
||||||
|
$query.=" AND firstoccurence < :enddate";
|
||||||
|
$values['enddate'] = $timeRange['end']->getTimeStamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare($query);
|
||||||
|
$stmt->execute($values);
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||||
|
if ($requirePostFilter) {
|
||||||
|
if (!$this->validateFilterForObject($row, $filters)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$result[] = $row['uri'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds support for sharing features to a CalDAV server.
|
||||||
|
*
|
||||||
|
* Note: This feature is experimental, and may change in between different
|
||||||
|
* SabreDAV versions.
|
||||||
|
*
|
||||||
|
* Early warning: Currently SabreDAV provides no implementation for this. This
|
||||||
|
* is, because in it's current state there is no elegant way to do this.
|
||||||
|
* The problem lies in the fact that a real CalDAV server with sharing support
|
||||||
|
* would first need email support (with invite notifications), and really also
|
||||||
|
* a browser-frontend that allows people to accept or reject these shares.
|
||||||
|
*
|
||||||
|
* In addition, the CalDAV backends are currently kept as independent as
|
||||||
|
* possible, and should not be aware of principals, email addresses or
|
||||||
|
* accounts.
|
||||||
|
*
|
||||||
|
* Adding an implementation for Sharing to standard-sabredav would contradict
|
||||||
|
* these goals, so for this reason this is currently not implemented, although
|
||||||
|
* it may very well in the future; but probably not before SabreDAV 2.0.
|
||||||
|
*
|
||||||
|
* The interface works however, so if you implement all this, and do it
|
||||||
|
* correctly sharing _will_ work. It's not particularly easy, and I _urge you_
|
||||||
|
* to make yourself acquainted with the following document first:
|
||||||
|
*
|
||||||
|
* https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing.txt
|
||||||
|
*
|
||||||
|
* An overview
|
||||||
|
* ===========
|
||||||
|
*
|
||||||
|
* Implementing this interface will allow a user to share his or her calendars
|
||||||
|
* to other users. Effectively, when a calendar is shared the calendar will
|
||||||
|
* show up in both the Sharer's and Sharee's calendar-home root.
|
||||||
|
* This interface adds a few methods that ensure that this happens, and there
|
||||||
|
* are also a number of new requirements in the base-class you must now follow.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* How it works
|
||||||
|
* ============
|
||||||
|
*
|
||||||
|
* When a user shares a calendar, the addShare() method will be called with a
|
||||||
|
* list of sharees that are now added, and a list of sharees that have been
|
||||||
|
* removed.
|
||||||
|
* Removal is instant, but when a sharee is added the sharee first gets a
|
||||||
|
* chance to accept or reject the invitation for a share.
|
||||||
|
*
|
||||||
|
* After a share is accepted, the calendar will be returned from
|
||||||
|
* getUserCalendars for both the sharer, and the sharee.
|
||||||
|
*
|
||||||
|
* If the sharee deletes the calendar, only their share gets deleted. When the
|
||||||
|
* owner deletes a calendar, it will be removed for everybody.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Notifications
|
||||||
|
* =============
|
||||||
|
*
|
||||||
|
* During all these sharing operations, a lot of notifications are sent back
|
||||||
|
* and forward.
|
||||||
|
*
|
||||||
|
* Whenever the list of sharees for a calendar has been changed (they have been
|
||||||
|
* added, removed or modified) all sharees should get a notification for this
|
||||||
|
* change.
|
||||||
|
* This notification is always represented by:
|
||||||
|
*
|
||||||
|
* Sabre_CalDAV_Notifications_Notification_Invite
|
||||||
|
*
|
||||||
|
* In the case of an invite, the sharee may reply with an 'accept' or
|
||||||
|
* 'decline'. These are always represented by:
|
||||||
|
*
|
||||||
|
* Sabre_CalDAV_Notifications_Notification_Invite
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Calendar access by sharees
|
||||||
|
* ==========================
|
||||||
|
*
|
||||||
|
* As mentioned earlier, shared calendars must now also be returned for
|
||||||
|
* getCalendarsForUser for sharees. A few things change though.
|
||||||
|
*
|
||||||
|
* The following properties must be specified:
|
||||||
|
*
|
||||||
|
* 1. {http://calendarserver.org/ns/}shared-url
|
||||||
|
*
|
||||||
|
* This property MUST contain the url to the original calendar, that is.. the
|
||||||
|
* path to the calendar from the owner.
|
||||||
|
*
|
||||||
|
* 2. {http://sabredav.org/ns}owner-principal
|
||||||
|
*
|
||||||
|
* This is a url to to the principal who is sharing the calendar.
|
||||||
|
*
|
||||||
|
* 3. {http://sabredav.org/ns}read-only
|
||||||
|
*
|
||||||
|
* This should be either 0 or 1, depending on if the user has read-only or
|
||||||
|
* read-write access to the calendar.
|
||||||
|
*
|
||||||
|
* Only when this is done, the calendar will correctly be marked as a calendar
|
||||||
|
* that's shared to him, thus allowing clients to display the correct interface
|
||||||
|
* and ACL enforcement.
|
||||||
|
*
|
||||||
|
* If a sharee deletes their calendar, only their instance of the calendar
|
||||||
|
* should be deleted, the original should still exists.
|
||||||
|
* Pretty much any 'dead' WebDAV properties on these shared calendars should be
|
||||||
|
* specific to a user. This means that if the displayname is changed by a
|
||||||
|
* sharee, the original is not affected. This is also true for:
|
||||||
|
* * The description
|
||||||
|
* * The color
|
||||||
|
* * The order
|
||||||
|
* * And any other dead properties.
|
||||||
|
*
|
||||||
|
* Properties like a ctag should not be different for multiple instances of the
|
||||||
|
* calendar.
|
||||||
|
*
|
||||||
|
* Lastly, objects *within* calendars should also have user-specific data. The
|
||||||
|
* two things that are user-specific are:
|
||||||
|
* * VALARM objects
|
||||||
|
* * The TRANSP property
|
||||||
|
*
|
||||||
|
* This _also_ implies that if a VALARM is deleted by a sharee for some event,
|
||||||
|
* this has no effect on the original VALARM.
|
||||||
|
*
|
||||||
|
* Understandably, the this last requirement is one of the hardest.
|
||||||
|
* Realisticly, I can see people ignoring this part of the spec, but that could
|
||||||
|
* cause a different set of issues.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Publishing
|
||||||
|
* ==========
|
||||||
|
*
|
||||||
|
* When a user publishes a url, the server should generate a 'publish url'.
|
||||||
|
* This is a read-only url, anybody can use to consume the calendar feed.
|
||||||
|
*
|
||||||
|
* Calendars are in one of two states:
|
||||||
|
* * published
|
||||||
|
* * unpublished
|
||||||
|
*
|
||||||
|
* If a calendar is published, the following property should be returned
|
||||||
|
* for each calendar in getCalendarsForPrincipal.
|
||||||
|
*
|
||||||
|
* {http://calendarserver.org/ns/}publish-url
|
||||||
|
*
|
||||||
|
* This element should contain a {DAV:}href element, which points to the
|
||||||
|
* public url that does not require authentication. Unlike every other href,
|
||||||
|
* this url must be absolute.
|
||||||
|
*
|
||||||
|
* Ideally, the following property is always returned
|
||||||
|
*
|
||||||
|
* {http://calendarserver.org/ns/}pre-publish-url
|
||||||
|
*
|
||||||
|
* This property should contain the url that the calendar _would_ have, if it
|
||||||
|
* were to be published. iCal uses this to display the url, before the user
|
||||||
|
* will actually publish it.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Selectively disabling publish or share feature
|
||||||
|
* ==============================================
|
||||||
|
*
|
||||||
|
* If Sabre_CalDAV_Property_AllowedSharingModes is returned from
|
||||||
|
* getCalendarsByUser, this allows the server to specify wether either sharing,
|
||||||
|
* or publishing is supported.
|
||||||
|
*
|
||||||
|
* This allows a client to determine in advance which features are available,
|
||||||
|
* and update the interface appropriately. If this property is not returned by
|
||||||
|
* the backend, the SharingPlugin automatically injects it and assumes both
|
||||||
|
* features are available.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
interface Sabre_CalDAV_Backend_SharingSupport extends Sabre_CalDAV_Backend_NotificationSupport {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the list of shares.
|
||||||
|
*
|
||||||
|
* The first array is a list of people that are to be added to the
|
||||||
|
* calendar.
|
||||||
|
*
|
||||||
|
* Every element in the add array has the following properties:
|
||||||
|
* * href - A url. Usually a mailto: address
|
||||||
|
* * commonName - Usually a first and last name, or false
|
||||||
|
* * summary - A description of the share, can also be false
|
||||||
|
* * readOnly - A boolean value
|
||||||
|
*
|
||||||
|
* Every element in the remove array is just the address string.
|
||||||
|
*
|
||||||
|
* Note that if the calendar is currently marked as 'not shared' by and
|
||||||
|
* this method is called, the calendar should be 'upgraded' to a shared
|
||||||
|
* calendar.
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param array $add
|
||||||
|
* @param array $remove
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function updateShares($calendarId, array $add, array $remove);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of people whom this calendar is shared with.
|
||||||
|
*
|
||||||
|
* Every element in this array should have the following properties:
|
||||||
|
* * href - Often a mailto: address
|
||||||
|
* * commonName - Optional, for example a first + last name
|
||||||
|
* * status - See the Sabre_CalDAV_SharingPlugin::STATUS_ constants.
|
||||||
|
* * readOnly - boolean
|
||||||
|
* * summary - Optional, a description for the share
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function getShares($calendarId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when a user replied to a request to share.
|
||||||
|
*
|
||||||
|
* If the user chose to accept the share, this method should return the
|
||||||
|
* newly created calendar url.
|
||||||
|
*
|
||||||
|
* @param string href The sharee who is replying (often a mailto: address)
|
||||||
|
* @param int status One of the SharingPlugin::STATUS_* constants
|
||||||
|
* @param string $calendarUri The url to the calendar thats being shared
|
||||||
|
* @param string $inReplyTo The unique id this message is a response to
|
||||||
|
* @param string $summary A description of the reply
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publishes a calendar
|
||||||
|
*
|
||||||
|
* @param mixed $calendarId
|
||||||
|
* @param bool $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function setPublishStatus($calendarId, $value);
|
||||||
|
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
|
||||||
/**
|
/**
|
||||||
* CalDAV backend
|
* CalDAV backend
|
||||||
*
|
*
|
||||||
* @var Sabre_CalDAV_Backend_Abstract
|
* @var Sabre_CalDAV_Backend_BackendInterface
|
||||||
*/
|
*/
|
||||||
protected $caldavBackend;
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
@ -39,16 +39,15 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
* @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
|
||||||
* @param array $calendarInfo
|
* @param array $calendarInfo
|
||||||
*/
|
*/
|
||||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $calendarInfo) {
|
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $calendarInfo) {
|
||||||
|
|
||||||
$this->caldavBackend = $caldavBackend;
|
$this->caldavBackend = $caldavBackend;
|
||||||
$this->principalBackend = $principalBackend;
|
$this->principalBackend = $principalBackend;
|
||||||
$this->calendarInfo = $calendarInfo;
|
$this->calendarInfo = $calendarInfo;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,9 +91,6 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
|
||||||
case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' :
|
case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' :
|
||||||
$response[$prop] = new Sabre_CalDAV_Property_SupportedCollationSet();
|
$response[$prop] = new Sabre_CalDAV_Property_SupportedCollationSet();
|
||||||
break;
|
break;
|
||||||
case '{DAV:}owner' :
|
|
||||||
$response[$prop] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF,$this->calendarInfo['principaluri']);
|
|
||||||
break;
|
|
||||||
default :
|
default :
|
||||||
if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop];
|
if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop];
|
||||||
break;
|
break;
|
||||||
|
@ -110,12 +106,21 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
|
||||||
* The contained calendar objects are for example Events or Todo's.
|
* The contained calendar objects are for example Events or Todo's.
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @return Sabre_DAV_ICalendarObject
|
* @return Sabre_CalDAV_ICalendarObject
|
||||||
*/
|
*/
|
||||||
public function getChild($name) {
|
public function getChild($name) {
|
||||||
|
|
||||||
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
|
$obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
|
||||||
if (!$obj) throw new Sabre_DAV_Exception_NotFound('Calendar object not found');
|
if (!$obj) throw new Sabre_DAV_Exception_NotFound('Calendar object not found');
|
||||||
|
|
||||||
|
$obj['acl'] = $this->getACL();
|
||||||
|
// Removing the irrelivant
|
||||||
|
foreach($obj['acl'] as $key=>$acl) {
|
||||||
|
if ($acl['privilege'] === '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy') {
|
||||||
|
unset($obj['acl'][$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
|
return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -130,6 +135,13 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
|
||||||
$objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
|
$objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
|
||||||
$children = array();
|
$children = array();
|
||||||
foreach($objs as $obj) {
|
foreach($objs as $obj) {
|
||||||
|
$obj['acl'] = $this->getACL();
|
||||||
|
// Removing the irrelivant
|
||||||
|
foreach($obj['acl'] as $key=>$acl) {
|
||||||
|
if ($acl['privilege'] === '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}read-free-busy') {
|
||||||
|
unset($obj['acl'][$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
$children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
|
$children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
|
||||||
}
|
}
|
||||||
return $children;
|
return $children;
|
||||||
|
@ -262,27 +274,27 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
|
||||||
return array(
|
return array(
|
||||||
array(
|
array(
|
||||||
'privilege' => '{DAV:}read',
|
'privilege' => '{DAV:}read',
|
||||||
'principal' => $this->calendarInfo['principaluri'],
|
'principal' => $this->getOwner(),
|
||||||
'protected' => true,
|
'protected' => true,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'privilege' => '{DAV:}write',
|
'privilege' => '{DAV:}write',
|
||||||
'principal' => $this->calendarInfo['principaluri'],
|
'principal' => $this->getOwner(),
|
||||||
'protected' => true,
|
'protected' => true,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'privilege' => '{DAV:}read',
|
'privilege' => '{DAV:}read',
|
||||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
|
'principal' => $this->getOwner() . '/calendar-proxy-write',
|
||||||
'protected' => true,
|
'protected' => true,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'privilege' => '{DAV:}write',
|
'privilege' => '{DAV:}write',
|
||||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
|
'principal' => $this->getOwner() . '/calendar-proxy-write',
|
||||||
'protected' => true,
|
'protected' => true,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'privilege' => '{DAV:}read',
|
'privilege' => '{DAV:}read',
|
||||||
'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
|
'principal' => $this->getOwner() . '/calendar-proxy-read',
|
||||||
'protected' => true,
|
'protected' => true,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
|
@ -340,4 +352,27 @@ class Sabre_CalDAV_Calendar implements Sabre_CalDAV_ICalendar, Sabre_DAV_IProper
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a calendar-query on the contents of this calendar.
|
||||||
|
*
|
||||||
|
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||||
|
* calendar-query it is possible for a client to request a specific set of
|
||||||
|
* object, based on contents of iCalendar properties, date-ranges and
|
||||||
|
* iCalendar component types (VTODO, VEVENT).
|
||||||
|
*
|
||||||
|
* This method should just return a list of (relative) urls that match this
|
||||||
|
* query.
|
||||||
|
*
|
||||||
|
* The list of filters are specified as an array. The exact array is
|
||||||
|
* documented by Sabre_CalDAV_CalendarQueryParser.
|
||||||
|
*
|
||||||
|
* @param array $filters
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function calendarQuery(array $filters) {
|
||||||
|
|
||||||
|
return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV_ICalendarObject, Sabre_DAVACL_IACL {
|
class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV_ICalendarObject, Sabre_DAVACL_IACL {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sabre_CalDAV_Backend_Abstract
|
* Sabre_CalDAV_Backend_BackendInterface
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
|
@ -35,11 +35,11 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
* @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
|
||||||
* @param array $calendarInfo
|
* @param array $calendarInfo
|
||||||
* @param array $objectData
|
* @param array $objectData
|
||||||
*/
|
*/
|
||||||
public function __construct(Sabre_CalDAV_Backend_Abstract $caldavBackend,array $calendarInfo,array $objectData) {
|
public function __construct(Sabre_CalDAV_Backend_BackendInterface $caldavBackend,array $calendarInfo,array $objectData) {
|
||||||
|
|
||||||
$this->caldavBackend = $caldavBackend;
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
|
||||||
|
@ -85,8 +85,8 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV
|
||||||
/**
|
/**
|
||||||
* Updates the ICalendar-formatted object
|
* Updates the ICalendar-formatted object
|
||||||
*
|
*
|
||||||
* @param string $calendarData
|
* @param string|resource $calendarData
|
||||||
* @return void
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function put($calendarData) {
|
public function put($calendarData) {
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV
|
||||||
*/
|
*/
|
||||||
public function getContentType() {
|
public function getContentType() {
|
||||||
|
|
||||||
return 'text/calendar';
|
return 'text/calendar; charset=utf-8';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV
|
||||||
/**
|
/**
|
||||||
* Returns the last modification date as a unix timestamp
|
* Returns the last modification date as a unix timestamp
|
||||||
*
|
*
|
||||||
* @return time
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function getLastModified() {
|
public function getLastModified() {
|
||||||
|
|
||||||
|
@ -206,6 +206,12 @@ class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_CalDAV
|
||||||
*/
|
*/
|
||||||
public function getACL() {
|
public function getACL() {
|
||||||
|
|
||||||
|
// An alternative acl may be specified in the object data.
|
||||||
|
if (isset($this->objectData['acl'])) {
|
||||||
|
return $this->objectData['acl'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default ACL
|
||||||
return array(
|
return array(
|
||||||
array(
|
array(
|
||||||
'privilege' => '{DAV:}read',
|
'privilege' => '{DAV:}read',
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the calendar-query report request body.
|
* Parses the calendar-query report request body.
|
||||||
*
|
*
|
||||||
|
@ -68,7 +70,7 @@ class Sabre_CalDAV_CalendarQueryParser {
|
||||||
|
|
||||||
$this->xpath = new DOMXPath($dom);
|
$this->xpath = new DOMXPath($dom);
|
||||||
$this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
|
$this->xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
|
||||||
$this->xpath->registerNameSpace('dav','urn:DAV');
|
$this->xpath->registerNameSpace('dav','DAV:');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,12 +243,12 @@ class Sabre_CalDAV_CalendarQueryParser {
|
||||||
$timeRangeNode = $timeRangeNodes->item(0);
|
$timeRangeNode = $timeRangeNodes->item(0);
|
||||||
|
|
||||||
if ($start = $timeRangeNode->getAttribute('start')) {
|
if ($start = $timeRangeNode->getAttribute('start')) {
|
||||||
$start = Sabre_VObject_DateTimeParser::parseDateTime($start);
|
$start = VObject\DateTimeParser::parseDateTime($start);
|
||||||
} else {
|
} else {
|
||||||
$start = null;
|
$start = null;
|
||||||
}
|
}
|
||||||
if ($end = $timeRangeNode->getAttribute('end')) {
|
if ($end = $timeRangeNode->getAttribute('end')) {
|
||||||
$end = Sabre_VObject_DateTimeParser::parseDateTime($end);
|
$end = VObject\DateTimeParser::parseDateTime($end);
|
||||||
} else {
|
} else {
|
||||||
$end = null;
|
$end = null;
|
||||||
}
|
}
|
||||||
|
@ -274,13 +276,13 @@ class Sabre_CalDAV_CalendarQueryParser {
|
||||||
if(!$start) {
|
if(!$start) {
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element');
|
throw new Sabre_DAV_Exception_BadRequest('The "start" attribute is required for the CALDAV:expand element');
|
||||||
}
|
}
|
||||||
$start = Sabre_VObject_DateTimeParser::parseDateTime($start);
|
$start = VObject\DateTimeParser::parseDateTime($start);
|
||||||
|
|
||||||
$end = $parentNode->getAttribute('end');
|
$end = $parentNode->getAttribute('end');
|
||||||
if(!$end) {
|
if(!$end) {
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element');
|
throw new Sabre_DAV_Exception_BadRequest('The "end" attribute is required for the CALDAV:expand element');
|
||||||
}
|
}
|
||||||
$end = Sabre_VObject_DateTimeParser::parseDateTime($end);
|
$end = VObject\DateTimeParser::parseDateTime($end);
|
||||||
|
|
||||||
if ($end <= $start) {
|
if ($end <= $start) {
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');
|
throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CalendarQuery Validator
|
* CalendarQuery Validator
|
||||||
*
|
*
|
||||||
|
@ -22,11 +24,11 @@ class Sabre_CalDAV_CalendarQueryValidator {
|
||||||
*
|
*
|
||||||
* The list of filters must be formatted as parsed by Sabre_CalDAV_CalendarQueryParser
|
* The list of filters must be formatted as parsed by Sabre_CalDAV_CalendarQueryParser
|
||||||
*
|
*
|
||||||
* @param Sabre_VObject_Component $vObject
|
* @param VObject\Component $vObject
|
||||||
* @param array $filters
|
* @param array $filters
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function validate(Sabre_VObject_Component $vObject,array $filters) {
|
public function validate(VObject\Component $vObject,array $filters) {
|
||||||
|
|
||||||
// The top level object is always a component filter.
|
// The top level object is always a component filter.
|
||||||
// We'll parse it manually, as it's pretty simple.
|
// We'll parse it manually, as it's pretty simple.
|
||||||
|
@ -48,11 +50,11 @@ class Sabre_CalDAV_CalendarQueryValidator {
|
||||||
* component we're checking should be specified, not the component to check
|
* component we're checking should be specified, not the component to check
|
||||||
* itself.
|
* itself.
|
||||||
*
|
*
|
||||||
* @param Sabre_VObject_Component $parent
|
* @param VObject\Component $parent
|
||||||
* @param array $filters
|
* @param array $filters
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function validateCompFilters(Sabre_VObject_Component $parent, array $filters) {
|
protected function validateCompFilters(VObject\Component $parent, array $filters) {
|
||||||
|
|
||||||
foreach($filters as $filter) {
|
foreach($filters as $filter) {
|
||||||
|
|
||||||
|
@ -117,11 +119,11 @@ class Sabre_CalDAV_CalendarQueryValidator {
|
||||||
* property we're checking should be specified, not the property to check
|
* property we're checking should be specified, not the property to check
|
||||||
* itself.
|
* itself.
|
||||||
*
|
*
|
||||||
* @param Sabre_VObject_Component $parent
|
* @param VObject\Component $parent
|
||||||
* @param array $filters
|
* @param array $filters
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function validatePropFilters(Sabre_VObject_Component $parent, array $filters) {
|
protected function validatePropFilters(VObject\Component $parent, array $filters) {
|
||||||
|
|
||||||
foreach($filters as $filter) {
|
foreach($filters as $filter) {
|
||||||
|
|
||||||
|
@ -187,11 +189,11 @@ class Sabre_CalDAV_CalendarQueryValidator {
|
||||||
* parameter we're checking should be specified, not the parameter to check
|
* parameter we're checking should be specified, not the parameter to check
|
||||||
* itself.
|
* itself.
|
||||||
*
|
*
|
||||||
* @param Sabre_VObject_Property $parent
|
* @param VObject\Property $parent
|
||||||
* @param array $filters
|
* @param array $filters
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function validateParamFilters(Sabre_VObject_Property $parent, array $filters) {
|
protected function validateParamFilters(VObject\Property $parent, array $filters) {
|
||||||
|
|
||||||
foreach($filters as $filter) {
|
foreach($filters as $filter) {
|
||||||
|
|
||||||
|
@ -243,11 +245,11 @@ class Sabre_CalDAV_CalendarQueryValidator {
|
||||||
* A single text-match should be specified as well as the specific property
|
* A single text-match should be specified as well as the specific property
|
||||||
* or parameter we need to validate.
|
* or parameter we need to validate.
|
||||||
*
|
*
|
||||||
* @param Sabre_VObject_Node $parent
|
* @param VObject\Node $parent
|
||||||
* @param array $textMatch
|
* @param array $textMatch
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function validateTextMatch(Sabre_VObject_Node $parent, array $textMatch) {
|
protected function validateTextMatch(VObject\Node $parent, array $textMatch) {
|
||||||
|
|
||||||
$value = (string)$parent;
|
$value = (string)$parent;
|
||||||
|
|
||||||
|
@ -263,12 +265,12 @@ class Sabre_CalDAV_CalendarQueryValidator {
|
||||||
* This is all based on the rules specified in rfc4791, which are quite
|
* This is all based on the rules specified in rfc4791, which are quite
|
||||||
* complex.
|
* complex.
|
||||||
*
|
*
|
||||||
* @param Sabre_VObject_Node $component
|
* @param VObject\Node $component
|
||||||
* @param DateTime $start
|
* @param DateTime $start
|
||||||
* @param DateTime $end
|
* @param DateTime $end
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function validateTimeRange(Sabre_VObject_Node $component, $start, $end) {
|
protected function validateTimeRange(VObject\Node $component, $start, $end) {
|
||||||
|
|
||||||
if (is_null($start)) {
|
if (is_null($start)) {
|
||||||
$start = new DateTime('1900-01-01');
|
$start = new DateTime('1900-01-01');
|
||||||
|
@ -296,7 +298,7 @@ class Sabre_CalDAV_CalendarQueryValidator {
|
||||||
if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
|
if ($component->parent->name === 'VEVENT' && $component->parent->RRULE) {
|
||||||
|
|
||||||
// Fire up the iterator!
|
// Fire up the iterator!
|
||||||
$it = new Sabre_VObject_RecurrenceIterator($component->parent->parent, (string)$component->parent->UID);
|
$it = new VObject\RecurrenceIterator($component->parent->parent, (string)$component->parent->UID);
|
||||||
while($it->valid()) {
|
while($it->valid()) {
|
||||||
$expandedEvent = $it->getEventObject();
|
$expandedEvent = $it->getEventObject();
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Users collection
|
* Calendars collection
|
||||||
*
|
*
|
||||||
* This object is responsible for generating a collection of users.
|
* This object is responsible for generating a list of calendar-homes for each
|
||||||
|
* user.
|
||||||
*
|
*
|
||||||
* @package Sabre
|
* @package Sabre
|
||||||
* @subpackage CalDAV
|
* @subpackage CalDAV
|
||||||
|
@ -16,7 +17,7 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollec
|
||||||
/**
|
/**
|
||||||
* CalDAV backend
|
* CalDAV backend
|
||||||
*
|
*
|
||||||
* @var Sabre_CalDAV_Backend_Abstract
|
* @var Sabre_CalDAV_Backend_BackendInterface
|
||||||
*/
|
*/
|
||||||
protected $caldavBackend;
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
@ -32,10 +33,10 @@ class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollec
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
* @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
|
||||||
* @param string $principalPrefix
|
* @param string $principalPrefix
|
||||||
*/
|
*/
|
||||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_Abstract $caldavBackend, $principalPrefix = 'principals') {
|
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $principalPrefix = 'principals') {
|
||||||
|
|
||||||
parent::__construct($principalBackend, $principalPrefix);
|
parent::__construct($principalBackend, $principalPrefix);
|
||||||
$this->caldavBackend = $caldavBackend;
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sabre_CalDAV_Exception_InvalidComponentType
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_Exception_InvalidComponentType extends Sabre_DAV_Exception_Forbidden {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds in extra information in the xml response.
|
||||||
|
*
|
||||||
|
* This method adds the {CALDAV:}supported-calendar-component as defined in rfc4791
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $errorNode
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
|
||||||
|
|
||||||
|
$doc = $errorNode->ownerDocument;
|
||||||
|
|
||||||
|
$np = $doc->createElementNS(Sabre_CalDAV_Plugin::NS_CALDAV,'cal:supported-calendar-component');
|
||||||
|
$errorNode->appendChild($np);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ICS Exporter
|
* ICS Exporter
|
||||||
*
|
*
|
||||||
|
@ -82,7 +84,7 @@ class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin {
|
||||||
*/
|
*/
|
||||||
public function generateICS(array $nodes) {
|
public function generateICS(array $nodes) {
|
||||||
|
|
||||||
$calendar = new Sabre_VObject_Component('vcalendar');
|
$calendar = new VObject\Component('vcalendar');
|
||||||
$calendar->version = '2.0';
|
$calendar->version = '2.0';
|
||||||
if (Sabre_DAV_Server::$exposeVersion) {
|
if (Sabre_DAV_Server::$exposeVersion) {
|
||||||
$calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN';
|
$calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN';
|
||||||
|
@ -103,7 +105,7 @@ class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin {
|
||||||
}
|
}
|
||||||
$nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'];
|
$nodeData = $node[200]['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'];
|
||||||
|
|
||||||
$nodeComp = Sabre_VObject_Reader::read($nodeData);
|
$nodeComp = VObject\Reader::read($nodeData);
|
||||||
|
|
||||||
foreach($nodeComp->children() as $child) {
|
foreach($nodeComp->children() as $child) {
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,23 @@
|
||||||
*/
|
*/
|
||||||
interface Sabre_CalDAV_ICalendar extends Sabre_DAV_ICollection {
|
interface Sabre_CalDAV_ICalendar extends Sabre_DAV_ICollection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a calendar-query on the contents of this calendar.
|
||||||
|
*
|
||||||
|
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||||
|
* calendar-query it is possible for a client to request a specific set of
|
||||||
|
* object, based on contents of iCalendar properties, date-ranges and
|
||||||
|
* iCalendar component types (VTODO, VEVENT).
|
||||||
|
*
|
||||||
|
* This method should just return a list of (relative) urls that match this
|
||||||
|
* query.
|
||||||
|
*
|
||||||
|
* The list of filters are specified as an array. The exact array is
|
||||||
|
* documented by Sabre_CalDAV_CalendarQueryParser.
|
||||||
|
*
|
||||||
|
* @param array $filters
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function calendarQuery(array $filters);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface represents a Calendar that can be shared with other users.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
interface Sabre_CalDAV_IShareableCalendar extends Sabre_CalDAV_ICalendar {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the list of shares.
|
||||||
|
*
|
||||||
|
* The first array is a list of people that are to be added to the
|
||||||
|
* calendar.
|
||||||
|
*
|
||||||
|
* Every element in the add array has the following properties:
|
||||||
|
* * href - A url. Usually a mailto: address
|
||||||
|
* * commonName - Usually a first and last name, or false
|
||||||
|
* * summary - A description of the share, can also be false
|
||||||
|
* * readOnly - A boolean value
|
||||||
|
*
|
||||||
|
* Every element in the remove array is just the address string.
|
||||||
|
*
|
||||||
|
* @param array $add
|
||||||
|
* @param array $remove
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function updateShares(array $add, array $remove);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of people whom this calendar is shared with.
|
||||||
|
*
|
||||||
|
* Every element in this array should have the following properties:
|
||||||
|
* * href - Often a mailto: address
|
||||||
|
* * commonName - Optional, for example a first + last name
|
||||||
|
* * status - See the Sabre_CalDAV_SharingPlugin::STATUS_ constants.
|
||||||
|
* * readOnly - boolean
|
||||||
|
* * summary - Optional, a description for the share
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function getShares();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface represents a Calendar that is shared by a different user.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
interface Sabre_CalDAV_ISharedCalendar extends Sabre_CalDAV_ICalendar {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should return the url of the owners' copy of the shared
|
||||||
|
* calendar.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function getSharedUrl();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node represents a list of notifications.
|
||||||
|
*
|
||||||
|
* It provides no additional functionality, but you must implement this
|
||||||
|
* interface to allow the Notifications plugin to mark the collection
|
||||||
|
* as a notifications collection.
|
||||||
|
*
|
||||||
|
* This collection should only return Sabre_CalDAV_Notifications_INode nodes as
|
||||||
|
* its children.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_Notifications_Collection extends Sabre_DAV_Collection implements Sabre_CalDAV_Notifications_ICollection, Sabre_DAVACL_IACL {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The notification backend
|
||||||
|
*
|
||||||
|
* @var Sabre_CalDAV_Backend_NotificationSupport
|
||||||
|
*/
|
||||||
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Principal uri
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $principalUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param Sabre_CalDAV_Backend_NotificationSupport $caldavBackend
|
||||||
|
* @param string $principalUri
|
||||||
|
*/
|
||||||
|
public function __construct(Sabre_CalDAV_Backend_NotificationSupport $caldavBackend, $principalUri) {
|
||||||
|
|
||||||
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
$this->principalUri = $principalUri;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all notifications for a principal
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getChildren() {
|
||||||
|
|
||||||
|
$children = array();
|
||||||
|
$notifications = $this->caldavBackend->getNotificationsForPrincipal($this->principalUri);
|
||||||
|
|
||||||
|
foreach($notifications as $notification) {
|
||||||
|
|
||||||
|
$children[] = new Sabre_CalDAV_Notifications_Node(
|
||||||
|
$this->caldavBackend,
|
||||||
|
$this->principalUri,
|
||||||
|
$notification
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $children;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this object
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName() {
|
||||||
|
|
||||||
|
return 'notifications';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner principal
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getOwner() {
|
||||||
|
|
||||||
|
return $this->principalUri;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a group principal
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getGroup() {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL() {
|
||||||
|
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'protected' => true,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'protected' => true,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the ACL
|
||||||
|
*
|
||||||
|
* This method will receive a list of new ACE's as an array argument.
|
||||||
|
*
|
||||||
|
* @param array $acl
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setACL(array $acl) {
|
||||||
|
|
||||||
|
throw new Sabre_DAV_Exception_NotImplemented('Updating ACLs is not implemented here');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of supported privileges for this node.
|
||||||
|
*
|
||||||
|
* The returned data structure is a list of nested privileges.
|
||||||
|
* See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
|
||||||
|
* standard structure.
|
||||||
|
*
|
||||||
|
* If null is returned from this method, the default privilege set is used,
|
||||||
|
* which is fine for most common usecases.
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getSupportedPrivilegeSet() {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node represents a list of notifications.
|
||||||
|
*
|
||||||
|
* It provides no additional functionality, but you must implement this
|
||||||
|
* interface to allow the Notifications plugin to mark the collection
|
||||||
|
* as a notifications collection.
|
||||||
|
*
|
||||||
|
* This collection should only return Sabre_CalDAV_Notifications_INode nodes as
|
||||||
|
* its children.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
interface Sabre_CalDAV_Notifications_ICollection extends Sabre_DAV_ICollection {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node represents a single notification.
|
||||||
|
*
|
||||||
|
* The signature is mostly identical to that of Sabre_DAV_IFile, but the get() method
|
||||||
|
* MUST return an xml document that matches the requirements of the
|
||||||
|
* 'caldav-notifications.txt' spec.
|
||||||
|
*
|
||||||
|
* For a complete example, check out the Notification class, which contains
|
||||||
|
* some helper functions.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
interface Sabre_CalDAV_Notifications_INode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method must return an xml element, using the
|
||||||
|
* Sabre_CalDAV_Notifications_INotificationType classes.
|
||||||
|
*
|
||||||
|
* @return Sabre_DAVNotification_INotificationType
|
||||||
|
*/
|
||||||
|
function getNotificationType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the etag for the notification.
|
||||||
|
*
|
||||||
|
* The etag must be surrounded by litteral double-quotes.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function getETag();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface reflects a single notification type.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
interface Sabre_CalDAV_Notifications_INotificationType extends Sabre_DAV_PropertyInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method serializes the entire notification, as it is used in the
|
||||||
|
* response body.
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function serializeBody(Sabre_DAV_Server $server, \DOMElement $node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a unique id for this notification
|
||||||
|
*
|
||||||
|
* This is just the base url. This should generally be some kind of unique
|
||||||
|
* id.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function getId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ETag for this notification.
|
||||||
|
*
|
||||||
|
* The ETag must be surrounded by literal double-quotes.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
function getETag();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This node represents a single notification.
|
||||||
|
*
|
||||||
|
* The signature is mostly identical to that of Sabre_DAV_IFile, but the get() method
|
||||||
|
* MUST return an xml document that matches the requirements of the
|
||||||
|
* 'caldav-notifications.txt' spec.
|
||||||
|
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_Notifications_Node extends Sabre_DAV_File implements Sabre_CalDAV_Notifications_INode, Sabre_DAVACL_IACL {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The notification backend
|
||||||
|
*
|
||||||
|
* @var Sabre_CalDAV_Backend_NotificationSupport
|
||||||
|
*/
|
||||||
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual notification
|
||||||
|
*
|
||||||
|
* @var Sabre_CalDAV_Notifications_INotificationType
|
||||||
|
*/
|
||||||
|
protected $notification;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Owner principal of the notification
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $principalUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param Sabre_CalDAV_Backend_NotificationSupport $caldavBackend
|
||||||
|
* @param string $principalUri
|
||||||
|
* @param Sabre_CalDAV_Notifications_INotificationType $notification
|
||||||
|
*/
|
||||||
|
public function __construct(Sabre_CalDAV_Backend_NotificationSupport $caldavBackend, $principalUri, Sabre_CalDAV_Notifications_INotificationType $notification) {
|
||||||
|
|
||||||
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
$this->principalUri = $principalUri;
|
||||||
|
$this->notification = $notification;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path name for this notification
|
||||||
|
*
|
||||||
|
* @return id
|
||||||
|
*/
|
||||||
|
public function getName() {
|
||||||
|
|
||||||
|
return $this->notification->getId() . '.xml';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the etag for the notification.
|
||||||
|
*
|
||||||
|
* The etag must be surrounded by litteral double-quotes.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getETag() {
|
||||||
|
|
||||||
|
return $this->notification->getETag();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method must return an xml element, using the
|
||||||
|
* Sabre_CalDAV_Notifications_INotificationType classes.
|
||||||
|
*
|
||||||
|
* @return Sabre_DAVNotification_INotificationType
|
||||||
|
*/
|
||||||
|
public function getNotificationType() {
|
||||||
|
|
||||||
|
return $this->notification;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes this notification
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function delete() {
|
||||||
|
|
||||||
|
$this->caldavBackend->deleteNotification($this->getOwner(), $this->notification);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner principal
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getOwner() {
|
||||||
|
|
||||||
|
return $this->principalUri;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a group principal
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getGroup() {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL() {
|
||||||
|
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'protected' => true,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'protected' => true,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the ACL
|
||||||
|
*
|
||||||
|
* This method will receive a list of new ACE's as an array argument.
|
||||||
|
*
|
||||||
|
* @param array $acl
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setACL(array $acl) {
|
||||||
|
|
||||||
|
throw new Sabre_DAV_Exception_NotImplemented('Updating ACLs is not implemented here');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of supported privileges for this node.
|
||||||
|
*
|
||||||
|
* The returned data structure is a list of nested privileges.
|
||||||
|
* See Sabre_DAVACL_Plugin::getDefaultSupportedPrivilegeSet for a simple
|
||||||
|
* standard structure.
|
||||||
|
*
|
||||||
|
* If null is returned from this method, the default privilege set is used,
|
||||||
|
* which is fine for most common usecases.
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getSupportedPrivilegeSet() {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,276 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Sabre_CalDAV_SharingPlugin as SharingPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents the cs:invite-notification notification element.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_Notifications_Notification_Invite extends Sabre_DAV_Property implements Sabre_CalDAV_Notifications_INotificationType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unique id for the message
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp of the notification
|
||||||
|
*
|
||||||
|
* @var DateTime
|
||||||
|
*/
|
||||||
|
protected $dtStamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A url to the recipient of the notification. This can be an email
|
||||||
|
* address (mailto:), or a principal url.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $href;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of message, see the SharingPlugin::STATUS_* constants.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if access to a calendar is read-only.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $readOnly;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A url to the shared calendar.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $hostUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Url to the sharer of the calendar
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $organizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the sharer.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $commonName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A description of the share request
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $summary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Etag for the notification
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $etag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of supported components
|
||||||
|
*
|
||||||
|
* @var Sabre_CalDAV_Property_SupportedCalendarComponentSet
|
||||||
|
*/
|
||||||
|
protected $supportedComponents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the Invite notification.
|
||||||
|
*
|
||||||
|
* This constructor receives an array with the following elements:
|
||||||
|
*
|
||||||
|
* * id - A unique id
|
||||||
|
* * etag - The etag
|
||||||
|
* * dtStamp - A DateTime object with a timestamp for the notification.
|
||||||
|
* * type - The type of notification, see SharingPlugin::STATUS_*
|
||||||
|
* constants for details.
|
||||||
|
* * readOnly - This must be set to true, if this is an invite for
|
||||||
|
* read-only access to a calendar.
|
||||||
|
* * hostUrl - A url to the shared calendar.
|
||||||
|
* * organizer - Url to the sharer principal.
|
||||||
|
* * commonName - The real name of the sharer (optional).
|
||||||
|
* * summary - Description of the share, can be the same as the
|
||||||
|
* calendar, but may also be modified (optional).
|
||||||
|
* * supportedComponents - An instance of
|
||||||
|
* Sabre_CalDAV_Property_SupportedCalendarComponentSet.
|
||||||
|
* This allows the client to determine which components
|
||||||
|
* will be supported in the shared calendar. This is
|
||||||
|
* also optional.
|
||||||
|
*
|
||||||
|
* @param array $values All the options
|
||||||
|
*/
|
||||||
|
public function __construct(array $values) {
|
||||||
|
|
||||||
|
$required = array(
|
||||||
|
'id',
|
||||||
|
'etag',
|
||||||
|
'href',
|
||||||
|
'dtStamp',
|
||||||
|
'type',
|
||||||
|
'readOnly',
|
||||||
|
'hostUrl',
|
||||||
|
'organizer',
|
||||||
|
);
|
||||||
|
foreach($required as $item) {
|
||||||
|
if (!isset($values[$item])) {
|
||||||
|
throw new InvalidArgumentException($item . ' is a required constructor option');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($values as $key=>$value) {
|
||||||
|
if (!property_exists($this, $key)) {
|
||||||
|
throw new InvalidArgumentException('Unknown option: ' . $key);
|
||||||
|
}
|
||||||
|
$this->$key = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the notification as a single property.
|
||||||
|
*
|
||||||
|
* You should usually just encode the single top-level element of the
|
||||||
|
* notification.
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function serialize(Sabre_DAV_Server $server, \DOMElement $node) {
|
||||||
|
|
||||||
|
$prop = $node->ownerDocument->createElement('cs:invite-notification');
|
||||||
|
$node->appendChild($prop);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method serializes the entire notification, as it is used in the
|
||||||
|
* response body.
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function serializeBody(Sabre_DAV_Server $server, \DOMElement $node) {
|
||||||
|
|
||||||
|
$doc = $node->ownerDocument;
|
||||||
|
|
||||||
|
$dt = $doc->createElement('cs:dtstamp');
|
||||||
|
$this->dtStamp->setTimezone(new \DateTimezone('GMT'));
|
||||||
|
$dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z')));
|
||||||
|
$node->appendChild($dt);
|
||||||
|
|
||||||
|
$prop = $doc->createElement('cs:invite-notification');
|
||||||
|
$node->appendChild($prop);
|
||||||
|
|
||||||
|
$uid = $doc->createElement('cs:uid');
|
||||||
|
$uid->appendChild( $doc->createTextNode($this->id) );
|
||||||
|
$prop->appendChild($uid);
|
||||||
|
|
||||||
|
$href = $doc->createElement('d:href');
|
||||||
|
$href->appendChild( $doc->createTextNode( $this->href ) );
|
||||||
|
$prop->appendChild($href);
|
||||||
|
|
||||||
|
$nodeName = null;
|
||||||
|
switch($this->type) {
|
||||||
|
|
||||||
|
case SharingPlugin::STATUS_ACCEPTED :
|
||||||
|
$nodeName = 'cs:invite-accepted';
|
||||||
|
break;
|
||||||
|
case SharingPlugin::STATUS_DECLINED :
|
||||||
|
$nodeName = 'cs:invite-declined';
|
||||||
|
break;
|
||||||
|
case SharingPlugin::STATUS_DELETED :
|
||||||
|
$nodeName = 'cs:invite-deleted';
|
||||||
|
break;
|
||||||
|
case SharingPlugin::STATUS_NORESPONSE :
|
||||||
|
$nodeName = 'cs:invite-noresponse';
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
$prop->appendChild(
|
||||||
|
$doc->createElement($nodeName)
|
||||||
|
);
|
||||||
|
$hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl);
|
||||||
|
$hostUrl = $doc->createElement('cs:hosturl');
|
||||||
|
$hostUrl->appendChild($hostHref);
|
||||||
|
$prop->appendChild($hostUrl);
|
||||||
|
|
||||||
|
$access = $doc->createElement('cs:access');
|
||||||
|
if ($this->readOnly) {
|
||||||
|
$access->appendChild($doc->createElement('cs:read'));
|
||||||
|
} else {
|
||||||
|
$access->appendChild($doc->createElement('cs:read-write'));
|
||||||
|
}
|
||||||
|
$prop->appendChild($access);
|
||||||
|
|
||||||
|
$organizerHref = $doc->createElement('d:href', $server->getBaseUri() . $this->organizer);
|
||||||
|
$organizerUrl = $doc->createElement('cs:organizer');
|
||||||
|
if ($this->commonName) {
|
||||||
|
$commonName = $doc->createElement('cs:common-name');
|
||||||
|
$commonName->appendChild($doc->createTextNode($this->commonName));
|
||||||
|
$organizerUrl->appendChild($commonName);
|
||||||
|
}
|
||||||
|
$organizerUrl->appendChild($organizerHref);
|
||||||
|
$prop->appendChild($organizerUrl);
|
||||||
|
|
||||||
|
if ($this->summary) {
|
||||||
|
$summary = $doc->createElement('cs:summary');
|
||||||
|
$summary->appendChild($doc->createTextNode($this->summary));
|
||||||
|
$prop->appendChild($summary);
|
||||||
|
}
|
||||||
|
if ($this->supportedComponents) {
|
||||||
|
|
||||||
|
$xcomp = $doc->createElement('cal:supported-calendar-component-set');
|
||||||
|
$this->supportedComponents->serialize($server, $xcomp);
|
||||||
|
$prop->appendChild($xcomp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a unique id for this notification
|
||||||
|
*
|
||||||
|
* This is just the base url. This should generally be some kind of unique
|
||||||
|
* id.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getId() {
|
||||||
|
|
||||||
|
return $this->id;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ETag for this notification.
|
||||||
|
*
|
||||||
|
* The ETag must be surrounded by literal double-quotes.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getETag() {
|
||||||
|
|
||||||
|
return $this->etag;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Sabre_CalDAV_SharingPlugin as SharingPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents the cs:invite-reply notification element.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_Notifications_Notification_InviteReply extends Sabre_DAV_Property implements Sabre_CalDAV_Notifications_INotificationType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unique id for the message
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp of the notification
|
||||||
|
*
|
||||||
|
* @var DateTime
|
||||||
|
*/
|
||||||
|
protected $dtStamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique id of the notification this was a reply to.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $inReplyTo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A url to the recipient of the original (!) notification.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $href;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of message, see the SharingPlugin::STATUS_ constants.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A url to the shared calendar.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $hostUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A description of the share request
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $summary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification Etag
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $etag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the Invite Reply Notification.
|
||||||
|
*
|
||||||
|
* This constructor receives an array with the following elements:
|
||||||
|
*
|
||||||
|
* * id - A unique id
|
||||||
|
* * etag - The etag
|
||||||
|
* * dtStamp - A DateTime object with a timestamp for the notification.
|
||||||
|
* * inReplyTo - This should refer to the 'id' of the notification
|
||||||
|
* this is a reply to.
|
||||||
|
* * type - The type of notification, see SharingPlugin::STATUS_*
|
||||||
|
* constants for details.
|
||||||
|
* * hostUrl - A url to the shared calendar.
|
||||||
|
* * summary - Description of the share, can be the same as the
|
||||||
|
* calendar, but may also be modified (optional).
|
||||||
|
*/
|
||||||
|
public function __construct(array $values) {
|
||||||
|
|
||||||
|
$required = array(
|
||||||
|
'id',
|
||||||
|
'etag',
|
||||||
|
'href',
|
||||||
|
'dtStamp',
|
||||||
|
'inReplyTo',
|
||||||
|
'type',
|
||||||
|
'hostUrl',
|
||||||
|
);
|
||||||
|
foreach($required as $item) {
|
||||||
|
if (!isset($values[$item])) {
|
||||||
|
throw new InvalidArgumentException($item . ' is a required constructor option');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($values as $key=>$value) {
|
||||||
|
if (!property_exists($this, $key)) {
|
||||||
|
throw new InvalidArgumentException('Unknown option: ' . $key);
|
||||||
|
}
|
||||||
|
$this->$key = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the notification as a single property.
|
||||||
|
*
|
||||||
|
* You should usually just encode the single top-level element of the
|
||||||
|
* notification.
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function serialize(Sabre_DAV_Server $server, \DOMElement $node) {
|
||||||
|
|
||||||
|
$prop = $node->ownerDocument->createElement('cs:invite-reply');
|
||||||
|
$node->appendChild($prop);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method serializes the entire notification, as it is used in the
|
||||||
|
* response body.
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function serializeBody(Sabre_DAV_Server $server, \DOMElement $node) {
|
||||||
|
|
||||||
|
$doc = $node->ownerDocument;
|
||||||
|
|
||||||
|
$dt = $doc->createElement('cs:dtstamp');
|
||||||
|
$this->dtStamp->setTimezone(new \DateTimezone('GMT'));
|
||||||
|
$dt->appendChild($doc->createTextNode($this->dtStamp->format('Ymd\\THis\\Z')));
|
||||||
|
$node->appendChild($dt);
|
||||||
|
|
||||||
|
$prop = $doc->createElement('cs:invite-reply');
|
||||||
|
$node->appendChild($prop);
|
||||||
|
|
||||||
|
$uid = $doc->createElement('cs:uid');
|
||||||
|
$uid->appendChild($doc->createTextNode($this->id));
|
||||||
|
$prop->appendChild($uid);
|
||||||
|
|
||||||
|
$inReplyTo = $doc->createElement('cs:in-reply-to');
|
||||||
|
$inReplyTo->appendChild( $doc->createTextNode($this->inReplyTo) );
|
||||||
|
$prop->appendChild($inReplyTo);
|
||||||
|
|
||||||
|
$href = $doc->createElement('d:href');
|
||||||
|
$href->appendChild( $doc->createTextNode($this->href) );
|
||||||
|
$prop->appendChild($href);
|
||||||
|
|
||||||
|
$nodeName = null;
|
||||||
|
switch($this->type) {
|
||||||
|
|
||||||
|
case SharingPlugin::STATUS_ACCEPTED :
|
||||||
|
$nodeName = 'cs:invite-accepted';
|
||||||
|
break;
|
||||||
|
case SharingPlugin::STATUS_DECLINED :
|
||||||
|
$nodeName = 'cs:invite-declined';
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
$prop->appendChild(
|
||||||
|
$doc->createElement($nodeName)
|
||||||
|
);
|
||||||
|
$hostHref = $doc->createElement('d:href', $server->getBaseUri() . $this->hostUrl);
|
||||||
|
$hostUrl = $doc->createElement('cs:hosturl');
|
||||||
|
$hostUrl->appendChild($hostHref);
|
||||||
|
$prop->appendChild($hostUrl);
|
||||||
|
|
||||||
|
if ($this->summary) {
|
||||||
|
$summary = $doc->createElement('cs:summary');
|
||||||
|
$summary->appendChild($doc->createTextNode($this->summary));
|
||||||
|
$prop->appendChild($summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a unique id for this notification
|
||||||
|
*
|
||||||
|
* This is just the base url. This should generally be some kind of unique
|
||||||
|
* id.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getId() {
|
||||||
|
|
||||||
|
return $this->id;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ETag for this notification.
|
||||||
|
*
|
||||||
|
* The ETag must be surrounded by literal double-quotes.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getETag() {
|
||||||
|
|
||||||
|
return $this->etag;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SystemStatus notification
|
||||||
|
*
|
||||||
|
* This notification can be used to indicate to the user that the system is
|
||||||
|
* down.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_Notifications_Notification_SystemStatus extends Sabre_DAV_Property implements Sabre_CalDAV_Notifications_INotificationType {
|
||||||
|
|
||||||
|
const TYPE_LOW = 1;
|
||||||
|
const TYPE_MEDIUM = 2;
|
||||||
|
const TYPE_HIGH = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unique id
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of alert. This should be one of the TYPE_ constants.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A human-readable description of the problem.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A url to a website with more information for the user.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $href;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification Etag
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $etag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the notification.
|
||||||
|
*
|
||||||
|
* Some kind of unique id should be provided. This is used to generate a
|
||||||
|
* url.
|
||||||
|
*
|
||||||
|
* @param string $id
|
||||||
|
* @param string $etag
|
||||||
|
* @param int $type
|
||||||
|
* @param string $description
|
||||||
|
* @param string $href
|
||||||
|
*/
|
||||||
|
public function __construct($id, $etag, $type = self::TYPE_HIGH, $description = null, $href = null) {
|
||||||
|
|
||||||
|
$this->id = $id;
|
||||||
|
$this->type = $type;
|
||||||
|
$this->description = $description;
|
||||||
|
$this->href = $href;
|
||||||
|
$this->etag = $etag;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the notification as a single property.
|
||||||
|
*
|
||||||
|
* You should usually just encode the single top-level element of the
|
||||||
|
* notification.
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function serialize(Sabre_DAV_Server $server, \DOMElement $node) {
|
||||||
|
|
||||||
|
switch($this->type) {
|
||||||
|
case self::TYPE_LOW :
|
||||||
|
$type = 'low';
|
||||||
|
break;
|
||||||
|
case self::TYPE_MEDIUM :
|
||||||
|
$type = 'medium';
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
case self::TYPE_HIGH :
|
||||||
|
$type = 'high';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$prop = $node->ownerDocument->createElement('cs:systemstatus');
|
||||||
|
$prop->setAttribute('type', $type);
|
||||||
|
|
||||||
|
$node->appendChild($prop);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method serializes the entire notification, as it is used in the
|
||||||
|
* response body.
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function serializeBody(Sabre_DAV_Server $server, \DOMElement $node) {
|
||||||
|
|
||||||
|
switch($this->type) {
|
||||||
|
case self::TYPE_LOW :
|
||||||
|
$type = 'low';
|
||||||
|
break;
|
||||||
|
case self::TYPE_MEDIUM :
|
||||||
|
$type = 'medium';
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
case self::TYPE_HIGH :
|
||||||
|
$type = 'high';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$prop = $node->ownerDocument->createElement('cs:systemstatus');
|
||||||
|
$prop->setAttribute('type', $type);
|
||||||
|
|
||||||
|
if ($this->description) {
|
||||||
|
$text = $node->ownerDocument->createTextNode($this->description);
|
||||||
|
$desc = $node->ownerDocument->createElement('cs:description');
|
||||||
|
$desc->appendChild($text);
|
||||||
|
$prop->appendChild($desc);
|
||||||
|
}
|
||||||
|
if ($this->href) {
|
||||||
|
$text = $node->ownerDocument->createTextNode($this->href);
|
||||||
|
$href = $node->ownerDocument->createElement('d:href');
|
||||||
|
$href->appendChild($text);
|
||||||
|
$prop->appendChild($href);
|
||||||
|
}
|
||||||
|
|
||||||
|
$node->appendChild($prop);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a unique id for this notification
|
||||||
|
*
|
||||||
|
* This is just the base url. This should generally be some kind of unique
|
||||||
|
* id.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getId() {
|
||||||
|
|
||||||
|
return $this->id;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the ETag for this notification.
|
||||||
|
*
|
||||||
|
* The ETag must be surrounded by literal double-quotes.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getETag() {
|
||||||
|
|
||||||
|
return $this->etag;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CalDAV plugin
|
* CalDAV plugin
|
||||||
*
|
*
|
||||||
|
@ -24,16 +26,6 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
*/
|
*/
|
||||||
const NS_CALENDARSERVER = 'http://calendarserver.org/ns/';
|
const NS_CALENDARSERVER = 'http://calendarserver.org/ns/';
|
||||||
|
|
||||||
/**
|
|
||||||
* The following constants are used to differentiate
|
|
||||||
* the various filters for the calendar-query report
|
|
||||||
*/
|
|
||||||
const FILTER_COMPFILTER = 1;
|
|
||||||
const FILTER_TIMERANGE = 3;
|
|
||||||
const FILTER_PROPFILTER = 4;
|
|
||||||
const FILTER_PARAMFILTER = 5;
|
|
||||||
const FILTER_TEXTMATCH = 6;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The hardcoded root for calendar objects. It is unfortunate
|
* The hardcoded root for calendar objects. It is unfortunate
|
||||||
* that we're stuck with it, but it will have to do for now
|
* that we're stuck with it, but it will have to do for now
|
||||||
|
@ -172,16 +164,19 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
$server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
|
$server->subscribeEvent('onBrowserPostAction', array($this,'browserPostAction'));
|
||||||
$server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
|
$server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'));
|
||||||
$server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
|
$server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'));
|
||||||
|
$server->subscribeEvent('beforeMethod', array($this,'beforeMethod'));
|
||||||
|
|
||||||
$server->xmlNamespaces[self::NS_CALDAV] = 'cal';
|
$server->xmlNamespaces[self::NS_CALDAV] = 'cal';
|
||||||
$server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs';
|
$server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs';
|
||||||
|
|
||||||
$server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet';
|
$server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet';
|
||||||
|
$server->propertyMap['{' . self::NS_CALDAV . '}schedule-calendar-transp'] = 'Sabre_CalDAV_Property_ScheduleCalendarTransp';
|
||||||
|
|
||||||
$server->resourceTypeMapping['Sabre_CalDAV_ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
|
$server->resourceTypeMapping['Sabre_CalDAV_ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
|
||||||
$server->resourceTypeMapping['Sabre_CalDAV_Schedule_IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox';
|
$server->resourceTypeMapping['Sabre_CalDAV_Schedule_IOutbox'] = '{urn:ietf:params:xml:ns:caldav}schedule-outbox';
|
||||||
$server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
|
$server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
|
||||||
$server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
|
$server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
|
||||||
|
$server->resourceTypeMapping['Sabre_CalDAV_Notifications_ICollection'] = '{' . self::NS_CALENDARSERVER . '}notification';
|
||||||
|
|
||||||
array_push($server->protectedProperties,
|
array_push($server->protectedProperties,
|
||||||
|
|
||||||
|
@ -205,7 +200,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
// CalendarServer extensions
|
// CalendarServer extensions
|
||||||
'{' . self::NS_CALENDARSERVER . '}getctag',
|
'{' . self::NS_CALENDARSERVER . '}getctag',
|
||||||
'{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
|
'{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
|
||||||
'{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'
|
'{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for',
|
||||||
|
'{' . self::NS_CALENDARSERVER . '}notification-URL',
|
||||||
|
'{' . self::NS_CALENDARSERVER . '}notificationtype'
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -226,6 +223,13 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
// unknownMethod event.
|
// unknownMethod event.
|
||||||
return false;
|
return false;
|
||||||
case 'POST' :
|
case 'POST' :
|
||||||
|
|
||||||
|
// Checking if this is a text/calendar content type
|
||||||
|
$contentType = $this->server->httpRequest->getHeader('Content-Type');
|
||||||
|
if (strpos($contentType, 'text/calendar')!==0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Checking if we're talking to an outbox
|
// Checking if we're talking to an outbox
|
||||||
try {
|
try {
|
||||||
$node = $this->server->tree->getNodeForPath($uri);
|
$node = $this->server->tree->getNodeForPath($uri);
|
||||||
|
@ -235,7 +239,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
if (!$node instanceof Sabre_CalDAV_Schedule_IOutbox)
|
if (!$node instanceof Sabre_CalDAV_Schedule_IOutbox)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
$this->outboxRequest($node);
|
$this->outboxRequest($node, $uri);
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -348,7 +352,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
if (in_array($calProp,$requestedProperties)) {
|
if (in_array($calProp,$requestedProperties)) {
|
||||||
|
|
||||||
$addresses = $node->getAlternateUriSet();
|
$addresses = $node->getAlternateUriSet();
|
||||||
$addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl();
|
$addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl() . '/';
|
||||||
unset($requestedProperties[$calProp]);
|
unset($requestedProperties[$calProp]);
|
||||||
$returnedProperties[200][$calProp] = new Sabre_DAV_Property_HrefList($addresses, false);
|
$returnedProperties[200][$calProp] = new Sabre_DAV_Property_HrefList($addresses, false);
|
||||||
|
|
||||||
|
@ -390,8 +394,31 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notification-URL property
|
||||||
|
$notificationUrl = '{' . self::NS_CALENDARSERVER . '}notification-URL';
|
||||||
|
if (($index = array_search($notificationUrl, $requestedProperties)) !== false) {
|
||||||
|
$principalId = $node->getName();
|
||||||
|
$calendarHomePath = 'calendars/' . $principalId . '/notifications/';
|
||||||
|
unset($requestedProperties[$index]);
|
||||||
|
$returnedProperties[200][$notificationUrl] = new Sabre_DAV_Property_Href($calendarHomePath);
|
||||||
|
}
|
||||||
|
|
||||||
} // instanceof IPrincipal
|
} // instanceof IPrincipal
|
||||||
|
|
||||||
|
if ($node instanceof Sabre_CalDAV_Notifications_INode) {
|
||||||
|
|
||||||
|
$propertyName = '{' . self::NS_CALENDARSERVER . '}notificationtype';
|
||||||
|
if (($index = array_search($propertyName, $requestedProperties)) !== false) {
|
||||||
|
|
||||||
|
$returnedProperties[200][$propertyName] =
|
||||||
|
$node->getNotificationType();
|
||||||
|
|
||||||
|
unset($requestedProperties[$index]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // instanceof Notifications_INode
|
||||||
|
|
||||||
|
|
||||||
if ($node instanceof Sabre_CalDAV_ICalendarObject) {
|
if ($node instanceof Sabre_CalDAV_ICalendarObject) {
|
||||||
// The calendar-data property is not supposed to be a 'real'
|
// The calendar-data property is not supposed to be a 'real'
|
||||||
|
@ -424,11 +451,11 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
public function calendarMultiGetReport($dom) {
|
public function calendarMultiGetReport($dom) {
|
||||||
|
|
||||||
$properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
|
$properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
|
||||||
$hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
|
$hrefElems = $dom->getElementsByTagNameNS('DAV:','href');
|
||||||
|
|
||||||
$xpath = new DOMXPath($dom);
|
$xpath = new DOMXPath($dom);
|
||||||
$xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
|
$xpath->registerNameSpace('cal',Sabre_CalDAV_Plugin::NS_CALDAV);
|
||||||
$xpath->registerNameSpace('dav','urn:DAV');
|
$xpath->registerNameSpace('dav','DAV:');
|
||||||
|
|
||||||
$expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand');
|
$expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand');
|
||||||
if ($expand->length>0) {
|
if ($expand->length>0) {
|
||||||
|
@ -438,8 +465,8 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
if(!$start || !$end) {
|
if(!$start || !$end) {
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element');
|
throw new Sabre_DAV_Exception_BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element');
|
||||||
}
|
}
|
||||||
$start = Sabre_VObject_DateTimeParser::parseDateTime($start);
|
$start = VObject\DateTimeParser::parseDateTime($start);
|
||||||
$end = Sabre_VObject_DateTimeParser::parseDateTime($end);
|
$end = VObject\DateTimeParser::parseDateTime($end);
|
||||||
|
|
||||||
if ($end <= $start) {
|
if ($end <= $start) {
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');
|
throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the expand element.');
|
||||||
|
@ -458,7 +485,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
list($objProps) = $this->server->getPropertiesForPath($uri,$properties);
|
list($objProps) = $this->server->getPropertiesForPath($uri,$properties);
|
||||||
|
|
||||||
if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) {
|
if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) {
|
||||||
$vObject = Sabre_VObject_Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']);
|
$vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']);
|
||||||
$vObject->expand($start, $end);
|
$vObject->expand($start, $end);
|
||||||
$objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
|
$objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
|
||||||
}
|
}
|
||||||
|
@ -467,9 +494,12 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$prefer = $this->server->getHTTPPRefer();
|
||||||
|
|
||||||
$this->server->httpResponse->sendStatus(207);
|
$this->server->httpResponse->sendStatus(207);
|
||||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||||
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList));
|
$this->server->httpResponse->setHeader('Vary','Brief,Prefer');
|
||||||
|
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal']));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,6 +517,16 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
$parser = new Sabre_CalDAV_CalendarQueryParser($dom);
|
$parser = new Sabre_CalDAV_CalendarQueryParser($dom);
|
||||||
$parser->parse();
|
$parser->parse();
|
||||||
|
|
||||||
|
$node = $this->server->tree->getNodeForPath($this->server->getRequestUri());
|
||||||
|
$depth = $this->server->getHTTPDepth(0);
|
||||||
|
|
||||||
|
// The default result is an empty array
|
||||||
|
$result = array();
|
||||||
|
|
||||||
|
// The calendarobject was requested directly. In this case we handle
|
||||||
|
// this locally.
|
||||||
|
if ($depth == 0 && $node instanceof Sabre_CalDAV_ICalendarObject) {
|
||||||
|
|
||||||
$requestedCalendarData = true;
|
$requestedCalendarData = true;
|
||||||
$requestedProperties = $parser->requestedProperties;
|
$requestedProperties = $parser->requestedProperties;
|
||||||
|
|
||||||
|
@ -500,41 +540,72 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
$requestedCalendarData = false;
|
$requestedCalendarData = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are the list of nodes that potentially match the requirement
|
$properties = $this->server->getPropertiesForPath(
|
||||||
$candidateNodes = $this->server->getPropertiesForPath(
|
|
||||||
$this->server->getRequestUri(),
|
$this->server->getRequestUri(),
|
||||||
$requestedProperties,
|
$requestedProperties,
|
||||||
$this->server->getHTTPDepth(0)
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
$verifiedNodes = array();
|
// This array should have only 1 element, the first calendar
|
||||||
|
// object.
|
||||||
|
$properties = current($properties);
|
||||||
|
|
||||||
|
// If there wasn't any calendar-data returned somehow, we ignore
|
||||||
|
// this.
|
||||||
|
if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) {
|
||||||
|
|
||||||
$validator = new Sabre_CalDAV_CalendarQueryValidator();
|
$validator = new Sabre_CalDAV_CalendarQueryValidator();
|
||||||
|
$vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
|
||||||
foreach($candidateNodes as $node) {
|
|
||||||
|
|
||||||
// If the node didn't have a calendar-data property, it must not be a calendar object
|
|
||||||
if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$vObject = Sabre_VObject_Reader::read($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
|
|
||||||
if ($validator->validate($vObject,$parser->filters)) {
|
if ($validator->validate($vObject,$parser->filters)) {
|
||||||
|
|
||||||
|
// If the client didn't require the calendar-data property,
|
||||||
|
// we won't give it back.
|
||||||
if (!$requestedCalendarData) {
|
if (!$requestedCalendarData) {
|
||||||
unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
|
unset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
|
||||||
}
|
} else {
|
||||||
if ($parser->expand) {
|
if ($parser->expand) {
|
||||||
$vObject->expand($parser->expand['start'], $parser->expand['end']);
|
$vObject->expand($parser->expand['start'], $parser->expand['end']);
|
||||||
$node[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
|
$properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
|
||||||
}
|
}
|
||||||
$verifiedNodes[] = $node;
|
}
|
||||||
|
|
||||||
|
$result = array($properties);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// If we're dealing with a calendar, the calendar itself is responsible
|
||||||
|
// for the calendar-query.
|
||||||
|
if ($node instanceof Sabre_CalDAV_ICalendar && $depth = 1) {
|
||||||
|
|
||||||
|
$nodePaths = $node->calendarQuery($parser->filters);
|
||||||
|
|
||||||
|
foreach($nodePaths as $path) {
|
||||||
|
|
||||||
|
list($properties) =
|
||||||
|
$this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $parser->requestedProperties);
|
||||||
|
|
||||||
|
if ($parser->expand) {
|
||||||
|
// We need to do some post-processing
|
||||||
|
$vObject = VObject\Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
|
||||||
|
$vObject->expand($parser->expand['start'], $parser->expand['end']);
|
||||||
|
$properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[] = $properties;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$prefer = $this->server->getHTTPPRefer();
|
||||||
|
|
||||||
$this->server->httpResponse->sendStatus(207);
|
$this->server->httpResponse->sendStatus(207);
|
||||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||||
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes));
|
$this->server->httpResponse->setHeader('Vary','Brief,Prefer');
|
||||||
|
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal']));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,10 +632,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
if ($start) {
|
if ($start) {
|
||||||
$start = Sabre_VObject_DateTimeParser::parseDateTime($start);
|
$start = VObject\DateTimeParser::parseDateTime($start);
|
||||||
}
|
}
|
||||||
if ($end) {
|
if ($end) {
|
||||||
$end = Sabre_VObject_DateTimeParser::parseDateTime($end);
|
$end = VObject\DateTimeParser::parseDateTime($end);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$start && !$end) {
|
if (!$start && !$end) {
|
||||||
|
@ -583,15 +654,33 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars');
|
throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars');
|
||||||
}
|
}
|
||||||
|
|
||||||
$objects = array_map(function($child) {
|
// Doing a calendar-query first, to make sure we get the most
|
||||||
$obj = $child->get();
|
// performance.
|
||||||
if (is_resource($obj)) {
|
$urls = $calendar->calendarQuery(array(
|
||||||
$obj = stream_get_contents($obj);
|
'name' => 'VCALENDAR',
|
||||||
}
|
'comp-filters' => array(
|
||||||
return $obj;
|
array(
|
||||||
}, $calendar->getChildren());
|
'name' => 'VEVENT',
|
||||||
|
'comp-filters' => array(),
|
||||||
|
'prop-filters' => array(),
|
||||||
|
'is-not-defined' => false,
|
||||||
|
'time-range' => array(
|
||||||
|
'start' => $start,
|
||||||
|
'end' => $end,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'prop-filters' => array(),
|
||||||
|
'is-not-defined' => false,
|
||||||
|
'time-range' => null,
|
||||||
|
));
|
||||||
|
|
||||||
$generator = new Sabre_VObject_FreeBusyGenerator();
|
$objects = array_map(function($url) use ($calendar) {
|
||||||
|
$obj = $calendar->getChild($url)->get();
|
||||||
|
return $obj;
|
||||||
|
}, $urls);
|
||||||
|
|
||||||
|
$generator = new VObject\FreeBusyGenerator();
|
||||||
$generator->setObjects($objects);
|
$generator->setObjects($objects);
|
||||||
$generator->setTimeRange($start, $end);
|
$generator->setTimeRange($start, $end);
|
||||||
$result = $generator->getResult();
|
$result = $generator->getResult();
|
||||||
|
@ -620,7 +709,7 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
if (!$node instanceof Sabre_CalDAV_ICalendarObject)
|
if (!$node instanceof Sabre_CalDAV_ICalendarObject)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
$this->validateICalendar($data);
|
$this->validateICalendar($data, $path);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -640,7 +729,52 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
if (!$parentNode instanceof Sabre_CalDAV_Calendar)
|
if (!$parentNode instanceof Sabre_CalDAV_Calendar)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
$this->validateICalendar($data);
|
$this->validateICalendar($data, $path);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is triggered before any HTTP request is handled.
|
||||||
|
*
|
||||||
|
* We use this to intercept GET calls to notification nodes, and return the
|
||||||
|
* proper response.
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string $path
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function beforeMethod($method, $path) {
|
||||||
|
|
||||||
|
if ($method!=='GET') return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$node = $this->server->tree->getNodeForPath($path);
|
||||||
|
} catch (Sabre_DAV_Exception_NotFound $e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$node instanceof Sabre_CalDAV_Notifications_INode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!$this->server->checkPreconditions(true)) return false;
|
||||||
|
|
||||||
|
$dom = new DOMDocument('1.0', 'UTF-8');
|
||||||
|
$dom->formatOutput = true;
|
||||||
|
|
||||||
|
$root = $dom->createElement('cs:notification');
|
||||||
|
foreach($this->server->xmlNamespaces as $namespace => $prefix) {
|
||||||
|
$root->setAttribute('xmlns:' . $prefix, $namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dom->appendChild($root);
|
||||||
|
$node->getNotificationType()->serializeBody($this->server, $root);
|
||||||
|
|
||||||
|
$this->server->httpResponse->setHeader('Content-Type','application/xml');
|
||||||
|
$this->server->httpResponse->setHeader('ETag',$node->getETag());
|
||||||
|
$this->server->httpResponse->sendStatus(200);
|
||||||
|
$this->server->httpResponse->sendBody($dom->saveXML());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,9 +784,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
* An exception is thrown if it's not.
|
* An exception is thrown if it's not.
|
||||||
*
|
*
|
||||||
* @param resource|string $data
|
* @param resource|string $data
|
||||||
|
* @param string $path
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function validateICalendar(&$data) {
|
protected function validateICalendar(&$data, $path) {
|
||||||
|
|
||||||
// If it's a stream, we convert it to a string first.
|
// If it's a stream, we convert it to a string first.
|
||||||
if (is_resource($data)) {
|
if (is_resource($data)) {
|
||||||
|
@ -664,9 +799,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
$vobj = Sabre_VObject_Reader::read($data);
|
$vobj = VObject\Reader::read($data);
|
||||||
|
|
||||||
} catch (Sabre_VObject_ParseException $e) {
|
} catch (VObject\ParseException $e) {
|
||||||
|
|
||||||
throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage());
|
throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid iCalendar 2.0 data. Parse error: ' . $e->getMessage());
|
||||||
|
|
||||||
|
@ -676,6 +811,11 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.');
|
throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support iCalendar objects.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the Supported Components for the target calendar
|
||||||
|
list($parentPath,$object) = Sabre_Dav_URLUtil::splitPath($path);
|
||||||
|
$calendarProperties = $this->server->getProperties($parentPath,array('{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'));
|
||||||
|
$supportedComponents = $calendarProperties['{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set']->getValue();
|
||||||
|
|
||||||
$foundType = null;
|
$foundType = null;
|
||||||
$foundUID = null;
|
$foundUID = null;
|
||||||
foreach($vobj->getComponents() as $component) {
|
foreach($vobj->getComponents() as $component) {
|
||||||
|
@ -687,6 +827,9 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
case 'VJOURNAL' :
|
case 'VJOURNAL' :
|
||||||
if (is_null($foundType)) {
|
if (is_null($foundType)) {
|
||||||
$foundType = $component->name;
|
$foundType = $component->name;
|
||||||
|
if (!in_array($foundType, $supportedComponents)) {
|
||||||
|
throw new Sabre_CalDAV_Exception_InvalidComponentType('This calendar only supports ' . implode(', ', $supportedComponents) . '. We found a ' . $foundType);
|
||||||
|
}
|
||||||
if (!isset($component->UID)) {
|
if (!isset($component->UID)) {
|
||||||
throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID');
|
throw new Sabre_DAV_Exception_BadRequest('Every ' . $component->name . ' component must have an UID');
|
||||||
}
|
}
|
||||||
|
@ -711,12 +854,81 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method handles POST requests to the schedule-outbox
|
* This method handles POST requests to the schedule-outbox.
|
||||||
|
*
|
||||||
|
* Currently, two types of requests are support:
|
||||||
|
* * FREEBUSY requests from RFC 6638
|
||||||
|
* * Simple iTIP messages from draft-desruisseaux-caldav-sched-04
|
||||||
|
*
|
||||||
|
* The latter is from an expired early draft of the CalDAV scheduling
|
||||||
|
* extensions, but iCal depends on a feature from that spec, so we
|
||||||
|
* implement it.
|
||||||
*
|
*
|
||||||
* @param Sabre_CalDAV_Schedule_IOutbox $outboxNode
|
* @param Sabre_CalDAV_Schedule_IOutbox $outboxNode
|
||||||
|
* @param string $outboxUri
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function outboxRequest(Sabre_CalDAV_Schedule_IOutbox $outboxNode) {
|
public function outboxRequest(Sabre_CalDAV_Schedule_IOutbox $outboxNode, $outboxUri) {
|
||||||
|
|
||||||
|
// Parsing the request body
|
||||||
|
try {
|
||||||
|
$vObject = VObject\Reader::read($this->server->httpRequest->getBody(true));
|
||||||
|
} catch (VObject\ParseException $e) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The incoming iCalendar object must have a METHOD property, and a
|
||||||
|
// component. The combination of both determines what type of request
|
||||||
|
// this is.
|
||||||
|
$componentType = null;
|
||||||
|
foreach($vObject->getComponents() as $component) {
|
||||||
|
if ($component->name !== 'VTIMEZONE') {
|
||||||
|
$componentType = $component->name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_null($componentType)) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validating the METHOD
|
||||||
|
$method = strtoupper((string)$vObject->METHOD);
|
||||||
|
if (!$method) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('A METHOD property must be specified in iTIP messages');
|
||||||
|
}
|
||||||
|
|
||||||
|
// So we support two types of requests:
|
||||||
|
//
|
||||||
|
// REQUEST with a VFREEBUSY component
|
||||||
|
// REQUEST, REPLY, ADD, CANCEL on VEVENT components
|
||||||
|
|
||||||
|
$acl = $this->server->getPlugin('acl');
|
||||||
|
|
||||||
|
if ($componentType === 'VFREEBUSY' && $method === 'REQUEST') {
|
||||||
|
|
||||||
|
$acl && $acl->checkPrivileges($outboxUri,'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy');
|
||||||
|
$this->handleFreeBusyRequest($outboxNode, $vObject);
|
||||||
|
|
||||||
|
} elseif ($componentType === 'VEVENT' && in_array($method, array('REQUEST','REPLY','ADD','CANCEL'))) {
|
||||||
|
|
||||||
|
$acl && $acl->checkPrivileges($outboxUri,'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-post-vevent');
|
||||||
|
$this->handleEventNotification($outboxNode, $vObject);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
throw new Sabre_DAV_Exception_NotImplemented('SabreDAV supports only VFREEBUSY (REQUEST) and VEVENT (REQUEST, REPLY, ADD, CANCEL)');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method handles the REQUEST, REPLY, ADD and CANCEL methods for
|
||||||
|
* VEVENT iTip messages.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function handleEventNotification(Sabre_CalDAV_Schedule_IOutbox $outboxNode, VObject\Component $vObject) {
|
||||||
|
|
||||||
$originator = $this->server->httpRequest->getHeader('Originator');
|
$originator = $this->server->httpRequest->getHeader('Originator');
|
||||||
$recipients = $this->server->httpRequest->getHeader('Recipient');
|
$recipients = $this->server->httpRequest->getHeader('Recipient');
|
||||||
|
@ -760,38 +972,10 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
throw new Sabre_DAV_Exception_Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header');
|
throw new Sabre_DAV_Exception_Forbidden('The addresses specified in the Originator header did not match any addresses in the owners calendar-user-address-set header');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
$result = $this->iMIPMessage($originator, $recipients, $vObject, $principal);
|
||||||
$vObject = Sabre_VObject_Reader::read($this->server->httpRequest->getBody(true));
|
|
||||||
} catch (Sabre_VObject_ParseException $e) {
|
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The request body must be a valid iCalendar object. Parse error: ' . $e->getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking for the object type
|
|
||||||
$componentType = null;
|
|
||||||
foreach($vObject->getComponents() as $component) {
|
|
||||||
if ($component->name !== 'VTIMEZONE') {
|
|
||||||
$componentType = $component->name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (is_null($componentType)) {
|
|
||||||
throw new Sabre_DAV_Exception_BadRequest('We expected at least one VTODO, VJOURNAL, VFREEBUSY or VEVENT component');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validating the METHOD
|
|
||||||
$method = strtoupper((string)$vObject->METHOD);
|
|
||||||
if (!$method) {
|
|
||||||
throw new Sabre_DAV_Exception_BadRequest('A METHOD property must be specified in iTIP messages');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_array($method, array('REQUEST','REPLY','ADD','CANCEL')) && $componentType==='VEVENT') {
|
|
||||||
$result = $this->iMIPMessage($originator, $recipients, $vObject);
|
|
||||||
$this->server->httpResponse->sendStatus(200);
|
$this->server->httpResponse->sendStatus(200);
|
||||||
$this->server->httpResponse->setHeader('Content-Type','application/xml');
|
$this->server->httpResponse->setHeader('Content-Type','application/xml');
|
||||||
$this->server->httpResponse->sendBody($this->generateScheduleResponse($result));
|
$this->server->httpResponse->sendBody($this->generateScheduleResponse($result));
|
||||||
} else {
|
|
||||||
throw new Sabre_DAV_Exception_NotImplemented('This iTIP method is currently not implemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -813,15 +997,15 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
*
|
*
|
||||||
* @param string $originator
|
* @param string $originator
|
||||||
* @param array $recipients
|
* @param array $recipients
|
||||||
* @param Sabre_VObject_Component $vObject
|
* @param Sabre\VObject\Component $vObject
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
protected function iMIPMessage($originator, array $recipients, Sabre_VObject_Component $vObject) {
|
protected function iMIPMessage($originator, array $recipients, VObject\Component $vObject, $principal) {
|
||||||
|
|
||||||
if (!$this->imipHandler) {
|
if (!$this->imipHandler) {
|
||||||
$resultStatus = '5.2;This server does not support this operation';
|
$resultStatus = '5.2;This server does not support this operation';
|
||||||
} else {
|
} else {
|
||||||
$this->imipHandler->sendMessage($originator, $recipients, $vObject);
|
$this->imipHandler->sendMessage($originator, $recipients, $vObject, $principal);
|
||||||
$resultStatus = '2.0;Success';
|
$resultStatus = '2.0;Success';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,7 +1016,6 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -877,6 +1060,204 @@ class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is responsible for parsing a free-busy query request and
|
||||||
|
* returning it's result.
|
||||||
|
*
|
||||||
|
* @param Sabre_CalDAV_Schedule_IOutbox $outbox
|
||||||
|
* @param string $request
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function handleFreeBusyRequest(Sabre_CalDAV_Schedule_IOutbox $outbox, VObject\Component $vObject) {
|
||||||
|
|
||||||
|
$vFreeBusy = $vObject->VFREEBUSY;
|
||||||
|
$organizer = $vFreeBusy->organizer;
|
||||||
|
|
||||||
|
$organizer = (string)$organizer;
|
||||||
|
|
||||||
|
// Validating if the organizer matches the owner of the inbox.
|
||||||
|
$owner = $outbox->getOwner();
|
||||||
|
|
||||||
|
$caldavNS = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}';
|
||||||
|
|
||||||
|
$uas = $caldavNS . 'calendar-user-address-set';
|
||||||
|
$props = $this->server->getProperties($owner,array($uas));
|
||||||
|
|
||||||
|
if (empty($props[$uas]) || !in_array($organizer, $props[$uas]->getHrefs())) {
|
||||||
|
throw new Sabre_DAV_Exception_Forbidden('The organizer in the request did not match any of the addresses for the owner of this inbox');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($vFreeBusy->ATTENDEE)) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('You must at least specify 1 attendee');
|
||||||
|
}
|
||||||
|
|
||||||
|
$attendees = array();
|
||||||
|
foreach($vFreeBusy->ATTENDEE as $attendee) {
|
||||||
|
$attendees[]= (string)$attendee;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!isset($vFreeBusy->DTSTART) || !isset($vFreeBusy->DTEND)) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('DTSTART and DTEND must both be specified');
|
||||||
|
}
|
||||||
|
|
||||||
|
$startRange = $vFreeBusy->DTSTART->getDateTime();
|
||||||
|
$endRange = $vFreeBusy->DTEND->getDateTime();
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
foreach($attendees as $attendee) {
|
||||||
|
$results[] = $this->getFreeBusyForEmail($attendee, $startRange, $endRange, $vObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dom = new DOMDocument('1.0','utf-8');
|
||||||
|
$dom->formatOutput = true;
|
||||||
|
$scheduleResponse = $dom->createElement('cal:schedule-response');
|
||||||
|
foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
|
||||||
|
|
||||||
|
$scheduleResponse->setAttribute('xmlns:' . $prefix,$namespace);
|
||||||
|
|
||||||
|
}
|
||||||
|
$dom->appendChild($scheduleResponse);
|
||||||
|
|
||||||
|
foreach($results as $result) {
|
||||||
|
$response = $dom->createElement('cal:response');
|
||||||
|
|
||||||
|
$recipient = $dom->createElement('cal:recipient');
|
||||||
|
$recipientHref = $dom->createElement('d:href');
|
||||||
|
|
||||||
|
$recipientHref->appendChild($dom->createTextNode($result['href']));
|
||||||
|
$recipient->appendChild($recipientHref);
|
||||||
|
$response->appendChild($recipient);
|
||||||
|
|
||||||
|
$reqStatus = $dom->createElement('cal:request-status');
|
||||||
|
$reqStatus->appendChild($dom->createTextNode($result['request-status']));
|
||||||
|
$response->appendChild($reqStatus);
|
||||||
|
|
||||||
|
if (isset($result['calendar-data'])) {
|
||||||
|
|
||||||
|
$calendardata = $dom->createElement('cal:calendar-data');
|
||||||
|
$calendardata->appendChild($dom->createTextNode(str_replace("\r\n","\n",$result['calendar-data']->serialize())));
|
||||||
|
$response->appendChild($calendardata);
|
||||||
|
|
||||||
|
}
|
||||||
|
$scheduleResponse->appendChild($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->server->httpResponse->sendStatus(200);
|
||||||
|
$this->server->httpResponse->setHeader('Content-Type','application/xml');
|
||||||
|
$this->server->httpResponse->sendBody($dom->saveXML());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns free-busy information for a specific address. The returned
|
||||||
|
* data is an array containing the following properties:
|
||||||
|
*
|
||||||
|
* calendar-data : A VFREEBUSY VObject
|
||||||
|
* request-status : an iTip status code.
|
||||||
|
* href: The principal's email address, as requested
|
||||||
|
*
|
||||||
|
* The following request status codes may be returned:
|
||||||
|
* * 2.0;description
|
||||||
|
* * 3.7;description
|
||||||
|
*
|
||||||
|
* @param string $email address
|
||||||
|
* @param DateTime $start
|
||||||
|
* @param DateTime $end
|
||||||
|
* @param Sabre_VObject_Component $request
|
||||||
|
* @return Sabre_VObject_Component
|
||||||
|
*/
|
||||||
|
protected function getFreeBusyForEmail($email, DateTime $start, DateTime $end, VObject\Component $request) {
|
||||||
|
|
||||||
|
$caldavNS = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}';
|
||||||
|
|
||||||
|
$aclPlugin = $this->server->getPlugin('acl');
|
||||||
|
if (substr($email,0,7)==='mailto:') $email = substr($email,7);
|
||||||
|
|
||||||
|
$result = $aclPlugin->principalSearch(
|
||||||
|
array('{http://sabredav.org/ns}email-address' => $email),
|
||||||
|
array(
|
||||||
|
'{DAV:}principal-URL', $caldavNS . 'calendar-home-set',
|
||||||
|
'{http://sabredav.org/ns}email-address',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!count($result)) {
|
||||||
|
return array(
|
||||||
|
'request-status' => '3.7;Could not find principal',
|
||||||
|
'href' => 'mailto:' . $email,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($result[0][200][$caldavNS . 'calendar-home-set'])) {
|
||||||
|
return array(
|
||||||
|
'request-status' => '3.7;No calendar-home-set property found',
|
||||||
|
'href' => 'mailto:' . $email,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$homeSet = $result[0][200][$caldavNS . 'calendar-home-set']->getHref();
|
||||||
|
|
||||||
|
// Grabbing the calendar list
|
||||||
|
$objects = array();
|
||||||
|
foreach($this->server->tree->getNodeForPath($homeSet)->getChildren() as $node) {
|
||||||
|
if (!$node instanceof Sabre_CalDAV_ICalendar) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$aclPlugin->checkPrivileges($homeSet . $node->getName() ,$caldavNS . 'read-free-busy');
|
||||||
|
|
||||||
|
// Getting the list of object uris within the time-range
|
||||||
|
$urls = $node->calendarQuery(array(
|
||||||
|
'name' => 'VCALENDAR',
|
||||||
|
'comp-filters' => array(
|
||||||
|
array(
|
||||||
|
'name' => 'VEVENT',
|
||||||
|
'comp-filters' => array(),
|
||||||
|
'prop-filters' => array(),
|
||||||
|
'is-not-defined' => false,
|
||||||
|
'time-range' => array(
|
||||||
|
'start' => $start,
|
||||||
|
'end' => $end,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'prop-filters' => array(),
|
||||||
|
'is-not-defined' => false,
|
||||||
|
'time-range' => null,
|
||||||
|
));
|
||||||
|
|
||||||
|
$calObjects = array_map(function($url) use ($node) {
|
||||||
|
$obj = $node->getChild($url)->get();
|
||||||
|
return $obj;
|
||||||
|
}, $urls);
|
||||||
|
|
||||||
|
$objects = array_merge($objects,$calObjects);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$vcalendar = VObject\Component::create('VCALENDAR');
|
||||||
|
$vcalendar->VERSION = '2.0';
|
||||||
|
$vcalendar->METHOD = 'REPLY';
|
||||||
|
$vcalendar->CALSCALE = 'GREGORIAN';
|
||||||
|
$vcalendar->PRODID = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN';
|
||||||
|
|
||||||
|
$generator = new VObject\FreeBusyGenerator();
|
||||||
|
$generator->setObjects($objects);
|
||||||
|
$generator->setTimeRange($start, $end);
|
||||||
|
$generator->setBaseObject($vcalendar);
|
||||||
|
|
||||||
|
$result = $generator->getResult();
|
||||||
|
|
||||||
|
$vcalendar->VFREEBUSY->ATTENDEE = 'mailto:' . $email;
|
||||||
|
$vcalendar->VFREEBUSY->UID = (string)$request->VFREEBUSY->UID;
|
||||||
|
$vcalendar->VFREEBUSY->ORGANIZER = clone $request->VFREEBUSY->ORGANIZER;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'calendar-data' => $result,
|
||||||
|
'request-status' => '2.0;Success',
|
||||||
|
'href' => 'mailto:' . $email,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to generate HTML output for the
|
* This method is used to generate HTML output for the
|
||||||
* Sabre_DAV_Browser_Plugin. This allows us to generate an interface users
|
* Sabre_DAV_Browser_Plugin. This allows us to generate an interface users
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AllowedSharingModes
|
||||||
|
*
|
||||||
|
* This property encodes the 'allowed-sharing-modes' property, as defined by
|
||||||
|
* the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/
|
||||||
|
* namespace.
|
||||||
|
*
|
||||||
|
* This property is a representation of the supported-calendar_component-set
|
||||||
|
* property in the CalDAV namespace. It simply requires an array of components,
|
||||||
|
* such as VEVENT, VTODO
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_Property_AllowedSharingModes extends Sabre_DAV_Property {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not a calendar can be shared with another user
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $canBeShared;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the calendar can be placed on a public url.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $canBePublished;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param bool $canBeShared
|
||||||
|
* @param bool $canBePublished
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($canBeShared, $canBePublished) {
|
||||||
|
|
||||||
|
$this->canBeShared = $canBeShared;
|
||||||
|
$this->canBePublished = $canBePublished;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the property in a DOMDocument
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
|
||||||
|
|
||||||
|
$doc = $node->ownerDocument;
|
||||||
|
if ($this->canBeShared) {
|
||||||
|
$xcomp = $doc->createElement('cs:can-be-shared');
|
||||||
|
$node->appendChild($xcomp);
|
||||||
|
}
|
||||||
|
if ($this->canBePublished) {
|
||||||
|
$xcomp = $doc->createElement('cs:can-be-published');
|
||||||
|
$node->appendChild($xcomp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Sabre_CalDAV_SharingPlugin as SharingPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invite property
|
||||||
|
*
|
||||||
|
* This property encodes the 'invite' property, as defined by
|
||||||
|
* the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/
|
||||||
|
* namespace.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_Property_Invite extends Sabre_DAV_Property {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of users a calendar has been shared to.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $users;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the property.
|
||||||
|
*
|
||||||
|
* Users is an array. Each element of the array has the following
|
||||||
|
* properties:
|
||||||
|
*
|
||||||
|
* * href - Often a mailto: address
|
||||||
|
* * commonName - Optional, for example a first and lastname for a user.
|
||||||
|
* * status - One of the SharingPlugin::STATUS_* constants.
|
||||||
|
* * readOnly - true or false
|
||||||
|
* * summary - Optional, description of the share
|
||||||
|
*
|
||||||
|
* @param array $users
|
||||||
|
*/
|
||||||
|
public function __construct(array $users) {
|
||||||
|
|
||||||
|
$this->users = $users;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of users, as it was passed to the constructor.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getValue() {
|
||||||
|
|
||||||
|
return $this->users;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the property in a DOMDocument
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
|
||||||
|
|
||||||
|
$doc = $node->ownerDocument;
|
||||||
|
foreach($this->users as $user) {
|
||||||
|
|
||||||
|
$xuser = $doc->createElement('cs:user');
|
||||||
|
|
||||||
|
$href = $doc->createElement('d:href');
|
||||||
|
$href->appendChild($doc->createTextNode($user['href']));
|
||||||
|
$xuser->appendChild($href);
|
||||||
|
|
||||||
|
if (isset($user['commonName']) && $user['commonName']) {
|
||||||
|
$commonName = $doc->createElement('cs:common-name');
|
||||||
|
$commonName->appendChild($doc->createTextNode($user['commonName']));
|
||||||
|
$xuser->appendChild($commonName);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($user['status']) {
|
||||||
|
|
||||||
|
case SharingPlugin::STATUS_ACCEPTED :
|
||||||
|
$status = $doc->createElement('cs:invite-accepted');
|
||||||
|
$xuser->appendChild($status);
|
||||||
|
break;
|
||||||
|
case SharingPlugin::STATUS_DECLINED :
|
||||||
|
$status = $doc->createElement('cs:invite-declined');
|
||||||
|
$xuser->appendChild($status);
|
||||||
|
break;
|
||||||
|
case SharingPlugin::STATUS_NORESPONSE :
|
||||||
|
$status = $doc->createElement('cs:invite-noresponse');
|
||||||
|
$xuser->appendChild($status);
|
||||||
|
break;
|
||||||
|
case SharingPlugin::STATUS_INVALID :
|
||||||
|
$status = $doc->createElement('cs:invite-invalid');
|
||||||
|
$xuser->appendChild($status);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$xaccess = $doc->createElement('cs:access');
|
||||||
|
|
||||||
|
if ($user['readOnly']) {
|
||||||
|
$xaccess->appendChild(
|
||||||
|
$doc->createElement('cs:read')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$xaccess->appendChild(
|
||||||
|
$doc->createElement('cs:read-write')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$xuser->appendChild($xaccess);
|
||||||
|
|
||||||
|
if (isset($user['summary']) && $user['summary']) {
|
||||||
|
$summary = $doc->createElement('cs:summary');
|
||||||
|
$summary->appendChild($doc->createTextNode($user['summary']));
|
||||||
|
$xuser->appendChild($summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
$node->appendChild($xuser);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserializes the property.
|
||||||
|
*
|
||||||
|
* This static method should return a an instance of this object.
|
||||||
|
*
|
||||||
|
* @param DOMElement $prop
|
||||||
|
* @return Sabre_DAV_IProperty
|
||||||
|
*/
|
||||||
|
static function unserialize(DOMElement $prop) {
|
||||||
|
|
||||||
|
$xpath = new \DOMXPath($prop->ownerDocument);
|
||||||
|
$xpath->registerNamespace('cs', Sabre_CalDAV_Plugin::NS_CALENDARSERVER);
|
||||||
|
$xpath->registerNamespace('d', 'DAV:');
|
||||||
|
|
||||||
|
$users = array();
|
||||||
|
|
||||||
|
foreach($xpath->query('cs:user', $prop) as $user) {
|
||||||
|
|
||||||
|
$status = null;
|
||||||
|
if ($xpath->evaluate('boolean(cs:invite-accepted)', $user)) {
|
||||||
|
$status = SharingPlugin::STATUS_ACCEPTED;
|
||||||
|
} elseif ($xpath->evaluate('boolean(cs:invite-declined)', $user)) {
|
||||||
|
$status = SharingPlugin::STATUS_DECLINED;
|
||||||
|
} elseif ($xpath->evaluate('boolean(cs:invite-noresponse)', $user)) {
|
||||||
|
$status = SharingPlugin::STATUS_NORESPONSE;
|
||||||
|
} elseif ($xpath->evaluate('boolean(cs:invite-invalid)', $user)) {
|
||||||
|
$status = SharingPlugin::STATUS_INVALID;
|
||||||
|
} else {
|
||||||
|
throw new Sabre_DAV_Exception('Every cs:user property must have one of cs:invite-accepted, cs:invite-declined, cs:invite-noresponse or cs:invite-invalid');
|
||||||
|
}
|
||||||
|
$users[] = array(
|
||||||
|
'href' => $xpath->evaluate('string(d:href)', $user),
|
||||||
|
'commonName' => $xpath->evaluate('string(cs:common-name)', $user),
|
||||||
|
'readOnly' => $xpath->evaluate('boolean(cs:access/cs:read)', $user),
|
||||||
|
'summary' => $xpath->evaluate('string(cs:summary)', $user),
|
||||||
|
'status' => $status,
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new self($users);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* schedule-calendar-transp property.
|
||||||
|
*
|
||||||
|
* This property is a representation of the schedule-calendar-transp property.
|
||||||
|
* This property is defined in RFC6638 (caldav scheduling).
|
||||||
|
*
|
||||||
|
* Its values are either 'transparent' or 'opaque'. If it's transparent, it
|
||||||
|
* means that this calendar will not be taken into consideration when a
|
||||||
|
* different user queries for free-busy information. If it's 'opaque', it will.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_Property_ScheduleCalendarTransp extends Sabre_DAV_Property {
|
||||||
|
|
||||||
|
const TRANSPARENT = 'transparent';
|
||||||
|
const OPAQUE = 'opaque';
|
||||||
|
|
||||||
|
protected $value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the property
|
||||||
|
*
|
||||||
|
* @param string $value
|
||||||
|
*/
|
||||||
|
public function __construct($value) {
|
||||||
|
|
||||||
|
if ($value !== self::TRANSPARENT && $value !== self::OPAQUE) {
|
||||||
|
throw new \InvalidArgumentException('The value must either be specified as "transparent" or "opaque"');
|
||||||
|
}
|
||||||
|
$this->value = $value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current value
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getValue() {
|
||||||
|
|
||||||
|
return $this->value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes the property in a DOMDocument
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
|
||||||
|
|
||||||
|
$doc = $node->ownerDocument;
|
||||||
|
switch($this->value) {
|
||||||
|
case self::TRANSPARENT :
|
||||||
|
$xval = $doc->createElement('cal:transparent');
|
||||||
|
break;
|
||||||
|
case self::OPAQUE :
|
||||||
|
$xval = $doc->createElement('cal:opaque');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$node->appendChild($xval);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserializes the DOMElement back into a Property class.
|
||||||
|
*
|
||||||
|
* @param DOMElement $node
|
||||||
|
* @return Sabre_CalDAV_Property_ScheduleCalendarTransp
|
||||||
|
*/
|
||||||
|
static function unserialize(DOMElement $node) {
|
||||||
|
|
||||||
|
$value = null;
|
||||||
|
foreach($node->childNodes as $childNode) {
|
||||||
|
switch(Sabre_DAV_XMLUtil::toClarkNotation($childNode)) {
|
||||||
|
case '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}opaque' :
|
||||||
|
$value = self::OPAQUE;
|
||||||
|
break;
|
||||||
|
case '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}transparent' :
|
||||||
|
$value = self::TRANSPARENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_null($value))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new self($value);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iMIP handler.
|
* iMIP handler.
|
||||||
*
|
*
|
||||||
|
@ -42,12 +44,13 @@ class Sabre_CalDAV_Schedule_IMip {
|
||||||
/**
|
/**
|
||||||
* Sends one or more iTip messages through email.
|
* Sends one or more iTip messages through email.
|
||||||
*
|
*
|
||||||
* @param string $originator
|
* @param string $originator Originator Email
|
||||||
* @param array $recipients
|
* @param array $recipients Array of email addresses
|
||||||
* @param Sabre_VObject_Component $vObject
|
* @param Sabre\VObject\Component $vObject
|
||||||
|
* @param string $principal Principal Url of the originator
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function sendMessage($originator, array $recipients, Sabre_VObject_Component $vObject) {
|
public function sendMessage($originator, array $recipients, VObject\Component $vObject, $principal) {
|
||||||
|
|
||||||
foreach($recipients as $recipient) {
|
foreach($recipients as $recipient) {
|
||||||
|
|
||||||
|
@ -83,6 +86,9 @@ class Sabre_CalDAV_Schedule_IMip {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
// This is deemed untestable in a reasonable manner
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is reponsible for sending the actual email.
|
* This function is reponsible for sending the actual email.
|
||||||
*
|
*
|
||||||
|
@ -94,11 +100,11 @@ class Sabre_CalDAV_Schedule_IMip {
|
||||||
*/
|
*/
|
||||||
protected function mail($to, $subject, $body, array $headers) {
|
protected function mail($to, $subject, $body, array $headers) {
|
||||||
|
|
||||||
|
|
||||||
mail($to, $subject, $body, implode("\r\n", $headers));
|
mail($to, $subject, $body, implode("\r\n", $headers));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
*/
|
*/
|
||||||
class Sabre_CalDAV_Schedule_Outbox extends Sabre_DAV_Directory implements Sabre_CalDAV_Schedule_IOutbox {
|
class Sabre_CalDAV_Schedule_Outbox extends Sabre_DAV_Collection implements Sabre_CalDAV_Schedule_IOutbox {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The principal Uri
|
* The principal Uri
|
||||||
|
@ -103,6 +103,11 @@ class Sabre_CalDAV_Schedule_Outbox extends Sabre_DAV_Directory implements Sabre_
|
||||||
'principal' => $this->getOwner(),
|
'principal' => $this->getOwner(),
|
||||||
'protected' => true,
|
'protected' => true,
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-post-vevent',
|
||||||
|
'principal' => $this->getOwner(),
|
||||||
|
'protected' => true,
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
'privilege' => '{DAV:}read',
|
'privilege' => '{DAV:}read',
|
||||||
'principal' => $this->getOwner(),
|
'principal' => $this->getOwner(),
|
||||||
|
@ -144,6 +149,9 @@ class Sabre_CalDAV_Schedule_Outbox extends Sabre_DAV_Directory implements Sabre_
|
||||||
$default['aggregates'][] = array(
|
$default['aggregates'][] = array(
|
||||||
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy',
|
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-query-freebusy',
|
||||||
);
|
);
|
||||||
|
$default['aggregates'][] = array(
|
||||||
|
'privilege' => '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}schedule-post-vevent',
|
||||||
|
);
|
||||||
|
|
||||||
return $default;
|
return $default;
|
||||||
|
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CalDAV server
|
|
||||||
*
|
|
||||||
* Deprecated! Warning: This class is now officially deprecated
|
|
||||||
*
|
|
||||||
* This script is a convenience script. It quickly sets up a WebDAV server
|
|
||||||
* with caldav and ACL support, and it creates the root 'principals' and
|
|
||||||
* 'calendars' collections.
|
|
||||||
*
|
|
||||||
* Note that if you plan to do anything moderately complex, you are advised to
|
|
||||||
* not subclass this server, but use Sabre_DAV_Server directly instead. This
|
|
||||||
* class is nothing more than an 'easy setup'.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage CalDAV
|
|
||||||
* @deprecated Don't use this class anymore, it will be removed in version 1.7.
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_CalDAV_Server extends Sabre_DAV_Server {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The authentication realm
|
|
||||||
*
|
|
||||||
* Note that if this changes, the hashes in the auth backend must also
|
|
||||||
* be recalculated.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $authRealm = 'SabreDAV';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the object. A PDO object must be passed to setup all the backends.
|
|
||||||
*
|
|
||||||
* @param PDO $pdo
|
|
||||||
*/
|
|
||||||
public function __construct(PDO $pdo) {
|
|
||||||
|
|
||||||
/* Backends */
|
|
||||||
$authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo);
|
|
||||||
$calendarBackend = new Sabre_CalDAV_Backend_PDO($pdo);
|
|
||||||
$principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo);
|
|
||||||
|
|
||||||
/* Directory structure */
|
|
||||||
$tree = array(
|
|
||||||
new Sabre_CalDAV_Principal_Collection($principalBackend),
|
|
||||||
new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend),
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Initializing server */
|
|
||||||
parent::__construct($tree);
|
|
||||||
|
|
||||||
/* Server Plugins */
|
|
||||||
$authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,$this->authRealm);
|
|
||||||
$this->addPlugin($authPlugin);
|
|
||||||
|
|
||||||
$aclPlugin = new Sabre_DAVACL_Plugin();
|
|
||||||
$this->addPlugin($aclPlugin);
|
|
||||||
|
|
||||||
$caldavPlugin = new Sabre_CalDAV_Plugin();
|
|
||||||
$this->addPlugin($caldavPlugin);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object represents a CalDAV calendar that can be shared with other
|
||||||
|
* users.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_ShareableCalendar extends Sabre_CalDAV_Calendar implements Sabre_CalDAV_IShareableCalendar {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the list of shares.
|
||||||
|
*
|
||||||
|
* The first array is a list of people that are to be added to the
|
||||||
|
* calendar.
|
||||||
|
*
|
||||||
|
* Every element in the add array has the following properties:
|
||||||
|
* * href - A url. Usually a mailto: address
|
||||||
|
* * commonName - Usually a first and last name, or false
|
||||||
|
* * summary - A description of the share, can also be false
|
||||||
|
* * readOnly - A boolean value
|
||||||
|
*
|
||||||
|
* Every element in the remove array is just the address string.
|
||||||
|
*
|
||||||
|
* @param array $add
|
||||||
|
* @param array $remove
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function updateShares(array $add, array $remove) {
|
||||||
|
|
||||||
|
$this->caldavBackend->updateShares($this->calendarInfo['id'], $add, $remove);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of people whom this calendar is shared with.
|
||||||
|
*
|
||||||
|
* Every element in this array should have the following properties:
|
||||||
|
* * href - Often a mailto: address
|
||||||
|
* * commonName - Optional, for example a first + last name
|
||||||
|
* * status - See the Sabre_CalDAV_SharingPlugin::STATUS_ constants.
|
||||||
|
* * readOnly - boolean
|
||||||
|
* * summary - Optional, a description for the share
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getShares() {
|
||||||
|
|
||||||
|
return $this->caldavBackend->getShares($this->calendarInfo['id']);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks this calendar as published.
|
||||||
|
*
|
||||||
|
* Publishing a calendar should automatically create a read-only, public,
|
||||||
|
* subscribable calendar.
|
||||||
|
*
|
||||||
|
* @param bool $value
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setPublishStatus($value) {
|
||||||
|
|
||||||
|
$this->caldavBackend->setPublishStatus($this->calendarInfo['id'], $value);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object represents a CalDAV calendar that is shared by a different user.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_SharedCalendar extends Sabre_CalDAV_Calendar implements Sabre_CalDAV_ISharedCalendar {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||||
|
* @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
|
||||||
|
* @param array $calendarInfo
|
||||||
|
*/
|
||||||
|
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $calendarInfo) {
|
||||||
|
|
||||||
|
$required = array(
|
||||||
|
'{http://calendarserver.org/ns/}shared-url',
|
||||||
|
'{http://sabredav.org/ns}owner-principal',
|
||||||
|
'{http://sabredav.org/ns}read-only',
|
||||||
|
);
|
||||||
|
foreach($required as $r) {
|
||||||
|
if (!isset($calendarInfo[$r])) {
|
||||||
|
throw new InvalidArgumentException('The ' . $r . ' property must be specified for SharedCalendar(s)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($principalBackend, $caldavBackend, $calendarInfo);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should return the url of the owners' copy of the shared
|
||||||
|
* calendar.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getSharedUrl() {
|
||||||
|
|
||||||
|
return $this->calendarInfo['{http://calendarserver.org/ns/}shared-url'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the owner principal
|
||||||
|
*
|
||||||
|
* This must be a url to a principal, or null if there's no owner
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getOwner() {
|
||||||
|
|
||||||
|
return $this->calendarInfo['{http://sabredav.org/ns}owner-principal'];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of ACE's for this node.
|
||||||
|
*
|
||||||
|
* Each ACE has the following properties:
|
||||||
|
* * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
|
||||||
|
* currently the only supported privileges
|
||||||
|
* * 'principal', a url to the principal who owns the node
|
||||||
|
* * 'protected' (optional), indicating that this ACE is not allowed to
|
||||||
|
* be updated.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getACL() {
|
||||||
|
|
||||||
|
// The top-level ACL only contains access information for the true
|
||||||
|
// owner of the calendar, so we need to add the information for the
|
||||||
|
// sharee.
|
||||||
|
$acl = parent::getACL();
|
||||||
|
$acl[] = array(
|
||||||
|
'privilege' => '{DAV:}read',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'],
|
||||||
|
'protected' => true,
|
||||||
|
);
|
||||||
|
if (!$this->calendarInfo['{http://sabredav.org/ns}read-only']) {
|
||||||
|
$acl[] = array(
|
||||||
|
'privilege' => '{DAV:}write',
|
||||||
|
'principal' => $this->calendarInfo['principaluri'],
|
||||||
|
'protected' => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $acl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,475 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This plugin implements support for caldav sharing.
|
||||||
|
*
|
||||||
|
* This spec is defined at:
|
||||||
|
* http://svn.calendarserver.org/repository/calendarserver/CalendarServer/trunk/doc/Extensions/caldav-sharing.txt
|
||||||
|
*
|
||||||
|
* See:
|
||||||
|
* Sabre_CalDAV_Backend_SharingSupport for all the documentation.
|
||||||
|
*
|
||||||
|
* Note: This feature is experimental, and may change in between different
|
||||||
|
* SabreDAV versions.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CalDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CalDAV_SharingPlugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These are the various status constants used by sharing-messages.
|
||||||
|
*/
|
||||||
|
const STATUS_ACCEPTED = 1;
|
||||||
|
const STATUS_DECLINED = 2;
|
||||||
|
const STATUS_DELETED = 3;
|
||||||
|
const STATUS_NORESPONSE = 4;
|
||||||
|
const STATUS_INVALID = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to SabreDAV server object.
|
||||||
|
*
|
||||||
|
* @var Sabre_DAV_Server
|
||||||
|
*/
|
||||||
|
protected $server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should return a list of server-features.
|
||||||
|
*
|
||||||
|
* This is for example 'versioning' and is added to the DAV: header
|
||||||
|
* in an OPTIONS response.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getFeatures() {
|
||||||
|
|
||||||
|
return array('calendarserver-sharing');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a plugin name.
|
||||||
|
*
|
||||||
|
* Using this name other plugins will be able to access other plugins
|
||||||
|
* using Sabre_DAV_Server::getPlugin
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPluginName() {
|
||||||
|
|
||||||
|
return 'caldav-sharing';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This initializes the plugin.
|
||||||
|
*
|
||||||
|
* This function is called by Sabre_DAV_Server, after
|
||||||
|
* addPlugin is called.
|
||||||
|
*
|
||||||
|
* This method should set up the required event subscriptions.
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function initialize(Sabre_DAV_Server $server) {
|
||||||
|
|
||||||
|
$this->server = $server;
|
||||||
|
//$server->resourceTypeMapping['Sabre_CalDAV_IShareableCalendar'] = '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-owner';
|
||||||
|
$server->resourceTypeMapping['Sabre_CalDAV_ISharedCalendar'] = '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared';
|
||||||
|
|
||||||
|
array_push(
|
||||||
|
$this->server->protectedProperties,
|
||||||
|
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}invite',
|
||||||
|
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes',
|
||||||
|
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-url'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties'));
|
||||||
|
$this->server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties'));
|
||||||
|
$this->server->subscribeEvent('updateProperties', array($this, 'updateProperties'));
|
||||||
|
$this->server->subscribeEvent('unknownMethod', array($this,'unknownMethod'));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is triggered when properties are requested for a certain
|
||||||
|
* node.
|
||||||
|
*
|
||||||
|
* This allows us to inject any properties early.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param Sabre_DAV_INode $node
|
||||||
|
* @param array $requestedProperties
|
||||||
|
* @param array $returnedProperties
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function beforeGetProperties($path, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {
|
||||||
|
|
||||||
|
if ($node instanceof Sabre_CalDAV_IShareableCalendar) {
|
||||||
|
if (($index = array_search('{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}invite', $requestedProperties))!==false) {
|
||||||
|
|
||||||
|
unset($requestedProperties[$index]);
|
||||||
|
$returnedProperties[200]['{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}invite'] =
|
||||||
|
new Sabre_CalDAV_Property_Invite(
|
||||||
|
$node->getShares()
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if ($node instanceof Sabre_CalDAV_ISharedCalendar) {
|
||||||
|
if (($index = array_search('{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-url', $requestedProperties))!==false) {
|
||||||
|
|
||||||
|
unset($requestedProperties[$index]);
|
||||||
|
$returnedProperties[200]['{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-url'] =
|
||||||
|
new Sabre_DAV_Property_Href(
|
||||||
|
$node->getSharedUrl()
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is triggered *after* all properties have been retrieved.
|
||||||
|
* This allows us to inject the correct resourcetype for calendars that
|
||||||
|
* have been shared.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* @param array $properties
|
||||||
|
* @param Sabre_DAV_INode $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function afterGetProperties($path, &$properties, Sabre_DAV_INode $node) {
|
||||||
|
|
||||||
|
if ($node instanceof Sabre_CalDAV_IShareableCalendar) {
|
||||||
|
if (isset($properties[200]['{DAV:}resourcetype'])) {
|
||||||
|
if (count($node->getShares())>0) {
|
||||||
|
$properties[200]['{DAV:}resourcetype']->add(
|
||||||
|
'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-owner'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$propName = '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes';
|
||||||
|
if (array_key_exists($propName, $properties[404])) {
|
||||||
|
unset($properties[404][$propName]);
|
||||||
|
$properties[200][$propName] = new Sabre_CalDAV_Property_AllowedSharingModes(true,false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is trigged when a user attempts to update a node's
|
||||||
|
* properties.
|
||||||
|
*
|
||||||
|
* A previous draft of the sharing spec stated that it was possible to use
|
||||||
|
* PROPPATCH to remove 'shared-owner' from the resourcetype, thus unsharing
|
||||||
|
* the calendar.
|
||||||
|
*
|
||||||
|
* Even though this is no longer in the current spec, we keep this around
|
||||||
|
* because OS X 10.7 may still make use of this feature.
|
||||||
|
*
|
||||||
|
* @param array $mutations
|
||||||
|
* @param array $result
|
||||||
|
* @param Sabre_DAV_INode $node
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function updateProperties(array &$mutations, array &$result, Sabre_DAV_INode $node) {
|
||||||
|
|
||||||
|
if (!$node instanceof Sabre_CalDAV_IShareableCalendar)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!isset($mutations['{DAV:}resourcetype'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only doing something if shared-owner is indeed not in the list.
|
||||||
|
if($mutations['{DAV:}resourcetype']->is('{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}shared-owner')) return;
|
||||||
|
|
||||||
|
$shares = $node->getShares();
|
||||||
|
$remove = array();
|
||||||
|
foreach($shares as $share) {
|
||||||
|
$remove[] = $share['href'];
|
||||||
|
}
|
||||||
|
$node->updateShares(array(), $remove);
|
||||||
|
|
||||||
|
// We're marking this update as 200 OK
|
||||||
|
$result[200]['{DAV:}resourcetype'] = null;
|
||||||
|
|
||||||
|
// Removing it from the mutations list
|
||||||
|
unset($mutations['{DAV:}resourcetype']);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is triggered when the server didn't know how to handle a
|
||||||
|
* certain request.
|
||||||
|
*
|
||||||
|
* We intercept this to handle POST requests on calendars.
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string $uri
|
||||||
|
* @return null|bool
|
||||||
|
*/
|
||||||
|
public function unknownMethod($method, $uri) {
|
||||||
|
|
||||||
|
if ($method!=='POST') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only handling xml
|
||||||
|
$contentType = $this->server->httpRequest->getHeader('Content-Type');
|
||||||
|
if (strpos($contentType,'application/xml')===false && strpos($contentType,'text/xml')===false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Making sure the node exists
|
||||||
|
try {
|
||||||
|
$node = $this->server->tree->getNodeForPath($uri);
|
||||||
|
} catch (Sabre_DAV_Exception_NotFound $e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$dom = Sabre_DAV_XMLUtil::loadDOMDocument($this->server->httpRequest->getBody(true));
|
||||||
|
|
||||||
|
$documentType = Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild);
|
||||||
|
|
||||||
|
switch($documentType) {
|
||||||
|
|
||||||
|
// Dealing with the 'share' document, which modified invitees on a
|
||||||
|
// calendar.
|
||||||
|
case '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}share' :
|
||||||
|
|
||||||
|
// We can only deal with IShareableCalendar objects
|
||||||
|
if (!$node instanceof Sabre_CalDAV_IShareableCalendar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting ACL info
|
||||||
|
$acl = $this->server->getPlugin('acl');
|
||||||
|
|
||||||
|
// If there's no ACL support, we allow everything
|
||||||
|
if ($acl) {
|
||||||
|
$acl->checkPrivileges($uri, '{DAV:}write');
|
||||||
|
}
|
||||||
|
|
||||||
|
$mutations = $this->parseShareRequest($dom);
|
||||||
|
|
||||||
|
$node->updateShares($mutations[0], $mutations[1]);
|
||||||
|
|
||||||
|
$this->server->httpResponse->sendStatus(200);
|
||||||
|
// Adding this because sending a response body may cause issues,
|
||||||
|
// and I wanted some type of indicator the response was handled.
|
||||||
|
$this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well');
|
||||||
|
|
||||||
|
// Breaking the event chain
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// The invite-reply document is sent when the user replies to an
|
||||||
|
// invitation of a calendar share.
|
||||||
|
case '{'. Sabre_CalDAV_Plugin::NS_CALENDARSERVER.'}invite-reply' :
|
||||||
|
|
||||||
|
// This only works on the calendar-home-root node.
|
||||||
|
if (!$node instanceof Sabre_CalDAV_UserCalendars) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting ACL info
|
||||||
|
$acl = $this->server->getPlugin('acl');
|
||||||
|
|
||||||
|
// If there's no ACL support, we allow everything
|
||||||
|
if ($acl) {
|
||||||
|
$acl->checkPrivileges($uri, '{DAV:}write');
|
||||||
|
}
|
||||||
|
|
||||||
|
$message = $this->parseInviteReplyRequest($dom);
|
||||||
|
|
||||||
|
$url = $node->shareReply(
|
||||||
|
$message['href'],
|
||||||
|
$message['status'],
|
||||||
|
$message['calendarUri'],
|
||||||
|
$message['inReplyTo'],
|
||||||
|
$message['summary']
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->server->httpResponse->sendStatus(200);
|
||||||
|
// Adding this because sending a response body may cause issues,
|
||||||
|
// and I wanted some type of indicator the response was handled.
|
||||||
|
$this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well');
|
||||||
|
|
||||||
|
if ($url) {
|
||||||
|
$dom = new DOMDocument('1.0', 'UTF-8');
|
||||||
|
$dom->formatOutput = true;
|
||||||
|
|
||||||
|
$root = $dom->createElement('cs:shared-as');
|
||||||
|
foreach($this->server->xmlNamespaces as $namespace => $prefix) {
|
||||||
|
$root->setAttribute('xmlns:' . $prefix, $namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dom->appendChild($root);
|
||||||
|
$href = new Sabre_DAV_Property_Href($url);
|
||||||
|
|
||||||
|
$href->serialize($this->server, $root);
|
||||||
|
$this->server->httpResponse->setHeader('Content-Type','application/xml');
|
||||||
|
$this->server->httpResponse->sendBody($dom->saveXML());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Breaking the event chain
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}publish-calendar' :
|
||||||
|
|
||||||
|
// We can only deal with IShareableCalendar objects
|
||||||
|
if (!$node instanceof Sabre_CalDAV_IShareableCalendar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting ACL info
|
||||||
|
$acl = $this->server->getPlugin('acl');
|
||||||
|
|
||||||
|
// If there's no ACL support, we allow everything
|
||||||
|
if ($acl) {
|
||||||
|
$acl->checkPrivileges($uri, '{DAV:}write');
|
||||||
|
}
|
||||||
|
|
||||||
|
$node->setPublishStatus(true);
|
||||||
|
|
||||||
|
// iCloud sends back the 202, so we will too.
|
||||||
|
$this->server->httpResponse->sendStatus(202);
|
||||||
|
|
||||||
|
// Adding this because sending a response body may cause issues,
|
||||||
|
// and I wanted some type of indicator the response was handled.
|
||||||
|
$this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well');
|
||||||
|
|
||||||
|
// Breaking the event chain
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}unpublish-calendar' :
|
||||||
|
|
||||||
|
// We can only deal with IShareableCalendar objects
|
||||||
|
if (!$node instanceof Sabre_CalDAV_IShareableCalendar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getting ACL info
|
||||||
|
$acl = $this->server->getPlugin('acl');
|
||||||
|
|
||||||
|
// If there's no ACL support, we allow everything
|
||||||
|
if ($acl) {
|
||||||
|
$acl->checkPrivileges($uri, '{DAV:}write');
|
||||||
|
}
|
||||||
|
|
||||||
|
$node->setPublishStatus(false);
|
||||||
|
|
||||||
|
$this->server->httpResponse->sendStatus(200);
|
||||||
|
|
||||||
|
// Adding this because sending a response body may cause issues,
|
||||||
|
// and I wanted some type of indicator the response was handled.
|
||||||
|
$this->server->httpResponse->setHeader('X-Sabre-Status', 'everything-went-well');
|
||||||
|
|
||||||
|
// Breaking the event chain
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the 'share' POST request.
|
||||||
|
*
|
||||||
|
* This method returns an array, containing two arrays.
|
||||||
|
* The first array is a list of new sharees. Every element is a struct
|
||||||
|
* containing a:
|
||||||
|
* * href element. (usually a mailto: address)
|
||||||
|
* * commonName element (often a first and lastname, but can also be
|
||||||
|
* false)
|
||||||
|
* * readOnly (true or false)
|
||||||
|
* * summary (A description of the share, can also be false)
|
||||||
|
*
|
||||||
|
* The second array is a list of sharees that are to be removed. This is
|
||||||
|
* just a simple array with 'hrefs'.
|
||||||
|
*
|
||||||
|
* @param DOMDocument $dom
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function parseShareRequest(DOMDocument $dom) {
|
||||||
|
|
||||||
|
$xpath = new \DOMXPath($dom);
|
||||||
|
$xpath->registerNamespace('cs', Sabre_CalDAV_Plugin::NS_CALENDARSERVER);
|
||||||
|
$xpath->registerNamespace('d', 'DAV:');
|
||||||
|
|
||||||
|
|
||||||
|
$set = array();
|
||||||
|
$elems = $xpath->query('cs:set');
|
||||||
|
|
||||||
|
for($i=0; $i < $elems->length; $i++) {
|
||||||
|
|
||||||
|
$xset = $elems->item($i);
|
||||||
|
$set[] = array(
|
||||||
|
'href' => $xpath->evaluate('string(d:href)', $xset),
|
||||||
|
'commonName' => $xpath->evaluate('string(cs:common-name)', $xset),
|
||||||
|
'summary' => $xpath->evaluate('string(cs:summary)', $xset),
|
||||||
|
'readOnly' => $xpath->evaluate('boolean(cs:read)', $xset)!==false
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$remove = array();
|
||||||
|
$elems = $xpath->query('cs:remove');
|
||||||
|
|
||||||
|
for($i=0; $i < $elems->length; $i++) {
|
||||||
|
|
||||||
|
$xremove = $elems->item($i);
|
||||||
|
$remove[] = $xpath->evaluate('string(d:href)', $xremove);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($set, $remove);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the 'invite-reply' POST request.
|
||||||
|
*
|
||||||
|
* This method returns an array, containing the following properties:
|
||||||
|
* * href - The sharee who is replying
|
||||||
|
* * status - One of the self::STATUS_* constants
|
||||||
|
* * calendarUri - The url of the shared calendar
|
||||||
|
* * inReplyTo - The unique id of the share invitation.
|
||||||
|
* * summary - Optional description of the reply.
|
||||||
|
*
|
||||||
|
* @param DOMDocument $dom
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function parseInviteReplyRequest(DOMDocument $dom) {
|
||||||
|
|
||||||
|
$xpath = new \DOMXPath($dom);
|
||||||
|
$xpath->registerNamespace('cs', Sabre_CalDAV_Plugin::NS_CALENDARSERVER);
|
||||||
|
$xpath->registerNamespace('d', 'DAV:');
|
||||||
|
|
||||||
|
$hostHref = $xpath->evaluate('string(cs:hosturl/d:href)');
|
||||||
|
if (!$hostHref) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('The {' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}hosturl/{DAV:}href element is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'href' => $xpath->evaluate('string(d:href)'),
|
||||||
|
'calendarUri' => $this->server->calculateUri($hostHref),
|
||||||
|
'inReplyTo' => $xpath->evaluate('string(cs:in-reply-to)'),
|
||||||
|
'summary' => $xpath->evaluate('string(cs:summary)'),
|
||||||
|
'status' => $xpath->evaluate('boolean(cs:invite-accepted)')?self::STATUS_ACCEPTED:self::STATUS_DECLINED
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre
|
||||||
/**
|
/**
|
||||||
* CalDAV backend
|
* CalDAV backend
|
||||||
*
|
*
|
||||||
* @var Sabre_CalDAV_Backend_Abstract
|
* @var Sabre_CalDAV_Backend_BackendInterface
|
||||||
*/
|
*/
|
||||||
protected $caldavBackend;
|
protected $caldavBackend;
|
||||||
|
|
||||||
|
@ -36,10 +36,10 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
* @param Sabre_DAVACL_IPrincipalBackend $principalBackend
|
||||||
* @param Sabre_CalDAV_Backend_Abstract $caldavBackend
|
* @param Sabre_CalDAV_Backend_BackendInterface $caldavBackend
|
||||||
* @param mixed $userUri
|
* @param mixed $userUri
|
||||||
*/
|
*/
|
||||||
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $userUri) {
|
public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_BackendInterface $caldavBackend, $userUri) {
|
||||||
|
|
||||||
$this->principalBackend = $principalBackend;
|
$this->principalBackend = $principalBackend;
|
||||||
$this->caldavBackend = $caldavBackend;
|
$this->caldavBackend = $caldavBackend;
|
||||||
|
@ -168,9 +168,22 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre
|
||||||
$calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
|
$calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
|
||||||
$objs = array();
|
$objs = array();
|
||||||
foreach($calendars as $calendar) {
|
foreach($calendars as $calendar) {
|
||||||
|
if ($this->caldavBackend instanceof Sabre_CalDAV_Backend_SharingSupport) {
|
||||||
|
if (isset($calendar['{http://calendarserver.org/ns/}shared-url'])) {
|
||||||
|
$objs[] = new Sabre_CalDAV_SharedCalendar($this->principalBackend, $this->caldavBackend, $calendar);
|
||||||
|
} else {
|
||||||
|
$objs[] = new Sabre_CalDAV_ShareableCalendar($this->principalBackend, $this->caldavBackend, $calendar);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
$objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar);
|
$objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
$objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']);
|
$objs[] = new Sabre_CalDAV_Schedule_Outbox($this->principalInfo['uri']);
|
||||||
|
|
||||||
|
// We're adding a notifications node, if it's supported by the backend.
|
||||||
|
if ($this->caldavBackend instanceof Sabre_CalDAV_Backend_NotificationSupport) {
|
||||||
|
$objs[] = new Sabre_CalDAV_Notifications_Collection($this->caldavBackend, $this->principalInfo['uri']);
|
||||||
|
}
|
||||||
return $objs;
|
return $objs;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -185,8 +198,22 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre
|
||||||
*/
|
*/
|
||||||
public function createExtendedCollection($name, array $resourceType, array $properties) {
|
public function createExtendedCollection($name, array $resourceType, array $properties) {
|
||||||
|
|
||||||
if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) {
|
$isCalendar = false;
|
||||||
throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
|
foreach($resourceType as $rt) {
|
||||||
|
switch ($rt) {
|
||||||
|
case '{DAV:}collection' :
|
||||||
|
case '{http://calendarserver.org/ns/}shared-owner' :
|
||||||
|
// ignore
|
||||||
|
break;
|
||||||
|
case '{urn:ietf:params:xml:ns:caldav}calendar' :
|
||||||
|
$isCalendar = true;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType: ' . $rt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$isCalendar) {
|
||||||
|
throw new Sabre_DAV_Exception_InvalidResourceType('You can only create calendars in this collection');
|
||||||
}
|
}
|
||||||
$this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties);
|
$this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties);
|
||||||
|
|
||||||
|
@ -295,4 +322,27 @@ class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when a user replied to a request to share.
|
||||||
|
*
|
||||||
|
* This method should return the url of the newly created calendar if the
|
||||||
|
* share was accepted.
|
||||||
|
*
|
||||||
|
* @param string href The sharee who is replying (often a mailto: address)
|
||||||
|
* @param int status One of the SharingPlugin::STATUS_* constants
|
||||||
|
* @param string $calendarUri The url to the calendar thats being shared
|
||||||
|
* @param string $inReplyTo The unique id this message is a response to
|
||||||
|
* @param string $summary A description of the reply
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) {
|
||||||
|
|
||||||
|
if (!$this->caldavBackend instanceof Sabre_CalDAV_Backend_SharingSupport) {
|
||||||
|
throw new Sabre_DAV_Exception_NotImplemented('Sharing support is not implemented by this backend.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->caldavBackend->shareReply($href, $status, $calendarUri, $inReplyTo, $summary);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ class Sabre_CalDAV_Version {
|
||||||
/**
|
/**
|
||||||
* Full version number
|
* Full version number
|
||||||
*/
|
*/
|
||||||
const VERSION = '1.6.4';
|
const VERSION = '1.7.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stability : alpha, beta, stable
|
* Stability : alpha, beta, stable
|
||||||
|
|
|
@ -16,28 +16,47 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Begin includes
|
// Begin includes
|
||||||
include __DIR__ . '/Backend/Abstract.php';
|
include __DIR__ . '/Backend/BackendInterface.php';
|
||||||
include __DIR__ . '/Backend/PDO.php';
|
include __DIR__ . '/Backend/NotificationSupport.php';
|
||||||
|
include __DIR__ . '/Backend/SharingSupport.php';
|
||||||
include __DIR__ . '/CalendarQueryParser.php';
|
include __DIR__ . '/CalendarQueryParser.php';
|
||||||
include __DIR__ . '/CalendarQueryValidator.php';
|
include __DIR__ . '/CalendarQueryValidator.php';
|
||||||
include __DIR__ . '/CalendarRootNode.php';
|
include __DIR__ . '/CalendarRootNode.php';
|
||||||
|
include __DIR__ . '/Exception/InvalidComponentType.php';
|
||||||
include __DIR__ . '/ICalendar.php';
|
include __DIR__ . '/ICalendar.php';
|
||||||
include __DIR__ . '/ICalendarObject.php';
|
include __DIR__ . '/ICalendarObject.php';
|
||||||
include __DIR__ . '/ICSExportPlugin.php';
|
include __DIR__ . '/ICSExportPlugin.php';
|
||||||
|
include __DIR__ . '/IShareableCalendar.php';
|
||||||
|
include __DIR__ . '/ISharedCalendar.php';
|
||||||
|
include __DIR__ . '/Notifications/ICollection.php';
|
||||||
|
include __DIR__ . '/Notifications/INode.php';
|
||||||
|
include __DIR__ . '/Notifications/INotificationType.php';
|
||||||
|
include __DIR__ . '/Notifications/Node.php';
|
||||||
|
include __DIR__ . '/Notifications/Notification/Invite.php';
|
||||||
|
include __DIR__ . '/Notifications/Notification/InviteReply.php';
|
||||||
|
include __DIR__ . '/Notifications/Notification/SystemStatus.php';
|
||||||
include __DIR__ . '/Plugin.php';
|
include __DIR__ . '/Plugin.php';
|
||||||
include __DIR__ . '/Principal/Collection.php';
|
include __DIR__ . '/Principal/Collection.php';
|
||||||
include __DIR__ . '/Principal/ProxyRead.php';
|
include __DIR__ . '/Principal/ProxyRead.php';
|
||||||
include __DIR__ . '/Principal/ProxyWrite.php';
|
include __DIR__ . '/Principal/ProxyWrite.php';
|
||||||
include __DIR__ . '/Principal/User.php';
|
include __DIR__ . '/Principal/User.php';
|
||||||
|
include __DIR__ . '/Property/AllowedSharingModes.php';
|
||||||
|
include __DIR__ . '/Property/Invite.php';
|
||||||
|
include __DIR__ . '/Property/ScheduleCalendarTransp.php';
|
||||||
include __DIR__ . '/Property/SupportedCalendarComponentSet.php';
|
include __DIR__ . '/Property/SupportedCalendarComponentSet.php';
|
||||||
include __DIR__ . '/Property/SupportedCalendarData.php';
|
include __DIR__ . '/Property/SupportedCalendarData.php';
|
||||||
include __DIR__ . '/Property/SupportedCollationSet.php';
|
include __DIR__ . '/Property/SupportedCollationSet.php';
|
||||||
include __DIR__ . '/Schedule/IMip.php';
|
include __DIR__ . '/Schedule/IMip.php';
|
||||||
include __DIR__ . '/Schedule/IOutbox.php';
|
include __DIR__ . '/Schedule/IOutbox.php';
|
||||||
include __DIR__ . '/Schedule/Outbox.php';
|
include __DIR__ . '/Schedule/Outbox.php';
|
||||||
include __DIR__ . '/Server.php';
|
include __DIR__ . '/SharingPlugin.php';
|
||||||
include __DIR__ . '/UserCalendars.php';
|
include __DIR__ . '/UserCalendars.php';
|
||||||
include __DIR__ . '/Version.php';
|
include __DIR__ . '/Version.php';
|
||||||
|
include __DIR__ . '/Backend/Abstract.php';
|
||||||
|
include __DIR__ . '/Backend/PDO.php';
|
||||||
include __DIR__ . '/Calendar.php';
|
include __DIR__ . '/Calendar.php';
|
||||||
include __DIR__ . '/CalendarObject.php';
|
include __DIR__ . '/CalendarObject.php';
|
||||||
|
include __DIR__ . '/Notifications/Collection.php';
|
||||||
|
include __DIR__ . '/ShareableCalendar.php';
|
||||||
|
include __DIR__ . '/SharedCalendar.php';
|
||||||
// End includes
|
// End includes
|
||||||
|
|
|
@ -55,7 +55,7 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca
|
||||||
* Returns a card
|
* Returns a card
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @return Sabre_DAV_Card
|
* @return Sabre_CardDAV_ICard
|
||||||
*/
|
*/
|
||||||
public function getChild($name) {
|
public function getChild($name) {
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_Ca
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param resource $vcardData
|
* @param resource $vcardData
|
||||||
* @return void|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function createFile($name,$vcardData = null) {
|
public function createFile($name,$vcardData = null) {
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard,
|
||||||
* Updates the VCard-formatted object
|
* Updates the VCard-formatted object
|
||||||
*
|
*
|
||||||
* @param string $cardData
|
* @param string $cardData
|
||||||
* @return void
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
public function put($cardData) {
|
public function put($cardData) {
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard,
|
||||||
*/
|
*/
|
||||||
public function getContentType() {
|
public function getContentType() {
|
||||||
|
|
||||||
return 'text/x-vcard';
|
return 'text/x-vcard; charset=utf-8';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,13 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard,
|
||||||
if (isset($this->cardData['etag'])) {
|
if (isset($this->cardData['etag'])) {
|
||||||
return $this->cardData['etag'];
|
return $this->cardData['etag'];
|
||||||
} else {
|
} else {
|
||||||
return '"' . md5($this->get()) . '"';
|
$data = $this->get();
|
||||||
|
if (is_string($data)) {
|
||||||
|
return '"' . md5($data) . '"';
|
||||||
|
} else {
|
||||||
|
// We refuse to calculate the md5 if it's a stream.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -136,7 +142,7 @@ class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard,
|
||||||
/**
|
/**
|
||||||
* Returns the last modification date as a unix timestamp
|
* Returns the last modification date as a unix timestamp
|
||||||
*
|
*
|
||||||
* @return time
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function getLastModified() {
|
public function getLastModified() {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CardDAV plugin
|
* CardDAV plugin
|
||||||
*
|
*
|
||||||
|
@ -48,6 +50,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
/* Events */
|
/* Events */
|
||||||
$server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties'));
|
$server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties'));
|
||||||
|
$server->subscribeEvent('afterGetProperties', array($this, 'afterGetProperties'));
|
||||||
$server->subscribeEvent('updateProperties', array($this, 'updateProperties'));
|
$server->subscribeEvent('updateProperties', array($this, 'updateProperties'));
|
||||||
$server->subscribeEvent('report', array($this,'report'));
|
$server->subscribeEvent('report', array($this,'report'));
|
||||||
$server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
|
$server->subscribeEvent('onHTMLActionsPanel', array($this,'htmlActionsPanel'));
|
||||||
|
@ -153,10 +156,6 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
if (is_resource($val))
|
if (is_resource($val))
|
||||||
$val = stream_get_contents($val);
|
$val = stream_get_contents($val);
|
||||||
|
|
||||||
// Taking out \r to not screw up the xml output
|
|
||||||
//$returnedProperties[200][$addressDataProp] = str_replace("\r","", $val);
|
|
||||||
// The stripping of \r breaks the Mail App in OSX Mountain Lion
|
|
||||||
// this is fixed in master, but not backported. /Tanghus
|
|
||||||
$returnedProperties[200][$addressDataProp] = $val;
|
$returnedProperties[200][$addressDataProp] = $val;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -190,7 +189,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
* @param array $mutations
|
* @param array $mutations
|
||||||
* @param array $result
|
* @param array $result
|
||||||
* @param Sabre_DAV_INode $node
|
* @param Sabre_DAV_INode $node
|
||||||
* @return void
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function updateProperties(&$mutations, &$result, $node) {
|
public function updateProperties(&$mutations, &$result, $node) {
|
||||||
|
|
||||||
|
@ -272,7 +271,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
$properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
|
$properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
|
||||||
|
|
||||||
$hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
|
$hrefElems = $dom->getElementsByTagNameNS('DAV:','href');
|
||||||
$propertyList = array();
|
$propertyList = array();
|
||||||
|
|
||||||
foreach($hrefElems as $elem) {
|
foreach($hrefElems as $elem) {
|
||||||
|
@ -282,9 +281,12 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$prefer = $this->server->getHTTPPRefer();
|
||||||
|
|
||||||
$this->server->httpResponse->sendStatus(207);
|
$this->server->httpResponse->sendStatus(207);
|
||||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||||
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList));
|
$this->server->httpResponse->setHeader('Vary','Brief,Prefer');
|
||||||
|
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal']));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,9 +350,9 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
$vobj = Sabre_VObject_Reader::read($data);
|
$vobj = VObject\Reader::read($data);
|
||||||
|
|
||||||
} catch (Sabre_VObject_ParseException $e) {
|
} catch (VObject\ParseException $e) {
|
||||||
|
|
||||||
throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage());
|
throw new Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage());
|
||||||
|
|
||||||
|
@ -360,6 +362,10 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.');
|
throw new Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isset($vobj->UID)) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('Every vcard must have an UID.');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -424,9 +430,12 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$prefer = $this->server->getHTTPPRefer();
|
||||||
|
|
||||||
$this->server->httpResponse->sendStatus(207);
|
$this->server->httpResponse->sendStatus(207);
|
||||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||||
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($result));
|
$this->server->httpResponse->setHeader('Vary','Brief,Prefer');
|
||||||
|
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal']));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,7 +449,7 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
*/
|
*/
|
||||||
public function validateFilters($vcardData, array $filters, $test) {
|
public function validateFilters($vcardData, array $filters, $test) {
|
||||||
|
|
||||||
$vcard = Sabre_VObject_Reader::read($vcardData);
|
$vcard = VObject\Reader::read($vcardData);
|
||||||
|
|
||||||
if (!$filters) return true;
|
if (!$filters) return true;
|
||||||
|
|
||||||
|
@ -615,6 +624,30 @@ class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is triggered after webdav-properties have been retrieved.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function afterGetProperties($uri, &$properties) {
|
||||||
|
|
||||||
|
// If the request was made using the SOGO connector, we must rewrite
|
||||||
|
// the content-type property. By default SabreDAV will send back
|
||||||
|
// text/x-vcard; charset=utf-8, but for SOGO we must strip that last
|
||||||
|
// part.
|
||||||
|
if (!isset($properties[200]['{DAV:}getcontenttype']))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (strpos($this->server->httpRequest->getHeader('User-Agent'),'Thunderbird')===false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($properties[200]['{DAV:}getcontenttype'],'text/x-vcard')===0) {
|
||||||
|
$properties[200]['{DAV:}getcontenttype'] = 'text/x-vcard';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to generate HTML output for the
|
* This method is used to generate HTML output for the
|
||||||
* Sabre_DAV_Browser_Plugin. This allows us to generate an interface users
|
* Sabre_DAV_Browser_Plugin. This allows us to generate an interface users
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Sabre\VObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VCF Exporter
|
||||||
|
*
|
||||||
|
* This plugin adds the ability to export entire address books as .vcf files.
|
||||||
|
* This is useful for clients that don't support CardDAV yet. They often do
|
||||||
|
* support vcf files.
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage CardDAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @author Thomas Tanghus (http://tanghus.net/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_CardDAV_VCFExportPlugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to Server class
|
||||||
|
*
|
||||||
|
* @var Sabre_DAV_Server
|
||||||
|
*/
|
||||||
|
private $server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the plugin and registers event handlers
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function initialize(Sabre_DAV_Server $server) {
|
||||||
|
|
||||||
|
$this->server = $server;
|
||||||
|
$this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'beforeMethod' event handles. This event handles intercepts GET requests ending
|
||||||
|
* with ?export
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string $uri
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function beforeMethod($method, $uri) {
|
||||||
|
|
||||||
|
if ($method!='GET') return;
|
||||||
|
if ($this->server->httpRequest->getQueryString()!='export') return;
|
||||||
|
|
||||||
|
// splitting uri
|
||||||
|
list($uri) = explode('?',$uri,2);
|
||||||
|
|
||||||
|
$node = $this->server->tree->getNodeForPath($uri);
|
||||||
|
|
||||||
|
if (!($node instanceof Sabre_CardDAV_IAddressBook)) return;
|
||||||
|
|
||||||
|
// Checking ACL, if available.
|
||||||
|
if ($aclPlugin = $this->server->getPlugin('acl')) {
|
||||||
|
$aclPlugin->checkPrivileges($uri, '{DAV:}read');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->server->httpResponse->setHeader('Content-Type','text/directory');
|
||||||
|
$this->server->httpResponse->sendStatus(200);
|
||||||
|
|
||||||
|
$nodes = $this->server->getPropertiesForPath($uri, array(
|
||||||
|
'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data',
|
||||||
|
),1);
|
||||||
|
|
||||||
|
$this->server->httpResponse->sendBody($this->generateVCF($nodes));
|
||||||
|
|
||||||
|
// Returning false to break the event chain
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges all vcard objects, and builds one big vcf export
|
||||||
|
*
|
||||||
|
* @param array $nodes
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function generateVCF(array $nodes) {
|
||||||
|
|
||||||
|
$output = "";
|
||||||
|
|
||||||
|
foreach($nodes as $node) {
|
||||||
|
|
||||||
|
if (!isset($node[200]['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$nodeData = $node[200]['{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}address-data'];
|
||||||
|
|
||||||
|
// Parsing this node so VObject can clean up the output.
|
||||||
|
$output .=
|
||||||
|
VObject\Reader::read($nodeData)->serialize();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ class Sabre_CardDAV_Version {
|
||||||
/**
|
/**
|
||||||
* Full version number
|
* Full version number
|
||||||
*/
|
*/
|
||||||
const VERSION = '1.6.3';
|
const VERSION = '1.7.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stability : alpha, beta, stable
|
* Stability : alpha, beta, stable
|
||||||
|
|
|
@ -26,6 +26,7 @@ include __DIR__ . '/IDirectory.php';
|
||||||
include __DIR__ . '/Plugin.php';
|
include __DIR__ . '/Plugin.php';
|
||||||
include __DIR__ . '/Property/SupportedAddressData.php';
|
include __DIR__ . '/Property/SupportedAddressData.php';
|
||||||
include __DIR__ . '/UserAddressBooks.php';
|
include __DIR__ . '/UserAddressBooks.php';
|
||||||
|
include __DIR__ . '/VCFExportPlugin.php';
|
||||||
include __DIR__ . '/Version.php';
|
include __DIR__ . '/Version.php';
|
||||||
include __DIR__ . '/AddressBook.php';
|
include __DIR__ . '/AddressBook.php';
|
||||||
include __DIR__ . '/Card.php';
|
include __DIR__ . '/Card.php';
|
||||||
|
|
|
@ -338,7 +338,7 @@ class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
$icon = '';
|
$icon = '';
|
||||||
|
|
||||||
if ($this->enableAssets) {
|
if ($this->enableAssets) {
|
||||||
$node = $parent->getChild($name);
|
$node = $this->server->tree->getNodeForPath(($path?$path.'/':'') . $name);
|
||||||
foreach(array_reverse($this->iconMap) as $class=>$iconName) {
|
foreach(array_reverse($this->iconMap) as $class=>$iconName) {
|
||||||
|
|
||||||
if ($node instanceof $class) {
|
if ($node instanceof $class) {
|
||||||
|
|
|
@ -16,12 +16,25 @@
|
||||||
*/
|
*/
|
||||||
class Sabre_DAV_Client {
|
class Sabre_DAV_Client {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The propertyMap is a key-value array.
|
||||||
|
*
|
||||||
|
* If you use the propertyMap, any {DAV:}multistatus responses with the
|
||||||
|
* proeprties listed in this array, will automatically be mapped to a
|
||||||
|
* respective class.
|
||||||
|
*
|
||||||
|
* The {DAV:}resourcetype property is automatically added. This maps to
|
||||||
|
* Sabre_DAV_Property_ResourceType
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
public $propertyMap = array();
|
public $propertyMap = array();
|
||||||
|
|
||||||
protected $baseUri;
|
protected $baseUri;
|
||||||
protected $userName;
|
protected $userName;
|
||||||
protected $password;
|
protected $password;
|
||||||
protected $proxy;
|
protected $proxy;
|
||||||
|
protected $trustedCertificates;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic authentication
|
* Basic authentication
|
||||||
|
@ -87,6 +100,18 @@ class Sabre_DAV_Client {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add trusted root certificates to the webdav client.
|
||||||
|
*
|
||||||
|
* The parameter certificates should be a absulute path to a file
|
||||||
|
* which contains all trusted certificates
|
||||||
|
*
|
||||||
|
* @param string $certificates
|
||||||
|
*/
|
||||||
|
public function addTrustedCertificates($certificates) {
|
||||||
|
$this->trustedCertificates = $certificates;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Does a PROPFIND request
|
* Does a PROPFIND request
|
||||||
*
|
*
|
||||||
|
@ -143,13 +168,13 @@ class Sabre_DAV_Client {
|
||||||
if ($depth===0) {
|
if ($depth===0) {
|
||||||
reset($result);
|
reset($result);
|
||||||
$result = current($result);
|
$result = current($result);
|
||||||
return $result[200];
|
return isset($result[200])?$result[200]:array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$newResult = array();
|
$newResult = array();
|
||||||
foreach($result as $href => $statusList) {
|
foreach($result as $href => $statusList) {
|
||||||
|
|
||||||
$newResult[$href] = $statusList[200];
|
$newResult[$href] = isset($statusList[200])?$statusList[200]:array();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +304,10 @@ class Sabre_DAV_Client {
|
||||||
CURLOPT_MAXREDIRS => 5,
|
CURLOPT_MAXREDIRS => 5,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if($this->trustedCertificates) {
|
||||||
|
$curlSettings[CURLOPT_CAINFO] = $this->trustedCertificates;
|
||||||
|
}
|
||||||
|
|
||||||
switch ($method) {
|
switch ($method) {
|
||||||
case 'HEAD' :
|
case 'HEAD' :
|
||||||
|
|
||||||
|
@ -363,10 +392,30 @@ class Sabre_DAV_Client {
|
||||||
|
|
||||||
if ($response['statusCode']>=400) {
|
if ($response['statusCode']>=400) {
|
||||||
switch ($response['statusCode']) {
|
switch ($response['statusCode']) {
|
||||||
|
case 400 :
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('Bad request');
|
||||||
|
case 401 :
|
||||||
|
throw new Sabre_DAV_Exception_NotAuthenticated('Not authenticated');
|
||||||
|
case 402 :
|
||||||
|
throw new Sabre_DAV_Exception_PaymentRequired('Payment required');
|
||||||
|
case 403 :
|
||||||
|
throw new Sabre_DAV_Exception_Forbidden('Forbidden');
|
||||||
case 404:
|
case 404:
|
||||||
throw new Sabre_DAV_Exception_NotFound('Resource ' . $url . ' not found.');
|
throw new Sabre_DAV_Exception_NotFound('Resource not found.');
|
||||||
break;
|
case 405 :
|
||||||
|
throw new Sabre_DAV_Exception_MethodNotAllowed('Method not allowed');
|
||||||
|
case 409 :
|
||||||
|
throw new Sabre_DAV_Exception_Conflict('Conflict');
|
||||||
|
case 412 :
|
||||||
|
throw new Sabre_DAV_Exception_PreconditionFailed('Precondition failed');
|
||||||
|
case 416 :
|
||||||
|
throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('Requested Range Not Satisfiable');
|
||||||
|
case 500 :
|
||||||
|
throw new Sabre_DAV_Exception('Internal server error');
|
||||||
|
case 501 :
|
||||||
|
throw new Sabre_DAV_Exception_NotImplemented('Not Implemented');
|
||||||
|
case 507 :
|
||||||
|
throw new Sabre_DAV_Exception_InsufficientStorage('Insufficient storage');
|
||||||
default:
|
default:
|
||||||
throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')');
|
throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')');
|
||||||
}
|
}
|
||||||
|
@ -386,6 +435,7 @@ class Sabre_DAV_Client {
|
||||||
* @param array $settings
|
* @param array $settings
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
protected function curlRequest($url, $settings) {
|
protected function curlRequest($url, $settings) {
|
||||||
|
|
||||||
$curl = curl_init($url);
|
$curl = curl_init($url);
|
||||||
|
@ -399,6 +449,7 @@ class Sabre_DAV_Client {
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the full url based on the given url (which may be relative). All
|
* Returns the full url based on the given url (which may be relative). All
|
||||||
|
@ -453,19 +504,17 @@ class Sabre_DAV_Client {
|
||||||
*/
|
*/
|
||||||
public function parseMultiStatus($body) {
|
public function parseMultiStatus($body) {
|
||||||
|
|
||||||
$body = Sabre_DAV_XMLUtil::convertDAVNamespace($body);
|
|
||||||
|
|
||||||
$responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA);
|
$responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA);
|
||||||
if ($responseXML===false) {
|
if ($responseXML===false) {
|
||||||
throw new InvalidArgumentException('The passed data is not valid XML');
|
throw new InvalidArgumentException('The passed data is not valid XML');
|
||||||
}
|
}
|
||||||
|
|
||||||
$responseXML->registerXPathNamespace('d', 'urn:DAV');
|
$responseXML->registerXPathNamespace('d', 'DAV:');
|
||||||
|
|
||||||
$propResult = array();
|
$propResult = array();
|
||||||
|
|
||||||
foreach($responseXML->xpath('d:response') as $response) {
|
foreach($responseXML->xpath('d:response') as $response) {
|
||||||
$response->registerXPathNamespace('d', 'urn:DAV');
|
$response->registerXPathNamespace('d', 'DAV:');
|
||||||
$href = $response->xpath('d:href');
|
$href = $response->xpath('d:href');
|
||||||
$href = (string)$href[0];
|
$href = (string)$href[0];
|
||||||
|
|
||||||
|
@ -473,7 +522,7 @@ class Sabre_DAV_Client {
|
||||||
|
|
||||||
foreach($response->xpath('d:propstat') as $propStat) {
|
foreach($response->xpath('d:propstat') as $propStat) {
|
||||||
|
|
||||||
$propStat->registerXPathNamespace('d', 'urn:DAV');
|
$propStat->registerXPathNamespace('d', 'DAV:');
|
||||||
$status = $propStat->xpath('d:status');
|
$status = $propStat->xpath('d:status');
|
||||||
list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3);
|
list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3);
|
||||||
|
|
||||||
|
|
|
@ -17,9 +17,13 @@ abstract class Sabre_DAV_Collection extends Sabre_DAV_Node implements Sabre_DAV_
|
||||||
/**
|
/**
|
||||||
* Returns a child object, by its name.
|
* Returns a child object, by its name.
|
||||||
*
|
*
|
||||||
* This method makes use of the getChildren method to grab all the child nodes, and compares the name.
|
* This method makes use of the getChildren method to grab all the child
|
||||||
|
* nodes, and compares the name.
|
||||||
* Generally its wise to override this, as this can usually be optimized
|
* Generally its wise to override this, as this can usually be optimized
|
||||||
*
|
*
|
||||||
|
* This method must throw Sabre_DAV_Exception_NotFound if the node does not
|
||||||
|
* exist.
|
||||||
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @throws Sabre_DAV_Exception_NotFound
|
* @throws Sabre_DAV_Exception_NotFound
|
||||||
* @return Sabre_DAV_INode
|
* @return Sabre_DAV_INode
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Directory class
|
|
||||||
*
|
|
||||||
* This class is now deprecated in favor of the Sabre_DAV_Collection class.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage DAV
|
|
||||||
* @deprecated Use Sabre_DAV_Collection instead
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
abstract class Sabre_DAV_Directory extends Sabre_DAV_Collection {
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ReportNotImplemented
|
* ReportNotSupported
|
||||||
*
|
*
|
||||||
* This exception is thrown when the client requested an unknown report through the REPORT method
|
* This exception is thrown when the client requested an unknown report through the REPORT method
|
||||||
*
|
*
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
*/
|
*/
|
||||||
class Sabre_DAV_Exception_ReportNotImplemented extends Sabre_DAV_Exception_NotImplemented {
|
class Sabre_DAV_Exception_ReportNotSupported extends Sabre_DAV_Exception_Forbidden {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method allows the exception to include additional information into the WebDAV error response
|
* This method allows the exception to include additional information into the WebDAV error response
|
|
@ -17,7 +17,7 @@ class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICol
|
||||||
* Data will either be supplied as a stream resource, or in certain cases
|
* Data will either be supplied as a stream resource, or in certain cases
|
||||||
* as a string. Keep in mind that you may have to support either.
|
* as a string. Keep in mind that you may have to support either.
|
||||||
*
|
*
|
||||||
* After succesful creation of the file, you may choose to return the ETag
|
* After successful creation of the file, you may choose to return the ETag
|
||||||
* of the new file here.
|
* of the new file here.
|
||||||
*
|
*
|
||||||
* The returned ETag must be surrounded by double-quotes (The quotes should
|
* The returned ETag must be surrounded by double-quotes (The quotes should
|
||||||
|
@ -58,6 +58,9 @@ class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICol
|
||||||
/**
|
/**
|
||||||
* Returns a specific child node, referenced by its name
|
* Returns a specific child node, referenced by its name
|
||||||
*
|
*
|
||||||
|
* This method must throw Sabre_DAV_Exception_NotFound if the node does not
|
||||||
|
* exist.
|
||||||
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @throws Sabre_DAV_Exception_NotFound
|
* @throws Sabre_DAV_Exception_NotFound
|
||||||
* @return Sabre_DAV_INode
|
* @return Sabre_DAV_INode
|
||||||
|
|
|
@ -17,7 +17,7 @@ class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DA
|
||||||
* Data will either be supplied as a stream resource, or in certain cases
|
* Data will either be supplied as a stream resource, or in certain cases
|
||||||
* as a string. Keep in mind that you may have to support either.
|
* as a string. Keep in mind that you may have to support either.
|
||||||
*
|
*
|
||||||
* After succesful creation of the file, you may choose to return the ETag
|
* After successful creation of the file, you may choose to return the ETag
|
||||||
* of the new file here.
|
* of the new file here.
|
||||||
*
|
*
|
||||||
* The returned ETag must be surrounded by double-quotes (The quotes should
|
* The returned ETag must be surrounded by double-quotes (The quotes should
|
||||||
|
@ -64,6 +64,9 @@ class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DA
|
||||||
/**
|
/**
|
||||||
* Returns a specific child node, referenced by its name
|
* Returns a specific child node, referenced by its name
|
||||||
*
|
*
|
||||||
|
* This method must throw Sabre_DAV_Exception_NotFound if the node does not
|
||||||
|
* exist.
|
||||||
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @throws Sabre_DAV_Exception_NotFound
|
* @throws Sabre_DAV_Exception_NotFound
|
||||||
* @return Sabre_DAV_INode
|
* @return Sabre_DAV_INode
|
||||||
|
|
|
@ -9,15 +9,15 @@
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
*/
|
*/
|
||||||
class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFile {
|
class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_PartialUpdate_IFile {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the data
|
* Updates the data
|
||||||
*
|
*
|
||||||
* data is a readable stream resource.
|
* data is a readable stream resource.
|
||||||
*
|
*
|
||||||
* @param resource $data
|
* @param resource|string $data
|
||||||
* @return void
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function put($data) {
|
public function put($data) {
|
||||||
|
|
||||||
|
@ -26,10 +26,34 @@ class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the data at a given offset
|
||||||
|
*
|
||||||
|
* The data argument is a readable stream resource.
|
||||||
|
* The offset argument is a 0-based offset where the data should be
|
||||||
|
* written.
|
||||||
|
*
|
||||||
|
* param resource|string $data
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function putRange($data, $offset) {
|
||||||
|
|
||||||
|
$f = fopen($this->path, 'c');
|
||||||
|
fseek($f,$offset-1);
|
||||||
|
if (is_string($data)) {
|
||||||
|
fwrite($f, $data);
|
||||||
|
} else {
|
||||||
|
stream_copy_to_stream($data,$f);
|
||||||
|
}
|
||||||
|
fclose($f);
|
||||||
|
return '"' . md5_file($this->path) . '"';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data
|
* Returns the data
|
||||||
*
|
*
|
||||||
* @return string
|
* @return resource
|
||||||
*/
|
*/
|
||||||
public function get() {
|
public function get() {
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ interface Sabre_DAV_ICollection extends Sabre_DAV_INode {
|
||||||
* Data will either be supplied as a stream resource, or in certain cases
|
* Data will either be supplied as a stream resource, or in certain cases
|
||||||
* as a string. Keep in mind that you may have to support either.
|
* as a string. Keep in mind that you may have to support either.
|
||||||
*
|
*
|
||||||
* After succesful creation of the file, you may choose to return the ETag
|
* After successful creation of the file, you may choose to return the ETag
|
||||||
* of the new file here.
|
* of the new file here.
|
||||||
*
|
*
|
||||||
* The returned ETag must be surrounded by double-quotes (The quotes should
|
* The returned ETag must be surrounded by double-quotes (The quotes should
|
||||||
|
@ -50,6 +50,9 @@ interface Sabre_DAV_ICollection extends Sabre_DAV_INode {
|
||||||
/**
|
/**
|
||||||
* Returns a specific child node, referenced by its name
|
* Returns a specific child node, referenced by its name
|
||||||
*
|
*
|
||||||
|
* This method must throw Sabre_DAV_Exception_NotFound if the node does not
|
||||||
|
* exist.
|
||||||
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @return Sabre_DAV_INode
|
* @return Sabre_DAV_INode
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -51,7 +51,7 @@ interface Sabre_DAV_IFile extends Sabre_DAV_INode {
|
||||||
*
|
*
|
||||||
* If null is returned, we'll assume application/octet-stream
|
* If null is returned, we'll assume application/octet-stream
|
||||||
*
|
*
|
||||||
* @return void
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
function getContentType();
|
function getContentType();
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,7 @@ class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
case 'MKCOL' :
|
case 'MKCOL' :
|
||||||
case 'PROPPATCH' :
|
case 'PROPPATCH' :
|
||||||
case 'PUT' :
|
case 'PUT' :
|
||||||
|
case 'PATCH' :
|
||||||
$lastLock = null;
|
$lastLock = null;
|
||||||
if (!$this->validateLock($uri,$lastLock))
|
if (!$this->validateLock($uri,$lastLock))
|
||||||
throw new Sabre_DAV_Exception_Locked($lastLock);
|
throw new Sabre_DAV_Exception_Locked($lastLock);
|
||||||
|
|
|
@ -27,7 +27,7 @@ abstract class Sabre_DAV_Node implements Sabre_DAV_INode {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deleted the current node
|
* Deletes the current node
|
||||||
*
|
*
|
||||||
* @throws Sabre_DAV_Exception_Forbidden
|
* @throws Sabre_DAV_Exception_Forbidden
|
||||||
* @return void
|
* @return void
|
||||||
|
|
|
@ -51,24 +51,30 @@ class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree {
|
||||||
$path = trim($path,'/');
|
$path = trim($path,'/');
|
||||||
if (isset($this->cache[$path])) return $this->cache[$path];
|
if (isset($this->cache[$path])) return $this->cache[$path];
|
||||||
|
|
||||||
//if (!$path || $path=='.') return $this->rootNode;
|
// Is it the root node?
|
||||||
$currentNode = $this->rootNode;
|
if (!strlen($path)) {
|
||||||
|
return $this->rootNode;
|
||||||
|
}
|
||||||
|
|
||||||
// We're splitting up the path variable into folder/subfolder components and traverse to the correct node..
|
// Attempting to fetch its parent
|
||||||
foreach(explode('/',$path) as $pathPart) {
|
list($parentName, $baseName) = Sabre_DAV_URLUtil::splitPath($path);
|
||||||
|
|
||||||
// If this part of the path is just a dot, it actually means we can skip it
|
// If there was no parent, we must simply ask it from the root node.
|
||||||
if ($pathPart=='.' || $pathPart=='') continue;
|
if ($parentName==="") {
|
||||||
|
$node = $this->rootNode->getChild($baseName);
|
||||||
|
} else {
|
||||||
|
// Otherwise, we recursively grab the parent and ask him/her.
|
||||||
|
$parent = $this->getNodeForPath($parentName);
|
||||||
|
|
||||||
if (!($currentNode instanceof Sabre_DAV_ICollection))
|
if (!($parent instanceof Sabre_DAV_ICollection))
|
||||||
throw new Sabre_DAV_Exception_NotFound('Could not find node at path: ' . $path);
|
throw new Sabre_DAV_Exception_NotFound('Could not find node at path: ' . $path);
|
||||||
|
|
||||||
$currentNode = $currentNode->getChild($pathPart);
|
$node = $parent->getChild($baseName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cache[$path] = $currentNode;
|
$this->cache[$path] = $node;
|
||||||
return $currentNode;
|
return $node;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface provides a way to modify only part of a target resource
|
||||||
|
* It may be used to update a file chunk, upload big a file into smaller
|
||||||
|
* chunks or resume an upload
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage DAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
interface Sabre_DAV_PartialUpdate_IFile extends Sabre_DAV_IFile {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the data at a given offset
|
||||||
|
*
|
||||||
|
* The data argument is a readable stream resource.
|
||||||
|
* The offset argument is an integer describing the offset. Contrary to
|
||||||
|
* what's sent in the request, the offset here is a 0-based index.
|
||||||
|
*
|
||||||
|
* After a successful put operation, you may choose to return an ETag. The
|
||||||
|
* etag must always be surrounded by double-quotes. These quotes must
|
||||||
|
* appear in the actual string you're returning.
|
||||||
|
*
|
||||||
|
* Clients may use the ETag from a PUT request to later on make sure that
|
||||||
|
* when they update the file, the contents haven't changed in the mean
|
||||||
|
* time.
|
||||||
|
*
|
||||||
|
* @param resource $data
|
||||||
|
* @param integer $offset
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
function putRange($data, $offset);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Partial update plugin (Patch method)
|
||||||
|
*
|
||||||
|
* This plugin provides a way to modify only part of a target resource
|
||||||
|
* It may bu used to update a file chunk, upload big a file into smaller
|
||||||
|
* chunks or resume an upload.
|
||||||
|
*
|
||||||
|
* $patchPlugin = new Sabre_DAV_Patch_Plugin();
|
||||||
|
* $server->addPlugin($patchPlugin);
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage DAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Jean-Tiare LE BIGOT (http://www.jtlebi.fr/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
class Sabre_DAV_PartialUpdate_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to server
|
||||||
|
*
|
||||||
|
* @var Sabre_DAV_Server
|
||||||
|
*/
|
||||||
|
protected $server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the plugin
|
||||||
|
*
|
||||||
|
* This method is automatically called by the Server class after addPlugin.
|
||||||
|
*
|
||||||
|
* @param Sabre_DAV_Server $server
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function initialize(Sabre_DAV_Server $server) {
|
||||||
|
|
||||||
|
$this->server = $server;
|
||||||
|
$server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a plugin name.
|
||||||
|
*
|
||||||
|
* Using this name other plugins will be able to access other plugins
|
||||||
|
* using Sabre_DAV_Server::getPlugin
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPluginName() {
|
||||||
|
|
||||||
|
return 'partialupdate';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by the Server if the user used an HTTP method
|
||||||
|
* the server didn't recognize.
|
||||||
|
*
|
||||||
|
* This plugin intercepts the PATCH methods.
|
||||||
|
*
|
||||||
|
* @param string $method
|
||||||
|
* @param string $uri
|
||||||
|
* @return bool|null
|
||||||
|
*/
|
||||||
|
public function unknownMethod($method, $uri) {
|
||||||
|
|
||||||
|
switch($method) {
|
||||||
|
|
||||||
|
case 'PATCH':
|
||||||
|
return $this->httpPatch($uri);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method to tell the server this plugin defines additional
|
||||||
|
* HTTP methods.
|
||||||
|
*
|
||||||
|
* This method is passed a uri. It should only return HTTP methods that are
|
||||||
|
* available for the specified uri.
|
||||||
|
*
|
||||||
|
* We claim to support PATCH method (partial update) if and only if
|
||||||
|
* - the node exist
|
||||||
|
* - the node implements our partial update interface
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getHTTPMethods($uri) {
|
||||||
|
|
||||||
|
$tree = $this->server->tree;
|
||||||
|
|
||||||
|
if ($tree->nodeExists($uri) &&
|
||||||
|
$tree->getNodeForPath($uri) instanceof Sabre_DAV_PartialUpdate_IFile) {
|
||||||
|
return array('PATCH');
|
||||||
|
}
|
||||||
|
|
||||||
|
return array();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of features for the HTTP OPTIONS Dav: header.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getFeatures() {
|
||||||
|
|
||||||
|
return array('sabredav-partialupdate');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patch an uri
|
||||||
|
*
|
||||||
|
* The WebDAV patch request can be used to modify only a part of an
|
||||||
|
* existing resource. If the resource does not exist yet and the first
|
||||||
|
* offset is not 0, the request fails
|
||||||
|
*
|
||||||
|
* @param string $uri
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function httpPatch($uri) {
|
||||||
|
|
||||||
|
// Get the node. Will throw a 404 if not found
|
||||||
|
$node = $this->server->tree->getNodeForPath($uri);
|
||||||
|
if (!($node instanceof Sabre_DAV_PartialUpdate_IFile)) {
|
||||||
|
throw new Sabre_DAV_Exception_MethodNotAllowed('The target resource does not support the PATCH method.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$range = $this->getHTTPUpdateRange();
|
||||||
|
|
||||||
|
if (!$range) {
|
||||||
|
throw new Sabre_DAV_Exception_BadRequest('No valid "X-Update-Range" found in the headers');
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentType = strtolower(
|
||||||
|
$this->server->httpRequest->getHeader('Content-Type')
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($contentType != 'application/x-sabredav-partialupdate') {
|
||||||
|
throw new Sabre_DAV_Exception_UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
$len = $this->server->httpRequest->getHeader('Content-Length');
|
||||||
|
|
||||||
|
// Load the begin and end data
|
||||||
|
$start = ($range[0])?$range[0]:0;
|
||||||
|
$end = ($range[1])?$range[1]:$len-1;
|
||||||
|
|
||||||
|
// Check consistency
|
||||||
|
if($end < $start)
|
||||||
|
throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
|
||||||
|
if($end - $start + 1 != $len)
|
||||||
|
throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[0] . ') and end (' . $range[1] . ') offsets');
|
||||||
|
|
||||||
|
// Checking If-None-Match and related headers.
|
||||||
|
if (!$this->server->checkPreconditions()) return;
|
||||||
|
|
||||||
|
if (!$this->server->broadcastEvent('beforeWriteContent',array($uri, $node, null)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
$body = $this->server->httpRequest->getBody();
|
||||||
|
$etag = $node->putRange($body, $start-1);
|
||||||
|
|
||||||
|
$this->server->broadcastEvent('afterWriteContent',array($uri, $node));
|
||||||
|
|
||||||
|
$this->server->httpResponse->setHeader('Content-Length','0');
|
||||||
|
if ($etag) $this->server->httpResponse->setHeader('ETag',$etag);
|
||||||
|
$this->server->httpResponse->sendStatus(204);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the HTTP custom range update header
|
||||||
|
*
|
||||||
|
* This method returns null if there is no well-formed HTTP range request
|
||||||
|
* header or array($start, $end).
|
||||||
|
*
|
||||||
|
* The first number is the offset of the first byte in the range.
|
||||||
|
* The second number is the offset of the last byte in the range.
|
||||||
|
*
|
||||||
|
* If the second offset is null, it should be treated as the offset of the last byte of the entity
|
||||||
|
* If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
public function getHTTPUpdateRange() {
|
||||||
|
|
||||||
|
$range = $this->server->httpRequest->getHeader('X-Update-Range');
|
||||||
|
if (is_null($range)) return null;
|
||||||
|
|
||||||
|
// Matching "Range: bytes=1234-5678: both numbers are optional
|
||||||
|
|
||||||
|
if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null;
|
||||||
|
|
||||||
|
if ($matches[1]==='' && $matches[2]==='') return null;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
$matches[1]!==''?$matches[1]:null,
|
||||||
|
$matches[2]!==''?$matches[2]:null,
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,10 +11,16 @@
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
*/
|
*/
|
||||||
abstract class Sabre_DAV_Property {
|
abstract class Sabre_DAV_Property implements Sabre_DAV_PropertyInterface {
|
||||||
|
|
||||||
abstract function serialize(Sabre_DAV_Server $server, DOMElement $prop);
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserializes the property.
|
||||||
|
*
|
||||||
|
* This static method should return a an instance of this object.
|
||||||
|
*
|
||||||
|
* @param DOMElement $prop
|
||||||
|
* @return Sabre_DAV_IProperty
|
||||||
|
*/
|
||||||
static function unserialize(DOMElement $prop) {
|
static function unserialize(DOMElement $prop) {
|
||||||
|
|
||||||
throw new Sabre_DAV_Exception('Unserialize has not been implemented for this class');
|
throw new Sabre_DAV_Exception('Unserialize has not been implemented for this class');
|
||||||
|
|
|
@ -82,7 +82,7 @@ class Sabre_DAV_Property_Href extends Sabre_DAV_Property implements Sabre_DAV_Pr
|
||||||
*/
|
*/
|
||||||
static function unserialize(DOMElement $dom) {
|
static function unserialize(DOMElement $dom) {
|
||||||
|
|
||||||
if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') {
|
if ($dom->firstChild && Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') {
|
||||||
return new self($dom->firstChild->textContent,false);
|
return new self($dom->firstChild->textContent,false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ class Sabre_DAV_Property_HrefList extends Sabre_DAV_Property {
|
||||||
* It will only decode {DAV:}href values.
|
* It will only decode {DAV:}href values.
|
||||||
*
|
*
|
||||||
* @param DOMElement $dom
|
* @param DOMElement $dom
|
||||||
* @return Sabre_DAV_Property_Href
|
* @return Sabre_DAV_Property_HrefList
|
||||||
*/
|
*/
|
||||||
static function unserialize(DOMElement $dom) {
|
static function unserialize(DOMElement $dom) {
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,7 @@ class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DA
|
||||||
if (is_scalar($propertyValue)) {
|
if (is_scalar($propertyValue)) {
|
||||||
$text = $document->createTextNode($propertyValue);
|
$text = $document->createTextNode($propertyValue);
|
||||||
$currentProperty->appendChild($text);
|
$currentProperty->appendChild($text);
|
||||||
} elseif ($propertyValue instanceof Sabre_DAV_Property) {
|
} elseif ($propertyValue instanceof Sabre_DAV_PropertyInterface) {
|
||||||
$propertyValue->serialize($server,$currentProperty);
|
$propertyValue->serialize($server,$currentProperty);
|
||||||
} elseif (!is_null($propertyValue)) {
|
} elseif (!is_null($propertyValue)) {
|
||||||
throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName);
|
throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName);
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyInterface
|
||||||
|
*
|
||||||
|
* Implement this interface to create new complex properties
|
||||||
|
*
|
||||||
|
* @package Sabre
|
||||||
|
* @subpackage DAV
|
||||||
|
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
||||||
|
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||||
|
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||||
|
*/
|
||||||
|
interface Sabre_DAV_PropertyInterface {
|
||||||
|
|
||||||
|
public function serialize(Sabre_DAV_Server $server, DOMElement $prop);
|
||||||
|
|
||||||
|
static function unserialize(DOMElement $prop);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
class Sabre_DAV_Server {
|
class Sabre_DAV_Server {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inifinity is used for some request supporting the HTTP Depth header and indicates that the operation should traverse the entire tree
|
* Infinity is used for some request supporting the HTTP Depth header and indicates that the operation should traverse the entire tree
|
||||||
*/
|
*/
|
||||||
const DEPTH_INFINITY = -1;
|
const DEPTH_INFINITY = -1;
|
||||||
|
|
||||||
|
@ -102,7 +102,6 @@ class Sabre_DAV_Server {
|
||||||
'{DAV:}getetag',
|
'{DAV:}getetag',
|
||||||
'{DAV:}getlastmodified',
|
'{DAV:}getlastmodified',
|
||||||
'{DAV:}lockdiscovery',
|
'{DAV:}lockdiscovery',
|
||||||
'{DAV:}resourcetype',
|
|
||||||
'{DAV:}supportedlock',
|
'{DAV:}supportedlock',
|
||||||
|
|
||||||
// RFC4331
|
// RFC4331
|
||||||
|
@ -162,7 +161,7 @@ class Sabre_DAV_Server {
|
||||||
* If an array is passed, we automatically create a root node, and use
|
* If an array is passed, we automatically create a root node, and use
|
||||||
* the nodes in the array as top-level children.
|
* the nodes in the array as top-level children.
|
||||||
*
|
*
|
||||||
* @param Sabre_DAV_Tree|Sabre_DAV_INode|null $treeOrNode The tree object
|
* @param Sabre_DAV_Tree|Sabre_DAV_INode|array|null $treeOrNode The tree object
|
||||||
*/
|
*/
|
||||||
public function __construct($treeOrNode = null) {
|
public function __construct($treeOrNode = null) {
|
||||||
|
|
||||||
|
@ -207,6 +206,10 @@ class Sabre_DAV_Server {
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->broadcastEvent('exception', array($e));
|
||||||
|
} catch (Exception $ignore) {
|
||||||
|
}
|
||||||
$DOM = new DOMDocument('1.0','utf-8');
|
$DOM = new DOMDocument('1.0','utf-8');
|
||||||
$DOM->formatOutput = true;
|
$DOM->formatOutput = true;
|
||||||
|
|
||||||
|
@ -214,17 +217,23 @@ class Sabre_DAV_Server {
|
||||||
$error->setAttribute('xmlns:s',self::NS_SABREDAV);
|
$error->setAttribute('xmlns:s',self::NS_SABREDAV);
|
||||||
$DOM->appendChild($error);
|
$DOM->appendChild($error);
|
||||||
|
|
||||||
$error->appendChild($DOM->createElement('s:exception',get_class($e)));
|
$h = function($v) {
|
||||||
$error->appendChild($DOM->createElement('s:message',$e->getMessage()));
|
|
||||||
|
return htmlspecialchars($v, ENT_NOQUOTES, 'UTF-8');
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$error->appendChild($DOM->createElement('s:exception',$h(get_class($e))));
|
||||||
|
$error->appendChild($DOM->createElement('s:message',$h($e->getMessage())));
|
||||||
if ($this->debugExceptions) {
|
if ($this->debugExceptions) {
|
||||||
$error->appendChild($DOM->createElement('s:file',$e->getFile()));
|
$error->appendChild($DOM->createElement('s:file',$h($e->getFile())));
|
||||||
$error->appendChild($DOM->createElement('s:line',$e->getLine()));
|
$error->appendChild($DOM->createElement('s:line',$h($e->getLine())));
|
||||||
$error->appendChild($DOM->createElement('s:code',$e->getCode()));
|
$error->appendChild($DOM->createElement('s:code',$h($e->getCode())));
|
||||||
$error->appendChild($DOM->createElement('s:stacktrace',$e->getTraceAsString()));
|
$error->appendChild($DOM->createElement('s:stacktrace',$h($e->getTraceAsString())));
|
||||||
|
|
||||||
}
|
}
|
||||||
if (self::$exposeVersion) {
|
if (self::$exposeVersion) {
|
||||||
$error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION));
|
$error->appendChild($DOM->createElement('s:sabredav-version',$h(Sabre_DAV_Version::VERSION)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if($e instanceof Sabre_DAV_Exception) {
|
if($e instanceof Sabre_DAV_Exception) {
|
||||||
|
@ -508,7 +517,7 @@ class Sabre_DAV_Server {
|
||||||
|
|
||||||
if (!$this->checkPreconditions(true)) return false;
|
if (!$this->checkPreconditions(true)) return false;
|
||||||
|
|
||||||
if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects');
|
if (!$node instanceof Sabre_DAV_IFile) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects');
|
||||||
$body = $node->get();
|
$body = $node->get();
|
||||||
|
|
||||||
// Converting string into stream, if needed.
|
// Converting string into stream, if needed.
|
||||||
|
@ -696,6 +705,7 @@ class Sabre_DAV_Server {
|
||||||
// This is a multi-status response
|
// This is a multi-status response
|
||||||
$this->httpResponse->sendStatus(207);
|
$this->httpResponse->sendStatus(207);
|
||||||
$this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
$this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||||
|
$this->httpResponse->setHeader('Vary','Brief,Prefer');
|
||||||
|
|
||||||
// Normally this header is only needed for OPTIONS responses, however..
|
// Normally this header is only needed for OPTIONS responses, however..
|
||||||
// iCal seems to also depend on these being set for PROPFIND. Since
|
// iCal seems to also depend on these being set for PROPFIND. Since
|
||||||
|
@ -704,7 +714,10 @@ class Sabre_DAV_Server {
|
||||||
foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures());
|
foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures());
|
||||||
$this->httpResponse->setHeader('DAV',implode(', ',$features));
|
$this->httpResponse->setHeader('DAV',implode(', ',$features));
|
||||||
|
|
||||||
$data = $this->generateMultiStatus($newProperties);
|
$prefer = $this->getHTTPPrefer();
|
||||||
|
$minimal = $prefer['return-minimal'];
|
||||||
|
|
||||||
|
$data = $this->generateMultiStatus($newProperties, $minimal);
|
||||||
$this->httpResponse->sendBody($data);
|
$this->httpResponse->sendBody($data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -724,6 +737,30 @@ class Sabre_DAV_Server {
|
||||||
|
|
||||||
$result = $this->updateProperties($uri, $newProperties);
|
$result = $this->updateProperties($uri, $newProperties);
|
||||||
|
|
||||||
|
$prefer = $this->getHTTPPrefer();
|
||||||
|
$this->httpResponse->setHeader('Vary','Brief,Prefer');
|
||||||
|
|
||||||
|
if ($prefer['return-minimal']) {
|
||||||
|
|
||||||
|
// If return-minimal is specified, we only have to check if the
|
||||||
|
// request was succesful, and don't need to return the
|
||||||
|
// multi-status.
|
||||||
|
$ok = true;
|
||||||
|
foreach($result as $code=>$prop) {
|
||||||
|
if ((int)$code > 299) {
|
||||||
|
$ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ok) {
|
||||||
|
|
||||||
|
$this->httpResponse->sendStatus(204);
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$this->httpResponse->sendStatus(207);
|
$this->httpResponse->sendStatus(207);
|
||||||
$this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
$this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||||
|
|
||||||
|
@ -927,7 +964,7 @@ class Sabre_DAV_Server {
|
||||||
* This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo
|
* This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo
|
||||||
*
|
*
|
||||||
* @param string $uri
|
* @param string $uri
|
||||||
* @return void
|
* @return bool
|
||||||
*/
|
*/
|
||||||
protected function httpMove($uri) {
|
protected function httpMove($uri) {
|
||||||
|
|
||||||
|
@ -1009,7 +1046,7 @@ class Sabre_DAV_Server {
|
||||||
if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) {
|
if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) {
|
||||||
|
|
||||||
// If broadcastEvent returned true, it means the report was not supported
|
// If broadcastEvent returned true, it means the report was not supported
|
||||||
throw new Sabre_DAV_Exception_ReportNotImplemented();
|
throw new Sabre_DAV_Exception_ReportNotSupported();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1158,6 +1195,85 @@ class Sabre_DAV_Server {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the HTTP Prefer header information.
|
||||||
|
*
|
||||||
|
* The prefer header is defined in:
|
||||||
|
* http://tools.ietf.org/html/draft-snell-http-prefer-14
|
||||||
|
*
|
||||||
|
* This method will return an array with options.
|
||||||
|
*
|
||||||
|
* Currently, the following options may be returned:
|
||||||
|
* array(
|
||||||
|
* 'return-asynch' => true,
|
||||||
|
* 'return-minimal' => true,
|
||||||
|
* 'return-representation' => true,
|
||||||
|
* 'wait' => 30,
|
||||||
|
* 'strict' => true,
|
||||||
|
* 'lenient' => true,
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* This method also supports the Brief header, and will also return
|
||||||
|
* 'return-minimal' if the brief header was set to 't'.
|
||||||
|
*
|
||||||
|
* For the boolean options, false will be returned if the headers are not
|
||||||
|
* specified. For the integer options it will be 'null'.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getHTTPPrefer() {
|
||||||
|
|
||||||
|
$result = array(
|
||||||
|
'return-asynch' => false,
|
||||||
|
'return-minimal' => false,
|
||||||
|
'return-representation' => false,
|
||||||
|
'wait' => null,
|
||||||
|
'strict' => false,
|
||||||
|
'lenient' => false,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($prefer = $this->httpRequest->getHeader('Prefer')) {
|
||||||
|
|
||||||
|
$parameters = array_map('trim',
|
||||||
|
explode(',', $prefer)
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach($parameters as $parameter) {
|
||||||
|
|
||||||
|
// Right now our regex only supports the tokens actually
|
||||||
|
// specified in the draft. We may need to expand this if new
|
||||||
|
// tokens get registered.
|
||||||
|
if(!preg_match('/^(?P<token>[a-z0-9-]+)(?:=(?P<value>[0-9]+))?$/', $parameter, $matches)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($matches['token']) {
|
||||||
|
|
||||||
|
case 'return-asynch' :
|
||||||
|
case 'return-minimal' :
|
||||||
|
case 'return-representation' :
|
||||||
|
case 'strict' :
|
||||||
|
case 'lenient' :
|
||||||
|
$result[$matches['token']] = true;
|
||||||
|
break;
|
||||||
|
case 'wait' :
|
||||||
|
$result[$matches['token']] = $matches['value'];
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->httpRequest->getHeader('Brief')=='t') {
|
||||||
|
$result['return-minimal'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns information about Copy and Move requests
|
* Returns information about Copy and Move requests
|
||||||
|
@ -1433,15 +1549,18 @@ class Sabre_DAV_Server {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties));
|
$this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties, $node));
|
||||||
|
|
||||||
$newProperties['href'] = trim($myPath,'/');
|
$newProperties['href'] = trim($myPath,'/');
|
||||||
|
|
||||||
// Its is a WebDAV recommendation to add a trailing slash to collectionnames.
|
// Its is a WebDAV recommendation to add a trailing slash to collectionnames.
|
||||||
// Apple's iCal also requires a trailing slash for principals (rfc 3744).
|
// Apple's iCal also requires a trailing slash for principals (rfc 3744), though this is non-standard.
|
||||||
// Therefore we add a trailing / for any non-file. This might need adjustments
|
if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype'])) {
|
||||||
// if we find there are other edge cases.
|
$rt = $newProperties[200]['{DAV:}resourcetype'];
|
||||||
if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype']) && count($newProperties[200]['{DAV:}resourcetype']->getValue())>0) $newProperties['href'] .='/';
|
if ($rt->is('{DAV:}collection') || $rt->is('{DAV:}principal')) {
|
||||||
|
$newProperties['href'] .='/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the resourcetype property was manually added to the requested property list,
|
// If the resourcetype property was manually added to the requested property list,
|
||||||
// we will remove it again.
|
// we will remove it again.
|
||||||
|
@ -1476,11 +1595,14 @@ class Sabre_DAV_Server {
|
||||||
if (!$this->broadcastEvent('beforeBind',array($uri))) return false;
|
if (!$this->broadcastEvent('beforeBind',array($uri))) return false;
|
||||||
|
|
||||||
$parent = $this->tree->getNodeForPath($dir);
|
$parent = $this->tree->getNodeForPath($dir);
|
||||||
|
if (!$parent instanceof Sabre_DAV_ICollection) {
|
||||||
|
throw new Sabre_DAV_Exception_Conflict('Files can only be created as children of collections');
|
||||||
|
}
|
||||||
|
|
||||||
if (!$this->broadcastEvent('beforeCreateFile',array($uri, &$data, $parent))) return false;
|
if (!$this->broadcastEvent('beforeCreateFile',array($uri, &$data, $parent))) return false;
|
||||||
|
|
||||||
$etag = $parent->createFile($name,$data);
|
$etag = $parent->createFile($name,$data);
|
||||||
$this->tree->markDirty($dir);
|
$this->tree->markDirty($dir . '/' . $name);
|
||||||
|
|
||||||
$this->broadcastEvent('afterBind',array($uri));
|
$this->broadcastEvent('afterBind',array($uri));
|
||||||
$this->broadcastEvent('afterCreateFile',array($uri, $parent));
|
$this->broadcastEvent('afterCreateFile',array($uri, $parent));
|
||||||
|
@ -1901,12 +2023,15 @@ class Sabre_DAV_Server {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a WebDAV propfind response body based on a list of nodes
|
* Generates a WebDAV propfind response body based on a list of nodes.
|
||||||
|
*
|
||||||
|
* If 'strip404s' is set to true, all 404 responses will be removed.
|
||||||
*
|
*
|
||||||
* @param array $fileProperties The list with nodes
|
* @param array $fileProperties The list with nodes
|
||||||
|
* @param bool strip404s
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function generateMultiStatus(array $fileProperties) {
|
public function generateMultiStatus(array $fileProperties, $strip404s = false) {
|
||||||
|
|
||||||
$dom = new DOMDocument('1.0','utf-8');
|
$dom = new DOMDocument('1.0','utf-8');
|
||||||
//$dom->formatOutput = true;
|
//$dom->formatOutput = true;
|
||||||
|
@ -1925,6 +2050,10 @@ class Sabre_DAV_Server {
|
||||||
$href = $entry['href'];
|
$href = $entry['href'];
|
||||||
unset($entry['href']);
|
unset($entry['href']);
|
||||||
|
|
||||||
|
if ($strip404s && isset($entry[404])) {
|
||||||
|
unset($entry[404]);
|
||||||
|
}
|
||||||
|
|
||||||
$response = new Sabre_DAV_Property_Response($href,$entry);
|
$response = new Sabre_DAV_Property_Response($href,$entry);
|
||||||
$response->serialize($this,$multiStatus);
|
$response->serialize($this,$multiStatus);
|
||||||
|
|
||||||
|
@ -1995,7 +2124,7 @@ class Sabre_DAV_Server {
|
||||||
if (!$body) return array();
|
if (!$body) return array();
|
||||||
|
|
||||||
$dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
|
$dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
|
||||||
$elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0);
|
$elem = $dom->getElementsByTagNameNS('DAV:','propfind')->item(0);
|
||||||
return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem));
|
return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ abstract class Sabre_DAV_ServerPlugin {
|
||||||
* This function is called by Sabre_DAV_Server, after
|
* This function is called by Sabre_DAV_Server, after
|
||||||
* addPlugin is called.
|
* addPlugin is called.
|
||||||
*
|
*
|
||||||
* This method should set up the requires event subscriptions.
|
* This method should set up the required event subscriptions.
|
||||||
*
|
*
|
||||||
* @param Sabre_DAV_Server $server
|
* @param Sabre_DAV_Server $server
|
||||||
* @return void
|
* @return void
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection {
|
||||||
/**
|
/**
|
||||||
* Creates this node
|
* Creates this node
|
||||||
*
|
*
|
||||||
* The name of the node must be passed, child nodes can also be bassed.
|
* The name of the node must be passed, child nodes can also be passed.
|
||||||
* This nodes must be instances of Sabre_DAV_INode
|
* This nodes must be instances of Sabre_DAV_INode
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
|
@ -78,6 +78,9 @@ class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection {
|
||||||
* This method makes use of the getChildren method to grab all the child nodes, and compares the name.
|
* This method makes use of the getChildren method to grab all the child nodes, and compares the name.
|
||||||
* Generally its wise to override this, as this can usually be optimized
|
* Generally its wise to override this, as this can usually be optimized
|
||||||
*
|
*
|
||||||
|
* This method must throw Sabre_DAV_Exception_NotFound if the node does not
|
||||||
|
* exist.
|
||||||
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @throws Sabre_DAV_Exception_NotFound
|
* @throws Sabre_DAV_Exception_NotFound
|
||||||
* @return Sabre_DAV_INode
|
* @return Sabre_DAV_INode
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SimpleDirectory
|
|
||||||
*
|
|
||||||
* The SimpleDirectory is used to quickly setup static directory structures.
|
|
||||||
* Just create the object with a proper name, and add children to use it.
|
|
||||||
*
|
|
||||||
* This class is now deprecated, use Sabre_DAV_SimpleCollection instead.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage DAV
|
|
||||||
* @deprecated Use Sabre_DAV_SimpleCollection instead.
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_DAV_SimpleDirectory extends Sabre_DAV_SimpleCollection {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ class Sabre_DAV_Version {
|
||||||
/**
|
/**
|
||||||
* Full version number
|
* Full version number
|
||||||
*/
|
*/
|
||||||
const VERSION = '1.6.4';
|
const VERSION = '1.7.1';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stability : alpha, beta, stable
|
* Stability : alpha, beta, stable
|
||||||
|
|
|
@ -20,9 +20,6 @@ class Sabre_DAV_XMLUtil {
|
||||||
* {http://www.example.org}myelem
|
* {http://www.example.org}myelem
|
||||||
*
|
*
|
||||||
* This format is used throughout the SabreDAV sourcecode.
|
* This format is used throughout the SabreDAV sourcecode.
|
||||||
* Elements encoded with the urn:DAV namespace will
|
|
||||||
* be returned as if they were in the DAV: namespace. This is to avoid
|
|
||||||
* compatibility problems.
|
|
||||||
*
|
*
|
||||||
* This function will return null if a nodetype other than an Element is passed.
|
* This function will return null if a nodetype other than an Element is passed.
|
||||||
*
|
*
|
||||||
|
@ -33,8 +30,7 @@ class Sabre_DAV_XMLUtil {
|
||||||
|
|
||||||
if ($dom->nodeType !== XML_ELEMENT_NODE) return null;
|
if ($dom->nodeType !== XML_ELEMENT_NODE) return null;
|
||||||
|
|
||||||
// Mapping back to the real namespace, in case it was dav
|
$ns = $dom->namespaceURI;
|
||||||
if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI;
|
|
||||||
|
|
||||||
// Mapping to clark notation
|
// Mapping to clark notation
|
||||||
return '{' . $ns . '}' . $dom->localName;
|
return '{' . $ns . '}' . $dom->localName;
|
||||||
|
@ -64,29 +60,11 @@ class Sabre_DAV_XMLUtil {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method takes an XML document (as string) and converts all instances of the
|
|
||||||
* DAV: namespace to urn:DAV
|
|
||||||
*
|
|
||||||
* This is unfortunately needed, because the DAV: namespace violates the xml namespaces
|
|
||||||
* spec, and causes the DOM to throw errors
|
|
||||||
*
|
|
||||||
* @param string $xmlDocument
|
|
||||||
* @return array|string|null
|
|
||||||
*/
|
|
||||||
static function convertDAVNamespace($xmlDocument) {
|
|
||||||
|
|
||||||
// This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV:
|
|
||||||
// namespace is actually a violation of the XML namespaces specification, and will cause errors
|
|
||||||
return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method provides a generic way to load a DOMDocument for WebDAV use.
|
* This method provides a generic way to load a DOMDocument for WebDAV use.
|
||||||
*
|
*
|
||||||
* This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors.
|
* This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors.
|
||||||
* It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV.
|
* It does not preserve whitespace.
|
||||||
*
|
*
|
||||||
* @param string $xml
|
* @param string $xml
|
||||||
* @throws Sabre_DAV_Exception_BadRequest
|
* @throws Sabre_DAV_Exception_BadRequest
|
||||||
|
@ -118,11 +96,12 @@ class Sabre_DAV_XMLUtil {
|
||||||
libxml_clear_errors();
|
libxml_clear_errors();
|
||||||
|
|
||||||
$dom = new DOMDocument();
|
$dom = new DOMDocument();
|
||||||
$dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR);
|
|
||||||
|
|
||||||
// We don't generally care about any whitespace
|
// We don't generally care about any whitespace
|
||||||
$dom->preserveWhiteSpace = false;
|
$dom->preserveWhiteSpace = false;
|
||||||
|
|
||||||
|
$dom->loadXML($xml,LIBXML_NOWARNING | LIBXML_NOERROR);
|
||||||
|
|
||||||
if ($error = libxml_get_last_error()) {
|
if ($error = libxml_get_last_error()) {
|
||||||
libxml_clear_errors();
|
libxml_clear_errors();
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')');
|
throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')');
|
||||||
|
|
|
@ -28,7 +28,7 @@ include __DIR__ . '/Locks/Backend/PDO.php';
|
||||||
include __DIR__ . '/Locks/LockInfo.php';
|
include __DIR__ . '/Locks/LockInfo.php';
|
||||||
include __DIR__ . '/Node.php';
|
include __DIR__ . '/Node.php';
|
||||||
include __DIR__ . '/Property/IHref.php';
|
include __DIR__ . '/Property/IHref.php';
|
||||||
include __DIR__ . '/Property.php';
|
include __DIR__ . '/PropertyInterface.php';
|
||||||
include __DIR__ . '/Server.php';
|
include __DIR__ . '/Server.php';
|
||||||
include __DIR__ . '/ServerPlugin.php';
|
include __DIR__ . '/ServerPlugin.php';
|
||||||
include __DIR__ . '/StringUtil.php';
|
include __DIR__ . '/StringUtil.php';
|
||||||
|
@ -60,7 +60,7 @@ include __DIR__ . '/Exception/NotFound.php';
|
||||||
include __DIR__ . '/Exception/NotImplemented.php';
|
include __DIR__ . '/Exception/NotImplemented.php';
|
||||||
include __DIR__ . '/Exception/PaymentRequired.php';
|
include __DIR__ . '/Exception/PaymentRequired.php';
|
||||||
include __DIR__ . '/Exception/PreconditionFailed.php';
|
include __DIR__ . '/Exception/PreconditionFailed.php';
|
||||||
include __DIR__ . '/Exception/ReportNotImplemented.php';
|
include __DIR__ . '/Exception/ReportNotSupported.php';
|
||||||
include __DIR__ . '/Exception/RequestedRangeNotSatisfiable.php';
|
include __DIR__ . '/Exception/RequestedRangeNotSatisfiable.php';
|
||||||
include __DIR__ . '/Exception/UnsupportedMediaType.php';
|
include __DIR__ . '/Exception/UnsupportedMediaType.php';
|
||||||
include __DIR__ . '/FS/Node.php';
|
include __DIR__ . '/FS/Node.php';
|
||||||
|
@ -72,6 +72,18 @@ include __DIR__ . '/IQuota.php';
|
||||||
include __DIR__ . '/Locks/Plugin.php';
|
include __DIR__ . '/Locks/Plugin.php';
|
||||||
include __DIR__ . '/Mount/Plugin.php';
|
include __DIR__ . '/Mount/Plugin.php';
|
||||||
include __DIR__ . '/ObjectTree.php';
|
include __DIR__ . '/ObjectTree.php';
|
||||||
|
include __DIR__ . '/PartialUpdate/IFile.php';
|
||||||
|
include __DIR__ . '/PartialUpdate/Plugin.php';
|
||||||
|
include __DIR__ . '/Property.php';
|
||||||
|
include __DIR__ . '/Tree/Filesystem.php';
|
||||||
|
include __DIR__ . '/Collection.php';
|
||||||
|
include __DIR__ . '/Exception/ConflictingLock.php';
|
||||||
|
include __DIR__ . '/Exception/FileNotFound.php';
|
||||||
|
include __DIR__ . '/File.php';
|
||||||
|
include __DIR__ . '/FS/Directory.php';
|
||||||
|
include __DIR__ . '/FS/File.php';
|
||||||
|
include __DIR__ . '/FSExt/Directory.php';
|
||||||
|
include __DIR__ . '/FSExt/File.php';
|
||||||
include __DIR__ . '/Property/GetLastModified.php';
|
include __DIR__ . '/Property/GetLastModified.php';
|
||||||
include __DIR__ . '/Property/Href.php';
|
include __DIR__ . '/Property/Href.php';
|
||||||
include __DIR__ . '/Property/HrefList.php';
|
include __DIR__ . '/Property/HrefList.php';
|
||||||
|
@ -81,17 +93,6 @@ include __DIR__ . '/Property/Response.php';
|
||||||
include __DIR__ . '/Property/ResponseList.php';
|
include __DIR__ . '/Property/ResponseList.php';
|
||||||
include __DIR__ . '/Property/SupportedLock.php';
|
include __DIR__ . '/Property/SupportedLock.php';
|
||||||
include __DIR__ . '/Property/SupportedReportSet.php';
|
include __DIR__ . '/Property/SupportedReportSet.php';
|
||||||
include __DIR__ . '/Tree/Filesystem.php';
|
|
||||||
include __DIR__ . '/Collection.php';
|
|
||||||
include __DIR__ . '/Directory.php';
|
|
||||||
include __DIR__ . '/Exception/ConflictingLock.php';
|
|
||||||
include __DIR__ . '/Exception/FileNotFound.php';
|
|
||||||
include __DIR__ . '/File.php';
|
|
||||||
include __DIR__ . '/FS/Directory.php';
|
|
||||||
include __DIR__ . '/FS/File.php';
|
|
||||||
include __DIR__ . '/FSExt/Directory.php';
|
|
||||||
include __DIR__ . '/FSExt/File.php';
|
|
||||||
include __DIR__ . '/SimpleCollection.php';
|
include __DIR__ . '/SimpleCollection.php';
|
||||||
include __DIR__ . '/SimpleDirectory.php';
|
|
||||||
include __DIR__ . '/SimpleFile.php';
|
include __DIR__ . '/SimpleFile.php';
|
||||||
// End includes
|
// End includes
|
||||||
|
|
|
@ -107,7 +107,7 @@ abstract class Sabre_DAVACL_AbstractPrincipalCollection extends Sabre_DAV_Collec
|
||||||
*
|
*
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @throws Sabre_DAV_Exception_NotFound
|
* @throws Sabre_DAV_Exception_NotFound
|
||||||
* @return Sabre_DAV_IPrincipal
|
* @return Sabre_DAVACL_IPrincipal
|
||||||
*/
|
*/
|
||||||
public function getChild($name) {
|
public function getChild($name) {
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ interface Sabre_DAVACL_IACL extends Sabre_DAV_INode {
|
||||||
/**
|
/**
|
||||||
* Updates the ACL
|
* Updates the ACL
|
||||||
*
|
*
|
||||||
* This method will receive a list of new ACE's.
|
* This method will receive a list of new ACE's as an array argument.
|
||||||
*
|
*
|
||||||
* @param array $acl
|
* @param array $acl
|
||||||
* @return void
|
* @return void
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
/**
|
/**
|
||||||
* SabreDAV ACL Plugin
|
* SabreDAV ACL Plugin
|
||||||
*
|
*
|
||||||
* This plugin provides funcitonality to enforce ACL permissions.
|
* This plugin provides functionality to enforce ACL permissions.
|
||||||
* ACL is defined in RFC3744.
|
* ACL is defined in RFC3744.
|
||||||
*
|
*
|
||||||
* In addition it also provides support for the {DAV:}current-user-principal
|
* In addition it also provides support for the {DAV:}current-user-principal
|
||||||
|
@ -233,6 +233,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
$authPlugin = $this->server->getPlugin('auth');
|
$authPlugin = $this->server->getPlugin('auth');
|
||||||
if (is_null($authPlugin)) return null;
|
if (is_null($authPlugin)) return null;
|
||||||
|
/** @var $authPlugin Sabre_DAV_Auth_Plugin */
|
||||||
|
|
||||||
$userName = $authPlugin->getCurrentUser();
|
$userName = $authPlugin->getCurrentUser();
|
||||||
if (!$userName) return null;
|
if (!$userName) return null;
|
||||||
|
@ -241,6 +242,14 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This array holds a cache for all the principals that are associated with
|
||||||
|
* a single principal.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $currentUserPrincipalsCache = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of principals that's associated to the current
|
* Returns a list of principals that's associated to the current
|
||||||
* user, either directly or through group membership.
|
* user, either directly or through group membership.
|
||||||
|
@ -253,6 +262,11 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
if (is_null($currentUser)) return array();
|
if (is_null($currentUser)) return array();
|
||||||
|
|
||||||
|
// First check our cache
|
||||||
|
if (isset($this->currentUserPrincipalsCache[$currentUser])) {
|
||||||
|
return $this->currentUserPrincipalsCache[$currentUser];
|
||||||
|
}
|
||||||
|
|
||||||
$check = array($currentUser);
|
$check = array($currentUser);
|
||||||
$principals = array($currentUser);
|
$principals = array($currentUser);
|
||||||
|
|
||||||
|
@ -277,6 +291,9 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store the result in the cache
|
||||||
|
$this->currentUserPrincipalsCache[$currentUser] = $principals;
|
||||||
|
|
||||||
return $principals;
|
return $principals;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -771,7 +788,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
* @param array $requestedProperties
|
* @param array $requestedProperties
|
||||||
* @param array $returnedProperties
|
* @param array $returnedProperties
|
||||||
* @TODO really should be broken into multiple methods, or even a class.
|
* @TODO really should be broken into multiple methods, or even a class.
|
||||||
* @return void
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function beforeGetProperties($uri, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {
|
public function beforeGetProperties($uri, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {
|
||||||
|
|
||||||
|
@ -895,6 +912,18 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
$returnedProperties[200]['{DAV:}acl-restrictions'] = new Sabre_DAVACL_Property_AclRestrictions();
|
$returnedProperties[200]['{DAV:}acl-restrictions'] = new Sabre_DAVACL_Property_AclRestrictions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Adding ACL properties */
|
||||||
|
if ($node instanceof Sabre_DAVACL_IACL) {
|
||||||
|
|
||||||
|
if (false !== ($index = array_search('{DAV:}owner', $requestedProperties))) {
|
||||||
|
|
||||||
|
unset($requestedProperties[$index]);
|
||||||
|
$returnedProperties[200]['{DAV:}owner'] = new Sabre_DAV_Property_Href($node->getOwner() . '/');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -928,6 +957,9 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
$node->setGroupMemberSet($memberSet);
|
$node->setGroupMemberSet($memberSet);
|
||||||
|
// We must also clear our cache, just in case
|
||||||
|
|
||||||
|
$this->currentUserPrincipalsCache = array();
|
||||||
|
|
||||||
$result[200]['{DAV:}group-member-set'] = null;
|
$result[200]['{DAV:}group-member-set'] = null;
|
||||||
unset($propertyDelta['{DAV:}group-member-set']);
|
unset($propertyDelta['{DAV:}group-member-set']);
|
||||||
|
@ -935,7 +967,7 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method handels HTTP REPORT requests
|
* This method handles HTTP REPORT requests
|
||||||
*
|
*
|
||||||
* @param string $reportName
|
* @param string $reportName
|
||||||
* @param DOMNode $dom
|
* @param DOMNode $dom
|
||||||
|
@ -1268,10 +1300,12 @@ class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
|
||||||
}
|
}
|
||||||
$result = $this->principalSearch($searchProperties, $requestedProperties, $uri);
|
$result = $this->principalSearch($searchProperties, $requestedProperties, $uri);
|
||||||
|
|
||||||
$xml = $this->server->generateMultiStatus($result);
|
$prefer = $this->server->getHTTPPRefer();
|
||||||
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
|
||||||
$this->server->httpResponse->sendStatus(207);
|
$this->server->httpResponse->sendStatus(207);
|
||||||
$this->server->httpResponse->sendBody($xml);
|
$this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
|
||||||
|
$this->server->httpResponse->setHeader('Vary','Brief,Prefer');
|
||||||
|
$this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal']));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,11 +88,11 @@ class Sabre_DAVACL_Property_Acl extends Sabre_DAV_Property {
|
||||||
static public function unserialize(DOMElement $dom) {
|
static public function unserialize(DOMElement $dom) {
|
||||||
|
|
||||||
$privileges = array();
|
$privileges = array();
|
||||||
$xaces = $dom->getElementsByTagNameNS('urn:DAV','ace');
|
$xaces = $dom->getElementsByTagNameNS('DAV:','ace');
|
||||||
for($ii=0; $ii < $xaces->length; $ii++) {
|
for($ii=0; $ii < $xaces->length; $ii++) {
|
||||||
|
|
||||||
$xace = $xaces->item($ii);
|
$xace = $xaces->item($ii);
|
||||||
$principal = $xace->getElementsByTagNameNS('urn:DAV','principal');
|
$principal = $xace->getElementsByTagNameNS('DAV:','principal');
|
||||||
if ($principal->length !== 1) {
|
if ($principal->length !== 1) {
|
||||||
throw new Sabre_DAV_Exception_BadRequest('Each {DAV:}ace element must have one {DAV:}principal element');
|
throw new Sabre_DAV_Exception_BadRequest('Each {DAV:}ace element must have one {DAV:}principal element');
|
||||||
}
|
}
|
||||||
|
@ -116,17 +116,17 @@ class Sabre_DAVACL_Property_Acl extends Sabre_DAV_Property {
|
||||||
|
|
||||||
$protected = false;
|
$protected = false;
|
||||||
|
|
||||||
if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) {
|
if ($xace->getElementsByTagNameNS('DAV:','protected')->length > 0) {
|
||||||
$protected = true;
|
$protected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$grants = $xace->getElementsByTagNameNS('urn:DAV','grant');
|
$grants = $xace->getElementsByTagNameNS('DAV:','grant');
|
||||||
if ($grants->length < 1) {
|
if ($grants->length < 1) {
|
||||||
throw new Sabre_DAV_Exception_NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported');
|
throw new Sabre_DAV_Exception_NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported');
|
||||||
}
|
}
|
||||||
$grant = $grants->item(0);
|
$grant = $grants->item(0);
|
||||||
|
|
||||||
$xprivs = $grant->getElementsByTagNameNS('urn:DAV','privilege');
|
$xprivs = $grant->getElementsByTagNameNS('DAV:','privilege');
|
||||||
for($jj=0; $jj<$xprivs->length; $jj++) {
|
for($jj=0; $jj<$xprivs->length; $jj++) {
|
||||||
|
|
||||||
$xpriv = $xprivs->item($jj);
|
$xpriv = $xprivs->item($jj);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
|
class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To specify a not-logged-in user, use the UNAUTHENTICTED principal
|
* To specify a not-logged-in user, use the UNAUTHENTICATED principal
|
||||||
*/
|
*/
|
||||||
const UNAUTHENTICATED = 1;
|
const UNAUTHENTICATED = 1;
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabr
|
||||||
* Deserializes a DOM element into a property object.
|
* Deserializes a DOM element into a property object.
|
||||||
*
|
*
|
||||||
* @param DOMElement $dom
|
* @param DOMElement $dom
|
||||||
* @return Sabre_DAV_Property_Principal
|
* @return Sabre_DAVACL_Property_Principal
|
||||||
*/
|
*/
|
||||||
static public function unserialize(DOMElement $dom) {
|
static public function unserialize(DOMElement $dom) {
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ class Sabre_DAVACL_Version {
|
||||||
/**
|
/**
|
||||||
* Full version number
|
* Full version number
|
||||||
*/
|
*/
|
||||||
const VERSION = '1.6.0';
|
const VERSION = '1.7.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stability : alpha, beta, stable
|
* Stability : alpha, beta, stable
|
||||||
|
|
|
@ -184,7 +184,7 @@ class Sabre_HTTP_Request {
|
||||||
* This method returns a readable stream resource.
|
* This method returns a readable stream resource.
|
||||||
* If the asString parameter is set to true, a string is sent instead.
|
* If the asString parameter is set to true, a string is sent instead.
|
||||||
*
|
*
|
||||||
* @param bool asString
|
* @param bool $asString
|
||||||
* @return resource
|
* @return resource
|
||||||
*/
|
*/
|
||||||
public function getBody($asString = false) {
|
public function getBody($asString = false) {
|
||||||
|
|
|
@ -85,6 +85,9 @@ class Sabre_HTTP_Response {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @codeCoverageIgnoreStart
|
||||||
|
// We cannot reasonably test header() related methods.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends an HTTP status header to the client
|
* Sends an HTTP status header to the client
|
||||||
*
|
*
|
||||||
|
@ -114,7 +117,9 @@ class Sabre_HTTP_Response {
|
||||||
return header($name . ': ' . $value, $replace);
|
return header($name . ': ' . $value, $replace);
|
||||||
else return false;
|
else return false;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// @codeCoverageIgnoreEnd
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a bunch of HTTP Headers
|
* Sets a bunch of HTTP Headers
|
||||||
|
|
|
@ -14,7 +14,7 @@ class Sabre_HTTP_Version {
|
||||||
/**
|
/**
|
||||||
* Full version number
|
* Full version number
|
||||||
*/
|
*/
|
||||||
const VERSION = '1.6.4';
|
const VERSION = '1.7.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stability : alpha, beta, stable
|
* Stability : alpha, beta, stable
|
||||||
|
|
|
@ -1,365 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VObject Component
|
|
||||||
*
|
|
||||||
* This class represents a VCALENDAR/VCARD component. A component is for example
|
|
||||||
* VEVENT, VTODO and also VCALENDAR. It starts with BEGIN:COMPONENTNAME and
|
|
||||||
* ends with END:COMPONENTNAME
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Component extends Sabre_VObject_Element {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name, for example VEVENT
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Children properties and components
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $children = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If coponents are added to this map, they will be automatically mapped
|
|
||||||
* to their respective classes, if parsed by the reader or constructed with
|
|
||||||
* the 'create' method.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
static public $classMap = array(
|
|
||||||
'VCALENDAR' => 'Sabre_VObject_Component_VCalendar',
|
|
||||||
'VEVENT' => 'Sabre_VObject_Component_VEvent',
|
|
||||||
'VTODO' => 'Sabre_VObject_Component_VTodo',
|
|
||||||
'VJOURNAL' => 'Sabre_VObject_Component_VJournal',
|
|
||||||
'VALARM' => 'Sabre_VObject_Component_VAlarm',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the new component by name, but in addition will also see if
|
|
||||||
* there's a class mapped to the property name.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param string $value
|
|
||||||
* @return Sabre_VObject_Component
|
|
||||||
*/
|
|
||||||
static public function create($name, $value = null) {
|
|
||||||
|
|
||||||
$name = strtoupper($name);
|
|
||||||
|
|
||||||
if (isset(self::$classMap[$name])) {
|
|
||||||
return new self::$classMap[$name]($name, $value);
|
|
||||||
} else {
|
|
||||||
return new self($name, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new component.
|
|
||||||
*
|
|
||||||
* By default this object will iterate over its own children, but this can
|
|
||||||
* be overridden with the iterator argument
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param Sabre_VObject_ElementList $iterator
|
|
||||||
*/
|
|
||||||
public function __construct($name, Sabre_VObject_ElementList $iterator = null) {
|
|
||||||
|
|
||||||
$this->name = strtoupper($name);
|
|
||||||
if (!is_null($iterator)) $this->iterator = $iterator;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns the object back into a serialized blob.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function serialize() {
|
|
||||||
|
|
||||||
$str = "BEGIN:" . $this->name . "\r\n";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gives a component a 'score' for sorting purposes.
|
|
||||||
*
|
|
||||||
* This is solely used by the childrenSort method.
|
|
||||||
*
|
|
||||||
* A higher score means the item will be higher in the list
|
|
||||||
*
|
|
||||||
* @param Sabre_VObject_Node $n
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
$sortScore = function($n) {
|
|
||||||
|
|
||||||
if ($n instanceof Sabre_VObject_Component) {
|
|
||||||
// We want to encode VTIMEZONE first, this is a personal
|
|
||||||
// preference.
|
|
||||||
if ($n->name === 'VTIMEZONE') {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// VCARD version 4.0 wants the VERSION property to appear first
|
|
||||||
if ($n->name === 'VERSION') {
|
|
||||||
return 3;
|
|
||||||
} else {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
usort($this->children, function($a, $b) use ($sortScore) {
|
|
||||||
|
|
||||||
$sA = $sortScore($a);
|
|
||||||
$sB = $sortScore($b);
|
|
||||||
|
|
||||||
if ($sA === $sB) return 0;
|
|
||||||
|
|
||||||
return ($sA > $sB) ? -1 : 1;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach($this->children as $child) $str.=$child->serialize();
|
|
||||||
$str.= "END:" . $this->name . "\r\n";
|
|
||||||
|
|
||||||
return $str;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new component or element
|
|
||||||
*
|
|
||||||
* You can call this method with the following syntaxes:
|
|
||||||
*
|
|
||||||
* add(Sabre_VObject_Element $element)
|
|
||||||
* add(string $name, $value)
|
|
||||||
*
|
|
||||||
* The first version adds an Element
|
|
||||||
* The second adds a property as a string.
|
|
||||||
*
|
|
||||||
* @param mixed $item
|
|
||||||
* @param mixed $itemValue
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function add($item, $itemValue = null) {
|
|
||||||
|
|
||||||
if ($item instanceof Sabre_VObject_Element) {
|
|
||||||
if (!is_null($itemValue)) {
|
|
||||||
throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject');
|
|
||||||
}
|
|
||||||
$item->parent = $this;
|
|
||||||
$this->children[] = $item;
|
|
||||||
} elseif(is_string($item)) {
|
|
||||||
|
|
||||||
if (!is_scalar($itemValue)) {
|
|
||||||
throw new InvalidArgumentException('The second argument must be scalar');
|
|
||||||
}
|
|
||||||
$item = Sabre_VObject_Property::create($item,$itemValue);
|
|
||||||
$item->parent = $this;
|
|
||||||
$this->children[] = $item;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an iterable list of children
|
|
||||||
*
|
|
||||||
* @return Sabre_VObject_ElementList
|
|
||||||
*/
|
|
||||||
public function children() {
|
|
||||||
|
|
||||||
return new Sabre_VObject_ElementList($this->children);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array with elements that match the specified name.
|
|
||||||
*
|
|
||||||
* This function is also aware of MIME-Directory groups (as they appear in
|
|
||||||
* vcards). This means that if a property is grouped as "HOME.EMAIL", it
|
|
||||||
* will also be returned when searching for just "EMAIL". If you want to
|
|
||||||
* search for a property in a specific group, you can select on the entire
|
|
||||||
* string ("HOME.EMAIL"). If you want to search on a specific property that
|
|
||||||
* has not been assigned a group, specify ".EMAIL".
|
|
||||||
*
|
|
||||||
* Keys are retained from the 'children' array, which may be confusing in
|
|
||||||
* certain cases.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function select($name) {
|
|
||||||
|
|
||||||
$group = null;
|
|
||||||
$name = strtoupper($name);
|
|
||||||
if (strpos($name,'.')!==false) {
|
|
||||||
list($group,$name) = explode('.', $name, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = array();
|
|
||||||
foreach($this->children as $key=>$child) {
|
|
||||||
|
|
||||||
if (
|
|
||||||
strtoupper($child->name) === $name &&
|
|
||||||
(is_null($group) || ( $child instanceof Sabre_VObject_Property && strtoupper($child->group) === $group))
|
|
||||||
) {
|
|
||||||
|
|
||||||
$result[$key] = $child;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reset($result);
|
|
||||||
return $result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method only returns a list of sub-components. Properties are
|
|
||||||
* ignored.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getComponents() {
|
|
||||||
|
|
||||||
$result = array();
|
|
||||||
foreach($this->children as $child) {
|
|
||||||
if ($child instanceof Sabre_VObject_Component) {
|
|
||||||
$result[] = $child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Magic property accessors {{{ */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Using 'get' you will either get a property or component,
|
|
||||||
*
|
|
||||||
* If there were no child-elements found with the specified name,
|
|
||||||
* null is returned.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return Sabre_VObject_Property
|
|
||||||
*/
|
|
||||||
public function __get($name) {
|
|
||||||
|
|
||||||
$matches = $this->select($name);
|
|
||||||
if (count($matches)===0) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
$firstMatch = current($matches);
|
|
||||||
/** @var $firstMatch Sabre_VObject_Property */
|
|
||||||
$firstMatch->setIterator(new Sabre_VObject_ElementList(array_values($matches)));
|
|
||||||
return $firstMatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method checks if a sub-element with the specified name exists.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function __isset($name) {
|
|
||||||
|
|
||||||
$matches = $this->select($name);
|
|
||||||
return count($matches)>0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Using the setter method you can add properties or subcomponents
|
|
||||||
*
|
|
||||||
* You can either pass a Sabre_VObject_Component, Sabre_VObject_Property
|
|
||||||
* object, or a string to automatically create a Property.
|
|
||||||
*
|
|
||||||
* If the item already exists, it will be removed. If you want to add
|
|
||||||
* a new item with the same name, always use the add() method.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param mixed $value
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __set($name, $value) {
|
|
||||||
|
|
||||||
$matches = $this->select($name);
|
|
||||||
$overWrite = count($matches)?key($matches):null;
|
|
||||||
|
|
||||||
if ($value instanceof Sabre_VObject_Component || $value instanceof Sabre_VObject_Property) {
|
|
||||||
$value->parent = $this;
|
|
||||||
if (!is_null($overWrite)) {
|
|
||||||
$this->children[$overWrite] = $value;
|
|
||||||
} else {
|
|
||||||
$this->children[] = $value;
|
|
||||||
}
|
|
||||||
} elseif (is_scalar($value)) {
|
|
||||||
$property = Sabre_VObject_Property::create($name,$value);
|
|
||||||
$property->parent = $this;
|
|
||||||
if (!is_null($overWrite)) {
|
|
||||||
$this->children[$overWrite] = $property;
|
|
||||||
} else {
|
|
||||||
$this->children[] = $property;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException('You must pass a Sabre_VObject_Component, Sabre_VObject_Property or scalar type');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all properties and components within this component.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __unset($name) {
|
|
||||||
|
|
||||||
$matches = $this->select($name);
|
|
||||||
foreach($matches as $k=>$child) {
|
|
||||||
|
|
||||||
unset($this->children[$k]);
|
|
||||||
$child->parent = null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is automatically called when the object is cloned.
|
|
||||||
* Specifically, this will ensure all child elements are also cloned.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __clone() {
|
|
||||||
|
|
||||||
foreach($this->children as $key=>$child) {
|
|
||||||
$this->children[$key] = clone $child;
|
|
||||||
$this->children[$key]->parent = $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VAlarm component
|
|
||||||
*
|
|
||||||
* This component contains some additional functionality specific for VALARMs.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Component_VAlarm extends Sabre_VObject_Component {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a DateTime object when this alarm is going to trigger.
|
|
||||||
*
|
|
||||||
* This ignores repeated alarm, only the first trigger is returned.
|
|
||||||
*
|
|
||||||
* @return DateTime
|
|
||||||
*/
|
|
||||||
public function getEffectiveTriggerTime() {
|
|
||||||
|
|
||||||
$trigger = $this->TRIGGER;
|
|
||||||
if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') {
|
|
||||||
$triggerDuration = Sabre_VObject_DateTimeParser::parseDuration($this->TRIGGER);
|
|
||||||
$related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START';
|
|
||||||
|
|
||||||
$parentComponent = $this->parent;
|
|
||||||
if ($related === 'START') {
|
|
||||||
$effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
|
|
||||||
$effectiveTrigger->add($triggerDuration);
|
|
||||||
} else {
|
|
||||||
if ($parentComponent->name === 'VTODO') {
|
|
||||||
$endProp = 'DUE';
|
|
||||||
} elseif ($parentComponent->name === 'VEVENT') {
|
|
||||||
$endProp = 'DTEND';
|
|
||||||
} else {
|
|
||||||
throw new Sabre_DAV_Exception('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($parentComponent->$endProp)) {
|
|
||||||
$effectiveTrigger = clone $parentComponent->$endProp->getDateTime();
|
|
||||||
$effectiveTrigger->add($triggerDuration);
|
|
||||||
} elseif (isset($parentComponent->DURATION)) {
|
|
||||||
$effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
|
|
||||||
$duration = Sabre_VObject_DateTimeParser::parseDuration($parentComponent->DURATION);
|
|
||||||
$effectiveTrigger->add($duration);
|
|
||||||
$effectiveTrigger->add($triggerDuration);
|
|
||||||
} else {
|
|
||||||
$effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
|
|
||||||
$effectiveTrigger->add($triggerDuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$effectiveTrigger = $trigger->getDateTime();
|
|
||||||
}
|
|
||||||
return $effectiveTrigger;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true or false depending on if the event falls in the specified
|
|
||||||
* time-range. This is used for filtering purposes.
|
|
||||||
*
|
|
||||||
* The rules used to determine if an event falls within the specified
|
|
||||||
* time-range is based on the CalDAV specification.
|
|
||||||
*
|
|
||||||
* @param DateTime $start
|
|
||||||
* @param DateTime $end
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isInTimeRange(DateTime $start, DateTime $end) {
|
|
||||||
|
|
||||||
$effectiveTrigger = $this->getEffectiveTriggerTime();
|
|
||||||
|
|
||||||
if (isset($this->DURATION)) {
|
|
||||||
$duration = Sabre_VObject_DateTimeParser::parseDuration($this->DURATION);
|
|
||||||
$repeat = (string)$this->repeat;
|
|
||||||
if (!$repeat) {
|
|
||||||
$repeat = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$period = new DatePeriod($effectiveTrigger, $duration, (int)$repeat);
|
|
||||||
|
|
||||||
foreach($period as $occurrence) {
|
|
||||||
|
|
||||||
if ($start <= $occurrence && $end > $occurrence) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return ($start <= $effectiveTrigger && $end > $effectiveTrigger);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,133 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The VCalendar component
|
|
||||||
*
|
|
||||||
* This component adds functionality to a component, specific for a VCALENDAR.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Component_VCalendar extends Sabre_VObject_Component {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of all 'base components'. For instance, if an Event has
|
|
||||||
* a recurrence rule, and one instance is overridden, the overridden event
|
|
||||||
* will have the same UID, but will be excluded from this list.
|
|
||||||
*
|
|
||||||
* VTIMEZONE components will always be excluded.
|
|
||||||
*
|
|
||||||
* @param string $componentName filter by component name
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getBaseComponents($componentName = null) {
|
|
||||||
|
|
||||||
$components = array();
|
|
||||||
foreach($this->children as $component) {
|
|
||||||
|
|
||||||
if (!$component instanceof Sabre_VObject_Component)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (isset($component->{'RECURRENCE-ID'}))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ($componentName && $component->name !== strtoupper($componentName))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ($component->name === 'VTIMEZONE')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$components[] = $component;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $components;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If this calendar object, has events with recurrence rules, this method
|
|
||||||
* can be used to expand the event into multiple sub-events.
|
|
||||||
*
|
|
||||||
* Each event will be stripped from it's recurrence information, and only
|
|
||||||
* the instances of the event in the specified timerange will be left
|
|
||||||
* alone.
|
|
||||||
*
|
|
||||||
* In addition, this method will cause timezone information to be stripped,
|
|
||||||
* and normalized to UTC.
|
|
||||||
*
|
|
||||||
* This method will alter the VCalendar. This cannot be reversed.
|
|
||||||
*
|
|
||||||
* This functionality is specifically used by the CalDAV standard. It is
|
|
||||||
* possible for clients to request expand events, if they are rather simple
|
|
||||||
* clients and do not have the possibility to calculate recurrences.
|
|
||||||
*
|
|
||||||
* @param DateTime $start
|
|
||||||
* @param DateTime $end
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function expand(DateTime $start, DateTime $end) {
|
|
||||||
|
|
||||||
$newEvents = array();
|
|
||||||
|
|
||||||
foreach($this->select('VEVENT') as $key=>$vevent) {
|
|
||||||
|
|
||||||
if (isset($vevent->{'RECURRENCE-ID'})) {
|
|
||||||
unset($this->children[$key]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (!$vevent->rrule) {
|
|
||||||
unset($this->children[$key]);
|
|
||||||
if ($vevent->isInTimeRange($start, $end)) {
|
|
||||||
$newEvents[] = $vevent;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$uid = (string)$vevent->uid;
|
|
||||||
if (!$uid) {
|
|
||||||
throw new LogicException('Event did not have a UID!');
|
|
||||||
}
|
|
||||||
|
|
||||||
$it = new Sabre_VObject_RecurrenceIterator($this, $vevent->uid);
|
|
||||||
$it->fastForward($start);
|
|
||||||
|
|
||||||
while($it->valid() && $it->getDTStart() < $end) {
|
|
||||||
|
|
||||||
if ($it->getDTEnd() > $start) {
|
|
||||||
|
|
||||||
$newEvents[] = $it->getEventObject();
|
|
||||||
|
|
||||||
}
|
|
||||||
$it->next();
|
|
||||||
|
|
||||||
}
|
|
||||||
unset($this->children[$key]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($newEvents as $newEvent) {
|
|
||||||
|
|
||||||
foreach($newEvent->children as $child) {
|
|
||||||
if ($child instanceof Sabre_VObject_Property_DateTime &&
|
|
||||||
$child->getDateType() == Sabre_VObject_Property_DateTime::LOCALTZ) {
|
|
||||||
$child->setDateTime($child->getDateTime(),Sabre_VObject_Property_DateTime::UTC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->add($newEvent);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing all VTIMEZONE components
|
|
||||||
unset($this->VTIMEZONE);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VEvent component
|
|
||||||
*
|
|
||||||
* This component contains some additional functionality specific for VEVENT's.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Component_VEvent extends Sabre_VObject_Component {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true or false depending on if the event falls in the specified
|
|
||||||
* time-range. This is used for filtering purposes.
|
|
||||||
*
|
|
||||||
* The rules used to determine if an event falls within the specified
|
|
||||||
* time-range is based on the CalDAV specification.
|
|
||||||
*
|
|
||||||
* @param DateTime $start
|
|
||||||
* @param DateTime $end
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isInTimeRange(DateTime $start, DateTime $end) {
|
|
||||||
|
|
||||||
if ($this->RRULE) {
|
|
||||||
$it = new Sabre_VObject_RecurrenceIterator($this);
|
|
||||||
$it->fastForward($start);
|
|
||||||
|
|
||||||
// We fast-forwarded to a spot where the end-time of the
|
|
||||||
// recurrence instance exceeded the start of the requested
|
|
||||||
// time-range.
|
|
||||||
//
|
|
||||||
// If the starttime of the recurrence did not exceed the
|
|
||||||
// end of the time range as well, we have a match.
|
|
||||||
return ($it->getDTStart() < $end && $it->getDTEnd() > $start);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$effectiveStart = $this->DTSTART->getDateTime();
|
|
||||||
if (isset($this->DTEND)) {
|
|
||||||
|
|
||||||
// The DTEND property is considered non inclusive. So for a 3 day
|
|
||||||
// event in july, dtstart and dtend would have to be July 1st and
|
|
||||||
// July 4th respectively.
|
|
||||||
//
|
|
||||||
// See:
|
|
||||||
// http://tools.ietf.org/html/rfc5545#page-54
|
|
||||||
$effectiveEnd = $this->DTEND->getDateTime();
|
|
||||||
|
|
||||||
} elseif (isset($this->DURATION)) {
|
|
||||||
$effectiveEnd = clone $effectiveStart;
|
|
||||||
$effectiveEnd->add( Sabre_VObject_DateTimeParser::parseDuration($this->DURATION) );
|
|
||||||
} elseif ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) {
|
|
||||||
$effectiveEnd = clone $effectiveStart;
|
|
||||||
$effectiveEnd->modify('+1 day');
|
|
||||||
} else {
|
|
||||||
$effectiveEnd = clone $effectiveStart;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
($start <= $effectiveEnd) && ($end > $effectiveStart)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,46 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VJournal component
|
|
||||||
*
|
|
||||||
* This component contains some additional functionality specific for VJOURNALs.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Component_VJournal extends Sabre_VObject_Component {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true or false depending on if the event falls in the specified
|
|
||||||
* time-range. This is used for filtering purposes.
|
|
||||||
*
|
|
||||||
* The rules used to determine if an event falls within the specified
|
|
||||||
* time-range is based on the CalDAV specification.
|
|
||||||
*
|
|
||||||
* @param DateTime $start
|
|
||||||
* @param DateTime $end
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isInTimeRange(DateTime $start, DateTime $end) {
|
|
||||||
|
|
||||||
$dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
|
|
||||||
if ($dtstart) {
|
|
||||||
$effectiveEnd = clone $dtstart;
|
|
||||||
if ($this->DTSTART->getDateType() == Sabre_VObject_Element_DateTime::DATE) {
|
|
||||||
$effectiveEnd->modify('+1 day');
|
|
||||||
}
|
|
||||||
|
|
||||||
return ($start <= $effectiveEnd && $end > $dtstart);
|
|
||||||
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,68 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VTodo component
|
|
||||||
*
|
|
||||||
* This component contains some additional functionality specific for VTODOs.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Component_VTodo extends Sabre_VObject_Component {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true or false depending on if the event falls in the specified
|
|
||||||
* time-range. This is used for filtering purposes.
|
|
||||||
*
|
|
||||||
* The rules used to determine if an event falls within the specified
|
|
||||||
* time-range is based on the CalDAV specification.
|
|
||||||
*
|
|
||||||
* @param DateTime $start
|
|
||||||
* @param DateTime $end
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isInTimeRange(DateTime $start, DateTime $end) {
|
|
||||||
|
|
||||||
$dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
|
|
||||||
$duration = isset($this->DURATION)?Sabre_VObject_DateTimeParser::parseDuration($this->DURATION):null;
|
|
||||||
$due = isset($this->DUE)?$this->DUE->getDateTime():null;
|
|
||||||
$completed = isset($this->COMPLETED)?$this->COMPLETED->getDateTime():null;
|
|
||||||
$created = isset($this->CREATED)?$this->CREATED->getDateTime():null;
|
|
||||||
|
|
||||||
if ($dtstart) {
|
|
||||||
if ($duration) {
|
|
||||||
$effectiveEnd = clone $dtstart;
|
|
||||||
$effectiveEnd->add($duration);
|
|
||||||
return $start <= $effectiveEnd && $end > $dtstart;
|
|
||||||
} elseif ($due) {
|
|
||||||
return
|
|
||||||
($start < $due || $start <= $dtstart) &&
|
|
||||||
($end > $dtstart || $end >= $due);
|
|
||||||
} else {
|
|
||||||
return $start <= $dtstart && $end > $dtstart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($due) {
|
|
||||||
return ($start < $due && $end >= $due);
|
|
||||||
}
|
|
||||||
if ($completed && $created) {
|
|
||||||
return
|
|
||||||
($start <= $created || $start <= $completed) &&
|
|
||||||
($end >= $created || $end >= $completed);
|
|
||||||
}
|
|
||||||
if ($completed) {
|
|
||||||
return ($start <= $completed && $end >= $completed);
|
|
||||||
}
|
|
||||||
if ($created) {
|
|
||||||
return ($end > $created);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,181 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DateTimeParser
|
|
||||||
*
|
|
||||||
* This class is responsible for parsing the several different date and time
|
|
||||||
* formats iCalendar and vCards have.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_DateTimeParser {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
|
|
||||||
*
|
|
||||||
* Specifying a reference timezone is optional. It will only be used
|
|
||||||
* if the non-UTC format is used. The argument is used as a reference, the
|
|
||||||
* returned DateTime object will still be in the UTC timezone.
|
|
||||||
*
|
|
||||||
* @param string $dt
|
|
||||||
* @param DateTimeZone $tz
|
|
||||||
* @return DateTime
|
|
||||||
*/
|
|
||||||
static public function parseDateTime($dt,DateTimeZone $tz = null) {
|
|
||||||
|
|
||||||
// Format is YYYYMMDD + "T" + hhmmss
|
|
||||||
$result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches);
|
|
||||||
|
|
||||||
if (!$result) {
|
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar datetime value is incorrect: ' . $dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($matches[7]==='Z' || is_null($tz)) {
|
|
||||||
$tz = new DateTimeZone('UTC');
|
|
||||||
}
|
|
||||||
$date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz);
|
|
||||||
|
|
||||||
// Still resetting the timezone, to normalize everything to UTC
|
|
||||||
$date->setTimeZone(new DateTimeZone('UTC'));
|
|
||||||
return $date;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an iCalendar (rfc5545) formatted date and returns a DateTime object
|
|
||||||
*
|
|
||||||
* @param string $date
|
|
||||||
* @return DateTime
|
|
||||||
*/
|
|
||||||
static public function parseDate($date) {
|
|
||||||
|
|
||||||
// Format is YYYYMMDD
|
|
||||||
$result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches);
|
|
||||||
|
|
||||||
if (!$result) {
|
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date);
|
|
||||||
}
|
|
||||||
|
|
||||||
$date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC'));
|
|
||||||
return $date;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an iCalendar (RFC5545) formatted duration value.
|
|
||||||
*
|
|
||||||
* This method will either return a DateTimeInterval object, or a string
|
|
||||||
* suitable for strtotime or DateTime::modify.
|
|
||||||
*
|
|
||||||
* @param string $duration
|
|
||||||
* @param bool $asString
|
|
||||||
* @return DateInterval|string
|
|
||||||
*/
|
|
||||||
static public function parseDuration($duration, $asString = false) {
|
|
||||||
|
|
||||||
$result = preg_match('/^(?P<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $duration, $matches);
|
|
||||||
if (!$result) {
|
|
||||||
throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar duration value is incorrect: ' . $duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$asString) {
|
|
||||||
$invert = false;
|
|
||||||
if ($matches['plusminus']==='-') {
|
|
||||||
$invert = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$parts = array(
|
|
||||||
'week',
|
|
||||||
'day',
|
|
||||||
'hour',
|
|
||||||
'minute',
|
|
||||||
'second',
|
|
||||||
);
|
|
||||||
foreach($parts as $part) {
|
|
||||||
$matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// We need to re-construct the $duration string, because weeks and
|
|
||||||
// days are not supported by DateInterval in the same string.
|
|
||||||
$duration = 'P';
|
|
||||||
$days = $matches['day'];
|
|
||||||
if ($matches['week']) {
|
|
||||||
$days+=$matches['week']*7;
|
|
||||||
}
|
|
||||||
if ($days)
|
|
||||||
$duration.=$days . 'D';
|
|
||||||
|
|
||||||
if ($matches['minute'] || $matches['second'] || $matches['hour']) {
|
|
||||||
$duration.='T';
|
|
||||||
|
|
||||||
if ($matches['hour'])
|
|
||||||
$duration.=$matches['hour'].'H';
|
|
||||||
|
|
||||||
if ($matches['minute'])
|
|
||||||
$duration.=$matches['minute'].'M';
|
|
||||||
|
|
||||||
if ($matches['second'])
|
|
||||||
$duration.=$matches['second'].'S';
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($duration==='P') {
|
|
||||||
$duration = 'PT0S';
|
|
||||||
}
|
|
||||||
$iv = new DateInterval($duration);
|
|
||||||
if ($invert) $iv->invert = true;
|
|
||||||
|
|
||||||
return $iv;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$parts = array(
|
|
||||||
'week',
|
|
||||||
'day',
|
|
||||||
'hour',
|
|
||||||
'minute',
|
|
||||||
'second',
|
|
||||||
);
|
|
||||||
|
|
||||||
$newDur = '';
|
|
||||||
foreach($parts as $part) {
|
|
||||||
if (isset($matches[$part]) && $matches[$part]) {
|
|
||||||
$newDur.=' '.$matches[$part] . ' ' . $part . 's';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur);
|
|
||||||
if ($newDur === '+') { $newDur = '+0 seconds'; };
|
|
||||||
return $newDur;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses either a Date or DateTime, or Duration value.
|
|
||||||
*
|
|
||||||
* @param string $date
|
|
||||||
* @param DateTimeZone|string $referenceTZ
|
|
||||||
* @return DateTime|DateInterval
|
|
||||||
*/
|
|
||||||
static public function parse($date, $referenceTZ = null) {
|
|
||||||
|
|
||||||
if ($date[0]==='P' || ($date[0]==='-' && $date[1]==='P')) {
|
|
||||||
return self::parseDuration($date);
|
|
||||||
} elseif (strlen($date)===8) {
|
|
||||||
return self::parseDate($date);
|
|
||||||
} else {
|
|
||||||
return self::parseDateTime($date, $referenceTZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all elements
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
abstract class Sabre_VObject_Element extends Sabre_VObject_Node {
|
|
||||||
|
|
||||||
public $parent = null;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DateTime property
|
|
||||||
*
|
|
||||||
* this class got renamed to Sabre_VObject_Property_DateTime
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Element_DateTime extends Sabre_VObject_Property_DateTime {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local 'floating' time
|
|
||||||
*/
|
|
||||||
const LOCAL = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UTC-based time
|
|
||||||
*/
|
|
||||||
const UTC = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local time plus timezone
|
|
||||||
*/
|
|
||||||
const LOCALTZ = 3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only a date, time is ignored
|
|
||||||
*/
|
|
||||||
const DATE = 4;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Multi-DateTime property
|
|
||||||
*
|
|
||||||
* This class got renamed to Sabre_VObject_Property_MultiDateTime
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Element_MultiDateTime extends Sabre_VObject_Property_MultiDateTime {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,172 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VObject ElementList
|
|
||||||
*
|
|
||||||
* This class represents a list of elements. Lists are the result of queries,
|
|
||||||
* such as doing $vcalendar->vevent where there's multiple VEVENT objects.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inner elements
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $elements = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the element list.
|
|
||||||
*
|
|
||||||
* @param array $elements
|
|
||||||
*/
|
|
||||||
public function __construct(array $elements) {
|
|
||||||
|
|
||||||
$this->elements = $elements;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* {{{ Iterator interface */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current position
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $key = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns current item in iteration
|
|
||||||
*
|
|
||||||
* @return Sabre_VObject_Element
|
|
||||||
*/
|
|
||||||
public function current() {
|
|
||||||
|
|
||||||
return $this->elements[$this->key];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To the next item in the iterator
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function next() {
|
|
||||||
|
|
||||||
$this->key++;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current iterator key
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function key() {
|
|
||||||
|
|
||||||
return $this->key;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the current position in the iterator is a valid one
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function valid() {
|
|
||||||
|
|
||||||
return isset($this->elements[$this->key]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rewinds the iterator
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function rewind() {
|
|
||||||
|
|
||||||
$this->key = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
/* {{{ Countable interface */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of elements
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function count() {
|
|
||||||
|
|
||||||
return count($this->elements);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
/* {{{ ArrayAccess Interface */
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if an item exists through ArrayAccess.
|
|
||||||
*
|
|
||||||
* @param int $offset
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function offsetExists($offset) {
|
|
||||||
|
|
||||||
return isset($this->elements[$offset]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an item through ArrayAccess.
|
|
||||||
*
|
|
||||||
* @param int $offset
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function offsetGet($offset) {
|
|
||||||
|
|
||||||
return $this->elements[$offset];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets an item through ArrayAccess.
|
|
||||||
*
|
|
||||||
* @param int $offset
|
|
||||||
* @param mixed $value
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function offsetSet($offset,$value) {
|
|
||||||
|
|
||||||
throw new LogicException('You can not add new objects to an ElementList');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets an item through ArrayAccess.
|
|
||||||
*
|
|
||||||
* This method just forwards the request to the inner iterator
|
|
||||||
*
|
|
||||||
* @param int $offset
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function offsetUnset($offset) {
|
|
||||||
|
|
||||||
throw new LogicException('You can not remove objects from an ElementList');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,297 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class helps with generating FREEBUSY reports based on existing sets of
|
|
||||||
* objects.
|
|
||||||
*
|
|
||||||
* It only looks at VEVENT and VFREEBUSY objects from the sourcedata, and
|
|
||||||
* generates a single VFREEBUSY object.
|
|
||||||
*
|
|
||||||
* VFREEBUSY components are described in RFC5545, The rules for what should
|
|
||||||
* go in a single freebusy report is taken from RFC4791, section 7.10.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_FreeBusyGenerator {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Input objects
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start of range
|
|
||||||
*
|
|
||||||
* @var DateTime|null
|
|
||||||
*/
|
|
||||||
protected $start;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* End of range
|
|
||||||
*
|
|
||||||
* @var DateTime|null
|
|
||||||
*/
|
|
||||||
protected $end;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VCALENDAR object
|
|
||||||
*
|
|
||||||
* @var Sabre_VObject_Component
|
|
||||||
*/
|
|
||||||
protected $baseObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the VCALENDAR object.
|
|
||||||
*
|
|
||||||
* If this is set, it will not be generated for you. You are responsible
|
|
||||||
* for setting things like the METHOD, CALSCALE, VERSION, etc..
|
|
||||||
*
|
|
||||||
* The VFREEBUSY object will be automatically added though.
|
|
||||||
*
|
|
||||||
* @param Sabre_VObject_Component $vcalendar
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setBaseObject(Sabre_VObject_Component $vcalendar) {
|
|
||||||
|
|
||||||
$this->baseObject = $vcalendar;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the input objects
|
|
||||||
*
|
|
||||||
* Every object must either be a string or a Sabre_VObject_Component.
|
|
||||||
*
|
|
||||||
* @param array $objects
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setObjects(array $objects) {
|
|
||||||
|
|
||||||
$this->objects = array();
|
|
||||||
foreach($objects as $object) {
|
|
||||||
|
|
||||||
if (is_string($object)) {
|
|
||||||
$this->objects[] = Sabre_VObject_Reader::read($object);
|
|
||||||
} elseif ($object instanceof Sabre_VObject_Component) {
|
|
||||||
$this->objects[] = $object;
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException('You can only pass strings or Sabre_VObject_Component arguments to setObjects');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the time range
|
|
||||||
*
|
|
||||||
* Any freebusy object falling outside of this time range will be ignored.
|
|
||||||
*
|
|
||||||
* @param DateTime $start
|
|
||||||
* @param DateTime $end
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setTimeRange(DateTime $start = null, DateTime $end = null) {
|
|
||||||
|
|
||||||
$this->start = $start;
|
|
||||||
$this->end = $end;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the input data and returns a correct VFREEBUSY object, wrapped in
|
|
||||||
* a VCALENDAR.
|
|
||||||
*
|
|
||||||
* @return Sabre_VObject_Component
|
|
||||||
*/
|
|
||||||
public function getResult() {
|
|
||||||
|
|
||||||
$busyTimes = array();
|
|
||||||
|
|
||||||
foreach($this->objects as $object) {
|
|
||||||
|
|
||||||
foreach($object->getBaseComponents() as $component) {
|
|
||||||
|
|
||||||
switch($component->name) {
|
|
||||||
|
|
||||||
case 'VEVENT' :
|
|
||||||
|
|
||||||
$FBTYPE = 'BUSY';
|
|
||||||
if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (isset($component->STATUS)) {
|
|
||||||
$status = strtoupper($component->STATUS);
|
|
||||||
if ($status==='CANCELLED') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ($status==='TENTATIVE') {
|
|
||||||
$FBTYPE = 'BUSY-TENTATIVE';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$times = array();
|
|
||||||
|
|
||||||
if ($component->RRULE) {
|
|
||||||
|
|
||||||
$iterator = new Sabre_VObject_RecurrenceIterator($object, (string)$component->uid);
|
|
||||||
if ($this->start) {
|
|
||||||
$iterator->fastForward($this->start);
|
|
||||||
}
|
|
||||||
|
|
||||||
$maxRecurrences = 200;
|
|
||||||
|
|
||||||
while($iterator->valid() && --$maxRecurrences) {
|
|
||||||
|
|
||||||
$startTime = $iterator->getDTStart();
|
|
||||||
if ($this->end && $startTime > $this->end) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$times[] = array(
|
|
||||||
$iterator->getDTStart(),
|
|
||||||
$iterator->getDTEnd(),
|
|
||||||
);
|
|
||||||
|
|
||||||
$iterator->next();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
$startTime = $component->DTSTART->getDateTime();
|
|
||||||
if ($this->end && $startTime > $this->end) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$endTime = null;
|
|
||||||
if (isset($component->DTEND)) {
|
|
||||||
$endTime = $component->DTEND->getDateTime();
|
|
||||||
} elseif (isset($component->DURATION)) {
|
|
||||||
$duration = Sabre_VObject_DateTimeParser::parseDuration((string)$component->DURATION);
|
|
||||||
$endTime = clone $startTime;
|
|
||||||
$endTime->add($duration);
|
|
||||||
} elseif ($component->DTSTART->getDateType() === Sabre_VObject_Property_DateTime::DATE) {
|
|
||||||
$endTime = clone $startTime;
|
|
||||||
$endTime->modify('+1 day');
|
|
||||||
} else {
|
|
||||||
// The event had no duration (0 seconds)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$times[] = array($startTime, $endTime);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($times as $time) {
|
|
||||||
|
|
||||||
if ($this->end && $time[0] > $this->end) break;
|
|
||||||
if ($this->start && $time[1] < $this->start) break;
|
|
||||||
|
|
||||||
$busyTimes[] = array(
|
|
||||||
$time[0],
|
|
||||||
$time[1],
|
|
||||||
$FBTYPE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'VFREEBUSY' :
|
|
||||||
foreach($component->FREEBUSY as $freebusy) {
|
|
||||||
|
|
||||||
$fbType = isset($freebusy['FBTYPE'])?strtoupper($freebusy['FBTYPE']):'BUSY';
|
|
||||||
|
|
||||||
// Skipping intervals marked as 'free'
|
|
||||||
if ($fbType==='FREE')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$values = explode(',', $freebusy);
|
|
||||||
foreach($values as $value) {
|
|
||||||
list($startTime, $endTime) = explode('/', $value);
|
|
||||||
$startTime = Sabre_VObject_DateTimeParser::parseDateTime($startTime);
|
|
||||||
|
|
||||||
if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') {
|
|
||||||
$duration = Sabre_VObject_DateTimeParser::parseDuration($endTime);
|
|
||||||
$endTime = clone $startTime;
|
|
||||||
$endTime->add($duration);
|
|
||||||
} else {
|
|
||||||
$endTime = Sabre_VObject_DateTimeParser::parseDateTime($endTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->start && $this->start > $endTime) continue;
|
|
||||||
if($this->end && $this->end < $startTime) continue;
|
|
||||||
$busyTimes[] = array(
|
|
||||||
$startTime,
|
|
||||||
$endTime,
|
|
||||||
$fbType
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->baseObject) {
|
|
||||||
$calendar = $this->baseObject;
|
|
||||||
} else {
|
|
||||||
$calendar = new Sabre_VObject_Component('VCALENDAR');
|
|
||||||
$calendar->version = '2.0';
|
|
||||||
if (Sabre_DAV_Server::$exposeVersion) {
|
|
||||||
$calendar->prodid = '-//SabreDAV//Sabre VObject ' . Sabre_VObject_Version::VERSION . '//EN';
|
|
||||||
} else {
|
|
||||||
$calendar->prodid = '-//SabreDAV//Sabre VObject//EN';
|
|
||||||
}
|
|
||||||
$calendar->calscale = 'GREGORIAN';
|
|
||||||
}
|
|
||||||
|
|
||||||
$vfreebusy = new Sabre_VObject_Component('VFREEBUSY');
|
|
||||||
$calendar->add($vfreebusy);
|
|
||||||
|
|
||||||
if ($this->start) {
|
|
||||||
$dtstart = new Sabre_VObject_Property_DateTime('DTSTART');
|
|
||||||
$dtstart->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC);
|
|
||||||
$vfreebusy->add($dtstart);
|
|
||||||
}
|
|
||||||
if ($this->end) {
|
|
||||||
$dtend = new Sabre_VObject_Property_DateTime('DTEND');
|
|
||||||
$dtend->setDateTime($this->start,Sabre_VObject_Property_DateTime::UTC);
|
|
||||||
$vfreebusy->add($dtend);
|
|
||||||
}
|
|
||||||
$dtstamp = new Sabre_VObject_Property_DateTime('DTSTAMP');
|
|
||||||
$dtstamp->setDateTime(new DateTime('now'), Sabre_VObject_Property_DateTime::UTC);
|
|
||||||
$vfreebusy->add($dtstamp);
|
|
||||||
|
|
||||||
foreach($busyTimes as $busyTime) {
|
|
||||||
|
|
||||||
$busyTime[0]->setTimeZone(new DateTimeZone('UTC'));
|
|
||||||
$busyTime[1]->setTimeZone(new DateTimeZone('UTC'));
|
|
||||||
|
|
||||||
$prop = new Sabre_VObject_Property(
|
|
||||||
'FREEBUSY',
|
|
||||||
$busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')
|
|
||||||
);
|
|
||||||
$prop['FBTYPE'] = $busyTime[2];
|
|
||||||
$vfreebusy->add($prop);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $calendar;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,149 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for all nodes
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Countable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns the object back into a serialized blob.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
abstract function serialize();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterator override
|
|
||||||
*
|
|
||||||
* @var Sabre_VObject_ElementList
|
|
||||||
*/
|
|
||||||
protected $iterator = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A link to the parent node
|
|
||||||
*
|
|
||||||
* @var Sabre_VObject_Node
|
|
||||||
*/
|
|
||||||
public $parent = null;
|
|
||||||
|
|
||||||
/* {{{ IteratorAggregator interface */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the iterator for this object
|
|
||||||
*
|
|
||||||
* @return Sabre_VObject_ElementList
|
|
||||||
*/
|
|
||||||
public function getIterator() {
|
|
||||||
|
|
||||||
if (!is_null($this->iterator))
|
|
||||||
return $this->iterator;
|
|
||||||
|
|
||||||
return new Sabre_VObject_ElementList(array($this));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the overridden iterator
|
|
||||||
*
|
|
||||||
* Note that this is not actually part of the iterator interface
|
|
||||||
*
|
|
||||||
* @param Sabre_VObject_ElementList $iterator
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setIterator(Sabre_VObject_ElementList $iterator) {
|
|
||||||
|
|
||||||
$this->iterator = $iterator;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
/* {{{ Countable interface */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of elements
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function count() {
|
|
||||||
|
|
||||||
$it = $this->getIterator();
|
|
||||||
return $it->count();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
/* {{{ ArrayAccess Interface */
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if an item exists through ArrayAccess.
|
|
||||||
*
|
|
||||||
* This method just forwards the request to the inner iterator
|
|
||||||
*
|
|
||||||
* @param int $offset
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function offsetExists($offset) {
|
|
||||||
|
|
||||||
$iterator = $this->getIterator();
|
|
||||||
return $iterator->offsetExists($offset);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an item through ArrayAccess.
|
|
||||||
*
|
|
||||||
* This method just forwards the request to the inner iterator
|
|
||||||
*
|
|
||||||
* @param int $offset
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function offsetGet($offset) {
|
|
||||||
|
|
||||||
$iterator = $this->getIterator();
|
|
||||||
return $iterator->offsetGet($offset);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets an item through ArrayAccess.
|
|
||||||
*
|
|
||||||
* This method just forwards the request to the inner iterator
|
|
||||||
*
|
|
||||||
* @param int $offset
|
|
||||||
* @param mixed $value
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function offsetSet($offset,$value) {
|
|
||||||
|
|
||||||
$iterator = $this->getIterator();
|
|
||||||
return $iterator->offsetSet($offset,$value);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets an item through ArrayAccess.
|
|
||||||
*
|
|
||||||
* This method just forwards the request to the inner iterator
|
|
||||||
*
|
|
||||||
* @param int $offset
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function offsetUnset($offset) {
|
|
||||||
|
|
||||||
$iterator = $this->getIterator();
|
|
||||||
return $iterator->offsetUnset($offset);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VObject Parameter
|
|
||||||
*
|
|
||||||
* This class represents a parameter. A parameter is always tied to a property.
|
|
||||||
* In the case of:
|
|
||||||
* DTSTART;VALUE=DATE:20101108
|
|
||||||
* VALUE=DATE would be the parameter name and value.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Parameter extends Sabre_VObject_Node {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parameter name
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parameter value
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the object
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param string $value
|
|
||||||
*/
|
|
||||||
public function __construct($name, $value = null) {
|
|
||||||
|
|
||||||
$this->name = strtoupper($name);
|
|
||||||
$this->value = $value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns the object back into a serialized blob.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function serialize() {
|
|
||||||
|
|
||||||
if (is_null($this->value)) {
|
|
||||||
return $this->name;
|
|
||||||
}
|
|
||||||
$src = array(
|
|
||||||
'\\',
|
|
||||||
"\n",
|
|
||||||
';',
|
|
||||||
',',
|
|
||||||
);
|
|
||||||
$out = array(
|
|
||||||
'\\\\',
|
|
||||||
'\n',
|
|
||||||
'\;',
|
|
||||||
'\,',
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this->name . '=' . str_replace($src, $out, $this->value);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when this object is being cast to a string
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function __toString() {
|
|
||||||
|
|
||||||
return $this->value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception thrown by Sabre_VObject_Reader if an invalid object was attempted to be parsed.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_ParseException extends Exception { }
|
|
|
@ -1,348 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VObject Property
|
|
||||||
*
|
|
||||||
* A property in VObject is usually in the form PARAMNAME:paramValue.
|
|
||||||
* An example is : SUMMARY:Weekly meeting
|
|
||||||
*
|
|
||||||
* Properties can also have parameters:
|
|
||||||
* SUMMARY;LANG=en:Weekly meeting.
|
|
||||||
*
|
|
||||||
* Parameters can be accessed using the ArrayAccess interface.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Property extends Sabre_VObject_Element {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Propertyname
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Group name
|
|
||||||
*
|
|
||||||
* This may be something like 'HOME' for vcards.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $group;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property parameters
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
public $parameters = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Property value
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If properties are added to this map, they will be automatically mapped
|
|
||||||
* to their respective classes, if parsed by the reader or constructed with
|
|
||||||
* the 'create' method.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
static public $classMap = array(
|
|
||||||
'COMPLETED' => 'Sabre_VObject_Property_DateTime',
|
|
||||||
'CREATED' => 'Sabre_VObject_Property_DateTime',
|
|
||||||
'DTEND' => 'Sabre_VObject_Property_DateTime',
|
|
||||||
'DTSTAMP' => 'Sabre_VObject_Property_DateTime',
|
|
||||||
'DTSTART' => 'Sabre_VObject_Property_DateTime',
|
|
||||||
'DUE' => 'Sabre_VObject_Property_DateTime',
|
|
||||||
'EXDATE' => 'Sabre_VObject_Property_MultiDateTime',
|
|
||||||
'LAST-MODIFIED' => 'Sabre_VObject_Property_DateTime',
|
|
||||||
'RECURRENCE-ID' => 'Sabre_VObject_Property_DateTime',
|
|
||||||
'TRIGGER' => 'Sabre_VObject_Property_DateTime',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the new property by name, but in addition will also see if
|
|
||||||
* there's a class mapped to the property name.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param string $value
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
static public function create($name, $value = null) {
|
|
||||||
|
|
||||||
$name = strtoupper($name);
|
|
||||||
$shortName = $name;
|
|
||||||
$group = null;
|
|
||||||
if (strpos($shortName,'.')!==false) {
|
|
||||||
list($group, $shortName) = explode('.', $shortName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset(self::$classMap[$shortName])) {
|
|
||||||
return new self::$classMap[$shortName]($name, $value);
|
|
||||||
} else {
|
|
||||||
return new self($name, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new property object
|
|
||||||
*
|
|
||||||
* By default this object will iterate over its own children, but this can
|
|
||||||
* be overridden with the iterator argument
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param string $value
|
|
||||||
* @param Sabre_VObject_ElementList $iterator
|
|
||||||
*/
|
|
||||||
public function __construct($name, $value = null, $iterator = null) {
|
|
||||||
|
|
||||||
$name = strtoupper($name);
|
|
||||||
$group = null;
|
|
||||||
if (strpos($name,'.')!==false) {
|
|
||||||
list($group, $name) = explode('.', $name);
|
|
||||||
}
|
|
||||||
$this->name = $name;
|
|
||||||
$this->group = $group;
|
|
||||||
if (!is_null($iterator)) $this->iterator = $iterator;
|
|
||||||
$this->setValue($value);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the internal value
|
|
||||||
*
|
|
||||||
* @param string $value
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setValue($value) {
|
|
||||||
|
|
||||||
$this->value = $value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns the object back into a serialized blob.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function serialize() {
|
|
||||||
|
|
||||||
$str = $this->name;
|
|
||||||
if ($this->group) $str = $this->group . '.' . $this->name;
|
|
||||||
|
|
||||||
if (count($this->parameters)) {
|
|
||||||
foreach($this->parameters as $param) {
|
|
||||||
|
|
||||||
$str.=';' . $param->serialize();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$src = array(
|
|
||||||
'\\',
|
|
||||||
"\n",
|
|
||||||
);
|
|
||||||
$out = array(
|
|
||||||
'\\\\',
|
|
||||||
'\n',
|
|
||||||
);
|
|
||||||
$str.=':' . str_replace($src, $out, $this->value);
|
|
||||||
|
|
||||||
$out = '';
|
|
||||||
while(strlen($str)>0) {
|
|
||||||
if (strlen($str)>75) {
|
|
||||||
$out.= mb_strcut($str,0,75,'utf-8') . "\r\n";
|
|
||||||
$str = ' ' . mb_strcut($str,75,strlen($str),'utf-8');
|
|
||||||
} else {
|
|
||||||
$out.=$str . "\r\n";
|
|
||||||
$str='';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $out;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new componenten or element
|
|
||||||
*
|
|
||||||
* You can call this method with the following syntaxes:
|
|
||||||
*
|
|
||||||
* add(Sabre_VObject_Parameter $element)
|
|
||||||
* add(string $name, $value)
|
|
||||||
*
|
|
||||||
* The first version adds an Parameter
|
|
||||||
* The second adds a property as a string.
|
|
||||||
*
|
|
||||||
* @param mixed $item
|
|
||||||
* @param mixed $itemValue
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function add($item, $itemValue = null) {
|
|
||||||
|
|
||||||
if ($item instanceof Sabre_VObject_Parameter) {
|
|
||||||
if (!is_null($itemValue)) {
|
|
||||||
throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject');
|
|
||||||
}
|
|
||||||
$item->parent = $this;
|
|
||||||
$this->parameters[] = $item;
|
|
||||||
} elseif(is_string($item)) {
|
|
||||||
|
|
||||||
if (!is_scalar($itemValue) && !is_null($itemValue)) {
|
|
||||||
throw new InvalidArgumentException('The second argument must be scalar');
|
|
||||||
}
|
|
||||||
$parameter = new Sabre_VObject_Parameter($item,$itemValue);
|
|
||||||
$parameter->parent = $this;
|
|
||||||
$this->parameters[] = $parameter;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ArrayAccess interface {{{ */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if an array element exists
|
|
||||||
*
|
|
||||||
* @param mixed $name
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function offsetExists($name) {
|
|
||||||
|
|
||||||
if (is_int($name)) return parent::offsetExists($name);
|
|
||||||
|
|
||||||
$name = strtoupper($name);
|
|
||||||
|
|
||||||
foreach($this->parameters as $parameter) {
|
|
||||||
if ($parameter->name == $name) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a parameter, or parameter list.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return Sabre_VObject_Element
|
|
||||||
*/
|
|
||||||
public function offsetGet($name) {
|
|
||||||
|
|
||||||
if (is_int($name)) return parent::offsetGet($name);
|
|
||||||
$name = strtoupper($name);
|
|
||||||
|
|
||||||
$result = array();
|
|
||||||
foreach($this->parameters as $parameter) {
|
|
||||||
if ($parameter->name == $name)
|
|
||||||
$result[] = $parameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($result)===0) {
|
|
||||||
return null;
|
|
||||||
} elseif (count($result)===1) {
|
|
||||||
return $result[0];
|
|
||||||
} else {
|
|
||||||
$result[0]->setIterator(new Sabre_VObject_ElementList($result));
|
|
||||||
return $result[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new parameter
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param mixed $value
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function offsetSet($name, $value) {
|
|
||||||
|
|
||||||
if (is_int($name)) return parent::offsetSet($name, $value);
|
|
||||||
|
|
||||||
if (is_scalar($value)) {
|
|
||||||
if (!is_string($name))
|
|
||||||
throw new InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.');
|
|
||||||
|
|
||||||
$this->offsetUnset($name);
|
|
||||||
$parameter = new Sabre_VObject_Parameter($name, $value);
|
|
||||||
$parameter->parent = $this;
|
|
||||||
$this->parameters[] = $parameter;
|
|
||||||
|
|
||||||
} elseif ($value instanceof Sabre_VObject_Parameter) {
|
|
||||||
if (!is_null($name))
|
|
||||||
throw new InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a Sabre_VObject_Parameter. Add using $array[]=$parameterObject.');
|
|
||||||
|
|
||||||
$value->parent = $this;
|
|
||||||
$this->parameters[] = $value;
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException('You can only add parameters to the property object');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes one or more parameters with the specified name
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function offsetUnset($name) {
|
|
||||||
|
|
||||||
if (is_int($name)) return parent::offsetUnset($name);
|
|
||||||
$name = strtoupper($name);
|
|
||||||
|
|
||||||
foreach($this->parameters as $key=>$parameter) {
|
|
||||||
if ($parameter->name == $name) {
|
|
||||||
$parameter->parent = null;
|
|
||||||
unset($this->parameters[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }}} */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when this object is being cast to a string
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function __toString() {
|
|
||||||
|
|
||||||
return (string)$this->value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is automatically called when the object is cloned.
|
|
||||||
* Specifically, this will ensure all child elements are also cloned.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __clone() {
|
|
||||||
|
|
||||||
foreach($this->parameters as $key=>$child) {
|
|
||||||
$this->parameters[$key] = clone $child;
|
|
||||||
$this->parameters[$key]->parent = $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,260 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DateTime property
|
|
||||||
*
|
|
||||||
* This element is used for iCalendar properties such as the DTSTART property.
|
|
||||||
* It basically provides a few helper functions that make it easier to deal
|
|
||||||
* with these. It supports both DATE-TIME and DATE values.
|
|
||||||
*
|
|
||||||
* In order to use this correctly, you must call setDateTime and getDateTime to
|
|
||||||
* retrieve and modify dates respectively.
|
|
||||||
*
|
|
||||||
* If you use the 'value' or properties directly, this object does not keep
|
|
||||||
* reference and results might appear incorrectly.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Property_DateTime extends Sabre_VObject_Property {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local 'floating' time
|
|
||||||
*/
|
|
||||||
const LOCAL = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UTC-based time
|
|
||||||
*/
|
|
||||||
const UTC = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local time plus timezone
|
|
||||||
*/
|
|
||||||
const LOCALTZ = 3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only a date, time is ignored
|
|
||||||
*/
|
|
||||||
const DATE = 4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DateTime representation
|
|
||||||
*
|
|
||||||
* @var DateTime
|
|
||||||
*/
|
|
||||||
protected $dateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* dateType
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $dateType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the Date and Time.
|
|
||||||
*
|
|
||||||
* @param DateTime $dt
|
|
||||||
* @param int $dateType
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setDateTime(DateTime $dt, $dateType = self::LOCALTZ) {
|
|
||||||
|
|
||||||
switch($dateType) {
|
|
||||||
|
|
||||||
case self::LOCAL :
|
|
||||||
$this->setValue($dt->format('Ymd\\THis'));
|
|
||||||
$this->offsetUnset('VALUE');
|
|
||||||
$this->offsetUnset('TZID');
|
|
||||||
$this->offsetSet('VALUE','DATE-TIME');
|
|
||||||
break;
|
|
||||||
case self::UTC :
|
|
||||||
$dt->setTimeZone(new DateTimeZone('UTC'));
|
|
||||||
$this->setValue($dt->format('Ymd\\THis\\Z'));
|
|
||||||
$this->offsetUnset('VALUE');
|
|
||||||
$this->offsetUnset('TZID');
|
|
||||||
$this->offsetSet('VALUE','DATE-TIME');
|
|
||||||
break;
|
|
||||||
case self::LOCALTZ :
|
|
||||||
$this->setValue($dt->format('Ymd\\THis'));
|
|
||||||
$this->offsetUnset('VALUE');
|
|
||||||
$this->offsetUnset('TZID');
|
|
||||||
$this->offsetSet('VALUE','DATE-TIME');
|
|
||||||
$this->offsetSet('TZID', $dt->getTimeZone()->getName());
|
|
||||||
break;
|
|
||||||
case self::DATE :
|
|
||||||
$this->setValue($dt->format('Ymd'));
|
|
||||||
$this->offsetUnset('VALUE');
|
|
||||||
$this->offsetUnset('TZID');
|
|
||||||
$this->offsetSet('VALUE','DATE');
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
throw new InvalidArgumentException('You must pass a valid dateType constant');
|
|
||||||
|
|
||||||
}
|
|
||||||
$this->dateTime = $dt;
|
|
||||||
$this->dateType = $dateType;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current DateTime value.
|
|
||||||
*
|
|
||||||
* If no value was set, this method returns null.
|
|
||||||
*
|
|
||||||
* @return DateTime|null
|
|
||||||
*/
|
|
||||||
public function getDateTime() {
|
|
||||||
|
|
||||||
if ($this->dateTime)
|
|
||||||
return $this->dateTime;
|
|
||||||
|
|
||||||
list(
|
|
||||||
$this->dateType,
|
|
||||||
$this->dateTime
|
|
||||||
) = self::parseData($this->value, $this);
|
|
||||||
return $this->dateTime;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the type of Date format.
|
|
||||||
*
|
|
||||||
* This method returns one of the format constants. If no date was set,
|
|
||||||
* this method will return null.
|
|
||||||
*
|
|
||||||
* @return int|null
|
|
||||||
*/
|
|
||||||
public function getDateType() {
|
|
||||||
|
|
||||||
if ($this->dateType)
|
|
||||||
return $this->dateType;
|
|
||||||
|
|
||||||
list(
|
|
||||||
$this->dateType,
|
|
||||||
$this->dateTime,
|
|
||||||
) = self::parseData($this->value, $this);
|
|
||||||
return $this->dateType;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the internal data structure to figure out what the current date
|
|
||||||
* and time is.
|
|
||||||
*
|
|
||||||
* The returned array contains two elements:
|
|
||||||
* 1. A 'DateType' constant (as defined on this class), or null.
|
|
||||||
* 2. A DateTime object (or null)
|
|
||||||
*
|
|
||||||
* @param string|null $propertyValue The string to parse (yymmdd or
|
|
||||||
* ymmddThhmmss, etc..)
|
|
||||||
* @param Sabre_VObject_Property|null $property The instance of the
|
|
||||||
* property we're parsing.
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
static public function parseData($propertyValue, Sabre_VObject_Property $property = null) {
|
|
||||||
|
|
||||||
if (is_null($propertyValue)) {
|
|
||||||
return array(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
$date = '(?P<year>[1-2][0-9]{3})(?P<month>[0-1][0-9])(?P<date>[0-3][0-9])';
|
|
||||||
$time = '(?P<hour>[0-2][0-9])(?P<minute>[0-5][0-9])(?P<second>[0-5][0-9])';
|
|
||||||
$regex = "/^$date(T$time(?P<isutc>Z)?)?$/";
|
|
||||||
|
|
||||||
if (!preg_match($regex, $propertyValue, $matches)) {
|
|
||||||
throw new InvalidArgumentException($propertyValue . ' is not a valid DateTime or Date string');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($matches['hour'])) {
|
|
||||||
// Date-only
|
|
||||||
return array(
|
|
||||||
self::DATE,
|
|
||||||
new DateTime($matches['year'] . '-' . $matches['month'] . '-' . $matches['date'] . ' 00:00:00'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$dateStr =
|
|
||||||
$matches['year'] .'-' .
|
|
||||||
$matches['month'] . '-' .
|
|
||||||
$matches['date'] . ' ' .
|
|
||||||
$matches['hour'] . ':' .
|
|
||||||
$matches['minute'] . ':' .
|
|
||||||
$matches['second'];
|
|
||||||
|
|
||||||
if (isset($matches['isutc'])) {
|
|
||||||
$dt = new DateTime($dateStr,new DateTimeZone('UTC'));
|
|
||||||
$dt->setTimeZone(new DateTimeZone('UTC'));
|
|
||||||
return array(
|
|
||||||
self::UTC,
|
|
||||||
$dt
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finding the timezone.
|
|
||||||
$tzid = $property['TZID'];
|
|
||||||
if (!$tzid) {
|
|
||||||
return array(
|
|
||||||
self::LOCAL,
|
|
||||||
new DateTime($dateStr)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// tzid an Olson identifier?
|
|
||||||
$tz = new DateTimeZone($tzid->value);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
|
|
||||||
// Not an Olson id, we're going to try to find the information
|
|
||||||
// through the time zone name map.
|
|
||||||
$newtzid = Sabre_VObject_WindowsTimezoneMap::lookup($tzid->value);
|
|
||||||
if (is_null($newtzid)) {
|
|
||||||
|
|
||||||
// Not a well known time zone name either, we're going to try
|
|
||||||
// to find the information through the VTIMEZONE object.
|
|
||||||
|
|
||||||
// First we find the root object
|
|
||||||
$root = $property;
|
|
||||||
while($root->parent) {
|
|
||||||
$root = $root->parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($root->VTIMEZONE)) {
|
|
||||||
foreach($root->VTIMEZONE as $vtimezone) {
|
|
||||||
if (((string)$vtimezone->TZID) == $tzid) {
|
|
||||||
if (isset($vtimezone->{'X-LIC-LOCATION'})) {
|
|
||||||
$newtzid = (string)$vtimezone->{'X-LIC-LOCATION'};
|
|
||||||
} else {
|
|
||||||
// No libical location specified. As a last resort we could
|
|
||||||
// try matching $vtimezone's DST rules against all known
|
|
||||||
// time zones returned by DateTimeZone::list*
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$tz = new DateTimeZone($newtzid);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// If all else fails, we use the default PHP timezone
|
|
||||||
$tz = new DateTimeZone(date_default_timezone_get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$dt = new DateTime($dateStr, $tz);
|
|
||||||
$dt->setTimeZone($tz);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
self::LOCALTZ,
|
|
||||||
$dt
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Multi-DateTime property
|
|
||||||
*
|
|
||||||
* This element is used for iCalendar properties such as the EXDATE property.
|
|
||||||
* It basically provides a few helper functions that make it easier to deal
|
|
||||||
* with these. It supports both DATE-TIME and DATE values.
|
|
||||||
*
|
|
||||||
* In order to use this correctly, you must call setDateTimes and getDateTimes
|
|
||||||
* to retrieve and modify dates respectively.
|
|
||||||
*
|
|
||||||
* If you use the 'value' or properties directly, this object does not keep
|
|
||||||
* reference and results might appear incorrectly.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Property_MultiDateTime extends Sabre_VObject_Property {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DateTime representation
|
|
||||||
*
|
|
||||||
* @var DateTime[]
|
|
||||||
*/
|
|
||||||
protected $dateTimes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* dateType
|
|
||||||
*
|
|
||||||
* This is one of the Sabre_VObject_Property_DateTime constants.
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $dateType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the value
|
|
||||||
*
|
|
||||||
* @param array $dt Must be an array of DateTime objects.
|
|
||||||
* @param int $dateType
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setDateTimes(array $dt, $dateType = Sabre_VObject_Property_DateTime::LOCALTZ) {
|
|
||||||
|
|
||||||
foreach($dt as $i)
|
|
||||||
if (!$i instanceof DateTime)
|
|
||||||
throw new InvalidArgumentException('You must pass an array of DateTime objects');
|
|
||||||
|
|
||||||
$this->offsetUnset('VALUE');
|
|
||||||
$this->offsetUnset('TZID');
|
|
||||||
switch($dateType) {
|
|
||||||
|
|
||||||
case Sabre_VObject_Property_DateTime::LOCAL :
|
|
||||||
$val = array();
|
|
||||||
foreach($dt as $i) {
|
|
||||||
$val[] = $i->format('Ymd\\THis');
|
|
||||||
}
|
|
||||||
$this->setValue(implode(',',$val));
|
|
||||||
$this->offsetSet('VALUE','DATE-TIME');
|
|
||||||
break;
|
|
||||||
case Sabre_VObject_Property_DateTime::UTC :
|
|
||||||
$val = array();
|
|
||||||
foreach($dt as $i) {
|
|
||||||
$i->setTimeZone(new DateTimeZone('UTC'));
|
|
||||||
$val[] = $i->format('Ymd\\THis\\Z');
|
|
||||||
}
|
|
||||||
$this->setValue(implode(',',$val));
|
|
||||||
$this->offsetSet('VALUE','DATE-TIME');
|
|
||||||
break;
|
|
||||||
case Sabre_VObject_Property_DateTime::LOCALTZ :
|
|
||||||
$val = array();
|
|
||||||
foreach($dt as $i) {
|
|
||||||
$val[] = $i->format('Ymd\\THis');
|
|
||||||
}
|
|
||||||
$this->setValue(implode(',',$val));
|
|
||||||
$this->offsetSet('VALUE','DATE-TIME');
|
|
||||||
$this->offsetSet('TZID', $dt[0]->getTimeZone()->getName());
|
|
||||||
break;
|
|
||||||
case Sabre_VObject_Property_DateTime::DATE :
|
|
||||||
$val = array();
|
|
||||||
foreach($dt as $i) {
|
|
||||||
$val[] = $i->format('Ymd');
|
|
||||||
}
|
|
||||||
$this->setValue(implode(',',$val));
|
|
||||||
$this->offsetSet('VALUE','DATE');
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
throw new InvalidArgumentException('You must pass a valid dateType constant');
|
|
||||||
|
|
||||||
}
|
|
||||||
$this->dateTimes = $dt;
|
|
||||||
$this->dateType = $dateType;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current DateTime value.
|
|
||||||
*
|
|
||||||
* If no value was set, this method returns null.
|
|
||||||
*
|
|
||||||
* @return array|null
|
|
||||||
*/
|
|
||||||
public function getDateTimes() {
|
|
||||||
|
|
||||||
if ($this->dateTimes)
|
|
||||||
return $this->dateTimes;
|
|
||||||
|
|
||||||
$dts = array();
|
|
||||||
|
|
||||||
if (!$this->value) {
|
|
||||||
$this->dateTimes = null;
|
|
||||||
$this->dateType = null;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(explode(',',$this->value) as $val) {
|
|
||||||
list(
|
|
||||||
$type,
|
|
||||||
$dt
|
|
||||||
) = Sabre_VObject_Property_DateTime::parseData($val, $this);
|
|
||||||
$dts[] = $dt;
|
|
||||||
$this->dateType = $type;
|
|
||||||
}
|
|
||||||
$this->dateTimes = $dts;
|
|
||||||
return $this->dateTimes;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the type of Date format.
|
|
||||||
*
|
|
||||||
* This method returns one of the format constants. If no date was set,
|
|
||||||
* this method will return null.
|
|
||||||
*
|
|
||||||
* @return int|null
|
|
||||||
*/
|
|
||||||
public function getDateType() {
|
|
||||||
|
|
||||||
if ($this->dateType)
|
|
||||||
return $this->dateType;
|
|
||||||
|
|
||||||
if (!$this->value) {
|
|
||||||
$this->dateTimes = null;
|
|
||||||
$this->dateType = null;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dts = array();
|
|
||||||
foreach(explode(',',$this->value) as $val) {
|
|
||||||
list(
|
|
||||||
$type,
|
|
||||||
$dt
|
|
||||||
) = Sabre_VObject_Property_DateTime::parseData($val, $this);
|
|
||||||
$dts[] = $dt;
|
|
||||||
$this->dateType = $type;
|
|
||||||
}
|
|
||||||
$this->dateTimes = $dts;
|
|
||||||
return $this->dateType;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,183 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VCALENDAR/VCARD reader
|
|
||||||
*
|
|
||||||
* This class reads the vobject file, and returns a full element tree.
|
|
||||||
*
|
|
||||||
* TODO: this class currently completely works 'statically'. This is pointless,
|
|
||||||
* and defeats OOP principals. Needs refactoring in a future version.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Reader {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the file and returns the top component
|
|
||||||
*
|
|
||||||
* @param string $data
|
|
||||||
* @return Sabre_VObject_Element
|
|
||||||
*/
|
|
||||||
static function read($data) {
|
|
||||||
|
|
||||||
// Normalizing newlines
|
|
||||||
$data = str_replace(array("\r","\n\n"), array("\n","\n"), $data);
|
|
||||||
|
|
||||||
$lines = explode("\n", $data);
|
|
||||||
|
|
||||||
// Unfolding lines
|
|
||||||
$lines2 = array();
|
|
||||||
foreach($lines as $line) {
|
|
||||||
|
|
||||||
// Skipping empty lines
|
|
||||||
if (!$line) continue;
|
|
||||||
|
|
||||||
if ($line[0]===" " || $line[0]==="\t") {
|
|
||||||
$lines2[count($lines2)-1].=substr($line,1);
|
|
||||||
} else {
|
|
||||||
$lines2[] = $line;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($lines);
|
|
||||||
|
|
||||||
reset($lines2);
|
|
||||||
|
|
||||||
return self::readLine($lines2);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads and parses a single line.
|
|
||||||
*
|
|
||||||
* This method receives the full array of lines. The array pointer is used
|
|
||||||
* to traverse.
|
|
||||||
*
|
|
||||||
* @param array $lines
|
|
||||||
* @return Sabre_VObject_Element
|
|
||||||
*/
|
|
||||||
static private function readLine(&$lines) {
|
|
||||||
|
|
||||||
$line = current($lines);
|
|
||||||
$lineNr = key($lines);
|
|
||||||
next($lines);
|
|
||||||
|
|
||||||
// Components
|
|
||||||
if (stripos($line,"BEGIN:")===0) {
|
|
||||||
|
|
||||||
$componentName = strtoupper(substr($line,6));
|
|
||||||
$obj = Sabre_VObject_Component::create($componentName);
|
|
||||||
|
|
||||||
$nextLine = current($lines);
|
|
||||||
|
|
||||||
while(stripos($nextLine,"END:")!==0) {
|
|
||||||
|
|
||||||
$obj->add(self::readLine($lines));
|
|
||||||
|
|
||||||
$nextLine = current($lines);
|
|
||||||
|
|
||||||
if ($nextLine===false)
|
|
||||||
throw new Sabre_VObject_ParseException('Invalid VObject. Document ended prematurely.');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking component name of the 'END:' line.
|
|
||||||
if (substr($nextLine,4)!==$obj->name) {
|
|
||||||
throw new Sabre_VObject_ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"');
|
|
||||||
}
|
|
||||||
next($lines);
|
|
||||||
|
|
||||||
return $obj;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Properties
|
|
||||||
//$result = preg_match('/(?P<name>[A-Z0-9-]+)(?:;(?P<parameters>^(?<!:):))(.*)$/',$line,$matches);
|
|
||||||
|
|
||||||
|
|
||||||
$token = '[A-Z0-9-\.]+';
|
|
||||||
$parameters = "(?:;(?P<parameters>([^:^\"]|\"([^\"]*)\")*))?";
|
|
||||||
$regex = "/^(?P<name>$token)$parameters:(?P<value>.*)$/i";
|
|
||||||
|
|
||||||
$result = preg_match($regex,$line,$matches);
|
|
||||||
|
|
||||||
if (!$result) {
|
|
||||||
throw new Sabre_VObject_ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format');
|
|
||||||
}
|
|
||||||
|
|
||||||
$propertyName = strtoupper($matches['name']);
|
|
||||||
$propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) {
|
|
||||||
if ($matches[2]==='n' || $matches[2]==='N') {
|
|
||||||
return "\n";
|
|
||||||
} else {
|
|
||||||
return $matches[2];
|
|
||||||
}
|
|
||||||
}, $matches['value']);
|
|
||||||
|
|
||||||
$obj = Sabre_VObject_Property::create($propertyName, $propertyValue);
|
|
||||||
|
|
||||||
if ($matches['parameters']) {
|
|
||||||
|
|
||||||
foreach(self::readParameters($matches['parameters']) as $param) {
|
|
||||||
$obj->add($param);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $obj;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a parameter list from a property
|
|
||||||
*
|
|
||||||
* This method returns an array of Sabre_VObject_Parameter
|
|
||||||
*
|
|
||||||
* @param string $parameters
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
static private function readParameters($parameters) {
|
|
||||||
|
|
||||||
$token = '[A-Z0-9-]+';
|
|
||||||
|
|
||||||
$paramValue = '(?P<paramValue>[^\"^;]*|"[^"]*")';
|
|
||||||
|
|
||||||
$regex = "/(?<=^|;)(?P<paramName>$token)(=$paramValue(?=$|;))?/i";
|
|
||||||
preg_match_all($regex, $parameters, $matches, PREG_SET_ORDER);
|
|
||||||
|
|
||||||
$params = array();
|
|
||||||
foreach($matches as $match) {
|
|
||||||
|
|
||||||
$value = isset($match['paramValue'])?$match['paramValue']:null;
|
|
||||||
|
|
||||||
if (isset($value[0])) {
|
|
||||||
// Stripping quotes, if needed
|
|
||||||
if ($value[0] === '"') $value = substr($value,1,strlen($value)-2);
|
|
||||||
} else {
|
|
||||||
$value = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$value = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) {
|
|
||||||
if ($matches[2]==='n' || $matches[2]==='N') {
|
|
||||||
return "\n";
|
|
||||||
} else {
|
|
||||||
return $matches[2];
|
|
||||||
}
|
|
||||||
}, $value);
|
|
||||||
|
|
||||||
$params[] = new Sabre_VObject_Parameter($match['paramName'], $value);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $params;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class contains the version number for the VObject package
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_Version {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Full version number
|
|
||||||
*/
|
|
||||||
const VERSION = '1.3.4';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stability : alpha, beta, stable
|
|
||||||
*/
|
|
||||||
const STABILITY = 'stable';
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,128 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Time zone name translation
|
|
||||||
*
|
|
||||||
* This file translates well-known time zone names into "Olson database" time zone names.
|
|
||||||
*
|
|
||||||
* @package Sabre
|
|
||||||
* @subpackage VObject
|
|
||||||
* @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
|
|
||||||
* @author Frank Edelhaeuser (fedel@users.sourceforge.net)
|
|
||||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
||||||
*/
|
|
||||||
class Sabre_VObject_WindowsTimezoneMap {
|
|
||||||
|
|
||||||
protected static $map = array(
|
|
||||||
|
|
||||||
// from http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html
|
|
||||||
// snapshot taken on 2012/01/16
|
|
||||||
|
|
||||||
// windows
|
|
||||||
'AUS Central Standard Time'=>'Australia/Darwin',
|
|
||||||
'AUS Eastern Standard Time'=>'Australia/Sydney',
|
|
||||||
'Afghanistan Standard Time'=>'Asia/Kabul',
|
|
||||||
'Alaskan Standard Time'=>'America/Anchorage',
|
|
||||||
'Arab Standard Time'=>'Asia/Riyadh',
|
|
||||||
'Arabian Standard Time'=>'Asia/Dubai',
|
|
||||||
'Arabic Standard Time'=>'Asia/Baghdad',
|
|
||||||
'Argentina Standard Time'=>'America/Buenos_Aires',
|
|
||||||
'Armenian Standard Time'=>'Asia/Yerevan',
|
|
||||||
'Atlantic Standard Time'=>'America/Halifax',
|
|
||||||
'Azerbaijan Standard Time'=>'Asia/Baku',
|
|
||||||
'Azores Standard Time'=>'Atlantic/Azores',
|
|
||||||
'Bangladesh Standard Time'=>'Asia/Dhaka',
|
|
||||||
'Canada Central Standard Time'=>'America/Regina',
|
|
||||||
'Cape Verde Standard Time'=>'Atlantic/Cape_Verde',
|
|
||||||
'Caucasus Standard Time'=>'Asia/Yerevan',
|
|
||||||
'Cen. Australia Standard Time'=>'Australia/Adelaide',
|
|
||||||
'Central America Standard Time'=>'America/Guatemala',
|
|
||||||
'Central Asia Standard Time'=>'Asia/Almaty',
|
|
||||||
'Central Brazilian Standard Time'=>'America/Cuiaba',
|
|
||||||
'Central Europe Standard Time'=>'Europe/Budapest',
|
|
||||||
'Central European Standard Time'=>'Europe/Warsaw',
|
|
||||||
'Central Pacific Standard Time'=>'Pacific/Guadalcanal',
|
|
||||||
'Central Standard Time'=>'America/Chicago',
|
|
||||||
'Central Standard Time (Mexico)'=>'America/Mexico_City',
|
|
||||||
'China Standard Time'=>'Asia/Shanghai',
|
|
||||||
'Dateline Standard Time'=>'Etc/GMT+12',
|
|
||||||
'E. Africa Standard Time'=>'Africa/Nairobi',
|
|
||||||
'E. Australia Standard Time'=>'Australia/Brisbane',
|
|
||||||
'E. Europe Standard Time'=>'Europe/Minsk',
|
|
||||||
'E. South America Standard Time'=>'America/Sao_Paulo',
|
|
||||||
'Eastern Standard Time'=>'America/New_York',
|
|
||||||
'Egypt Standard Time'=>'Africa/Cairo',
|
|
||||||
'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg',
|
|
||||||
'FLE Standard Time'=>'Europe/Kiev',
|
|
||||||
'Fiji Standard Time'=>'Pacific/Fiji',
|
|
||||||
'GMT Standard Time'=>'Europe/London',
|
|
||||||
'GTB Standard Time'=>'Europe/Istanbul',
|
|
||||||
'Georgian Standard Time'=>'Asia/Tbilisi',
|
|
||||||
'Greenland Standard Time'=>'America/Godthab',
|
|
||||||
'Greenwich Standard Time'=>'Atlantic/Reykjavik',
|
|
||||||
'Hawaiian Standard Time'=>'Pacific/Honolulu',
|
|
||||||
'India Standard Time'=>'Asia/Calcutta',
|
|
||||||
'Iran Standard Time'=>'Asia/Tehran',
|
|
||||||
'Israel Standard Time'=>'Asia/Jerusalem',
|
|
||||||
'Jordan Standard Time'=>'Asia/Amman',
|
|
||||||
'Kamchatka Standard Time'=>'Asia/Kamchatka',
|
|
||||||
'Korea Standard Time'=>'Asia/Seoul',
|
|
||||||
'Magadan Standard Time'=>'Asia/Magadan',
|
|
||||||
'Mauritius Standard Time'=>'Indian/Mauritius',
|
|
||||||
'Mexico Standard Time'=>'America/Mexico_City',
|
|
||||||
'Mexico Standard Time 2'=>'America/Chihuahua',
|
|
||||||
'Mid-Atlantic Standard Time'=>'Etc/GMT+2',
|
|
||||||
'Middle East Standard Time'=>'Asia/Beirut',
|
|
||||||
'Montevideo Standard Time'=>'America/Montevideo',
|
|
||||||
'Morocco Standard Time'=>'Africa/Casablanca',
|
|
||||||
'Mountain Standard Time'=>'America/Denver',
|
|
||||||
'Mountain Standard Time (Mexico)'=>'America/Chihuahua',
|
|
||||||
'Myanmar Standard Time'=>'Asia/Rangoon',
|
|
||||||
'N. Central Asia Standard Time'=>'Asia/Novosibirsk',
|
|
||||||
'Namibia Standard Time'=>'Africa/Windhoek',
|
|
||||||
'Nepal Standard Time'=>'Asia/Katmandu',
|
|
||||||
'New Zealand Standard Time'=>'Pacific/Auckland',
|
|
||||||
'Newfoundland Standard Time'=>'America/St_Johns',
|
|
||||||
'North Asia East Standard Time'=>'Asia/Irkutsk',
|
|
||||||
'North Asia Standard Time'=>'Asia/Krasnoyarsk',
|
|
||||||
'Pacific SA Standard Time'=>'America/Santiago',
|
|
||||||
'Pacific Standard Time'=>'America/Los_Angeles',
|
|
||||||
'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel',
|
|
||||||
'Pakistan Standard Time'=>'Asia/Karachi',
|
|
||||||
'Paraguay Standard Time'=>'America/Asuncion',
|
|
||||||
'Romance Standard Time'=>'Europe/Paris',
|
|
||||||
'Russian Standard Time'=>'Europe/Moscow',
|
|
||||||
'SA Eastern Standard Time'=>'America/Cayenne',
|
|
||||||
'SA Pacific Standard Time'=>'America/Bogota',
|
|
||||||
'SA Western Standard Time'=>'America/La_Paz',
|
|
||||||
'SE Asia Standard Time'=>'Asia/Bangkok',
|
|
||||||
'Samoa Standard Time'=>'Pacific/Apia',
|
|
||||||
'Singapore Standard Time'=>'Asia/Singapore',
|
|
||||||
'South Africa Standard Time'=>'Africa/Johannesburg',
|
|
||||||
'Sri Lanka Standard Time'=>'Asia/Colombo',
|
|
||||||
'Syria Standard Time'=>'Asia/Damascus',
|
|
||||||
'Taipei Standard Time'=>'Asia/Taipei',
|
|
||||||
'Tasmania Standard Time'=>'Australia/Hobart',
|
|
||||||
'Tokyo Standard Time'=>'Asia/Tokyo',
|
|
||||||
'Tonga Standard Time'=>'Pacific/Tongatapu',
|
|
||||||
'US Eastern Standard Time'=>'America/Indianapolis',
|
|
||||||
'US Mountain Standard Time'=>'America/Phoenix',
|
|
||||||
'UTC'=>'Etc/GMT',
|
|
||||||
'UTC+12'=>'Etc/GMT-12',
|
|
||||||
'UTC-02'=>'Etc/GMT+2',
|
|
||||||
'UTC-11'=>'Etc/GMT+11',
|
|
||||||
'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar',
|
|
||||||
'Venezuela Standard Time'=>'America/Caracas',
|
|
||||||
'Vladivostok Standard Time'=>'Asia/Vladivostok',
|
|
||||||
'W. Australia Standard Time'=>'Australia/Perth',
|
|
||||||
'W. Central Africa Standard Time'=>'Africa/Lagos',
|
|
||||||
'W. Europe Standard Time'=>'Europe/Berlin',
|
|
||||||
'West Asia Standard Time'=>'Asia/Tashkent',
|
|
||||||
'West Pacific Standard Time'=>'Pacific/Port_Moresby',
|
|
||||||
'Yakutsk Standard Time'=>'Asia/Yakutsk',
|
|
||||||
);
|
|
||||||
|
|
||||||
static public function lookup($tzid) {
|
|
||||||
return isset(self::$map[$tzid]) ? self::$map[$tzid] : null;
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue