1 | <?php |
||||
2 | /* |
||||
3 | * SPDX-License-Identifier: AGPL-3.0-only |
||||
4 | * SPDX-FileCopyrightText: Copyright 2016 - 2018 Kopano b.v. |
||||
5 | * SPDX-FileCopyrightText: Copyright 2020 - 2024 grommunio GmbH |
||||
6 | * |
||||
7 | * grommunio CalDAV backend class which handles calendar related activities. |
||||
8 | */ |
||||
9 | |||||
10 | namespace grommunio\DAV; |
||||
11 | |||||
12 | use Sabre\CalDAV\Backend\AbstractBackend; |
||||
13 | use Sabre\CalDAV\Backend\SchedulingSupport; |
||||
14 | use Sabre\CalDAV\Backend\SyncSupport; |
||||
15 | |||||
16 | class GrommunioCalDavBackend extends AbstractBackend implements SchedulingSupport, SyncSupport { |
||||
17 | /* |
||||
18 | * TODO IMPLEMENT |
||||
19 | * |
||||
20 | * SubscriptionSupport, |
||||
21 | * SharingSupport, |
||||
22 | * |
||||
23 | */ |
||||
24 | |||||
25 | private $logger; |
||||
26 | protected $gDavBackend; |
||||
27 | |||||
28 | public const FILE_EXTENSION = '.ics'; |
||||
29 | // TODO: implement Task support - Issue: #10 |
||||
30 | public const MESSAGE_CLASSES = ['IPM.Appointment' /* , 'IPM.Note' */]; |
||||
31 | public const CONTAINER_CLASS = 'IPF.Appointment'; |
||||
32 | public const CONTAINER_CLASSES = ['IPF.Appointment', 'IPF.Task']; |
||||
33 | |||||
34 | /** |
||||
35 | * Constructor. |
||||
36 | */ |
||||
37 | public function __construct(GrommunioDavBackend $gDavBackend, GLogger $glogger) { |
||||
38 | $this->gDavBackend = $gDavBackend; |
||||
39 | $this->logger = $glogger; |
||||
40 | } |
||||
41 | |||||
42 | /** |
||||
43 | * Returns a list of calendars for a principal. |
||||
44 | * |
||||
45 | * Every project is an array with the following keys: |
||||
46 | * * id, a unique id that will be used by other functions to modify the |
||||
47 | * calendar. This can be the same as the uri or a database key. |
||||
48 | * * uri. This is just the 'base uri' or 'filename' of the calendar. |
||||
49 | * * principaluri. The owner of the calendar. Almost always the same as |
||||
50 | * principalUri passed to this method. |
||||
51 | * |
||||
52 | * Furthermore it can contain webdav properties in clark notation. A very |
||||
53 | * common one is '{DAV:}displayname'. |
||||
54 | * |
||||
55 | * Many clients also require: |
||||
56 | * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set |
||||
57 | * For this property, you can just return an instance of |
||||
58 | * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet. |
||||
59 | * |
||||
60 | * If you return {http://sabredav.org/ns}read-only and set the value to 1, |
||||
61 | * ACL will automatically be put in read-only mode. |
||||
62 | * |
||||
63 | * @param string $principalUri |
||||
64 | * |
||||
65 | * @return array |
||||
66 | */ |
||||
67 | public function getCalendarsForUser($principalUri) { |
||||
68 | $this->logger->trace("principalUri: %s", $principalUri); |
||||
69 | |||||
70 | return $this->gDavBackend->GetFolders($principalUri, static::CONTAINER_CLASSES); |
||||
71 | } |
||||
72 | |||||
73 | /** |
||||
74 | * Creates a new calendar for a principal. |
||||
75 | * |
||||
76 | * If the creation was a success, an id must be returned that can be used |
||||
77 | * to reference this calendar in other methods, such as updateCalendar. |
||||
78 | * |
||||
79 | * @param string $principalUri |
||||
80 | * @param string $calendarUri |
||||
81 | * |
||||
82 | * @return string |
||||
83 | */ |
||||
84 | public function createCalendar($principalUri, $calendarUri, array $properties) { |
||||
85 | $this->logger->trace("principalUri: %s - calendarUri: %s - properties: %s", $principalUri, $calendarUri, $properties); |
||||
86 | |||||
87 | // TODO Add displayname |
||||
88 | return $this->gDavBackend->CreateFolder($principalUri, $calendarUri, static::CONTAINER_CLASS, ""); |
||||
89 | } |
||||
90 | |||||
91 | /** |
||||
92 | * Delete a calendar and all its objects. |
||||
93 | * |
||||
94 | * @param string $calendarId |
||||
95 | */ |
||||
96 | public function deleteCalendar($calendarId) { |
||||
97 | $this->logger->trace("calendarId: %s", $calendarId); |
||||
98 | $success = $this->gDavBackend->DeleteFolder($calendarId); |
||||
99 | // TODO evaluate $success |
||||
100 | } |
||||
101 | |||||
102 | /** |
||||
103 | * Returns all calendar objects within a calendar. |
||||
104 | * |
||||
105 | * Every item contains an array with the following keys: |
||||
106 | * * calendardata - The iCalendar-compatible calendar data |
||||
107 | * * uri - a unique key which will be used to construct the uri. This can |
||||
108 | * be any arbitrary string, but making sure it ends with '.ics' is a |
||||
109 | * good idea. This is only the basename, or filename, not the full |
||||
110 | * path. |
||||
111 | * * lastmodified - a timestamp of the last modification time |
||||
112 | * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: |
||||
113 | * ' "abcdef"') |
||||
114 | * * size - The size of the calendar objects, in bytes. |
||||
115 | * * component - optional, a string containing the type of object, such |
||||
116 | * as 'vevent' or 'vtodo'. If specified, this will be used to populate |
||||
117 | * the Content-Type header. |
||||
118 | * |
||||
119 | * Note that the etag is optional, but it's highly encouraged to return for |
||||
120 | * speed reasons. |
||||
121 | * |
||||
122 | * The calendardata is also optional. If it's not returned |
||||
123 | * 'getCalendarObject' will be called later, which *is* expected to return |
||||
124 | * calendardata. |
||||
125 | * |
||||
126 | * If neither etag or size are specified, the calendardata will be |
||||
127 | * used/fetched to determine these numbers. If both are specified the |
||||
128 | * amount of times this is needed is reduced by a great degree. |
||||
129 | * |
||||
130 | * @param string $calendarId |
||||
131 | * |
||||
132 | * @return array |
||||
133 | */ |
||||
134 | public function getCalendarObjects($calendarId) { |
||||
135 | $result = $this->gDavBackend->GetObjects($calendarId, static::FILE_EXTENSION, ['types' => static::MESSAGE_CLASSES]); |
||||
136 | $this->logger->trace("calendarId: %s found %d objects", $calendarId, count($result)); |
||||
137 | |||||
138 | return $result; |
||||
139 | } |
||||
140 | |||||
141 | /** |
||||
142 | * Performs a calendar-query on the contents of this calendar. |
||||
143 | * |
||||
144 | * The calendar-query is defined in RFC4791 : CalDAV. Using the |
||||
145 | * calendar-query it is possible for a client to request a specific set of |
||||
146 | * object, based on contents of iCalendar properties, date-ranges and |
||||
147 | * iCalendar component types (VTODO, VEVENT). |
||||
148 | * |
||||
149 | * This method should just return a list of (relative) urls that match this |
||||
150 | * query. |
||||
151 | * |
||||
152 | * The list of filters are specified as an array. The exact array is |
||||
153 | * documented by \Sabre\CalDAV\CalendarQueryParser. |
||||
154 | * |
||||
155 | * Note that it is extremely likely that getCalendarObject for every path |
||||
156 | * returned from this method will be called almost immediately after. You |
||||
157 | * may want to anticipate this to speed up these requests. |
||||
158 | * |
||||
159 | * This method provides a default implementation, which parses *all* the |
||||
160 | * iCalendar objects in the specified calendar. |
||||
161 | * |
||||
162 | * This default may well be good enough for personal use, and calendars |
||||
163 | * that aren't very large. But if you anticipate high usage, big calendars |
||||
164 | * or high loads, you are strongly advised to optimize certain paths. |
||||
165 | * |
||||
166 | * The best way to do so is override this method and to optimize |
||||
167 | * specifically for 'common filters'. |
||||
168 | * |
||||
169 | * Requests that are extremely common are: |
||||
170 | * * requests for just VEVENTS |
||||
171 | * * requests for just VTODO |
||||
172 | * * requests with a time-range-filter on either VEVENT or VTODO. |
||||
173 | * |
||||
174 | * ..and combinations of these requests. It may not be worth it to try to |
||||
175 | * handle every possible situation and just rely on the (relatively |
||||
176 | * easy to use) CalendarQueryValidator to handle the rest. |
||||
177 | * |
||||
178 | * Note that especially time-range-filters may be difficult to parse. A |
||||
179 | * time-range filter specified on a VEVENT must for instance also handle |
||||
180 | * recurrence rules correctly. |
||||
181 | * A good example of how to interpret all these filters can also simply |
||||
182 | * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct |
||||
183 | * as possible, so it gives you a good idea on what type of stuff you need |
||||
184 | * to think of. |
||||
185 | * |
||||
186 | * @param mixed $calendarId |
||||
187 | * |
||||
188 | * @return array |
||||
189 | */ |
||||
190 | public function calendarQuery($calendarId, array $filters) { |
||||
191 | $start = $end = null; |
||||
192 | $types = []; |
||||
193 | foreach ($filters['comp-filters'] as $filter) { |
||||
194 | |||||
195 | if ($filter['name'] == 'VEVENT') { |
||||
196 | $types[] = 'IPM.Appointment'; |
||||
197 | } |
||||
198 | elseif ($filter['name'] == 'VTODO') { |
||||
199 | $types[] = 'IPM.Task'; |
||||
200 | } |
||||
201 | |||||
202 | /* will this work on tasks? */ |
||||
203 | if (is_array($filter['time-range']) && isset($filter['time-range']['start'], $filter['time-range']['end'])) { |
||||
204 | $start = $filter['time-range']['start']->getTimestamp(); |
||||
205 | $end = $filter['time-range']['end']->getTimestamp(); |
||||
206 | } |
||||
207 | } |
||||
208 | |||||
209 | $objfilters = []; |
||||
210 | if ($start != null && $end != null) { |
||||
211 | $objfilters["start"] = $start; |
||||
212 | $objfilters["end"] = $end; |
||||
213 | } |
||||
214 | if (!empty($types)) { |
||||
215 | $objfilters["types"] = $types; |
||||
216 | } |
||||
217 | |||||
218 | $objects = $this->gDavBackend->GetObjects($calendarId, static::FILE_EXTENSION, $objfilters); |
||||
219 | $result = []; |
||||
220 | foreach ($objects as $object) { |
||||
221 | $result[] = $object['uri']; |
||||
222 | } |
||||
223 | |||||
224 | return $result; |
||||
225 | } |
||||
226 | |||||
227 | /** |
||||
228 | * Returns information from a single calendar object, based on its object uri. |
||||
229 | * |
||||
230 | * The object uri is only the basename, or filename and not a full path. |
||||
231 | * |
||||
232 | * The returned array must have the same keys as getCalendarObjects. The |
||||
233 | * 'calendardata' object is required here though, while it's not required |
||||
234 | * for getCalendarObjects. |
||||
235 | * |
||||
236 | * This method must return null if the object did not exist. |
||||
237 | * |
||||
238 | * @param string $calendarId |
||||
239 | * @param string $objectUri |
||||
240 | * @param resource $mapifolder optional mapifolder resource, used if available |
||||
241 | * |
||||
242 | * @return null|array |
||||
243 | */ |
||||
244 | public function getCalendarObject($calendarId, $objectUri, $mapifolder = null) { |
||||
245 | $this->logger->trace("calendarId: %s - objectUri: %s - mapifolder: %s", $calendarId, $objectUri, $mapifolder); |
||||
246 | |||||
247 | if (!$mapifolder) { |
||||
248 | $mapifolder = $this->gDavBackend->GetMapiFolder($calendarId); |
||||
249 | } |
||||
250 | |||||
251 | $mapimessage = $this->gDavBackend->GetMapiMessageForId($calendarId, $objectUri, $mapifolder, static::FILE_EXTENSION); |
||||
252 | if (!$mapimessage) { |
||||
253 | $this->logger->info("Object NOT FOUND"); |
||||
254 | |||||
255 | return null; |
||||
256 | } |
||||
257 | |||||
258 | $realId = $this->gDavBackend->GetIdOfMapiMessage($calendarId, $mapimessage); |
||||
259 | |||||
260 | // this should be cached or moved to gDavBackend |
||||
261 | $session = $this->gDavBackend->GetSession(); |
||||
262 | $ab = $this->gDavBackend->GetAddressBook(); |
||||
263 | |||||
264 | $ics = mapi_mapitoical($session, $ab, $mapimessage, []); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
265 | if (!$ics && mapi_last_hresult()) { |
||||
266 | $this->logger->error("Error generating ical, error code: 0x%08X", mapi_last_hresult()); |
||||
267 | $ics = null; |
||||
268 | } |
||||
269 | elseif (!$ics) { |
||||
270 | $this->logger->error("Error generating ical, unknown error"); |
||||
271 | $ics = null; |
||||
272 | } |
||||
273 | |||||
274 | $props = mapi_getprops($mapimessage, [PR_LAST_MODIFICATION_TIME]); |
||||
275 | |||||
276 | $r = [ |
||||
277 | 'id' => $realId, |
||||
278 | 'uri' => $realId . static::FILE_EXTENSION, |
||||
279 | 'etag' => '"' . $props[PR_LAST_MODIFICATION_TIME] . '"', |
||||
280 | 'lastmodified' => $props[PR_LAST_MODIFICATION_TIME], |
||||
281 | 'calendarid' => $calendarId, |
||||
282 | 'size' => strlen($ics), |
||||
283 | 'calendardata' => $ics, |
||||
284 | ]; |
||||
285 | $this->logger->trace("returned data id: %s - size: %d - etag: %s", $r['id'], $r['size'], $r['etag']); |
||||
286 | |||||
287 | return $r; |
||||
288 | } |
||||
289 | |||||
290 | /** |
||||
291 | * Creates a new calendar object. |
||||
292 | * |
||||
293 | * The object uri is only the basename, or filename and not a full path. |
||||
294 | * |
||||
295 | * It is possible return an etag from this function, which will be used in |
||||
296 | * the response to this PUT request. Note that the ETag must be surrounded |
||||
297 | * by double-quotes. |
||||
298 | * |
||||
299 | * However, you should only really return this ETag if you don't mangle the |
||||
300 | * calendar-data. If the result of a subsequent GET to this object is not |
||||
301 | * the exact same as this request body, you should omit the ETag. |
||||
302 | * |
||||
303 | * @param mixed $calendarId |
||||
304 | * @param string $objectUri |
||||
305 | * @param string $calendarData |
||||
306 | * |
||||
307 | * @return null|string |
||||
308 | */ |
||||
309 | public function createCalendarObject($calendarId, $objectUri, $calendarData) { |
||||
310 | $this->logger->trace("calendarId: %s - objectUri: %s", $calendarId, $objectUri); |
||||
311 | $objectId = $this->gDavBackend->GetObjectIdFromObjectUri($objectUri, static::FILE_EXTENSION); |
||||
312 | $folder = $this->gDavBackend->GetMapiFolder($calendarId); |
||||
313 | $mapimessage = $this->gDavBackend->CreateObject($calendarId, $folder, $objectId); |
||||
314 | $retval = $this->setData($calendarId, $mapimessage, $calendarData); |
||||
315 | if (!$retval) { |
||||
316 | return null; |
||||
317 | } |
||||
318 | |||||
319 | return '"' . $retval . '"'; |
||||
320 | } |
||||
321 | |||||
322 | /** |
||||
323 | * Updates an existing calendarobject, based on its uri. |
||||
324 | * |
||||
325 | * The object uri is only the basename, or filename and not a full path. |
||||
326 | * |
||||
327 | * It is possible return an etag from this function, which will be used in |
||||
328 | * the response to this PUT request. Note that the ETag must be surrounded |
||||
329 | * by double-quotes. |
||||
330 | * |
||||
331 | * However, you should only really return this ETag if you don't mangle the |
||||
332 | * calendar-data. If the result of a subsequent GET to this object is not |
||||
333 | * the exact same as this request body, you should omit the ETag. |
||||
334 | * |
||||
335 | * @param mixed $calendarId |
||||
336 | * @param string $objectUri |
||||
337 | * @param string $calendarData |
||||
338 | * |
||||
339 | * @return null|string |
||||
340 | */ |
||||
341 | public function updateCalendarObject($calendarId, $objectUri, $calendarData) { |
||||
342 | $this->logger->trace("calendarId: %s - objectUri: %s", $calendarId, $objectUri); |
||||
343 | |||||
344 | $folder = $this->gDavBackend->GetMapiFolder($calendarId); |
||||
345 | $mapimessage = $this->gDavBackend->GetMapiMessageForId($calendarId, $objectUri, null, static::FILE_EXTENSION); |
||||
346 | $retval = $this->setData($calendarId, $mapimessage, $calendarData); |
||||
347 | if (!$retval) { |
||||
348 | return null; |
||||
349 | } |
||||
350 | |||||
351 | return '"' . $retval . '"'; |
||||
352 | } |
||||
353 | |||||
354 | /** |
||||
355 | * Sets data for a calendar item. |
||||
356 | * |
||||
357 | * @param mixed $calendarId |
||||
358 | * @param mixed $mapimessage |
||||
359 | * @param string $ics |
||||
360 | * |
||||
361 | * @return null|string |
||||
362 | */ |
||||
363 | private function setData($calendarId, $mapimessage, $ics) { |
||||
364 | // this should be cached or moved to gDavBackend |
||||
365 | $store = $this->gDavBackend->GetStoreById($calendarId); |
||||
366 | $session = $this->gDavBackend->GetSession(); |
||||
367 | $ab = $this->gDavBackend->GetAddressBook(); |
||||
368 | |||||
369 | // Evolution sends daylight/standard information in the ical data |
||||
370 | // and some values are not supported by Outlook/Exchange. |
||||
371 | // Strip that data and leave only the last occurrences of |
||||
372 | // daylight/standard information. |
||||
373 | // @see GRAM-52 |
||||
374 | |||||
375 | $xLicLocation = stripos($ics, 'X-LIC-LOCATION:'); |
||||
376 | if (($xLicLocation !== false) && |
||||
377 | ( |
||||
378 | substr_count($ics, 'BEGIN:DAYLIGHT', $xLicLocation) > 0 || |
||||
379 | substr_count($ics, 'BEGIN:STANDARD', $xLicLocation) > 0 |
||||
380 | )) { |
||||
381 | $firstDaytime = stripos($ics, 'BEGIN:DAYLIGHT', $xLicLocation); |
||||
382 | $firstStandard = stripos($ics, 'BEGIN:STANDARD', $xLicLocation); |
||||
383 | |||||
384 | $lastDaytime = strripos($ics, 'BEGIN:DAYLIGHT', $xLicLocation); |
||||
385 | $lastStandard = strripos($ics, 'BEGIN:STANDARD', $xLicLocation); |
||||
386 | |||||
387 | // the first part of ics until the first piece of standard/daytime information |
||||
388 | $cutStart = $firstDaytime < $firstStandard ? $firstDaytime : $firstStandard; |
||||
389 | |||||
390 | if ($lastDaytime > $lastStandard) { |
||||
391 | // the part of the ics with the last piece of standard/daytime information |
||||
392 | $cutEnd = $lastDaytime; |
||||
393 | |||||
394 | // the positions of the last piece of standard information |
||||
395 | $cut1 = $lastStandard; |
||||
396 | $cut2 = strripos($ics, 'END:STANDARD', $lastStandard) + 14; // strlen('END:STANDARD') |
||||
397 | } |
||||
398 | else { |
||||
399 | // the part of the ics with the last piece of standard/daytime information |
||||
400 | $cutEnd = $lastStandard; |
||||
401 | |||||
402 | // the positions of the last piece of daylight information |
||||
403 | $cut1 = $lastDaytime; |
||||
404 | $cut2 = strripos($ics, 'END:DAYLIGHT', $lastDaytime) + 14; // strlen('END:DAYLIGHT') |
||||
405 | } |
||||
406 | |||||
407 | $ics = substr($ics, 0, $cutStart) . substr($ics, $cut1, $cut2 - $cut1) . substr($ics, $cutEnd); |
||||
408 | $this->logger->trace("newics: %s", $ics); |
||||
409 | } |
||||
410 | |||||
411 | $ok = mapi_icaltomapi($session, $store, $ab, $mapimessage, $ics, false); |
||||
0 ignored issues
–
show
The function
mapi_icaltomapi was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
412 | if (!$ok && mapi_last_hresult()) { |
||||
413 | $this->logger->error("Error updating mapi object, error code: 0x%08X", mapi_last_hresult()); |
||||
414 | |||||
415 | return null; |
||||
416 | } |
||||
417 | if (!$ok) { |
||||
418 | $this->logger->error("Error updating mapi object, unknown error"); |
||||
419 | |||||
420 | return null; |
||||
421 | } |
||||
422 | |||||
423 | $propList = MapiProps::GetAppointmentProperties(); |
||||
424 | $defaultProps = MapiProps::GetDefaultAppoinmentProperties(); |
||||
425 | $propsToSet = $this->gDavBackend->GetPropsToSet($calendarId, $mapimessage, $propList, $defaultProps); |
||||
426 | if (!empty($propsToSet)) { |
||||
427 | mapi_setprops($mapimessage, $propsToSet); |
||||
428 | } |
||||
429 | |||||
430 | mapi_savechanges($mapimessage); |
||||
431 | $props = mapi_getprops($mapimessage, [PR_LAST_MODIFICATION_TIME]); |
||||
432 | |||||
433 | return $props[PR_LAST_MODIFICATION_TIME]; |
||||
434 | } |
||||
435 | |||||
436 | /** |
||||
437 | * Deletes an existing calendar object. |
||||
438 | * |
||||
439 | * The object uri is only the basename, or filename and not a full path. |
||||
440 | * |
||||
441 | * @param string $calendarId |
||||
442 | * @param string $objectUri |
||||
443 | */ |
||||
444 | public function deleteCalendarObject($calendarId, $objectUri) { |
||||
445 | $this->logger->trace("calendarId: %s - objectUri: %s", $calendarId, $objectUri); |
||||
446 | |||||
447 | $mapifolder = $this->gDavBackend->GetMapiFolder($calendarId); |
||||
448 | |||||
449 | // to delete we need the PR_ENTRYID of the message |
||||
450 | // TODO move this part to GrommunioDavBackend |
||||
451 | $mapimessage = $this->gDavBackend->GetMapiMessageForId($calendarId, $objectUri, $mapifolder, static::FILE_EXTENSION); |
||||
452 | $props = mapi_getprops($mapimessage, [PR_ENTRYID]); |
||||
453 | mapi_folder_deletemessages($mapifolder, [$props[PR_ENTRYID]]); |
||||
454 | } |
||||
455 | |||||
456 | /** |
||||
457 | * Return a single scheduling object. |
||||
458 | * |
||||
459 | * TODO: Add implementation. |
||||
460 | * |
||||
461 | * @param string $principalUri |
||||
462 | * @param string $objectUri |
||||
463 | * |
||||
464 | * @return array |
||||
465 | */ |
||||
466 | public function getSchedulingObject($principalUri, $objectUri) { |
||||
467 | $this->logger->trace("principalUri: %s - objectUri: %s", $principalUri, $objectUri); |
||||
468 | |||||
469 | return []; |
||||
470 | } |
||||
471 | |||||
472 | /** |
||||
473 | * Returns scheduling objects for the principal URI. |
||||
474 | * |
||||
475 | * TODO: Add implementation. |
||||
476 | * |
||||
477 | * @param string $principalUri |
||||
478 | * |
||||
479 | * @return array |
||||
480 | */ |
||||
481 | public function getSchedulingObjects($principalUri) { |
||||
482 | $this->logger->trace("principalUri: %s", $principalUri); |
||||
483 | |||||
484 | return []; |
||||
485 | } |
||||
486 | |||||
487 | /** |
||||
488 | * Delete scheduling object. |
||||
489 | * |
||||
490 | * TODO: Add implementation. |
||||
491 | * |
||||
492 | * @param string $principalUri |
||||
493 | * @param string $objectUri |
||||
494 | */ |
||||
495 | public function deleteSchedulingObject($principalUri, $objectUri) { |
||||
496 | $this->logger->trace("principalUri: %s - objectUri: %s", $principalUri, $objectUri); |
||||
497 | } |
||||
498 | |||||
499 | /** |
||||
500 | * Create a new scheduling object. |
||||
501 | * |
||||
502 | * TODO: Add implementation. |
||||
503 | * |
||||
504 | * @param string $principalUri |
||||
505 | * @param string $objectUri |
||||
506 | * @param string $objectData |
||||
507 | */ |
||||
508 | public function createSchedulingObject($principalUri, $objectUri, $objectData) { |
||||
509 | $this->logger->trace("principalUri: %s - objectUri: %s - objectData: %s", $principalUri, $objectUri, $objectData); |
||||
510 | } |
||||
511 | |||||
512 | /** |
||||
513 | * Return CTAG for scheduling inbox. |
||||
514 | * |
||||
515 | * TODO: Add implementation. |
||||
516 | * |
||||
517 | * @param string $principalUri |
||||
518 | * |
||||
519 | * @return string |
||||
520 | */ |
||||
521 | public function getSchedulingInboxCtag($principalUri) { |
||||
522 | $this->logger->trace("principalUri: %s", $principalUri); |
||||
523 | |||||
524 | return "empty"; |
||||
525 | } |
||||
526 | |||||
527 | /** |
||||
528 | * The getChanges method returns all the changes that have happened, since |
||||
529 | * the specified syncToken in the specified calendar. |
||||
530 | * |
||||
531 | * This function should return an array, such as the following: |
||||
532 | * |
||||
533 | * [ |
||||
534 | * 'syncToken' => 'The current synctoken', |
||||
535 | * 'added' => [ |
||||
536 | * 'new.txt', |
||||
537 | * ], |
||||
538 | * 'modified' => [ |
||||
539 | * 'modified.txt', |
||||
540 | * ], |
||||
541 | * 'deleted' => [ |
||||
542 | * 'foo.php.bak', |
||||
543 | * 'old.txt' |
||||
544 | * ] |
||||
545 | * ); |
||||
546 | * |
||||
547 | * The returned syncToken property should reflect the *current* syncToken |
||||
548 | * of the calendar, as reported in the {http://sabredav.org/ns}sync-token |
||||
549 | * property This is * needed here too, to ensure the operation is atomic. |
||||
550 | * |
||||
551 | * If the $syncToken argument is specified as null, this is an initial |
||||
552 | * sync, and all members should be reported. |
||||
553 | * |
||||
554 | * The modified property is an array of nodenames that have changed since |
||||
555 | * the last token. |
||||
556 | * |
||||
557 | * The deleted property is an array with nodenames, that have been deleted |
||||
558 | * from collection. |
||||
559 | * |
||||
560 | * The $syncLevel argument is basically the 'depth' of the report. If it's |
||||
561 | * 1, you only have to report changes that happened only directly in |
||||
562 | * immediate descendants. If it's 2, it should also include changes from |
||||
563 | * the nodes below the child collections. (grandchildren) |
||||
564 | * |
||||
565 | * The $limit argument allows a client to specify how many results should |
||||
566 | * be returned at most. If the limit is not specified, it should be treated |
||||
567 | * as infinite. |
||||
568 | * |
||||
569 | * If the limit (infinite or not) is higher than you're willing to return, |
||||
570 | * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception. |
||||
571 | * |
||||
572 | * If the syncToken is expired (due to data cleanup) or unknown, you must |
||||
573 | * return null. |
||||
574 | * |
||||
575 | * The limit is 'suggestive'. You are free to ignore it. |
||||
576 | * |
||||
577 | * @param string $calendarId |
||||
578 | * @param string $syncToken |
||||
579 | * @param int $syncLevel |
||||
580 | * @param int $limit |
||||
581 | * |
||||
582 | * @return array |
||||
583 | */ |
||||
584 | public function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) { |
||||
585 | $this->logger->trace("calendarId: %s - syncToken: %s - syncLevel: %d - limit: %d", $calendarId, $syncToken, $syncLevel, $limit); |
||||
586 | |||||
587 | return $this->gDavBackend->Sync($calendarId, $syncToken, static::FILE_EXTENSION, $limit, ['types' => static::MESSAGE_CLASSES]); |
||||
588 | } |
||||
589 | } |
||||
590 |