AbstractBackend.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <?php
  2. namespace Sabre\CalDAV\Backend;
  3. use Sabre\VObject;
  4. use Sabre\CalDAV;
  5. /**
  6. * Abstract Calendaring backend. Extend this class to create your own backends.
  7. *
  8. * Checkout the BackendInterface for all the methods that must be implemented.
  9. *
  10. * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
  11. * @author Evert Pot (http://evertpot.com/)
  12. * @license http://sabre.io/license/ Modified BSD License
  13. */
  14. abstract class AbstractBackend implements BackendInterface {
  15. /**
  16. * Updates properties for a calendar.
  17. *
  18. * The list of mutations is stored in a Sabre\DAV\PropPatch object.
  19. * To do the actual updates, you must tell this object which properties
  20. * you're going to process with the handle() method.
  21. *
  22. * Calling the handle method is like telling the PropPatch object "I
  23. * promise I can handle updating this property".
  24. *
  25. * Read the PropPatch documenation for more info and examples.
  26. *
  27. * @param string $path
  28. * @param \Sabre\DAV\PropPatch $propPatch
  29. * @return void
  30. */
  31. function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) {
  32. }
  33. /**
  34. * Returns a list of calendar objects.
  35. *
  36. * This method should work identical to getCalendarObject, but instead
  37. * return all the calendar objects in the list as an array.
  38. *
  39. * If the backend supports this, it may allow for some speed-ups.
  40. *
  41. * @param mixed $calendarId
  42. * @param array $uris
  43. * @return array
  44. */
  45. function getMultipleCalendarObjects($calendarId, array $uris) {
  46. return array_map(function($uri) use ($calendarId) {
  47. return $this->getCalendarObject($calendarId, $uri);
  48. }, $uris);
  49. }
  50. /**
  51. * Performs a calendar-query on the contents of this calendar.
  52. *
  53. * The calendar-query is defined in RFC4791 : CalDAV. Using the
  54. * calendar-query it is possible for a client to request a specific set of
  55. * object, based on contents of iCalendar properties, date-ranges and
  56. * iCalendar component types (VTODO, VEVENT).
  57. *
  58. * This method should just return a list of (relative) urls that match this
  59. * query.
  60. *
  61. * The list of filters are specified as an array. The exact array is
  62. * documented by \Sabre\CalDAV\CalendarQueryParser.
  63. *
  64. * Note that it is extremely likely that getCalendarObject for every path
  65. * returned from this method will be called almost immediately after. You
  66. * may want to anticipate this to speed up these requests.
  67. *
  68. * This method provides a default implementation, which parses *all* the
  69. * iCalendar objects in the specified calendar.
  70. *
  71. * This default may well be good enough for personal use, and calendars
  72. * that aren't very large. But if you anticipate high usage, big calendars
  73. * or high loads, you are strongly adviced to optimize certain paths.
  74. *
  75. * The best way to do so is override this method and to optimize
  76. * specifically for 'common filters'.
  77. *
  78. * Requests that are extremely common are:
  79. * * requests for just VEVENTS
  80. * * requests for just VTODO
  81. * * requests with a time-range-filter on either VEVENT or VTODO.
  82. *
  83. * ..and combinations of these requests. It may not be worth it to try to
  84. * handle every possible situation and just rely on the (relatively
  85. * easy to use) CalendarQueryValidator to handle the rest.
  86. *
  87. * Note that especially time-range-filters may be difficult to parse. A
  88. * time-range filter specified on a VEVENT must for instance also handle
  89. * recurrence rules correctly.
  90. * A good example of how to interprete all these filters can also simply
  91. * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
  92. * as possible, so it gives you a good idea on what type of stuff you need
  93. * to think of.
  94. *
  95. * @param mixed $calendarId
  96. * @param array $filters
  97. * @return array
  98. */
  99. function calendarQuery($calendarId, array $filters) {
  100. $result = [];
  101. $objects = $this->getCalendarObjects($calendarId);
  102. foreach ($objects as $object) {
  103. if ($this->validateFilterForObject($object, $filters)) {
  104. $result[] = $object['uri'];
  105. }
  106. }
  107. return $result;
  108. }
  109. /**
  110. * This method validates if a filter (as passed to calendarQuery) matches
  111. * the given object.
  112. *
  113. * @param array $object
  114. * @param array $filters
  115. * @return bool
  116. */
  117. protected function validateFilterForObject(array $object, array $filters) {
  118. // Unfortunately, setting the 'calendardata' here is optional. If
  119. // it was excluded, we actually need another call to get this as
  120. // well.
  121. if (!isset($object['calendardata'])) {
  122. $object = $this->getCalendarObject($object['calendarid'], $object['uri']);
  123. }
  124. $vObject = VObject\Reader::read($object['calendardata']);
  125. $validator = new CalDAV\CalendarQueryValidator();
  126. $result = $validator->validate($vObject, $filters);
  127. // Destroy circular references so PHP will GC the object.
  128. $vObject->destroy();
  129. return $result;
  130. }
  131. /**
  132. * Searches through all of a users calendars and calendar objects to find
  133. * an object with a specific UID.
  134. *
  135. * This method should return the path to this object, relative to the
  136. * calendar home, so this path usually only contains two parts:
  137. *
  138. * calendarpath/objectpath.ics
  139. *
  140. * If the uid is not found, return null.
  141. *
  142. * This method should only consider * objects that the principal owns, so
  143. * any calendars owned by other principals that also appear in this
  144. * collection should be ignored.
  145. *
  146. * @param string $principalUri
  147. * @param string $uid
  148. * @return string|null
  149. */
  150. function getCalendarObjectByUID($principalUri, $uid) {
  151. // Note: this is a super slow naive implementation of this method. You
  152. // are highly recommended to optimize it, if your backend allows it.
  153. foreach ($this->getCalendarsForUser($principalUri) as $calendar) {
  154. // We must ignore calendars owned by other principals.
  155. if ($calendar['principaluri'] !== $principalUri) {
  156. continue;
  157. }
  158. // Ignore calendars that are shared.
  159. if (isset($calendar['{http://sabredav.org/ns}owner-principal']) && $calendar['{http://sabredav.org/ns}owner-principal'] !== $principalUri) {
  160. continue;
  161. }
  162. $results = $this->calendarQuery(
  163. $calendar['id'],
  164. [
  165. 'name' => 'VCALENDAR',
  166. 'prop-filters' => [],
  167. 'comp-filters' => [
  168. [
  169. 'name' => 'VEVENT',
  170. 'is-not-defined' => false,
  171. 'time-range' => null,
  172. 'comp-filters' => [],
  173. 'prop-filters' => [
  174. [
  175. 'name' => 'UID',
  176. 'is-not-defined' => false,
  177. 'time-range' => null,
  178. 'text-match' => [
  179. 'value' => $uid,
  180. 'negate-condition' => false,
  181. 'collation' => 'i;octet',
  182. ],
  183. 'param-filters' => [],
  184. ],
  185. ]
  186. ]
  187. ],
  188. ]
  189. );
  190. if ($results) {
  191. // We have a match
  192. return $calendar['uri'] . '/' . $results[0];
  193. }
  194. }
  195. }
  196. }