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/) @license 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.

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
$filename
Definition: buildRTE.php:89
mergeObjects(array $properties, array $inputObjects)
Merges all calendar objects, and builds one big iCalendar blob.
const NS_CALDAV
This is the official CalDAV namespace.
Definition: Plugin.php:33
static read($data, $options=0, $charset='UTF-8')
Parses a vCard or iCalendar object, and returns the top component.
Definition: Reader.php:42
$format
Definition: metadata.php:141
$response
$start
Definition: bench.php:8

References $end, $filename, $format, $nodes, $path, $response, $start, Sabre\CalDAV\ICSExportPlugin\mergeObjects(), Sabre\CalDAV\Plugin\NS_CALDAV, and Sabre\VObject\Reader\read().

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

+ 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

Reimplemented from Sabre\DAV\ServerPlugin.

Definition at line 368 of file ICSExportPlugin.php.

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.

◆ getPluginName()

Sabre\CalDAV\ICSExportPlugin::getPluginName ( )

Returns a plugin name.

Using this name other plugins will be able to access other plugins using \Sabre\DAV\Server::getPlugin

Returns
string

Reimplemented from Sabre\DAV\ServerPlugin.

Definition at line 351 of file ICSExportPlugin.php.

351 {
352
353 return 'ics-export';
354
355 }

◆ 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.

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 }
foreach($paths as $path) $request
Definition: asyncclient.php:32
generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response)
This method is responsible for generating the actual, full response.

References $end, $format, $path, $request, $response, $start, Sabre\CalDAV\ICSExportPlugin\generateResponse(), and Sabre\CalDAV\Plugin\NS_CALDAV.

+ 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.

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 }

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

◆ 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\Component\VCalendar

Definition at line 284 of file ICSExportPlugin.php.

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 }
static $exposeVersion
Definition: Server.php:184
const VERSION
Full version number.
Definition: Version.php:17

References $calendar, Sabre\DAV\Server\$exposeVersion, and Sabre\DAV\Version\VERSION.

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

+ 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: