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