ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
SharingPlugin.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Sabre\CalDAV;
4 
5 use 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) {
209  $share->access = DAV\Sharing\Plugin::ACCESS_NOACCESS;
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 }
This interface represents a HTTP response.
getPluginInfo()
Returns a bunch of meta-data about the plugin.
initialize(DAV\Server $server)
This initializes the plugin.
The RequestInterface represents a HTTP request.
$path
Definition: aliased.php:25
The baseclass for all server plugins.
setBody($body)
Updates the body resource with a new stream.
This class represents a set of properties that are going to be updated.
Definition: PropPatch.php:20
foreach($paths as $path) $request
Definition: asyncclient.php:32
getFeatures()
This method should return a list of server-features.
LocalHref property.
Definition: LocalHref.php:25
getBodyAsString()
Returns the body as a string.
The CalendarHome represents a node that is usually in a users&#39; calendar-homeset.
This interface represents a Calendar that is shared by a different user.
This class holds all the information about a PROPFIND request.
Definition: PropFind.php:11
catch(Exception $e) $message
const NS_CALENDARSERVER
This is the namespace for the proprietary calendarserver extensions.
Definition: Plugin.php:38
setStatus($status)
Sets the HTTP status code.
getUrl()
Returns the request url.
Main DAV server class.
Definition: Server.php:23
getHeader($name)
Returns a specific HTTP header, based on it&#39;s name.
The INode interface is the base interface, and the parent class of both ICollection and IFile...
Definition: INode.php:12
propFindLate(DAV\PropFind $propFind, DAV\INode $node)
This method is triggered after all properties have been retrieved.
getPluginName()
Returns a plugin name.
httpPost(RequestInterface $request, ResponseInterface $response)
We intercept this to handle POST requests on calendars.
getPath()
Returns the relative path.
$url
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
$response
propPatch($path, DAV\PropPatch $propPatch)
This method is trigged when a user attempts to update a node&#39;s properties.
setHeader($name, $value)
Updates a HTTP header.
propFindEarly(DAV\PropFind $propFind, DAV\INode $node)
This event is triggered when properties are requested for a certain node.