ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Sabre\CalDAV\ICSExportPlugin Class Reference

ICS Exporter. More...

+ Inheritance diagram for Sabre\CalDAV\ICSExportPlugin:
+ Collaboration diagram for Sabre\CalDAV\ICSExportPlugin:

Public Member Functions

 initialize (DAV\Server $server)
 Initializes the plugin and registers event handlers. More...
 
 httpGet (RequestInterface $request, ResponseInterface $response)
 Intercepts GET requests on calendar urls ending with ?export. More...
 
 mergeObjects (array $properties, array $inputObjects)
 Merges all calendar objects, and builds one big iCalendar blob. More...
 
 getPluginName ()
 Returns a plugin name. More...
 
 getPluginInfo ()
 Returns a bunch of meta-data about the plugin. More...
 
- Public Member Functions inherited from Sabre\DAV\ServerPlugin
 initialize (Server $server)
 This initializes the plugin. More...
 
 getFeatures ()
 This method should return a list of server-features. More...
 
 getHTTPMethods ($path)
 Use this method to tell the server this plugin defines additional HTTP methods. More...
 
 getPluginName ()
 Returns a plugin name. More...
 
 getSupportedReportSet ($uri)
 Returns a list of reports this plugin supports. More...
 
 getPluginInfo ()
 Returns a bunch of meta-data about the plugin. More...
 

Protected Member Functions

 generateResponse ($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response)
 This method is responsible for generating the actual, full response. More...
 

Protected Attributes

 $server
 

Detailed Description

ICS Exporter.

This plugin adds the ability to export entire calendars as .ics files. This is useful for clients that don't support CalDAV yet. They often do support ics files.

To use this, point a http client to a caldav calendar, and add ?expand to the url.

Further options that can be added to the url: start=123456789 - Only return events after the given unix timestamp end=123245679 - Only return events from before the given unix timestamp expand=1 - Strip timezone information and expand recurring events. If you'd like to expand, you must also specify start and end.

By default this plugin returns data in the text/calendar format (iCalendar 2.0). If you'd like to receive jCal data instead, you can use an Accept header:

Accept: application/calendar+json

Alternatively, you can also specify this in the url using accept=application/calendar+json, or accept=jcal for short. If the url parameter and Accept header is specified, the url parameter wins.

Note that specifying a start or end data implies that only events will be returned. VTODO and VJOURNAL will be stripped.

Author
Evert Pot (http://evertpot.com/) http://sabre.io/license/ Modified BSD License

Definition at line 47 of file ICSExportPlugin.php.

Member Function Documentation

◆ generateResponse()

Sabre\CalDAV\ICSExportPlugin::generateResponse (   $path,
  $start,
  $end,
  $expand,
  $componentType,
  $format,
  $properties,
ResponseInterface  $response 
)
protected

This method is responsible for generating the actual, full response.

Parameters
string$path
DateTime | null$start
DateTime | null$end
bool$expand
string$componentType
string$format
array$properties
ResponseInterface$response

Definition at line 170 of file ICSExportPlugin.php.

References $end, $filename, $format, $nodes, $path, $start, Sabre\CalDAV\ICSExportPlugin\mergeObjects(), Sabre\CalDAV\Plugin\NS_CALDAV, Sabre\VObject\Reader\read(), Sabre\HTTP\MessageInterface\setBody(), Sabre\HTTP\MessageInterface\setHeader(), and Sabre\HTTP\ResponseInterface\setStatus().

Referenced by Sabre\CalDAV\ICSExportPlugin\httpGet().

170  {
171 
172  $calDataProp = '{' . Plugin::NS_CALDAV . '}calendar-data';
173  $calendarNode = $this->server->tree->getNodeForPath($path);
174 
175  $blobs = [];
176  if ($start || $end || $componentType) {
177 
178  // If there was a start or end filter, we need to enlist
179  // calendarQuery for speed.
180  $queryResult = $calendarNode->calendarQuery([
181  'name' => 'VCALENDAR',
182  'comp-filters' => [
183  [
184  'name' => $componentType,
185  'comp-filters' => [],
186  'prop-filters' => [],
187  'is-not-defined' => false,
188  'time-range' => [
189  'start' => $start,
190  'end' => $end,
191  ],
192  ],
193  ],
194  'prop-filters' => [],
195  'is-not-defined' => false,
196  'time-range' => null,
197  ]);
198 
199  // queryResult is just a list of base urls. We need to prefix the
200  // calendar path.
201  $queryResult = array_map(
202  function($item) use ($path) {
203  return $path . '/' . $item;
204  },
205  $queryResult
206  );
207  $nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]);
208  unset($queryResult);
209 
210  } else {
211  $nodes = $this->server->getPropertiesForPath($path, [$calDataProp], 1);
212  }
213 
214  // Flattening the arrays
215  foreach ($nodes as $node) {
216  if (isset($node[200][$calDataProp])) {
217  $blobs[$node['href']] = $node[200][$calDataProp];
218  }
219  }
220  unset($nodes);
221 
222  $mergedCalendar = $this->mergeObjects(
223  $properties,
224  $blobs
225  );
226 
227  if ($expand) {
228  $calendarTimeZone = null;
229  // We're expanding, and for that we need to figure out the
230  // calendar's timezone.
231  $tzProp = '{' . Plugin::NS_CALDAV . '}calendar-timezone';
232  $tzResult = $this->server->getProperties($path, [$tzProp]);
233  if (isset($tzResult[$tzProp])) {
234  // This property contains a VCALENDAR with a single
235  // VTIMEZONE.
236  $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]);
237  $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
238  // Destroy circular references to PHP will GC the object.
239  $vtimezoneObj->destroy();
240  unset($vtimezoneObj);
241  } else {
242  // Defaulting to UTC.
243  $calendarTimeZone = new DateTimeZone('UTC');
244  }
245 
246  $mergedCalendar = $mergedCalendar->expand($start, $end, $calendarTimeZone);
247  }
248 
249  $filenameExtension = '.ics';
250 
251  switch ($format) {
252  case 'text/calendar' :
253  $mergedCalendar = $mergedCalendar->serialize();
254  $filenameExtension = '.ics';
255  break;
256  case 'application/calendar+json' :
257  $mergedCalendar = json_encode($mergedCalendar->jsonSerialize());
258  $filenameExtension = '.json';
259  break;
260  }
261 
262  $filename = preg_replace(
263  '/[^a-zA-Z0-9-_ ]/um',
264  '',
265  $calendarNode->getName()
266  );
267  $filename .= '-' . date('Y-m-d') . $filenameExtension;
268 
269  $response->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
270  $response->setHeader('Content-Type', $format);
271 
272  $response->setStatus(200);
273  $response->setBody($mergedCalendar);
274 
275  }
$path
Definition: aliased.php:25
$format
Definition: metadata.php:141
mergeObjects(array $properties, array $inputObjects)
Merges all calendar objects, and builds one big iCalendar blob.
$start
Definition: bench.php:8
$filename
Definition: buildRTE.php:89
static read($data, $options=0, $charset='UTF-8')
Parses a vCard or iCalendar object, and returns the top component.
Definition: Reader.php:42
$response
const NS_CALDAV
This is the official CalDAV namespace.
Definition: Plugin.php:33
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ getPluginInfo()

