/** * '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_CalDAV_Calendar) { return; } // Checking ACL, if available. if ($aclPlugin = $this->server->getPlugin('acl')) { $aclPlugin->checkPrivileges($uri, '{DAV:}read'); } $this->server->httpResponse->setHeader('Content-Type', 'text/calendar'); $this->server->httpResponse->sendStatus(200); $nodes = $this->server->getPropertiesForPath($uri, array('{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data'), 1); $this->server->httpResponse->sendBody($this->generateICS($nodes)); // Returning false to break the event chain return false; }
/** * This function handles the calendar-query REPORT * * This report is used by clients to request calendar objects based on * complex conditions. * * @param DOMNode $dom * @return void */ public function calendarQueryReport($dom) { $requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); $filterNode = $dom->getElementsByTagNameNS('urn:ietf:params:xml:ns:caldav', 'filter'); if ($filterNode->length !== 1) { throw new Sabre_DAV_Exception_BadRequest('The calendar-query report must have a filter element'); } $filters = Sabre_CalDAV_XMLUtil::parseCalendarQueryFilters($filterNode->item(0)); $requestedCalendarData = true; if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { // We always retrieve calendar-data, as we need it for filtering. $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; // If calendar-data wasn't explicitly requested, we need to remove // it after processing. $requestedCalendarData = false; } // These are the list of nodes that potentially match the requirement $candidateNodes = $this->server->getPropertiesForPath($this->server->getRequestUri(), $requestedProperties, $this->server->getHTTPDepth(0)); $verifiedNodes = array(); 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; } if ($this->validateFilters($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'], $filters)) { if (!$requestedCalendarData) { unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); } $verifiedNodes[] = $node; } } $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes)); }
/** * This method expands all the properties and returns * a list with property values * * @param array $path * @param array $requestedProperties the list of required properties * @param int $depth * @return array */ protected function expandProperties($path, array $requestedProperties, $depth) { $foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth); $result = array(); foreach ($foundProperties as $node) { foreach ($requestedProperties as $propertyName => $childRequestedProperties) { // We're only traversing if sub-properties were requested if (count($childRequestedProperties) === 0) { continue; } // We only have to do the expansion if the property was found // and it contains an href element. if (!array_key_exists($propertyName, $node[200])) { continue; } if ($node[200][$propertyName] instanceof Sabre_DAV_Property_IHref) { $hrefs = array($node[200][$propertyName]->getHref()); } elseif ($node[200][$propertyName] instanceof Sabre_DAV_Property_HrefList) { $hrefs = $node[200][$propertyName]->getHrefs(); } $childProps = array(); foreach ($hrefs as $href) { $childProps = array_merge($childProps, $this->expandProperties($href, $childRequestedProperties, 0)); } $node[200][$propertyName] = new Sabre_DAV_Property_ResponseList($childProps); } $result[] = new Sabre_DAV_Property_Response($path, $node); } return $result; }
protected function principalPropertySearchReport($dom) { $searchableProperties = array('{DAV:}displayname' => 'display name'); list($searchProperties, $requestedProperties) = $this->parsePrincipalPropertySearchReportRequest($dom); $uri = $this->server->getRequestUri(); $result = array(); $lookupResults = $this->server->getPropertiesForPath($uri, array_keys($searchProperties), 1); // The first item in the results is the parent, so we get rid of it. array_shift($lookupResults); $matches = array(); foreach ($lookupResults as $lookupResult) { foreach ($searchProperties as $searchProperty => $searchValue) { if (!isset($searchableProperties[$searchProperty])) { throw new Sabre_DAV_Exception_BadRequest('Searching for ' . $searchProperty . ' is not supported'); } if (isset($lookupResult[200][$searchProperty]) && mb_stripos($lookupResult[200][$searchProperty], $searchValue, 0, 'UTF-8') !== false) { $matches[] = $lookupResult['href']; } } } $matchProperties = array(); foreach ($matches as $match) { list($result) = $this->server->getPropertiesForPath($match, $requestedProperties, 0); $matchProperties[] = $result; } $xml = $this->server->generateMultiStatus($matchProperties); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->sendBody($xml); }
/** * Generates the html directory index for a given url * * @param string $path * @return string */ public function generateDirectoryIndex($path) { $html = "<html>\n<head>\n <title>Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . Sabre_DAV_Version::VERSION . "</title>\n <style type=\"text/css\"> body { Font-family: arial}</style>\n</head>\n<body>\n <h1>Index for " . $this->escapeHTML($path) . "/</h1>\n <table>\n <tr><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr>\n <tr><td colspan=\"4\"><hr /></td></tr>"; $files = $this->server->getPropertiesForPath($path, array('{DAV:}resourcetype', '{DAV:}getcontenttype', '{DAV:}getcontentlength', '{DAV:}getlastmodified'), 1); foreach ($files as $k => $file) { // This is the current directory, we can skip it if (rtrim($file['href'], '/') == $path) { continue; } list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']); $name = $this->escapeHTML($name); $type = null; if (isset($file[200]['{DAV:}resourcetype'])) { $type = $file[200]['{DAV:}resourcetype']->getValue(); // resourcetype can have multiple values if (is_array($type)) { $type = implode(', ', $type); } // Some name mapping is preferred switch ($type) { case '{DAV:}collection': $type = 'Collection'; break; } } // If no resourcetype was found, we attempt to use // the contenttype property if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { $type = $file[200]['{DAV:}getcontenttype']; } if (!$type) { $type = 'Unknown'; } $type = $this->escapeHTML($type); $size = isset($file[200]['{DAV:}getcontentlength']) ? (int) $file[200]['{DAV:}getcontentlength'] : ''; $lastmodified = isset($file[200]['{DAV:}getlastmodified']) ? $file[200]['{DAV:}getlastmodified']->getTime()->format(DateTime::ATOM) : ''; $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); $html .= "<tr>\n<td><a href=\"{$fullPath}\">{$name}</a></td>\n<td>{$type}</td>\n<td>{$size}</td>\n<td>{$lastmodified}</td>\n</tr>"; } $html .= "<tr><td colspan=\"4\"><hr /></td></tr>"; if ($this->enablePost) { $html .= '<tr><td><form method="post" action=""> <h3>Create new folder</h3> <input type="hidden" name="action" value="mkcol" /> Name: <input type="text" name="name" /><br /> <input type="submit" value="create" /> </form> <form method="post" action="" enctype="multipart/form-data"> <h3>Upload file</h3> <input type="hidden" name="action" value="put" /> Name (optional): <input type="text" name="name" /><br /> File: <input type="file" name="file" /><br /> <input type="submit" value="upload" /> </form> </td></tr>'; } $html .= "</table>\n <address>Generated by SabreDAV " . Sabre_DAV_Version::VERSION . "-" . Sabre_DAV_Version::STABILITY . " (c)2007-2010 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>\n</body>\n</html>"; return $html; }
/** * @depends testSupportedReportSetPropertyNonCalendar */ function testSupportedReportSetProperty() { $props = $this->server->getPropertiesForPath('/calendars/user1/UUID-123467', array('{DAV:}supported-report-set')); $this->assertArrayHasKey(0, $props); $this->assertArrayHasKey(200, $props[0]); $this->assertArrayHasKey('{DAV:}supported-report-set', $props[0][200]); $prop = $props[0][200]['{DAV:}supported-report-set']; $this->assertTrue($prop instanceof Sabre_DAV_Property_SupportedReportSet); $value = array('{urn:ietf:params:xml:ns:caldav}calendar-multiget', '{urn:ietf:params:xml:ns:caldav}calendar-query', '{urn:ietf:params:xml:ns:caldav}free-busy-query', '{DAV:}expand-property', '{DAV:}principal-property-search', '{DAV:}principal-search-property-set'); $this->assertEquals($value, $prop->getValue()); }
/** * Generates the html directory index for a given url * * @param string $path * @return string */ public function generateDirectoryIndex($path) { ob_start(); echo "<html>\n<head>\n <title>Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . Sabre_DAV_Version::VERSION . "</title>\n <style type=\"text/css\"> body { Font-family: arial}</style>\n</head>\n<body>\n <h1>Index for " . $this->escapeHTML($path) . "/</h1>\n <table>\n <tr><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr>\n <tr><td colspan=\"4\"><hr /></td></tr>"; $files = $this->server->getPropertiesForPath($path, array('{DAV:}resourcetype', '{DAV:}getcontenttype', '{DAV:}getcontentlength', '{DAV:}getlastmodified'), 1); foreach ($files as $k => $file) { // This is the current directory, we can skip it if ($file['href'] == $path) { continue; } $name = $this->escapeHTML(basename($file['href'])); if (isset($file[200]['{DAV:}resourcetype'])) { $type = $file[200]['{DAV:}resourcetype']->getValue(); if ($type == '{DAV:}collection') { $type = 'Directory'; } elseif ($type == '') { if (isset($file[200]['{DAV:}getcontenttype'])) { $type = $file[200]['{DAV:}getcontenttype']; } else { $type = 'Unknown'; } } elseif (is_array($type)) { $type = implode(', ', $type); } } $type = $this->escapeHTML($type); $size = isset($file[200]['{DAV:}getcontentlength']) ? (int) $file[200]['{DAV:}getcontentlength'] : ''; $lastmodified = isset($file[200]['{DAV:}getlastmodified']) ? date(DATE_ATOM, $file[200]['{DAV:}getlastmodified']->getTime()) : ''; $fullPath = '/' . trim($this->server->getBaseUri() . ($path ? $this->escapeHTML($path) . '/' : '') . $name, '/'); echo "<tr>\n<td><a href=\"{$fullPath}\">{$name}</a></td>\n<td>{$type}</td>\n<td>{$size}</td>\n<td>{$lastmodified}</td>\n</tr>"; } echo "<tr><td colspan=\"4\"><hr /></td></tr>"; if ($this->enablePost) { echo '<tr><td><form method="post" action=""> <h3>Create new folder</h3> <input type="hidden" name="action" value="mkcol" /> Name: <input type="text" name="name" /><br /> <input type="submit" value="create" /> </form> <form method="post" action="" enctype="multipart/form-data"> <h3>Upload file</h3> <input type="hidden" name="action" value="put" /> Name (optional): <input type="text" name="name" /><br /> File: <input type="file" name="file" /><br /> <input type="submit" value="upload" /> </form> </td></tr>'; } echo "</table>\n <address>Generated by SabreDAV " . Sabre_DAV_Version::VERSION . "-" . Sabre_DAV_Version::STABILITY . " (c)2007-2010 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>\n</body>\n</html>"; return ob_get_clean(); }
/** * principalPropertySearchReport * * This method is reponsible for handing the * {DAV:}principal-property-search report. This report can be used for * clients to search for groups of principals, based on the value of one * or more properties. * * @param DOMDocument $dom * @return void */ protected function principalPropertySearchReport(DOMDocument $dom) { $searchableProperties = array('{DAV:}displayname' => 'display name'); list($searchProperties, $requestedProperties, $applyToPrincipalCollectionSet) = $this->parsePrincipalPropertySearchReportRequest($dom); $result = array(); if ($applyToPrincipalCollectionSet) { $uris = array(); } else { $uris = array($this->server->getRequestUri()); } $lookupResults = array(); foreach ($uris as $uri) { $p = array_keys($searchProperties); $p[] = '{DAV:}resourcetype'; $r = $this->server->getPropertiesForPath($uri, $p, 1); // The first item in the results is the parent, so we get rid of it. array_shift($r); $lookupResults = array_merge($lookupResults, $r); } $matches = array(); foreach ($lookupResults as $lookupResult) { // We're only looking for principals if (!isset($lookupResult[200]['{DAV:}resourcetype']) || !$lookupResult[200]['{DAV:}resourcetype'] instanceof Sabre_DAV_Property_ResourceType || !$lookupResult[200]['{DAV:}resourcetype']->is('{DAV:}principal')) { continue; } foreach ($searchProperties as $searchProperty => $searchValue) { if (!isset($searchableProperties[$searchProperty])) { // If a property is not 'searchable', the spec dictates // this is not a match. continue; } if (isset($lookupResult[200][$searchProperty]) && mb_stripos($lookupResult[200][$searchProperty], $searchValue, 0, 'UTF-8') !== false) { $matches[] = $lookupResult['href']; } } } $matchProperties = array(); foreach ($matches as $match) { list($result) = $this->server->getPropertiesForPath($match, $requestedProperties, 0); $matchProperties[] = $result; } $xml = $this->server->generateMultiStatus($matchProperties); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->sendBody($xml); }
/** * This function handles the addressbook-query REPORT * * This report is used by the client to filter an addressbook based on a * complex query. * * @param DOMNode $dom * @return void */ protected function addressbookQueryReport($dom) { $query = new Sabre_CardDAV_AddressBookQueryParser($dom); $query->parse(); $depth = $this->server->getHTTPDepth(0); if ($depth == 0) { $candidateNodes = array($this->server->tree->getNodeForPath($this->server->getRequestUri())); } else { $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri()); } $validNodes = array(); foreach ($candidateNodes as $node) { if (!$node instanceof Sabre_CardDAV_ICard) { continue; } $blob = $node->get(); if (is_resource($blob)) { $blob = stream_get_contents($blob); } if (!$this->validateFilters($blob, $query->filters, $query->test)) { continue; } $validNodes[] = $node; if ($query->limit && $query->limit <= count($validNodes)) { // We hit the maximum number of items, we can stop now. break; } } $result = array(); foreach ($validNodes as $validNode) { if ($depth == 0) { $href = $this->server->getRequestUri(); } else { $href = $this->server->getRequestUri() . '/' . $validNode->getName(); } list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0); } $prefer = $this->server->getHTTPPRefer(); $this->server->httpResponse->sendStatus(207); $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'])); }
/** * This function handles the calendar-query REPORT * * This report is used by clients to request calendar objects based on * complex conditions. * * @param DOMNode $dom * @return void */ public function calendarQueryReport($dom) { $parser = new Sabre_CalDAV_CalendarQueryParser($dom); $parser->parse(); $requestedCalendarData = true; $requestedProperties = $parser->requestedProperties; if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { // We always retrieve calendar-data, as we need it for filtering. $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; // If calendar-data wasn't explicitly requested, we need to remove // it after processing. $requestedCalendarData = false; } // These are the list of nodes that potentially match the requirement $candidateNodes = $this->server->getPropertiesForPath($this->server->getRequestUri(), $requestedProperties, $this->server->getHTTPDepth(0)); $verifiedNodes = array(); $validator = new Sabre_CalDAV_CalendarQueryValidator(); 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 (!$requestedCalendarData) { unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); } if ($parser->expand) { $vObject->expand($parser->expand['start'], $parser->expand['end']); $node[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } $verifiedNodes[] = $node; } } $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes)); }
/** * Generates the html directory index for a given url * * @param string $path * @return string */ public function generateDirectoryIndex($path) { $version = ''; if (Sabre_DAV_Server::$exposeVersion) { $version = Sabre_DAV_Version::VERSION . "-" . Sabre_DAV_Version::STABILITY; } $html = "<html>\n<head>\n <title>Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . $version . "</title>\n <style type=\"text/css\">\n body { Font-family: arial}\n h1 { font-size: 150% }\n </style>\n "; if ($this->enableAssets) { $html .= '<link rel="shortcut icon" href="' . $this->getAssetUrl('favicon.ico') . '" type="image/vnd.microsoft.icon" />'; } $html .= "</head>\n<body>\n <h1>Index for " . $this->escapeHTML($path) . "/</h1>\n <table>\n <tr><th width=\"24\"></th><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr>\n <tr><td colspan=\"5\"><hr /></td></tr>"; $files = $this->server->getPropertiesForPath($path, array('{DAV:}displayname', '{DAV:}resourcetype', '{DAV:}getcontenttype', '{DAV:}getcontentlength', '{DAV:}getlastmodified'), 1); $parent = $this->server->tree->getNodeForPath($path); if ($path) { list($parentUri) = Sabre_DAV_URLUtil::splitPath($path); $fullPath = Sabre_DAV_URLUtil::encodePath($this->server->getBaseUri() . $parentUri); $icon = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="Parent" /></a>' : ''; $html .= "<tr>\n <td>{$icon}</td>\n <td><a href=\"{$fullPath}\">..</a></td>\n <td>[parent]</td>\n <td></td>\n <td></td>\n </tr>"; } foreach ($files as $file) { // This is the current directory, we can skip it if (rtrim($file['href'], '/') == $path) { continue; } list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']); $type = null; if (isset($file[200]['{DAV:}resourcetype'])) { $type = $file[200]['{DAV:}resourcetype']->getValue(); // resourcetype can have multiple values if (!is_array($type)) { $type = array($type); } foreach ($type as $k => $v) { // Some name mapping is preferred switch ($v) { case '{DAV:}collection': $type[$k] = 'Collection'; break; case '{DAV:}principal': $type[$k] = 'Principal'; break; case '{urn:ietf:params:xml:ns:carddav}addressbook': $type[$k] = 'Addressbook'; break; case '{urn:ietf:params:xml:ns:caldav}calendar': $type[$k] = 'Calendar'; break; case '{urn:ietf:params:xml:ns:caldav}schedule-inbox': $type[$k] = 'Schedule Inbox'; break; case '{urn:ietf:params:xml:ns:caldav}schedule-outbox': $type[$k] = 'Schedule Outbox'; break; case '{http://calendarserver.org/ns/}calendar-proxy-read': $type[$k] = 'Proxy-Read'; break; case '{http://calendarserver.org/ns/}calendar-proxy-write': $type[$k] = 'Proxy-Write'; break; } } $type = implode(', ', $type); } // If no resourcetype was found, we attempt to use // the contenttype property if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { $type = $file[200]['{DAV:}getcontenttype']; } if (!$type) { $type = 'Unknown'; } $size = isset($file[200]['{DAV:}getcontentlength']) ? (int) $file[200]['{DAV:}getcontentlength'] : ''; $lastmodified = isset($file[200]['{DAV:}getlastmodified']) ? $file[200]['{DAV:}getlastmodified']->getTime()->format(DateTime::ATOM) : ''; $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; $displayName = $this->escapeHTML($displayName); $type = $this->escapeHTML($type); $icon = ''; if ($this->enableAssets) { $node = $this->server->tree->getNodeForPath(($path ? $path . '/' : '') . $name); foreach (array_reverse($this->iconMap) as $class => $iconName) { if ($node instanceof $class) { $icon = '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl($iconName . $this->iconExtension) . '" alt="" width="24" /></a>'; break; } } } $html .= "<tr>\n <td>{$icon}</td>\n <td><a href=\"{$fullPath}\">{$displayName}</a></td>\n <td>{$type}</td>\n <td>{$size}</td>\n <td>{$lastmodified}</td>\n </tr>"; } $html .= "<tr><td colspan=\"5\"><hr /></td></tr>"; $output = ''; if ($this->enablePost) { $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output)); } $html .= $output; $html .= "</table>\n <address>Generated by SabreDAV " . $version . " (c)2007-2013 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>\n </body>\n </html>"; return $html; }
/** * This function handles the calendar-query REPORT * * This report is used by clients to request calendar objects based on * complex conditions. * * @param DOMNode $dom * @return void */ public function calendarQueryReport($dom) { $parser = new Sabre_CalDAV_CalendarQueryParser($dom); $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; $requestedProperties = $parser->requestedProperties; if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { // We always retrieve calendar-data, as we need it for filtering. $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; // If calendar-data wasn't explicitly requested, we need to remove // it after processing. $requestedCalendarData = false; } $properties = $this->server->getPropertiesForPath($this->server->getRequestUri(), $requestedProperties, 0); // 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(); $vObject = Sabre_VObject_Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); if ($validator->validate($vObject, $parser->filters)) { // If the client didn't require the calendar-data property, // we won't give it back. if (!$requestedCalendarData) { unset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); } else { if ($parser->expand) { $vObject->expand($parser->expand['start'], $parser->expand['end']); $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } } $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 = Sabre_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; } } $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result)); }