65        $server->on(
'method:GET', [$this, 
'httpGet'], 90);
 
   66        $server->on(
'browserButtonActions', 
function(
$path, $node, &$actions) {
 
   68                $actions .= 
'<a href="' . htmlspecialchars(
$path, ENT_QUOTES, 
'UTF-8') . 
'?export"><span class="oi" data-glyph="calendar"></span></a>';
 
   83        $queryParams = 
$request->getQueryParameters();
 
   84        if (!array_key_exists(
'export', $queryParams)) 
return;
 
   88        $node = $this->server->getProperties(
$path, [
 
   91            '{http://sabredav.org/ns}sync-token',
 
   93            '{http://apple.com/ns/ical/}calendar-color',
 
   96        if (!isset($node[
'{DAV:}resourcetype']) || !$node[
'{DAV:}resourcetype']->is(
'{' . 
Plugin::NS_CALDAV . 
'}calendar')) {
 
  100        $this->server->transactionType = 
'get-calendar-export';
 
  107        $componentType = 
false;
 
  108        if (isset($queryParams[
'start'])) {
 
  109            if (!ctype_digit($queryParams[
'start'])) {
 
  110                throw new BadRequest(
'The start= parameter must contain a unix timestamp');
 
  112            $start = DateTime::createFromFormat(
'U', $queryParams[
'start']);
 
  114        if (isset($queryParams[
'end'])) {
 
  115            if (!ctype_digit($queryParams[
'end'])) {
 
  116                throw new BadRequest(
'The end= parameter must contain a unix timestamp');
 
  118            $end = DateTime::createFromFormat(
'U', $queryParams[
'end']);
 
  120        if (isset($queryParams[
'expand']) && !!$queryParams[
'expand']) {
 
  122                throw new BadRequest(
'If you\'d like to expand recurrences, you must specify both a start= and end= parameter.');
 
  125            $componentType = 
'VEVENT';
 
  127        if (isset($queryParams[
'componentType'])) {
 
  128            if (!in_array($queryParams[
'componentType'], [
'VEVENT', 
'VTODO', 
'VJOURNAL'])) {
 
  129                throw new BadRequest(
'You are not allowed to search for components of type: ' . $queryParams[
'componentType'] . 
' here');
 
  131            $componentType = $queryParams[
'componentType'];
 
  134        $format = \Sabre\HTTP\Util::Negotiate(
 
  138                'application/calendar+json',
 
  142        if (isset($queryParams[
'accept'])) {
 
  143            if ($queryParams[
'accept'] === 
'application/calendar+json' || $queryParams[
'accept'] === 
'jcal') {
 
  144                $format = 
'application/calendar+json';
 
  173        $calendarNode = $this->server->tree->getNodeForPath(
$path);
 
  180            $queryResult = $calendarNode->calendarQuery([
 
  181                'name'         => 
'VCALENDAR',
 
  184                        'name'           => $componentType,
 
  185                        'comp-filters'   => [],
 
  186                        'prop-filters'   => [],
 
  187                        'is-not-defined' => 
false,
 
  194                'prop-filters'   => [],
 
  195                'is-not-defined' => 
false,
 
  196                'time-range'     => 
null,
 
  201            $queryResult = array_map(
 
  202                function($item) use (
$path) {
 
  203                    return $path . 
'/' . $item;
 
  207            $nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]);
 
  211            $nodes = $this->server->getPropertiesForPath(
$path, [$calDataProp], 1);
 
  215        foreach (
$nodes as $node) {
 
  216            if (isset($node[200][$calDataProp])) {
 
  217                $blobs[$node[
'href']] = $node[200][$calDataProp];
 
  228            $calendarTimeZone = 
null;
 
  232            $tzResult = $this->server->getProperties(
$path, [$tzProp]);
 
  233            if (isset($tzResult[$tzProp])) {
 
  237                $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
 
  239                $vtimezoneObj->destroy();
 
  240                unset($vtimezoneObj);
 
  243                $calendarTimeZone = 
new DateTimeZone(
'UTC');
 
  246            $mergedCalendar = $mergedCalendar->expand(
$start, 
$end, $calendarTimeZone);
 
  249        $filenameExtension = 
'.ics';
 
  252            case 'text/calendar' :
 
  253                $mergedCalendar = $mergedCalendar->serialize();
 
  254                $filenameExtension = 
'.ics';
 
  256            case 'application/calendar+json' :
 
  257                $mergedCalendar = json_encode($mergedCalendar->jsonSerialize());
 
  258                $filenameExtension = 
'.json';
 
  263            '/[^a-zA-Z0-9-_ ]/um',
 
  265            $calendarNode->getName()
 
  267        $filename .= 
'-' . date(
'Y-m-d') . $filenameExtension;
 
  269        $response->setHeader(
'Content-Disposition', 
'attachment; filename="' . 
$filename . 
'"');
 
  291            $calendar->PRODID = 
'-//SabreDAV//SabreDAV//EN';
 
  293        if (isset($properties[
'{DAV:}displayname'])) {
 
  294            $calendar->{
'X-WR-CALNAME'} = $properties[
'{DAV:}displayname'];
 
  296        if (isset($properties[
'{http://apple.com/ns/ical/}calendar-color'])) {
 
  297            $calendar->{
'X-APPLE-CALENDAR-COLOR'} = $properties[
'{http://apple.com/ns/ical/}calendar-color'];
 
  300        $collectedTimezones = [];
 
  305        foreach ($inputObjects as $href => $inputObject) {
 
  309            foreach ($nodeComp->children() as $child) {
 
  311                switch ($child->name) {
 
  315                        $objects[] = clone $child;
 
  321                        if (in_array((
string)$child->TZID, $collectedTimezones)) 
continue;
 
  323                        $timezones[] = clone $child;
 
  324                        $collectedTimezones[] = $child->TZID;
 
  331            $nodeComp->destroy();
 
  337        foreach ($objects as $obj) 
$calendar->add($obj);
 
  371            'name'        => $this->getPluginName(),
 
  372            'description' => 
'Adds the ability to export CalDAV calendars as a single iCalendar file.',
 
  373            'link'        => 
'http://sabre.io/dav/ics-export-plugin/',
 
foreach($paths as $path) $request
An exception for terminatinating execution or to throw for unit testing.
initialize(DAV\Server $server)
Initializes the plugin and registers event handlers.
httpGet(RequestInterface $request, ResponseInterface $response)
Intercepts GET requests on calendar urls ending with ?export.
mergeObjects(array $properties, array $inputObjects)
Merges all calendar objects, and builds one big iCalendar blob.
generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response)
This method is responsible for generating the actual, full response.
getPluginInfo()
Returns a bunch of meta-data about the plugin.
getPluginName()
Returns a plugin name.
const NS_CALDAV
This is the official CalDAV namespace.
The baseclass for all server plugins.
const VERSION
Full version number.
static read($data, $options=0, $charset='UTF-8')
Parses a vCard or iCalendar object, and returns the top component.
The RequestInterface represents a HTTP request.
This interface represents a HTTP response.