Sabre\CalDAV\ICSExportPlugin::getPluginInfo ( )

Returns a bunch of meta-data about the plugin.

Providing this information is optional, and is mainly displayed by the Browser plugin.

The description key in the returned array may contain html and will not be sanitized.

Returns
array

Definition at line 368 of file ICSExportPlugin.php.

References Sabre\CalDAV\ICSExportPlugin\getPluginName().

368  {
369 
370  return [
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/',
374  ];
375 
376  }
getPluginName()
Returns a plugin name.
+ Here is the call graph for this function:

◆ getPluginName()

Sabre\CalDAV\ICSExportPlugin::getPluginName ( )

Returns a plugin name.

Using this name other plugins will be able to access other plugins using ::getPlugin

Returns
string

Definition at line 351 of file ICSExportPlugin.php.

Referenced by Sabre\CalDAV\ICSExportPlugin\getPluginInfo().

351  {
352 
353  return 'ics-export';
354 
355  }
+ Here is the caller graph for this function:

◆ httpGet()

Sabre\CalDAV\ICSExportPlugin::httpGet ( RequestInterface  $request,
ResponseInterface  $response 
)

Intercepts GET requests on calendar urls ending with ?export.

Parameters
RequestInterface$request
ResponseInterface$response
Returns
bool

Definition at line 81 of file ICSExportPlugin.php.

References $end, $format, $path, $start, Sabre\CalDAV\ICSExportPlugin\generateResponse(), Sabre\HTTP\MessageInterface\getHeader(), Sabre\HTTP\RequestInterface\getPath(), Sabre\HTTP\RequestInterface\getQueryParameters(), and Sabre\CalDAV\Plugin\NS_CALDAV.

