Calendar.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. <?php
  2. namespace Sabre\CalDAV;
  3. use Sabre\DAV;
  4. use Sabre\DAVACL;
  5. use Sabre\DAV\PropPatch;
  6. /**
  7. * This object represents a CalDAV calendar.
  8. *
  9. * A calendar can contain multiple TODO and or Events. These are represented
  10. * as \Sabre\CalDAV\CalendarObject objects.
  11. *
  12. * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
  13. * @author Evert Pot (http://evertpot.com/)
  14. * @license http://sabre.io/license/ Modified BSD License
  15. */
  16. class Calendar implements ICalendar, DAV\IProperties, DAV\Sync\ISyncCollection, DAV\IMultiGet {
  17. /**
  18. * This is an array with calendar information
  19. *
  20. * @var array
  21. */
  22. protected $calendarInfo;
  23. /**
  24. * CalDAV backend
  25. *
  26. * @var Backend\BackendInterface
  27. */
  28. protected $caldavBackend;
  29. /**
  30. * Constructor
  31. *
  32. * @param Backend\BackendInterface $caldavBackend
  33. * @param array $calendarInfo
  34. */
  35. function __construct(Backend\BackendInterface $caldavBackend, $calendarInfo) {
  36. $this->caldavBackend = $caldavBackend;
  37. $this->calendarInfo = $calendarInfo;
  38. }
  39. /**
  40. * Returns the name of the calendar
  41. *
  42. * @return string
  43. */
  44. function getName() {
  45. return $this->calendarInfo['uri'];
  46. }
  47. /**
  48. * Updates properties on this node.
  49. *
  50. * This method received a PropPatch object, which contains all the
  51. * information about the update.
  52. *
  53. * To update specific properties, call the 'handle' method on this object.
  54. * Read the PropPatch documentation for more information.
  55. *
  56. * @param PropPatch $propPatch
  57. * @return void
  58. */
  59. function propPatch(PropPatch $propPatch) {
  60. return $this->caldavBackend->updateCalendar($this->calendarInfo['id'], $propPatch);
  61. }
  62. /**
  63. * Returns the list of properties
  64. *
  65. * @param array $requestedProperties
  66. * @return array
  67. */
  68. function getProperties($requestedProperties) {
  69. $response = [];
  70. foreach ($this->calendarInfo as $propName => $propValue) {
  71. if ($propName[0] === '{')
  72. $response[$propName] = $this->calendarInfo[$propName];
  73. }
  74. return $response;
  75. }
  76. /**
  77. * Returns a calendar object
  78. *
  79. * The contained calendar objects are for example Events or Todo's.
  80. *
  81. * @param string $name
  82. * @return \Sabre\CalDAV\ICalendarObject
  83. */
  84. function getChild($name) {
  85. $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
  86. if (!$obj) throw new DAV\Exception\NotFound('Calendar object not found');
  87. $obj['acl'] = $this->getChildACL();
  88. return new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
  89. }
  90. /**
  91. * Returns the full list of calendar objects
  92. *
  93. * @return array
  94. */
  95. function getChildren() {
  96. $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
  97. $children = [];
  98. foreach ($objs as $obj) {
  99. $obj['acl'] = $this->getChildACL();
  100. $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
  101. }
  102. return $children;
  103. }
  104. /**
  105. * This method receives a list of paths in it's first argument.
  106. * It must return an array with Node objects.
  107. *
  108. * If any children are not found, you do not have to return them.
  109. *
  110. * @param string[] $paths
  111. * @return array
  112. */
  113. function getMultipleChildren(array $paths) {
  114. $objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths);
  115. $children = [];
  116. foreach ($objs as $obj) {
  117. $obj['acl'] = $this->getChildACL();
  118. $children[] = new CalendarObject($this->caldavBackend, $this->calendarInfo, $obj);
  119. }
  120. return $children;
  121. }
  122. /**
  123. * Checks if a child-node exists.
  124. *
  125. * @param string $name
  126. * @return bool
  127. */
  128. function childExists($name) {
  129. $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name);
  130. if (!$obj)
  131. return false;
  132. else
  133. return true;
  134. }
  135. /**
  136. * Creates a new directory
  137. *
  138. * We actually block this, as subdirectories are not allowed in calendars.
  139. *
  140. * @param string $name
  141. * @return void
  142. */
  143. function createDirectory($name) {
  144. throw new DAV\Exception\MethodNotAllowed('Creating collections in calendar objects is not allowed');
  145. }
  146. /**
  147. * Creates a new file
  148. *
  149. * The contents of the new file must be a valid ICalendar string.
  150. *
  151. * @param string $name
  152. * @param resource $calendarData
  153. * @return string|null
  154. */
  155. function createFile($name, $calendarData = null) {
  156. if (is_resource($calendarData)) {
  157. $calendarData = stream_get_contents($calendarData);
  158. }
  159. return $this->caldavBackend->createCalendarObject($this->calendarInfo['id'], $name, $calendarData);
  160. }
  161. /**
  162. * Deletes the calendar.
  163. *
  164. * @return void
  165. */
  166. function delete() {
  167. $this->caldavBackend->deleteCalendar($this->calendarInfo['id']);
  168. }
  169. /**
  170. * Renames the calendar. Note that most calendars use the
  171. * {DAV:}displayname to display a name to display a name.
  172. *
  173. * @param string $newName
  174. * @return void
  175. */
  176. function setName($newName) {
  177. throw new DAV\Exception\MethodNotAllowed('Renaming calendars is not yet supported');
  178. }
  179. /**
  180. * Returns the last modification date as a unix timestamp.
  181. *
  182. * @return void
  183. */
  184. function getLastModified() {
  185. return null;
  186. }
  187. /**
  188. * Returns the owner principal
  189. *
  190. * This must be a url to a principal, or null if there's no owner
  191. *
  192. * @return string|null
  193. */
  194. function getOwner() {
  195. return $this->calendarInfo['principaluri'];
  196. }
  197. /**
  198. * Returns a group principal
  199. *
  200. * This must be a url to a principal, or null if there's no owner
  201. *
  202. * @return string|null
  203. */
  204. function getGroup() {
  205. return null;
  206. }
  207. /**
  208. * Returns a list of ACE's for this node.
  209. *
  210. * Each ACE has the following properties:
  211. * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
  212. * currently the only supported privileges
  213. * * 'principal', a url to the principal who owns the node
  214. * * 'protected' (optional), indicating that this ACE is not allowed to
  215. * be updated.
  216. *
  217. * @return array
  218. */
  219. function getACL() {
  220. $acl = [
  221. [
  222. 'privilege' => '{DAV:}read',
  223. 'principal' => $this->getOwner(),
  224. 'protected' => true,
  225. ],
  226. [
  227. 'privilege' => '{DAV:}read',
  228. 'principal' => $this->getOwner() . '/calendar-proxy-write',
  229. 'protected' => true,
  230. ],
  231. [
  232. 'privilege' => '{DAV:}read',
  233. 'principal' => $this->getOwner() . '/calendar-proxy-read',
  234. 'protected' => true,
  235. ],
  236. [
  237. 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy',
  238. 'principal' => '{DAV:}authenticated',
  239. 'protected' => true,
  240. ],
  241. ];
  242. if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) {
  243. $acl[] = [
  244. 'privilege' => '{DAV:}write',
  245. 'principal' => $this->getOwner(),
  246. 'protected' => true,
  247. ];
  248. $acl[] = [
  249. 'privilege' => '{DAV:}write',
  250. 'principal' => $this->getOwner() . '/calendar-proxy-write',
  251. 'protected' => true,
  252. ];
  253. }
  254. return $acl;
  255. }
  256. /**
  257. * This method returns the ACL's for calendar objects in this calendar.
  258. * The result of this method automatically gets passed to the
  259. * calendar-object nodes in the calendar.
  260. *
  261. * @return array
  262. */
  263. function getChildACL() {
  264. $acl = [
  265. [
  266. 'privilege' => '{DAV:}read',
  267. 'principal' => $this->getOwner(),
  268. 'protected' => true,
  269. ],
  270. [
  271. 'privilege' => '{DAV:}read',
  272. 'principal' => $this->getOwner() . '/calendar-proxy-write',
  273. 'protected' => true,
  274. ],
  275. [
  276. 'privilege' => '{DAV:}read',
  277. 'principal' => $this->getOwner() . '/calendar-proxy-read',
  278. 'protected' => true,
  279. ],
  280. ];
  281. if (empty($this->calendarInfo['{http://sabredav.org/ns}read-only'])) {
  282. $acl[] = [
  283. 'privilege' => '{DAV:}write',
  284. 'principal' => $this->getOwner(),
  285. 'protected' => true,
  286. ];
  287. $acl[] = [
  288. 'privilege' => '{DAV:}write',
  289. 'principal' => $this->getOwner() . '/calendar-proxy-write',
  290. 'protected' => true,
  291. ];
  292. }
  293. return $acl;
  294. }
  295. /**
  296. * Updates the ACL
  297. *
  298. * This method will receive a list of new ACE's.
  299. *
  300. * @param array $acl
  301. * @return void
  302. */
  303. function setACL(array $acl) {
  304. throw new DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
  305. }
  306. /**
  307. * Returns the list of supported privileges for this node.
  308. *
  309. * The returned data structure is a list of nested privileges.
  310. * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
  311. * standard structure.
  312. *
  313. * If null is returned from this method, the default privilege set is used,
  314. * which is fine for most common usecases.
  315. *
  316. * @return array|null
  317. */
  318. function getSupportedPrivilegeSet() {
  319. $default = DAVACL\Plugin::getDefaultSupportedPrivilegeSet();
  320. // We need to inject 'read-free-busy' in the tree, aggregated under
  321. // {DAV:}read.
  322. foreach ($default['aggregates'] as &$agg) {
  323. if ($agg['privilege'] !== '{DAV:}read') continue;
  324. $agg['aggregates'][] = [
  325. 'privilege' => '{' . Plugin::NS_CALDAV . '}read-free-busy',
  326. ];
  327. }
  328. return $default;
  329. }
  330. /**
  331. * Performs a calendar-query on the contents of this calendar.
  332. *
  333. * The calendar-query is defined in RFC4791 : CalDAV. Using the
  334. * calendar-query it is possible for a client to request a specific set of
  335. * object, based on contents of iCalendar properties, date-ranges and
  336. * iCalendar component types (VTODO, VEVENT).
  337. *
  338. * This method should just return a list of (relative) urls that match this
  339. * query.
  340. *
  341. * The list of filters are specified as an array. The exact array is
  342. * documented by Sabre\CalDAV\CalendarQueryParser.
  343. *
  344. * @param array $filters
  345. * @return array
  346. */
  347. function calendarQuery(array $filters) {
  348. return $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters);
  349. }
  350. /**
  351. * This method returns the current sync-token for this collection.
  352. * This can be any string.
  353. *
  354. * If null is returned from this function, the plugin assumes there's no
  355. * sync information available.
  356. *
  357. * @return string|null
  358. */
  359. function getSyncToken() {
  360. if (
  361. $this->caldavBackend instanceof Backend\SyncSupport &&
  362. isset($this->calendarInfo['{DAV:}sync-token'])
  363. ) {
  364. return $this->calendarInfo['{DAV:}sync-token'];
  365. }
  366. if (
  367. $this->caldavBackend instanceof Backend\SyncSupport &&
  368. isset($this->calendarInfo['{http://sabredav.org/ns}sync-token'])
  369. ) {
  370. return $this->calendarInfo['{http://sabredav.org/ns}sync-token'];
  371. }
  372. }
  373. /**
  374. * The getChanges method returns all the changes that have happened, since
  375. * the specified syncToken and the current collection.
  376. *
  377. * This function should return an array, such as the following:
  378. *
  379. * [
  380. * 'syncToken' => 'The current synctoken',
  381. * 'added' => [
  382. * 'new.txt',
  383. * ],
  384. * 'modified' => [
  385. * 'modified.txt',
  386. * ],
  387. * 'deleted' => [
  388. * 'foo.php.bak',
  389. * 'old.txt'
  390. * ]
  391. * ];
  392. *
  393. * The syncToken property should reflect the *current* syncToken of the
  394. * collection, as reported getSyncToken(). This is needed here too, to
  395. * ensure the operation is atomic.
  396. *
  397. * If the syncToken is specified as null, this is an initial sync, and all
  398. * members should be reported.
  399. *
  400. * The modified property is an array of nodenames that have changed since
  401. * the last token.
  402. *
  403. * The deleted property is an array with nodenames, that have been deleted
  404. * from collection.
  405. *
  406. * The second argument is basically the 'depth' of the report. If it's 1,
  407. * you only have to report changes that happened only directly in immediate
  408. * descendants. If it's 2, it should also include changes from the nodes
  409. * below the child collections. (grandchildren)
  410. *
  411. * The third (optional) argument allows a client to specify how many
  412. * results should be returned at most. If the limit is not specified, it
  413. * should be treated as infinite.
  414. *
  415. * If the limit (infinite or not) is higher than you're willing to return,
  416. * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
  417. *
  418. * If the syncToken is expired (due to data cleanup) or unknown, you must
  419. * return null.
  420. *
  421. * The limit is 'suggestive'. You are free to ignore it.
  422. *
  423. * @param string $syncToken
  424. * @param int $syncLevel
  425. * @param int $limit
  426. * @return array
  427. */
  428. function getChanges($syncToken, $syncLevel, $limit = null) {
  429. if (!$this->caldavBackend instanceof Backend\SyncSupport) {
  430. return null;
  431. }
  432. return $this->caldavBackend->getChangesForCalendar(
  433. $this->calendarInfo['id'],
  434. $syncToken,
  435. $syncLevel,
  436. $limit
  437. );
  438. }
  439. }