aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php
blob: c32c86489da6c40b582a7c07e291c97e284d8c7a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
<?php

declare(strict_types=1);

namespace Sabre\CalDAV\Backend;

use Sabre\CalDAV;
use Sabre\VObject;

/**
 * Abstract Calendaring backend. Extend this class to create your own backends.
 *
 * Checkout the BackendInterface for all the methods that must be implemented.
 *
 * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
 * @author Evert Pot (http://evertpot.com/)
 * @license http://sabre.io/license/ Modified BSD License
 */
abstract class AbstractBackend implements BackendInterface
{
    /**
     * Updates properties for a calendar.
     *
     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
     * To do the actual updates, you must tell this object which properties
     * you're going to process with the handle() method.
     *
     * Calling the handle method is like telling the PropPatch object "I
     * promise I can handle updating this property".
     *
     * Read the PropPatch documentation for more info and examples.
     *
     * @param mixed $calendarId
     */
    public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch)
    {
    }

    /**
     * Returns a list of calendar objects.
     *
     * This method should work identical to getCalendarObject, but instead
     * return all the calendar objects in the list as an array.
     *
     * If the backend supports this, it may allow for some speed-ups.
     *
     * @param mixed $calendarId
     *
     * @return array
     */
    public function getMultipleCalendarObjects($calendarId, array $uris)
    {
        return array_map(function ($uri) use ($calendarId) {
            return $this->getCalendarObject($calendarId, $uri);
        }, $uris);
    }

    /**
     * Performs a calendar-query on the contents of this calendar.
     *
     * The calendar-query is defined in RFC4791 : CalDAV. Using the
     * calendar-query it is possible for a client to request a specific set of
     * object, based on contents of iCalendar properties, date-ranges and
     * iCalendar component types (VTODO, VEVENT).
     *
     * This method should just return a list of (relative) urls that match this
     * query.
     *
     * The list of filters are specified as an array. The exact array is
     * documented by \Sabre\CalDAV\CalendarQueryParser.
     *
     * Note that it is extremely likely that getCalendarObject for every path
     * returned from this method will be called almost immediately after. You
     * may want to anticipate this to speed up these requests.
     *
     * This method provides a default implementation, which parses *all* the
     * iCalendar objects in the specified calendar.
     *
     * This default may well be good enough for personal use, and calendars
     * that aren't very large. But if you anticipate high usage, big calendars
     * or high loads, you are strongly adviced to optimize certain paths.
     *
     * The best way to do so is override this method and to optimize
     * specifically for 'common filters'.
     *
     * Requests that are extremely common are:
     *   * requests for just VEVENTS
     *   * requests for just VTODO
     *   * requests with a time-range-filter on either VEVENT or VTODO.
     *
     * ..and combinations of these requests. It may not be worth it to try to
     * handle every possible situation and just rely on the (relatively
     * easy to use) CalendarQueryValidator to handle the rest.
     *
     * Note that especially time-range-filters may be difficult to parse. A
     * time-range filter specified on a VEVENT must for instance also handle
     * recurrence rules correctly.
     * A good example of how to interprete all these filters can also simply
     * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
     * as possible, so it gives you a good idea on what type of stuff you need
     * to think of.
     *
     * @param mixed $calendarId
     *
     * @return array
     */
    public function calendarQuery($calendarId, array $filters)
    {
        $result = [];
        $objects = $this->getCalendarObjects($calendarId);

        foreach ($objects as $object) {
            if ($this->validateFilterForObject($object, $filters)) {
                $result[] = $object['uri'];
            }
        }

        return $result;
    }

    /**
     * This method validates if a filter (as passed to calendarQuery) matches
     * the given object.
     *
     * @return bool
     */
    protected function validateFilterForObject(array $object, array $filters)
    {
        // Unfortunately, setting the 'calendardata' here is optional. If
        // it was excluded, we actually need another call to get this as
        // well.
        if (!isset($object['calendardata'])) {
            $object = $this->getCalendarObject($object['calendarid'], $object['uri']);
        }

        $vObject = VObject\Reader::read($object['calendardata']);

        $validator = new CalDAV\CalendarQueryValidator();
        $result = $validator->validate($vObject, $filters);

        // Destroy circular references so PHP will GC the object.
        $vObject->destroy();

        return $result;
    }

    /**
     * Searches through all of a users calendars and calendar objects to find
     * an object with a specific UID.
     *
     * This method should return the path to this object, relative to the
     * calendar home, so this path usually only contains two parts:
     *
     * calendarpath/objectpath.ics
     *
     * If the uid is not found, return null.
     *
     * This method should only consider * objects that the principal owns, so
     * any calendars owned by other principals that also appear in this
     * collection should be ignored.
     *
     * @param string $principalUri
     * @param string $uid
     *
     * @return string|null
     */
    public function getCalendarObjectByUID($principalUri, $uid)
    {
        // Note: this is a super slow naive implementation of this method. You
        // are highly recommended to optimize it, if your backend allows it.
        foreach ($this->getCalendarsForUser($principalUri) as $calendar) {
            // We must ignore calendars owned by other principals.
            if ($calendar['principaluri'] !== $principalUri) {
                continue;
            }

            // Ignore calendars that are shared.
            if (isset($calendar['{http://sabredav.org/ns}owner-principal']) && $calendar['{http://sabredav.org/ns}owner-principal'] !== $principalUri) {
                continue;
            }

            $results = $this->calendarQuery(
                $calendar['id'],
                [
                    'name' => 'VCALENDAR',
                    'prop-filters' => [],
                    'comp-filters' => [
                        [
                            'name' => 'VEVENT',
                            'is-not-defined' => false,
                            'time-range' => null,
                            'comp-filters' => [],
                            'prop-filters' => [
                                [
                                    'name' => 'UID',
                                    'is-not-defined' => false,
                                    'time-range' => null,
                                    'text-match' => [
                                        'value' => $uid,
                                        'negate-condition' => false,
                                        'collation' => 'i;octet',
                                    ],
                                    'param-filters' => [],
                                ],
                            ],
                        ],
                    ],
                ]
            );
            if ($results) {
                // We have a match
                return $calendar['uri'].'/'.$results[0];
            }
        }
    }
}