ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
SharingPlugin.php
Go to the documentation of this file.
1<?php
2
3namespace Sabre\CalDAV;
4
5use Sabre\DAV;
9
27
33 protected $server;
34
43 function getFeatures() {
44
45 return ['calendarserver-sharing'];
46
47 }
48
57 function getPluginName() {
58
59 return 'caldav-sharing';
60
61 }
62
74 function initialize(DAV\Server $server) {
75
76 $this->server = $server;
77
78 if (is_null($this->server->getPlugin('sharing'))) {
79 throw new \LogicException('The generic "sharing" plugin must be loaded before the caldav sharing plugin. Call $server->addPlugin(new \Sabre\DAV\Sharing\Plugin()); before this one.');
80 }
81
82 array_push(
83 $this->server->protectedProperties,
84 '{' . Plugin::NS_CALENDARSERVER . '}invite',
85 '{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes',
86 '{' . Plugin::NS_CALENDARSERVER . '}shared-url'
87 );
88
89 $this->server->xml->elementMap['{' . Plugin::NS_CALENDARSERVER . '}share'] = 'Sabre\\CalDAV\\Xml\\Request\\Share';
90 $this->server->xml->elementMap['{' . Plugin::NS_CALENDARSERVER . '}invite-reply'] = 'Sabre\\CalDAV\\Xml\\Request\\InviteReply';
91
92 $this->server->on('propFind', [$this, 'propFindEarly']);
93 $this->server->on('propFind', [$this, 'propFindLate'], 150);
94 $this->server->on('propPatch', [$this, 'propPatch'], 40);
95 $this->server->on('method:POST', [$this, 'httpPost']);
96
97 }
98
109 function propFindEarly(DAV\PropFind $propFind, DAV\INode $node) {
110
111 if ($node instanceof ISharedCalendar) {
112
113 $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}invite', function() use ($node) {
114
115 // Fetching owner information
116 $props = $this->server->getPropertiesForPath($node->getOwner(), [
117 '{http://sabredav.org/ns}email-address',
118 '{DAV:}displayname',
119 ], 0);
120
121 $ownerInfo = [
122 'href' => $node->getOwner(),
123 ];
124
125 if (isset($props[0][200])) {
126
127 // We're mapping the internal webdav properties to the
128 // elements caldav-sharing expects.
129 if (isset($props[0][200]['{http://sabredav.org/ns}email-address'])) {
130 $ownerInfo['href'] = 'mailto:' . $props[0][200]['{http://sabredav.org/ns}email-address'];
131 }
132 if (isset($props[0][200]['{DAV:}displayname'])) {
133 $ownerInfo['commonName'] = $props[0][200]['{DAV:}displayname'];
134 }
135
136 }
137
138 return new Xml\Property\Invite(
139 $node->getInvites(),
140 $ownerInfo
141 );
142
143 });
144
145 }
146
147 }
148
158 function propFindLate(DAV\PropFind $propFind, DAV\INode $node) {
159
160 if ($node instanceof ISharedCalendar) {
161 $shareAccess = $node->getShareAccess();
162 if ($rt = $propFind->get('{DAV:}resourcetype')) {
163 switch ($shareAccess) {
164 case \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER :
165 $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared-owner');
166 break;
167 case \Sabre\DAV\Sharing\Plugin::ACCESS_READ :
168 case \Sabre\DAV\Sharing\Plugin::ACCESS_READWRITE :
169 $rt->add('{' . Plugin::NS_CALENDARSERVER . '}shared');
170 break;
171
172 }
173 }
174 $propFind->handle('{' . Plugin::NS_CALENDARSERVER . '}allowed-sharing-modes', function() {
175 return new Xml\Property\AllowedSharingModes(true, false);
176 });
177
178 }
179
180 }
181
197 function propPatch($path, DAV\PropPatch $propPatch) {
198
199 $node = $this->server->tree->getNodeForPath($path);
200 if (!$node instanceof ISharedCalendar)
201 return;
202
203 if ($node->getShareAccess() === \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER || $node->getShareAccess() === \Sabre\DAV\Sharing\Plugin::ACCESS_NOTSHARED) {
204
205 $propPatch->handle('{DAV:}resourcetype', function($value) use ($node) {
206 if ($value->is('{' . Plugin::NS_CALENDARSERVER . '}shared-owner')) return false;
207 $shares = $node->getInvites();
208 foreach ($shares as $share) {
210 }
211 $node->updateInvites($shares);
212
213 return true;
214
215 });
216
217 }
218
219 }
220
229
230 $path = $request->getPath();
231
232 // Only handling xml
233 $contentType = $request->getHeader('Content-Type');
234 if (strpos($contentType, 'application/xml') === false && strpos($contentType, 'text/xml') === false)
235 return;
236
237 // Making sure the node exists
238 try {
239 $node = $this->server->tree->getNodeForPath($path);
240 } catch (DAV\Exception\NotFound $e) {
241 return;
242 }
243
244 $requestBody = $request->getBodyAsString();
245
246 // If this request handler could not deal with this POST request, it
247 // will return 'null' and other plugins get a chance to handle the
248 // request.
249 //
250 // However, we already requested the full body. This is a problem,
251 // because a body can only be read once. This is why we preemptively
252 // re-populated the request body with the existing data.
253 $request->setBody($requestBody);
254
255 $message = $this->server->xml->parse($requestBody, $request->getUrl(), $documentType);
256
257 switch ($documentType) {
258
259 // Both the DAV:share-resource and CALENDARSERVER:share requests
260 // behave identically.
261 case '{' . Plugin::NS_CALENDARSERVER . '}share' :
262
263 $sharingPlugin = $this->server->getPlugin('sharing');
264 $sharingPlugin->shareResource($path, $message->sharees);
265
266 $response->setStatus(200);
267 // Adding this because sending a response body may cause issues,
268 // and I wanted some type of indicator the response was handled.
269 $response->setHeader('X-Sabre-Status', 'everything-went-well');
270
271 // Breaking the event chain
272 return false;
273
274 // The invite-reply document is sent when the user replies to an
275 // invitation of a calendar share.
276 case '{' . Plugin::NS_CALENDARSERVER . '}invite-reply' :
277
278 // This only works on the calendar-home-root node.
279 if (!$node instanceof CalendarHome) {
280 return;
281 }
282 $this->server->transactionType = 'post-invite-reply';
283
284 // Getting ACL info
285 $acl = $this->server->getPlugin('acl');
286
287 // If there's no ACL support, we allow everything
288 if ($acl) {
289 $acl->checkPrivileges($path, '{DAV:}write');
290 }
291
292 $url = $node->shareReply(
293 $message->href,
294 $message->status,
295 $message->calendarUri,
296 $message->inReplyTo,
297 $message->summary
298 );
299
300 $response->setStatus(200);
301 // Adding this because sending a response body may cause issues,
302 // and I wanted some type of indicator the response was handled.
303 $response->setHeader('X-Sabre-Status', 'everything-went-well');
304
305 if ($url) {
306 $writer = $this->server->xml->getWriter();
307 $writer->openMemory();
308 $writer->startDocument();
309 $writer->startElement('{' . Plugin::NS_CALENDARSERVER . '}shared-as');
310 $writer->write(new LocalHref($url));
311 $writer->endElement();
312 $response->setHeader('Content-Type', 'application/xml');
313 $response->setBody($writer->outputMemory());
314
315 }
316
317 // Breaking the event chain
318 return false;
319
320 case '{' . Plugin::NS_CALENDARSERVER . '}publish-calendar' :
321
322 // We can only deal with IShareableCalendar objects
323 if (!$node instanceof ISharedCalendar) {
324 return;
325 }
326 $this->server->transactionType = 'post-publish-calendar';
327
328 // Getting ACL info
329 $acl = $this->server->getPlugin('acl');
330
331 // If there's no ACL support, we allow everything
332 if ($acl) {
333 $acl->checkPrivileges($path, '{DAV:}share');
334 }
335
336 $node->setPublishStatus(true);
337
338 // iCloud sends back the 202, so we will too.
339 $response->setStatus(202);
340
341 // Adding this because sending a response body may cause issues,
342 // and I wanted some type of indicator the response was handled.
343 $response->setHeader('X-Sabre-Status', 'everything-went-well');
344
345 // Breaking the event chain
346 return false;
347
348 case '{' . Plugin::NS_CALENDARSERVER . '}unpublish-calendar' :
349
350 // We can only deal with IShareableCalendar objects
351 if (!$node instanceof ISharedCalendar) {
352 return;
353 }
354 $this->server->transactionType = 'post-unpublish-calendar';
355
356 // Getting ACL info
357 $acl = $this->server->getPlugin('acl');
358
359 // If there's no ACL support, we allow everything
360 if ($acl) {
361 $acl->checkPrivileges($path, '{DAV:}share');
362 }
363
364 $node->setPublishStatus(false);
365
366 $response->setStatus(200);
367
368 // Adding this because sending a response body may cause issues,
369 // and I wanted some type of indicator the response was handled.
370 $response->setHeader('X-Sabre-Status', 'everything-went-well');
371
372 // Breaking the event chain
373 return false;
374
375 }
376
377
378
379 }
380
392 function getPluginInfo() {
393
394 return [
395 'name' => $this->getPluginName(),
396 'description' => 'Adds support for caldav-sharing.',
397 'link' => 'http://sabre.io/dav/caldav-sharing/',
398 ];
399
400 }
401}
$path
Definition: aliased.php:25
foreach($paths as $path) $request
Definition: asyncclient.php:32
An exception for terminatinating execution or to throw for unit testing.
The CalendarHome represents a node that is usually in a users' calendar-homeset.
const NS_CALENDARSERVER
This is the namespace for the proprietary calendarserver extensions.
Definition: Plugin.php:38
This plugin implements support for caldav sharing.
propFindEarly(DAV\PropFind $propFind, DAV\INode $node)
This event is triggered when properties are requested for a certain node.
propFindLate(DAV\PropFind $propFind, DAV\INode $node)
This method is triggered after all properties have been retrieved.
propPatch($path, DAV\PropPatch $propPatch)
This method is trigged when a user attempts to update a node's properties.
httpPost(RequestInterface $request, ResponseInterface $response)
We intercept this to handle POST requests on calendars.
getFeatures()
This method should return a list of server-features.
initialize(DAV\Server $server)
This initializes the plugin.
getPluginInfo()
Returns a bunch of meta-data about the plugin.
getPluginName()
Returns a plugin name.
Main Exception class.
Definition: Exception.php:18
This class holds all the information about a PROPFIND request.
Definition: PropFind.php:11
This class represents a set of properties that are going to be updated.
Definition: PropPatch.php:20
The baseclass for all server plugins.
Main DAV server class.
Definition: Server.php:23
if(!file_exists(getcwd() . '/ilias.ini.php'))
registration confirmation script for ilias
Definition: confirmReg.php:12
This interface represents a Calendar that is shared by a different user.
The INode interface is the base interface, and the parent class of both ICollection and IFile.
Definition: INode.php:12
The RequestInterface represents a HTTP request.
This interface represents a HTTP response.
catch(Exception $e) $message
if( $path[strlen( $path) - 1]==='/') if(is_dir($path)) if(!file_exists( $path)) if(preg_match('#\.php$#D', mb_strtolower($path, 'UTF-8'))) $contentType
Definition: module.php:144
$url
$response
foreach( $values as $value)