CalendarHome.php 12 KB

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