/** * The expand-property report is defined in RFC3253 section 3.8. * * This report is very similar to a standard PROPFIND. The difference is * that it has the additional ability to look at properties containing a * {DAV:}href element, follow that property and grab additional elements * there. * * Other rfc's, such as ACL rely on this report, so it made sense to put * it in this plugin. * * @param string $path * @param Xml\Request\ExpandPropertyReport $report * @return void */ protected function expandPropertyReport($path, $report) { $depth = $this->server->getHTTPDepth(0); $result = $this->expandProperties($path, $report->properties, $depth); $xml = $this->server->xml->write('{DAV:}multistatus', new DAV\Xml\Response\MultiStatus($result), $this->server->getBaseUri()); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->setStatus(207); $this->server->httpResponse->setBody($xml); }
/** * Draws a table row for a property * * @param HtmlOutputHelper $html * @param mixed $value * @return string */ private function drawPropertyValue($html, $value) { if (is_scalar($value)) { return $html->h($value); } elseif ($value instanceof HtmlOutput) { return $value->toHtml($html); } elseif ($value instanceof \Sabre\Xml\XmlSerializable) { // There's no default html output for this property, we're going // to output the actual xml serialization instead. $xml = $this->server->xml->write('{DAV:}root', $value, $this->server->getBaseUri()); // removing first and last line, as they contain our root // element. $xml = explode("\n", $xml); $xml = array_slice($xml, 2, -2); return "<pre>" . $html->h(implode("\n", $xml)) . "</pre>"; } else { return "<em>unknown</em>"; } }
/** * This method takes a path/name of an asset and turns it into url * suiteable for http access. * * @param string $assetName * @return string */ protected function getAssetUrl($assetName) { return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName); }
/** * We intercept this to handle POST requests on calendars. * * @param RequestInterface $request * @param ResponseInterface $response * @return null|bool */ function httpPost(RequestInterface $request, ResponseInterface $response) { $path = $request->getPath(); // Only handling xml $contentType = $request->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($path); } catch (DAV\Exception\NotFound $e) { return; } $requestBody = $request->getBodyAsString(); // If this request handler could not deal with this POST request, it // will return 'null' and other plugins get a chance to handle the // request. // // However, we already requested the full body. This is a problem, // because a body can only be read once. This is why we preemptively // re-populated the request body with the existing data. $request->setBody($requestBody); $message = $this->server->xml->parse($requestBody, $request->getUrl(), $documentType); switch ($documentType) { // Dealing with the 'share' document, which modified invitees on a // calendar. case '{' . Plugin::NS_CALENDARSERVER . '}share': // We can only deal with IShareableCalendar objects if (!$node instanceof IShareableCalendar) { return; } $this->server->transactionType = 'post-calendar-share'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $node->updateShares($message->set, $message->remove); $response->setStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->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. // The invite-reply document is sent when the user replies to an // invitation of a calendar share. case '{' . Plugin::NS_CALENDARSERVER . '}invite-reply': // This only works on the calendar-home-root node. if (!$node instanceof CalendarHome) { return; } $this->server->transactionType = 'post-invite-reply'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $url = $node->shareReply($message->href, $message->status, $message->calendarUri, $message->inReplyTo, $message->summary); $response->setStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); if ($url) { $writer = $this->server->xml->getWriter($this->server->getBaseUri()); $writer->openMemory(); $writer->startDocument(); $writer->startElement('{' . Plugin::NS_CALENDARSERVER . '}shared-as'); $writer->write(new Href($url)); $writer->endElement(); $response->setHeader('Content-Type', 'application/xml'); $response->setBody($writer->outputMemory()); } // Breaking the event chain return false; case '{' . Plugin::NS_CALENDARSERVER . '}publish-calendar': // We can only deal with IShareableCalendar objects if (!$node instanceof IShareableCalendar) { return; } $this->server->transactionType = 'post-publish-calendar'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $node->setPublishStatus(true); // iCloud sends back the 202, so we will too. $response->setStatus(202); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; case '{' . Plugin::NS_CALENDARSERVER . '}unpublish-calendar': // We can only deal with IShareableCalendar objects if (!$node instanceof IShareableCalendar) { return; } $this->server->transactionType = 'post-unpublish-calendar'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { $acl->checkPrivileges($path, '{DAV:}write'); } $node->setPublishStatus(false); $response->setStatus(200); // Adding this because sending a response body may cause issues, // and I wanted some type of indicator the response was handled. $response->setHeader('X-Sabre-Status', 'everything-went-well'); // Breaking the event chain return false; } }
/** * Draws a table row for a property * * @param string $name * @param mixed $value * @return string */ private function drawPropertyRow($name, $value) { $view = 'unknown'; if (is_string($value)) { $view = 'string'; } elseif ($value instanceof DAV\Property) { $mapping = ['Sabre\\DAV\\Property\\IHref' => 'href', 'Sabre\\DAV\\Property\\HrefList' => 'hreflist', 'Sabre\\DAV\\Property\\SupportedMethodSet' => 'valuelist', 'Sabre\\DAV\\Property\\ResourceType' => 'xmlvaluelist', 'Sabre\\DAV\\Property\\SupportedReportSet' => 'xmlvaluelist', 'Sabre\\DAVACL\\Property\\CurrentUserPrivilegeSet' => 'xmlvaluelist', 'Sabre\\DAVACL\\Property\\SupportedPrivilegeSet' => 'supported-privilege-set']; $view = 'complex'; foreach ($mapping as $class => $val) { if ($value instanceof $class) { $view = $val; break; } } } list($ns, $localName) = DAV\XMLUtil::parseClarkNotation($name); $realName = $name; if (isset($this->server->xmlNamespaces[$ns])) { $name = $this->server->xmlNamespaces[$ns] . ':' . $localName; } ob_start(); $xmlValueDisplay = function ($propName) { $realPropName = $propName; list($ns, $localName) = DAV\XMLUtil::parseClarkNotation($propName); if (isset($this->server->xmlNamespaces[$ns])) { $propName = $this->server->xmlNamespaces[$ns] . ':' . $localName; } return "<span title=\"" . $this->escapeHTML($realPropName) . "\">" . $this->escapeHTML($propName) . "</span>"; }; echo "<tr><th><span title=\"", $this->escapeHTML($realName), "\">", $this->escapeHTML($name), "</span></th><td>"; switch ($view) { case 'href': echo "<a href=\"" . $this->server->getBaseUri() . $value->getHref() . '">' . $this->server->getBaseUri() . $value->getHref() . '</a>'; break; case 'hreflist': echo implode('<br />', array_map(function ($href) { if (stripos($href, 'mailto:') === 0 || stripos($href, '/') === 0 || stripos($href, 'http:') === 0 || stripos($href, 'https:') === 0) { return "<a href=\"" . $this->escapeHTML($href) . '">' . $this->escapeHTML($href) . '</a>'; } else { return "<a href=\"" . $this->escapeHTML($this->server->getBaseUri() . $href) . '">' . $this->escapeHTML($this->server->getBaseUri() . $href) . '</a>'; } }, $value->getHrefs())); break; case 'xmlvaluelist': echo implode(', ', array_map($xmlValueDisplay, $value->getValue())); break; case 'valuelist': echo $this->escapeHTML(implode(', ', $value->getValue())); break; case 'supported-privilege-set': $traverse = function ($priv) use(&$traverse, $xmlValueDisplay) { echo "<li>"; echo $xmlValueDisplay($priv['privilege']); if (isset($priv['abstract']) && $priv['abstract']) { echo " <i>(abstract)</i>"; } if (isset($priv['description'])) { echo " " . $this->escapeHTML($priv['description']); } if (isset($priv['aggregates'])) { echo "\n<ul>\n"; foreach ($priv['aggregates'] as $subPriv) { $traverse($subPriv); } echo "</ul>"; } echo "</li>\n"; }; echo "<ul class=\"tree\">"; $traverse($value->getValue(), ''); echo "</ul>\n"; break; case 'string': echo $this->escapeHTML($value); break; case 'complex': echo '<em title="' . get_class($value) . '">complex</em>'; break; default: echo '<em>unknown</em>'; break; } return ob_get_clean(); }