81  {
82 
83  $queryParams = $request->getQueryParameters();
84  if (!array_key_exists('export', $queryParams)) return;
85 
86  $path = $request->getPath();
87 
88  $node = $this->server->getProperties($path, [
89  '{DAV:}resourcetype',
90  '{DAV:}displayname',
91  '{http://sabredav.org/ns}sync-token',
92  '{DAV:}sync-token',
93  '{http://apple.com/ns/ical/}calendar-color',
94  ]);
95 
96  if (!isset($node['{DAV:}resourcetype']) || !$node['{DAV:}resourcetype']->is('{' . Plugin::NS_CALDAV . '}calendar')) {
97  return;
98  }
99  // Marking the transactionType, for logging purposes.
100  $this->server->transactionType = 'get-calendar-export';
101 
102  $properties = $node;
103 
104  $start = null;
105  $end = null;
106  $expand = false;
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');
111  }
112  $start = DateTime::createFromFormat('U', $queryParams['start']);
113  }
114  if (isset($queryParams['end'])) {
115  if (!ctype_digit($queryParams['end'])) {
116  throw new BadRequest('The end= parameter must contain a unix timestamp');
117  }
118  $end = DateTime::createFromFormat('U', $queryParams['end']);
119  }
120  if (isset($queryParams['expand']) && !!$queryParams['expand']) {
121  if (!$start || !$end) {
122  throw new BadRequest('If you\'d like to expand recurrences, you must specify both a start= and end= parameter.');
123  }
124  $expand = true;
125  $componentType = 'VEVENT';
126  }
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');
130  }
131  $componentType = $queryParams['componentType'];
132  }
133 
134  $format = \Sabre\HTTP\Util::Negotiate(
135  $request->getHeader('Accept'),
136  [
137  'text/calendar',
138  'application/calendar+json',
139  ]
140  );
141 
142  if (isset($queryParams['accept'])) {
143  if ($queryParams['accept'] === 'application/calendar+json' || $queryParams['accept'] === 'jcal') {
144  $format = 'application/calendar+json';
145  }
146  }
147  if (!$format) {
148  $format = 'text/calendar';
149  }
150 
151  $this->generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, $response);
152 
153  // Returning false to break the event chain
154  return false;
155 
156  }
$path
Definition: aliased.php:25
$format
Definition: metadata.php:141
foreach($paths as $path) $request
Definition: asyncclient.php:32
$start
Definition: bench.php:8
$response
const NS_CALDAV
This is the official CalDAV namespace.
Definition: Plugin.php:33
generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response)
This method is responsible for generating the actual, full response.
+ Here is the call graph for this function:

◆ initialize()

Sabre\CalDAV\ICSExportPlugin::initialize ( DAV\Server  $server)

Initializes the plugin and registers event handlers.

Parameters
\Sabre\DAV\Server$server
Returns
void

Definition at line 62 of file ICSExportPlugin.php.

References $path, and Sabre\CalDAV\ICSExportPlugin\$server.

62  {
63 
64  $this->server = $server;
65  $server->on('method:GET', [$this, 'httpGet'], 90);
66  $server->on('browserButtonActions', function($path, $node, &$actions) {
67  if ($node instanceof ICalendar) {
68  $actions .= '<a href="' . htmlspecialchars($path, ENT_QUOTES, 'UTF-8') . '?export"><span class="oi" data-glyph="calendar"></span></a>';
69  }
70  });
71 
72  }
$path
Definition: aliased.php:25

◆ mergeObjects()

Sabre\CalDAV\ICSExportPlugin::mergeObjects ( array  $properties,
array  $inputObjects 
)

Merges all calendar objects, and builds one big iCalendar blob.

Parameters
array$propertiesSome CalDAV properties
array$inputObjects
Returns
VObject

Definition at line 284 of file ICSExportPlugin.php.

References $calendar, Sabre\DAV\Server\$exposeVersion, $tz, Sabre\VObject\Reader\read(), and Sabre\DAV\Version\VERSION.

Referenced by Sabre\CalDAV\ICSExportPlugin\generateResponse().

284  {
285 
286  $calendar = new VObject\Component\VCalendar();
287  $calendar->VERSION = '2.0';
288  if (DAV\Server::$exposeVersion) {
289  $calendar->PRODID = '-//SabreDAV//SabreDAV ' . DAV\Version::VERSION . '//EN';
290  } else {
291  $calendar->PRODID = '-//SabreDAV//SabreDAV//EN';
292  }
293  if (isset($properties['{DAV:}displayname'])) {
294  $calendar->{'X-WR-CALNAME'} = $properties['{DAV:}displayname'];
295  }
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'];
298  }
299 
300  $collectedTimezones = [];
301 
302  $timezones = [];
303  $objects = [];
304 
305  foreach ($inputObjects as $href => $inputObject) {
306 
307  $nodeComp = VObject\Reader::read($inputObject);
308 
309  foreach ($nodeComp->children() as $child) {
310 
311  switch ($child->name) {
312  case 'VEVENT' :
313  case 'VTODO' :
314  case 'VJOURNAL' :
315  $objects[] = clone $child;
316  break;
317 
318  // VTIMEZONE is special, because we need to filter out the duplicates
319  case 'VTIMEZONE' :
320  // Naively just checking tzid.
321  if (in_array((string)$child->TZID, $collectedTimezones)) continue;
322 
323  $timezones[] = clone $child;
324  $collectedTimezones[] = $child->TZID;
325  break;
326 
327  }
328 
329  }
330  // Destroy circular references to PHP will GC the object.
331  $nodeComp->destroy();
332  unset($nodeComp);
333 
334  }
335 
336  foreach ($timezones as $tz) $calendar->add($tz);
337  foreach ($objects as $obj) $calendar->add($obj);
338 
339  return $calendar;
340 
341  }
const VERSION
Full version number.
Definition: Version.php:17
static $exposeVersion
Definition: Server.php:184
static read($data, $options=0, $charset='UTF-8')
Parses a vCard or iCalendar object, and returns the top component.
Definition: Reader.php:42
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Field Documentation

◆ $server

Sabre\CalDAV\ICSExportPlugin::$server
protected

Definition at line 54 of file ICSExportPlugin.php.

Referenced by Sabre\CalDAV\ICSExportPlugin\initialize().


The documentation for this class was generated from the following file: