1<?php 2 3namespace Sabre\CalDAV; 4 5use Sabre\DAV; 6use Sabre\DAV\Exception\NotFound; 7use Sabre\DAV\MkCol; 8use Sabre\DAVACL; 9use Sabre\HTTP\URLUtil; 10 11/** 12 * The CalendarHome represents a node that is usually in a users' 13 * calendar-homeset. 14 * 15 * It contains all the users' calendars, and can optionally contain a 16 * notifications collection, calendar subscriptions, a users' inbox, and a 17 * users' outbox. 18 * 19 * @copyright Copyright (C) fruux GmbH (https://fruux.com/) 20 * @author Evert Pot (http://evertpot.com/) 21 * @license http://sabre.io/license/ Modified BSD License 22 */ 23class CalendarHome implements DAV\IExtendedCollection, DAVACL\IACL { 24 25 use DAVACL\ACLTrait; 26 27 /** 28 * CalDAV backend 29 * 30 * @var Backend\BackendInterface 31 */ 32 protected $caldavBackend; 33 34 /** 35 * Principal information 36 * 37 * @var array 38 */ 39 protected $principalInfo; 40 41 /** 42 * Constructor 43 * 44 * @param Backend\BackendInterface $caldavBackend 45 * @param array $principalInfo 46 */ 47 function __construct(Backend\BackendInterface $caldavBackend, $principalInfo) { 48 49 $this->caldavBackend = $caldavBackend; 50 $this->principalInfo = $principalInfo; 51 52 } 53 54 /** 55 * Returns the name of this object 56 * 57 * @return string 58 */ 59 function getName() { 60 61 list(, $name) = URLUtil::splitPath($this->principalInfo['uri']); 62 return $name; 63 64 } 65 66 /** 67 * Updates the name of this object 68 * 69 * @param string $name 70 * @return void 71 */ 72 function setName($name) { 73 74 throw new DAV\Exception\Forbidden(); 75 76 } 77 78 /** 79 * Deletes this object 80 * 81 * @return void 82 */ 83 function delete() { 84 85 throw new DAV\Exception\Forbidden(); 86 87 } 88 89 /** 90 * Returns the last modification date 91 * 92 * @return int 93 */ 94 function getLastModified() { 95 96 return null; 97 98 } 99 100 /** 101 * Creates a new file under this object. 102 * 103 * This is currently not allowed 104 * 105 * @param string $filename 106 * @param resource $data 107 * @return void 108 */ 109 function createFile($filename, $data = null) { 110 111 throw new DAV\Exception\MethodNotAllowed('Creating new files in this collection is not supported'); 112 113 } 114 115 /** 116 * Creates a new directory under this object. 117 * 118 * This is currently not allowed. 119 * 120 * @param string $filename 121 * @return void 122 */ 123 function createDirectory($filename) { 124 125 throw new DAV\Exception\MethodNotAllowed('Creating new collections in this collection is not supported'); 126 127 } 128 129 /** 130 * Returns a single calendar, by name 131 * 132 * @param string $name 133 * @return Calendar 134 */ 135 function getChild($name) { 136 137 // Special nodes 138 if ($name === 'inbox' && $this->caldavBackend instanceof Backend\SchedulingSupport) { 139 return new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']); 140 } 141 if ($name === 'outbox' && $this->caldavBackend instanceof Backend\SchedulingSupport) { 142 return new Schedule\Outbox($this->principalInfo['uri']); 143 } 144 if ($name === 'notifications' && $this->caldavBackend instanceof Backend\NotificationSupport) { 145 return new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); 146 } 147 148 // Calendars 149 foreach ($this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']) as $calendar) { 150 if ($calendar['uri'] === $name) { 151 if ($this->caldavBackend instanceof Backend\SharingSupport) { 152 return new SharedCalendar($this->caldavBackend, $calendar); 153 } else { 154 return new Calendar($this->caldavBackend, $calendar); 155 } 156 } 157 } 158 159 if ($this->caldavBackend instanceof Backend\SubscriptionSupport) { 160 foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) { 161 if ($subscription['uri'] === $name) { 162 return new Subscriptions\Subscription($this->caldavBackend, $subscription); 163 } 164 } 165 166 } 167 168 throw new NotFound('Node with name \'' . $name . '\' could not be found'); 169 170 } 171 172 /** 173 * Checks if a calendar exists. 174 * 175 * @param string $name 176 * @return bool 177 */ 178 function childExists($name) { 179 180 try { 181 return !!$this->getChild($name); 182 } catch (NotFound $e) { 183 return false; 184 } 185 186 } 187 188 /** 189 * Returns a list of calendars 190 * 191 * @return array 192 */ 193 function getChildren() { 194 195 $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']); 196 $objs = []; 197 foreach ($calendars as $calendar) { 198 if ($this->caldavBackend instanceof Backend\SharingSupport) { 199 $objs[] = new SharedCalendar($this->caldavBackend, $calendar); 200 } else { 201 $objs[] = new Calendar($this->caldavBackend, $calendar); 202 } 203 } 204 205 if ($this->caldavBackend instanceof Backend\SchedulingSupport) { 206 $objs[] = new Schedule\Inbox($this->caldavBackend, $this->principalInfo['uri']); 207 $objs[] = new Schedule\Outbox($this->principalInfo['uri']); 208 } 209 210 // We're adding a notifications node, if it's supported by the backend. 211 if ($this->caldavBackend instanceof Backend\NotificationSupport) { 212 $objs[] = new Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']); 213 } 214 215 // If the backend supports subscriptions, we'll add those as well, 216 if ($this->caldavBackend instanceof Backend\SubscriptionSupport) { 217 foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) { 218 $objs[] = new Subscriptions\Subscription($this->caldavBackend, $subscription); 219 } 220 } 221 222 return $objs; 223 224 } 225 226 /** 227 * Creates a new calendar or subscription. 228 * 229 * @param string $name 230 * @param MkCol $mkCol 231 * @throws DAV\Exception\InvalidResourceType 232 * @return void 233 */ 234 function createExtendedCollection($name, MkCol $mkCol) { 235 236 $isCalendar = false; 237 $isSubscription = false; 238 foreach ($mkCol->getResourceType() as $rt) { 239 switch ($rt) { 240 case '{DAV:}collection' : 241 case '{http://calendarserver.org/ns/}shared-owner' : 242 // ignore 243 break; 244 case '{urn:ietf:params:xml:ns:caldav}calendar' : 245 $isCalendar = true; 246 break; 247 case '{http://calendarserver.org/ns/}subscribed' : 248 $isSubscription = true; 249 break; 250 default : 251 throw new DAV\Exception\InvalidResourceType('Unknown resourceType: ' . $rt); 252 } 253 } 254 255 $properties = $mkCol->getRemainingValues(); 256 $mkCol->setRemainingResultCode(201); 257 258 if ($isSubscription) { 259 if (!$this->caldavBackend instanceof Backend\SubscriptionSupport) { 260 throw new DAV\Exception\InvalidResourceType('This backend does not support subscriptions'); 261 } 262 $this->caldavBackend->createSubscription($this->principalInfo['uri'], $name, $properties); 263 264 } elseif ($isCalendar) { 265 $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties); 266 267 } else { 268 throw new DAV\Exception\InvalidResourceType('You can only create calendars and subscriptions in this collection'); 269 270 } 271 272 } 273 274 /** 275 * Returns the owner of the calendar home. 276 * 277 * @return string 278 */ 279 function getOwner() { 280 281 return $this->principalInfo['uri']; 282 283 } 284 285 /** 286 * Returns a list of ACE's for this node. 287 * 288 * Each ACE has the following properties: 289 * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 290 * currently the only supported privileges 291 * * 'principal', a url to the principal who owns the node 292 * * 'protected' (optional), indicating that this ACE is not allowed to 293 * be updated. 294 * 295 * @return array 296 */ 297 function getACL() { 298 299 return [ 300 [ 301 'privilege' => '{DAV:}read', 302 'principal' => $this->principalInfo['uri'], 303 'protected' => true, 304 ], 305 [ 306 'privilege' => '{DAV:}write', 307 'principal' => $this->principalInfo['uri'], 308 'protected' => true, 309 ], 310 [ 311 'privilege' => '{DAV:}read', 312 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', 313 'protected' => true, 314 ], 315 [ 316 'privilege' => '{DAV:}write', 317 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write', 318 'protected' => true, 319 ], 320 [ 321 'privilege' => '{DAV:}read', 322 'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read', 323 'protected' => true, 324 ], 325 326 ]; 327 328 } 329 330 331 /** 332 * This method is called when a user replied to a request to share. 333 * 334 * This method should return the url of the newly created calendar if the 335 * share was accepted. 336 * 337 * @param string $href The sharee who is replying (often a mailto: address) 338 * @param int $status One of the SharingPlugin::STATUS_* constants 339 * @param string $calendarUri The url to the calendar thats being shared 340 * @param string $inReplyTo The unique id this message is a response to 341 * @param string $summary A description of the reply 342 * @return null|string 343 */ 344 function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null) { 345 346 if (!$this->caldavBackend instanceof Backend\SharingSupport) { 347 throw new DAV\Exception\NotImplemented('Sharing support is not implemented by this backend.'); 348 } 349 350 return $this->caldavBackend->shareReply($href, $status, $calendarUri, $inReplyTo, $summary); 351 352 } 353 354 /** 355 * Searches through all of a users calendars and calendar objects to find 356 * an object with a specific UID. 357 * 358 * This method should return the path to this object, relative to the 359 * calendar home, so this path usually only contains two parts: 360 * 361 * calendarpath/objectpath.ics 362 * 363 * If the uid is not found, return null. 364 * 365 * This method should only consider * objects that the principal owns, so 366 * any calendars owned by other principals that also appear in this 367 * collection should be ignored. 368 * 369 * @param string $uid 370 * @return string|null 371 */ 372 function getCalendarObjectByUID($uid) { 373 374 return $this->caldavBackend->getCalendarObjectByUID($this->principalInfo['uri'], $uid); 375 376 } 377 378} 379