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