76        $node = $this->server->tree->getNodeForPath($parent);
 
   80                $node->getChild(
$name);
 
   82                return [
'MKCALENDAR'];
 
  108        $parts = explode(
'/', trim($principalUrl, 
'/'));
 
  109        if (count($parts) !== 2) 
return;
 
  110        if ($parts[0] !== 
'principals') 
return;
 
  112        return self::CALENDAR_ROOT . 
'/' . $parts[1];
 
  123        return [
'calendar-access', 
'calendar-proxy'];
 
  153        $node = $this->server->tree->getNodeForPath($uri);
 
  157            $reports[] = 
'{' . self::NS_CALDAV . 
'}calendar-multiget';
 
  158            $reports[] = 
'{' . self::NS_CALDAV . 
'}calendar-query';
 
  161            $reports[] = 
'{' . self::NS_CALDAV . 
'}free-busy-query';
 
  166        if ($node instanceof 
CalendarHome && $this->server->getPlugin(
'sync')) {
 
  167            $reports[] = 
'{DAV:}sync-collection';
 
  183        $server->on(
'method:MKCALENDAR',   [$this, 
'httpMkCalendar']);
 
  184        $server->on(
'report',              [$this, 
'report']);
 
  185        $server->on(
'propFind',            [$this, 
'propFind']);
 
  186        $server->on(
'onHTMLActionsPanel',  [$this, 
'htmlActionsPanel']);
 
  187        $server->on(
'beforeCreateFile',    [$this, 
'beforeCreateFile']);
 
  188        $server->on(
'beforeWriteContent',  [$this, 
'beforeWriteContent']);
 
  189        $server->on(
'afterMethod:GET',     [$this, 
'httpAfterGET']);
 
  190        $server->on(
'getSupportedPrivilegeSet', [$this, 
'getSupportedPrivilegeSet']);
 
  195        $server->xml->elementMap[
'{' . self::NS_CALDAV . 
'}supported-calendar-component-set'] = 
'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet';
 
  196        $server->xml->elementMap[
'{' . self::NS_CALDAV . 
'}calendar-query'] = 
'Sabre\\CalDAV\\Xml\\Request\\CalendarQueryReport';
 
  197        $server->xml->elementMap[
'{' . self::NS_CALDAV . 
'}calendar-multiget'] = 
'Sabre\\CalDAV\\Xml\\Request\\CalendarMultiGetReport';
 
  198        $server->xml->elementMap[
'{' . self::NS_CALDAV . 
'}free-busy-query'] = 
'Sabre\\CalDAV\\Xml\\Request\\FreeBusyQueryReport';
 
  199        $server->xml->elementMap[
'{' . self::NS_CALDAV . 
'}mkcalendar'] = 
'Sabre\\CalDAV\\Xml\\Request\\MkCalendar';
 
  200        $server->xml->elementMap[
'{' . self::NS_CALDAV . 
'}schedule-calendar-transp'] = 
'Sabre\\CalDAV\\Xml\\Property\\ScheduleCalendarTransp';
 
  201        $server->xml->elementMap[
'{' . self::NS_CALDAV . 
'}supported-calendar-component-set'] = 
'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet';
 
  203        $server->resourceTypeMapping[
'\\Sabre\\CalDAV\\ICalendar'] = 
'{urn:ietf:params:xml:ns:caldav}calendar';
 
  205        $server->resourceTypeMapping[
'\\Sabre\\CalDAV\\Principal\\IProxyRead'] = 
'{http://calendarserver.org/ns/}calendar-proxy-read';
 
  206        $server->resourceTypeMapping[
'\\Sabre\\CalDAV\\Principal\\IProxyWrite'] = 
'{http://calendarserver.org/ns/}calendar-proxy-write';
 
  208        array_push(
$server->protectedProperties,
 
  210            '{' . self::NS_CALDAV . 
'}supported-calendar-component-set',
 
  211            '{' . self::NS_CALDAV . 
'}supported-calendar-data',
 
  212            '{' . self::NS_CALDAV . 
'}max-resource-size',
 
  213            '{' . self::NS_CALDAV . 
'}min-date-time',
 
  214            '{' . self::NS_CALDAV . 
'}max-date-time',
 
  215            '{' . self::NS_CALDAV . 
'}max-instances',
 
  216            '{' . self::NS_CALDAV . 
'}max-attendees-per-instance',
 
  217            '{' . self::NS_CALDAV . 
'}calendar-home-set',
 
  218            '{' . self::NS_CALDAV . 
'}supported-collation-set',
 
  219            '{' . self::NS_CALDAV . 
'}calendar-data',
 
  222            '{' . self::NS_CALENDARSERVER . 
'}getctag',
 
  223            '{' . self::NS_CALENDARSERVER . 
'}calendar-proxy-read-for',
 
  224            '{' . self::NS_CALENDARSERVER . 
'}calendar-proxy-write-for' 
  229            $aclPlugin->principalSearchPropertySet[
'{' . self::NS_CALDAV . 
'}calendar-user-address-set'] = 
'Calendar address';
 
  243        switch ($reportName) {
 
  244            case '{' . self::NS_CALDAV . 
'}calendar-multiget' :
 
  245                $this->server->transactionType = 
'report-calendar-multiget';
 
  248            case '{' . self::NS_CALDAV . 
'}calendar-query' :
 
  249                $this->server->transactionType = 
'report-calendar-query';
 
  252            case '{' . self::NS_CALDAV . 
'}free-busy-query' :
 
  253                $this->server->transactionType = 
'report-free-busy-query';
 
  272        $body = 
$request->getBodyAsString();
 
  280                $mkcalendar = $this->server->xml->expect(
 
  281                    '{urn:ietf:params:xml:ns:caldav}mkcalendar',
 
  285                throw new BadRequest($e->getMessage(), 
null, $e);
 
  287            $properties = $mkcalendar->getProperties();
 
  297        if (isset($properties[
'{DAV:}resourcetype'])) {
 
  298            $resourceType = $properties[
'{DAV:}resourcetype']->getValue();
 
  300            $resourceType = [
'{DAV:}collection',
'{urn:ietf:params:xml:ns:caldav}calendar'];
 
  303        $this->server->createCollection(
$path, 
new MkCol($resourceType, $properties));
 
  306        $response->setHeader(
'Content-Length', 0);
 
  325        $ns = 
'{' . self::NS_CALDAV . 
'}';
 
  329            $propFind->handle($ns . 
'max-resource-size', $this->maxResourceSize);
 
  330            $propFind->handle($ns . 
'supported-calendar-data', 
function() {
 
  333            $propFind->handle($ns . 
'supported-collation-set', 
function() {
 
  341            $principalUrl = $node->getPrincipalUrl();
 
  343            $propFind->handle(
'{' . self::NS_CALDAV . 
'}calendar-home-set', 
function() use ($principalUrl) {
 
  346                if (is_null($calendarHomePath)) 
return null;
 
  347                return new LocalHref($calendarHomePath . 
'/');
 
  352            $propFind->handle(
'{' . self::NS_CALDAV . 
'}calendar-user-address-set', 
function() use ($node) {
 
  353                $addresses = $node->getAlternateUriSet();
 
  354                $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl() . 
'/';
 
  359            $propFind->handle(
'{' . self::NS_CALENDARSERVER . 
'}email-address-set', 
function() use ($node) {
 
  360                $addresses = $node->getAlternateUriSet();
 
  362                foreach ($addresses as $address) {
 
  363                    if (substr($address, 0, 7) === 
'mailto:') {
 
  364                        $emails[] = substr($address, 7);
 
  372            $propRead = 
'{' . self::NS_CALENDARSERVER . 
'}calendar-proxy-read-for';
 
  373            $propWrite = 
'{' . self::NS_CALENDARSERVER . 
'}calendar-proxy-write-for';
 
  375            if ($propFind->getStatus($propRead) === 404 || $propFind->getStatus($propWrite) === 404) {
 
  378                $membership = 
$aclPlugin->getPrincipalMembership($propFind->getPath());
 
  382                foreach ($membership as $group) {
 
  384                    $groupNode = $this->server->tree->getNodeForPath($group);
 
  391                    if ($groupNode instanceof 
Principal\IProxyRead) {
 
  392                        $readList[] = $listItem;
 
  394                    if ($groupNode instanceof 
Principal\IProxyWrite) {
 
  395                        $writeList[] = $listItem;
 
  400                $propFind->set($propRead, 
new LocalHref($readList));
 
  401                $propFind->set($propWrite, 
new LocalHref($writeList));
 
  412            $propFind->handle(
'{' . self::NS_CALDAV . 
'}calendar-data', 
function() use ($node) {
 
  414                if (is_resource($val))
 
  415                    $val = stream_get_contents($val);
 
  418                return str_replace(
"\r", 
"", $val);
 
  437        $needsJson = $report->contentType === 
'application/calendar+json';
 
  443            [$this->server, 
'calculateUri'],
 
  447        foreach ($this->server->getPropertiesForMultiplePaths(
$paths, $report->properties) as $uri => $objProps) {
 
  449            if (($needsJson || $report->expand) && isset($objProps[200][
'{' . self::NS_CALDAV . 
'}calendar-data'])) {
 
  452                if ($report->expand) {
 
  456                    if (!isset($timeZones[$calendarPath])) {
 
  458                        $tzProp = 
'{' . self::NS_CALDAV . 
'}calendar-timezone';
 
  459                        $tzResult = $this->server->getProperties($calendarPath, [$tzProp]);
 
  460                        if (isset($tzResult[$tzProp])) {
 
  464                            $timeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
 
  472                    $vObject = $vObject->expand($report->expand[
'start'], $report->expand[
'end'], $timeZones[$calendarPath]);
 
  475                    $objProps[200][
'{' . self::NS_CALDAV . 
'}calendar-data'] = json_encode($vObject->jsonSerialize());
 
  477                    $objProps[200][
'{' . self::NS_CALDAV . 
'}calendar-data'] = $vObject->serialize();
 
  484            $propertyList[] = $objProps;
 
  488        $prefer = $this->server->getHTTPPrefer();
 
  490        $this->server->httpResponse->setStatus(207);
 
  491        $this->server->httpResponse->setHeader(
'Content-Type', 
'application/xml; charset=utf-8');
 
  492        $this->server->httpResponse->setHeader(
'Vary', 
'Brief,Prefer');
 
  493        $this->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, $prefer[
'return'] === 
'minimal'));
 
  508        $path = $this->server->getRequestUri();
 
  510        $needsJson = $report->contentType === 
'application/calendar+json';
 
  512        $node = $this->server->tree->getNodeForPath($this->server->getRequestUri());
 
  513        $depth = $this->server->getHTTPDepth(0);
 
  518        $calendarTimeZone = 
null;
 
  519        if ($report->expand) {
 
  522            $tzProp = 
'{' . self::NS_CALDAV . 
'}calendar-timezone';
 
  523            $tzResult = $this->server->getProperties(
$path, [$tzProp]);
 
  524            if (isset($tzResult[$tzProp])) {
 
  528                $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
 
  532                $vtimezoneObj->destroy();
 
  535                $calendarTimeZone = 
new DateTimeZone(
'UTC');
 
  543            $requestedCalendarData = 
true;
 
  544            $requestedProperties = $report->properties;
 
  546            if (!in_array(
'{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) {
 
  549                $requestedProperties[] = 
'{urn:ietf:params:xml:ns:caldav}calendar-data';
 
  553                $requestedCalendarData = 
false;
 
  556            $properties = $this->server->getPropertiesForPath(
 
  558                $requestedProperties,
 
  564            $properties = current($properties);
 
  568            if (isset($properties[200][
'{urn:ietf:params:xml:ns:caldav}calendar-data'])) {
 
  572                $vObject = 
VObject\Reader::read($properties[200][
'{urn:ietf:params:xml:ns:caldav}calendar-data']);
 
  573                if ($validator->validate($vObject, $report->filters)) {
 
  577                    if (!$requestedCalendarData) {
 
  578                        unset($properties[200][
'{urn:ietf:params:xml:ns:caldav}calendar-data']);
 
  582                        if ($report->expand) {
 
  583                            $vObject = $vObject->expand($report->expand[
'start'], $report->expand[
'end'], $calendarTimeZone);
 
  586                            $properties[200][
'{' . self::NS_CALDAV . 
'}calendar-data'] = json_encode($vObject->jsonSerialize());
 
  587                        } elseif ($report->expand) {
 
  588                            $properties[200][
'{' . self::NS_CALDAV . 
'}calendar-data'] = $vObject->serialize();
 
  605            if (strpos($this->server->httpRequest->getHeader(
'User-Agent'), 
'MSFT-') === 0) {
 
  615                throw new BadRequest(
'A calendar-query REPORT on a calendar with a Depth: 0 is undefined. Set Depth to 1');
 
  624            $nodePaths = $node->calendarQuery($report->filters);
 
  626            foreach ($nodePaths as 
$path) {
 
  629                    $this->server->getPropertiesForPath($this->server->getRequestUri() . 
'/' . 
$path, $report->properties);
 
  631                if (($needsJson || $report->expand)) {
 
  634                    if ($report->expand) {
 
  635                        $vObject = $vObject->expand($report->expand[
'start'], $report->expand[
'end'], $calendarTimeZone);
 
  639                        $properties[200][
'{' . self::NS_CALDAV . 
'}calendar-data'] = json_encode($vObject->jsonSerialize());
 
  641                        $properties[200][
'{' . self::NS_CALDAV . 
'}calendar-data'] = $vObject->serialize();
 
  654        $prefer = $this->server->getHTTPPrefer();
 
  656        $this->server->httpResponse->setStatus(207);
 
  657        $this->server->httpResponse->setHeader(
'Content-Type', 
'application/xml; charset=utf-8');
 
  658        $this->server->httpResponse->setHeader(
'Vary', 
'Brief,Prefer');
 
  659        $this->server->httpResponse->setBody($this->server->generateMultiStatus(
$result, $prefer[
'return'] === 
'minimal'));
 
  672        $uri = $this->server->getRequestUri();
 
  674        $acl = $this->server->getPlugin(
'acl');
 
  676            $acl->checkPrivileges($uri, 
'{' . self::NS_CALDAV . 
'}read-free-busy');
 
  679        $calendar = $this->server->tree->getNodeForPath($uri);
 
  684        $tzProp = 
'{' . self::NS_CALDAV . 
'}calendar-timezone';
 
  688        $calendarProps = $this->server->getProperties($uri, [$tzProp]);
 
  690        if (isset($calendarProps[$tzProp])) {
 
  692            $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
 
  694            $vtimezoneObj->destroy();
 
  696            $calendarTimeZone = 
new DateTimeZone(
'UTC');
 
  702            'name'         => 
'VCALENDAR',
 
  706                    'comp-filters'   => [],
 
  707                    'prop-filters'   => [],
 
  708                    'is-not-defined' => 
false,
 
  710                        'start' => $report->start,
 
  711                        'end'   => $report->end,
 
  715            'prop-filters'   => [],
 
  716            'is-not-defined' => 
false,
 
  717            'time-range'     => 
null,
 
  726        $generator->setObjects($objects);
 
  727        $generator->setTimeRange($report->start, $report->end);
 
  728        $generator->setTimeZone($calendarTimeZone);
 
  729        $result = $generator->getResult();
 
  732        $this->server->httpResponse->setStatus(200);
 
  733        $this->server->httpResponse->setHeader(
'Content-Type', 
'text/calendar');
 
  734        $this->server->httpResponse->setHeader(
'Content-Length', strlen(
$result));
 
  735        $this->server->httpResponse->setBody(
$result);
 
  761        $parentNode = $this->server->tree->getNodeForPath($parent);
 
  770            $this->server->httpRequest,
 
  771            $this->server->httpResponse,
 
  799            $this->server->httpRequest,
 
  800            $this->server->httpResponse,
 
  823        if (is_resource(
$data)) {
 
  833            if (substr(
$data, 0, 1) === 
'[') {
 
  850        if (
$vobj->name !== 
'VCALENDAR') {
 
  854        $sCCS = 
'{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
 
  858        $calendarProperties = $this->server->getProperties($parentPath, [$sCCS]);
 
  860        if (isset($calendarProperties[$sCCS])) {
 
  861            $supportedComponents = $calendarProperties[$sCCS]->getValue();
 
  863            $supportedComponents = [
'VJOURNAL', 
'VTODO', 
'VEVENT'];
 
  868        foreach (
$vobj->getComponents() as $component) {
 
  869            switch ($component->name) {
 
  875                    $foundType = $component->name;
 
  881        if (!$foundType || !in_array($foundType, $supportedComponents)) {
 
  886        $prefer = $this->server->getHTTPPrefer();
 
  888        if ($prefer[
'handling'] !== 
'strict') {
 
  895        $warningMessage = 
null;
 
  901            if (
$message[
'level'] > $highestLevel) {
 
  904                $warningMessage = 
$message[
'message'];
 
  922        if ($warningMessage) {
 
  925                'iCalendar validation warning: ' . $warningMessage
 
  933        $subModified = 
false;
 
  936            'calendarObjectChange',
 
  947        if ($modified || $subModified) {
 
  952            if (!$modified && strcmp(
$data, $before) !== 0) {
 
  973            $supportedPrivilegeSet[
'{DAV:}read'][
'aggregates'][
'{' . self::NS_CALDAV . 
'}read-free-busy'] = [
 
  994        $output .= 
'<tr><td colspan="2"><form method="post" action=""> 
  995            <h3>Create new calendar</h3> 
  996            <input type="hidden" name="sabreAction" value="mkcol" /> 
  997            <input type="hidden" name="resourceType" value="{DAV:}collection,{' . self::NS_CALDAV . 
'}calendar" /> 
  998            <label>Name (uri):</label> <input type="text" name="name" /><br /> 
  999            <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br /> 
 1000            <input type="submit" value="create" /> 
 1019        if (strpos(
$response->getHeader(
'Content-Type'), 
'text/calendar') === 
false) {
 
 1025            [
'text/calendar', 
'application/calendar+json']
 
 1028        if (
$result !== 
'application/calendar+json') {
 
 1036        $jsonBody = json_encode(
$vobj->jsonSerialize());
 
 1042        $response->setHeader(
'Content-Type', 
'application/calendar+json');
 
 1043        $response->setHeader(
'Content-Length', strlen($jsonBody));
 
 1062            'description' => 
'Adds support for CalDAV (rfc4791)',
 
 1063            'link'        => 
'http://sabre.io/dav/caldav/',
 
foreach($paths as $path) $request
An exception for terminatinating execution or to throw for unit testing.
The CalendarHome represents a node that is usually in a users' calendar-homeset.
report($reportName, $report, $path)
This functions handles REPORT requests specific to CalDAV.
freeBusyQueryReport(Xml\Request\FreeBusyQueryReport $report)
This method is responsible for parsing the request and generating the response for the CALDAV:free-bu...
calendarMultiGetReport($report)
This function handles the calendar-multiget REPORT.
htmlActionsPanel(DAV\INode $node, &$output)
This method is used to generate HTML output for the DAV\Browser\Plugin.
getCalendarHomeForPrincipal($principalUrl)
Returns the path to a principal's calendar home.
getHTTPMethods($uri)
Use this method to tell the server this plugin defines additional HTTP methods.
$maxResourceSize
The default PDO storage uses a MySQL MEDIUMBLOB for iCalendar data, which can hold up to 2^24 = 16777...
initialize(DAV\Server $server)
Initializes the plugin.
beforeWriteContent($path, DAV\IFile $node, &$data, &$modified)
This method is triggered before a file gets updated with new content.
propFind(DAV\PropFind $propFind, DAV\INode $node)
PropFind.
const NS_CALDAV
This is the official CalDAV namespace.
getPluginInfo()
Returns a bunch of meta-data about the plugin.
httpAfterGet(RequestInterface $request, ResponseInterface $response)
This event is triggered after GET requests.
getSupportedReportSet($uri)
Returns a list of reports this plugin supports.
getSupportedPrivilegeSet(INode $node, array &$supportedPrivilegeSet)
This method is triggered whenever a subsystem reqeuests the privileges that are supported on a partic...
httpMkCalendar(RequestInterface $request, ResponseInterface $response)
This function handles the MKCALENDAR HTTP method, which creates a new calendar.
const NS_CALENDARSERVER
This is the namespace for the proprietary calendarserver extensions.
getPluginName()
Returns a plugin name.
getFeatures()
Returns a list of features for the DAV: HTTP header.
beforeCreateFile($path, &$data, DAV\ICollection $parentNode, &$modified)
This method is triggered before a new file is created.
calendarQueryReport($report)
This function handles the calendar-query REPORT.
const CALENDAR_ROOT
The hardcoded root for calendar objects.
validateICalendar(&$data, $path, &$modified, RequestInterface $request, ResponseInterface $response, $isNew)
Checks if the submitted iCalendar data is in fact, valid.
email-address-set property
Supported-calendar-data property.
supported-collation-set property
This class represents a MKCOL operation.
This class holds all the information about a PROPFIND request.
The baseclass for all server plugins.
The Request class represents a single HTTP request.
static negotiate($acceptHeaderValue, array $availableOptions)
Deprecated! Use negotiateContentType.
This class helps with generating FREEBUSY reports based on existing sets of objects.
const PROFILE_CALDAV
If this option is set, the validator will operate on iCalendar objects on the assumption that the vca...
const REPAIR
The following constants are used by the validate() method.
Exception thrown by Reader if an invalid object was attempted to be parsed.
static readJson($data, $options=0)
Parses a jCard or jCal object, and returns the top component.
static read($data, $options=0, $charset='UTF-8')
Parses a vCard or iCalendar object, and returns the top component.
This interface represents a node that may contain calendar objects.
CalendarObject interface.
The ICollection Interface.
The IExtendedCollection interface.
This interface represents a file in the directory tree.
The INode interface is the base interface, and the parent class of both ICollection and IFile.
The RequestInterface represents a HTTP request.
This interface represents a HTTP response.
catch(Exception $e) $message
split($path)
Returns the 'dirname' and 'basename' for a path.