Completed
Branch develop (fa72bb)
by
unknown
26:08
created
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SimplePDO.php 1 patch
Indentation   +232 added lines, -232 removed lines patch added patch discarded remove patch
@@ -35,255 +35,255 @@
 block discarded – undo
35 35
  */
36 36
 class SimplePDO extends AbstractBackend
37 37
 {
38
-    /**
39
-     * pdo.
40
-     *
41
-     * @var \PDO
42
-     */
43
-    protected $pdo;
38
+	/**
39
+	 * pdo.
40
+	 *
41
+	 * @var \PDO
42
+	 */
43
+	protected $pdo;
44 44
 
45
-    /**
46
-     * Creates the backend.
47
-     */
48
-    public function __construct(\PDO $pdo)
49
-    {
50
-        $this->pdo = $pdo;
51
-    }
45
+	/**
46
+	 * Creates the backend.
47
+	 */
48
+	public function __construct(\PDO $pdo)
49
+	{
50
+		$this->pdo = $pdo;
51
+	}
52 52
 
53
-    /**
54
-     * Returns a list of calendars for a principal.
55
-     *
56
-     * Every project is an array with the following keys:
57
-     *  * id, a unique id that will be used by other functions to modify the
58
-     *    calendar. This can be the same as the uri or a database key.
59
-     *  * uri. This is just the 'base uri' or 'filename' of the calendar.
60
-     *  * principaluri. The owner of the calendar. Almost always the same as
61
-     *    principalUri passed to this method.
62
-     *
63
-     * Furthermore it can contain webdav properties in clark notation. A very
64
-     * common one is '{DAV:}displayname'.
65
-     *
66
-     * Many clients also require:
67
-     * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
68
-     * For this property, you can just return an instance of
69
-     * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet.
70
-     *
71
-     * If you return {http://sabredav.org/ns}read-only and set the value to 1,
72
-     * ACL will automatically be put in read-only mode.
73
-     *
74
-     * @param string $principalUri
75
-     *
76
-     * @return array
77
-     */
78
-    public function getCalendarsForUser($principalUri)
79
-    {
80
-        // Making fields a comma-delimited list
81
-        $stmt = $this->pdo->prepare('SELECT id, uri FROM simple_calendars WHERE principaluri = ? ORDER BY id ASC');
82
-        $stmt->execute([$principalUri]);
53
+	/**
54
+	 * Returns a list of calendars for a principal.
55
+	 *
56
+	 * Every project is an array with the following keys:
57
+	 *  * id, a unique id that will be used by other functions to modify the
58
+	 *    calendar. This can be the same as the uri or a database key.
59
+	 *  * uri. This is just the 'base uri' or 'filename' of the calendar.
60
+	 *  * principaluri. The owner of the calendar. Almost always the same as
61
+	 *    principalUri passed to this method.
62
+	 *
63
+	 * Furthermore it can contain webdav properties in clark notation. A very
64
+	 * common one is '{DAV:}displayname'.
65
+	 *
66
+	 * Many clients also require:
67
+	 * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
68
+	 * For this property, you can just return an instance of
69
+	 * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet.
70
+	 *
71
+	 * If you return {http://sabredav.org/ns}read-only and set the value to 1,
72
+	 * ACL will automatically be put in read-only mode.
73
+	 *
74
+	 * @param string $principalUri
75
+	 *
76
+	 * @return array
77
+	 */
78
+	public function getCalendarsForUser($principalUri)
79
+	{
80
+		// Making fields a comma-delimited list
81
+		$stmt = $this->pdo->prepare('SELECT id, uri FROM simple_calendars WHERE principaluri = ? ORDER BY id ASC');
82
+		$stmt->execute([$principalUri]);
83 83
 
84
-        $calendars = [];
85
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
86
-            $calendars[] = [
87
-                'id' => $row['id'],
88
-                'uri' => $row['uri'],
89
-                'principaluri' => $principalUri,
90
-            ];
91
-        }
84
+		$calendars = [];
85
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
86
+			$calendars[] = [
87
+				'id' => $row['id'],
88
+				'uri' => $row['uri'],
89
+				'principaluri' => $principalUri,
90
+			];
91
+		}
92 92
 
93
-        return $calendars;
94
-    }
93
+		return $calendars;
94
+	}
95 95
 
96
-    /**
97
-     * Creates a new calendar for a principal.
98
-     *
99
-     * If the creation was a success, an id must be returned that can be used
100
-     * to reference this calendar in other methods, such as updateCalendar.
101
-     *
102
-     * @param string $principalUri
103
-     * @param string $calendarUri
104
-     *
105
-     * @return string
106
-     */
107
-    public function createCalendar($principalUri, $calendarUri, array $properties)
108
-    {
109
-        $stmt = $this->pdo->prepare('INSERT INTO simple_calendars (principaluri, uri) VALUES (?, ?)');
110
-        $stmt->execute([$principalUri, $calendarUri]);
96
+	/**
97
+	 * Creates a new calendar for a principal.
98
+	 *
99
+	 * If the creation was a success, an id must be returned that can be used
100
+	 * to reference this calendar in other methods, such as updateCalendar.
101
+	 *
102
+	 * @param string $principalUri
103
+	 * @param string $calendarUri
104
+	 *
105
+	 * @return string
106
+	 */
107
+	public function createCalendar($principalUri, $calendarUri, array $properties)
108
+	{
109
+		$stmt = $this->pdo->prepare('INSERT INTO simple_calendars (principaluri, uri) VALUES (?, ?)');
110
+		$stmt->execute([$principalUri, $calendarUri]);
111 111
 
112
-        return $this->pdo->lastInsertId();
113
-    }
112
+		return $this->pdo->lastInsertId();
113
+	}
114 114
 
115
-    /**
116
-     * Delete a calendar and all it's objects.
117
-     *
118
-     * @param string $calendarId
119
-     */
120
-    public function deleteCalendar($calendarId)
121
-    {
122
-        $stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ?');
123
-        $stmt->execute([$calendarId]);
115
+	/**
116
+	 * Delete a calendar and all it's objects.
117
+	 *
118
+	 * @param string $calendarId
119
+	 */
120
+	public function deleteCalendar($calendarId)
121
+	{
122
+		$stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ?');
123
+		$stmt->execute([$calendarId]);
124 124
 
125
-        $stmt = $this->pdo->prepare('DELETE FROM simple_calendars WHERE id = ?');
126
-        $stmt->execute([$calendarId]);
127
-    }
125
+		$stmt = $this->pdo->prepare('DELETE FROM simple_calendars WHERE id = ?');
126
+		$stmt->execute([$calendarId]);
127
+	}
128 128
 
129
-    /**
130
-     * Returns all calendar objects within a calendar.
131
-     *
132
-     * Every item contains an array with the following keys:
133
-     *   * calendardata - The iCalendar-compatible calendar data
134
-     *   * uri - a unique key which will be used to construct the uri. This can
135
-     *     be any arbitrary string, but making sure it ends with '.ics' is a
136
-     *     good idea. This is only the basename, or filename, not the full
137
-     *     path.
138
-     *   * lastmodified - a timestamp of the last modification time
139
-     *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
140
-     *   '  "abcdef"')
141
-     *   * size - The size of the calendar objects, in bytes.
142
-     *   * component - optional, a string containing the type of object, such
143
-     *     as 'vevent' or 'vtodo'. If specified, this will be used to populate
144
-     *     the Content-Type header.
145
-     *
146
-     * Note that the etag is optional, but it's highly encouraged to return for
147
-     * speed reasons.
148
-     *
149
-     * The calendardata is also optional. If it's not returned
150
-     * 'getCalendarObject' will be called later, which *is* expected to return
151
-     * calendardata.
152
-     *
153
-     * If neither etag or size are specified, the calendardata will be
154
-     * used/fetched to determine these numbers. If both are specified the
155
-     * amount of times this is needed is reduced by a great degree.
156
-     *
157
-     * @param string $calendarId
158
-     *
159
-     * @return array
160
-     */
161
-    public function getCalendarObjects($calendarId)
162
-    {
163
-        $stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ?');
164
-        $stmt->execute([$calendarId]);
129
+	/**
130
+	 * Returns all calendar objects within a calendar.
131
+	 *
132
+	 * Every item contains an array with the following keys:
133
+	 *   * calendardata - The iCalendar-compatible calendar data
134
+	 *   * uri - a unique key which will be used to construct the uri. This can
135
+	 *     be any arbitrary string, but making sure it ends with '.ics' is a
136
+	 *     good idea. This is only the basename, or filename, not the full
137
+	 *     path.
138
+	 *   * lastmodified - a timestamp of the last modification time
139
+	 *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
140
+	 *   '  "abcdef"')
141
+	 *   * size - The size of the calendar objects, in bytes.
142
+	 *   * component - optional, a string containing the type of object, such
143
+	 *     as 'vevent' or 'vtodo'. If specified, this will be used to populate
144
+	 *     the Content-Type header.
145
+	 *
146
+	 * Note that the etag is optional, but it's highly encouraged to return for
147
+	 * speed reasons.
148
+	 *
149
+	 * The calendardata is also optional. If it's not returned
150
+	 * 'getCalendarObject' will be called later, which *is* expected to return
151
+	 * calendardata.
152
+	 *
153
+	 * If neither etag or size are specified, the calendardata will be
154
+	 * used/fetched to determine these numbers. If both are specified the
155
+	 * amount of times this is needed is reduced by a great degree.
156
+	 *
157
+	 * @param string $calendarId
158
+	 *
159
+	 * @return array
160
+	 */
161
+	public function getCalendarObjects($calendarId)
162
+	{
163
+		$stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ?');
164
+		$stmt->execute([$calendarId]);
165 165
 
166
-        $result = [];
167
-        foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
168
-            $result[] = [
169
-                'id' => $row['id'],
170
-                'uri' => $row['uri'],
171
-                'etag' => '"'.md5($row['calendardata']).'"',
172
-                'calendarid' => $calendarId,
173
-                'size' => strlen($row['calendardata']),
174
-                'calendardata' => $row['calendardata'],
175
-            ];
176
-        }
166
+		$result = [];
167
+		foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
168
+			$result[] = [
169
+				'id' => $row['id'],
170
+				'uri' => $row['uri'],
171
+				'etag' => '"'.md5($row['calendardata']).'"',
172
+				'calendarid' => $calendarId,
173
+				'size' => strlen($row['calendardata']),
174
+				'calendardata' => $row['calendardata'],
175
+			];
176
+		}
177 177
 
178
-        return $result;
179
-    }
178
+		return $result;
179
+	}
180 180
 
181
-    /**
182
-     * Returns information from a single calendar object, based on it's object
183
-     * uri.
184
-     *
185
-     * The object uri is only the basename, or filename and not a full path.
186
-     *
187
-     * The returned array must have the same keys as getCalendarObjects. The
188
-     * 'calendardata' object is required here though, while it's not required
189
-     * for getCalendarObjects.
190
-     *
191
-     * This method must return null if the object did not exist.
192
-     *
193
-     * @param string $calendarId
194
-     * @param string $objectUri
195
-     *
196
-     * @return array|null
197
-     */
198
-    public function getCalendarObject($calendarId, $objectUri)
199
-    {
200
-        $stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?');
201
-        $stmt->execute([$calendarId, $objectUri]);
202
-        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
181
+	/**
182
+	 * Returns information from a single calendar object, based on it's object
183
+	 * uri.
184
+	 *
185
+	 * The object uri is only the basename, or filename and not a full path.
186
+	 *
187
+	 * The returned array must have the same keys as getCalendarObjects. The
188
+	 * 'calendardata' object is required here though, while it's not required
189
+	 * for getCalendarObjects.
190
+	 *
191
+	 * This method must return null if the object did not exist.
192
+	 *
193
+	 * @param string $calendarId
194
+	 * @param string $objectUri
195
+	 *
196
+	 * @return array|null
197
+	 */
198
+	public function getCalendarObject($calendarId, $objectUri)
199
+	{
200
+		$stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?');
201
+		$stmt->execute([$calendarId, $objectUri]);
202
+		$row = $stmt->fetch(\PDO::FETCH_ASSOC);
203 203
 
204
-        if (!$row) {
205
-            return null;
206
-        }
204
+		if (!$row) {
205
+			return null;
206
+		}
207 207
 
208
-        return [
209
-            'id' => $row['id'],
210
-            'uri' => $row['uri'],
211
-            'etag' => '"'.md5($row['calendardata']).'"',
212
-            'calendarid' => $calendarId,
213
-            'size' => strlen($row['calendardata']),
214
-            'calendardata' => $row['calendardata'],
215
-         ];
216
-    }
208
+		return [
209
+			'id' => $row['id'],
210
+			'uri' => $row['uri'],
211
+			'etag' => '"'.md5($row['calendardata']).'"',
212
+			'calendarid' => $calendarId,
213
+			'size' => strlen($row['calendardata']),
214
+			'calendardata' => $row['calendardata'],
215
+		 ];
216
+	}
217 217
 
218
-    /**
219
-     * Creates a new calendar object.
220
-     *
221
-     * The object uri is only the basename, or filename and not a full path.
222
-     *
223
-     * It is possible return an etag from this function, which will be used in
224
-     * the response to this PUT request. Note that the ETag must be surrounded
225
-     * by double-quotes.
226
-     *
227
-     * However, you should only really return this ETag if you don't mangle the
228
-     * calendar-data. If the result of a subsequent GET to this object is not
229
-     * the exact same as this request body, you should omit the ETag.
230
-     *
231
-     * @param mixed  $calendarId
232
-     * @param string $objectUri
233
-     * @param string $calendarData
234
-     *
235
-     * @return string|null
236
-     */
237
-    public function createCalendarObject($calendarId, $objectUri, $calendarData)
238
-    {
239
-        $stmt = $this->pdo->prepare('INSERT INTO simple_calendarobjects (calendarid, uri, calendardata) VALUES (?,?,?)');
240
-        $stmt->execute([
241
-            $calendarId,
242
-            $objectUri,
243
-            $calendarData,
244
-        ]);
218
+	/**
219
+	 * Creates a new calendar object.
220
+	 *
221
+	 * The object uri is only the basename, or filename and not a full path.
222
+	 *
223
+	 * It is possible return an etag from this function, which will be used in
224
+	 * the response to this PUT request. Note that the ETag must be surrounded
225
+	 * by double-quotes.
226
+	 *
227
+	 * However, you should only really return this ETag if you don't mangle the
228
+	 * calendar-data. If the result of a subsequent GET to this object is not
229
+	 * the exact same as this request body, you should omit the ETag.
230
+	 *
231
+	 * @param mixed  $calendarId
232
+	 * @param string $objectUri
233
+	 * @param string $calendarData
234
+	 *
235
+	 * @return string|null
236
+	 */
237
+	public function createCalendarObject($calendarId, $objectUri, $calendarData)
238
+	{
239
+		$stmt = $this->pdo->prepare('INSERT INTO simple_calendarobjects (calendarid, uri, calendardata) VALUES (?,?,?)');
240
+		$stmt->execute([
241
+			$calendarId,
242
+			$objectUri,
243
+			$calendarData,
244
+		]);
245 245
 
246
-        return '"'.md5($calendarData).'"';
247
-    }
246
+		return '"'.md5($calendarData).'"';
247
+	}
248 248
 
249
-    /**
250
-     * Updates an existing calendarobject, based on it's uri.
251
-     *
252
-     * The object uri is only the basename, or filename and not a full path.
253
-     *
254
-     * It is possible return an etag from this function, which will be used in
255
-     * the response to this PUT request. Note that the ETag must be surrounded
256
-     * by double-quotes.
257
-     *
258
-     * However, you should only really return this ETag if you don't mangle the
259
-     * calendar-data. If the result of a subsequent GET to this object is not
260
-     * the exact same as this request body, you should omit the ETag.
261
-     *
262
-     * @param mixed  $calendarId
263
-     * @param string $objectUri
264
-     * @param string $calendarData
265
-     *
266
-     * @return string|null
267
-     */
268
-    public function updateCalendarObject($calendarId, $objectUri, $calendarData)
269
-    {
270
-        $stmt = $this->pdo->prepare('UPDATE simple_calendarobjects SET calendardata = ? WHERE calendarid = ? AND uri = ?');
271
-        $stmt->execute([$calendarData, $calendarId, $objectUri]);
249
+	/**
250
+	 * Updates an existing calendarobject, based on it's uri.
251
+	 *
252
+	 * The object uri is only the basename, or filename and not a full path.
253
+	 *
254
+	 * It is possible return an etag from this function, which will be used in
255
+	 * the response to this PUT request. Note that the ETag must be surrounded
256
+	 * by double-quotes.
257
+	 *
258
+	 * However, you should only really return this ETag if you don't mangle the
259
+	 * calendar-data. If the result of a subsequent GET to this object is not
260
+	 * the exact same as this request body, you should omit the ETag.
261
+	 *
262
+	 * @param mixed  $calendarId
263
+	 * @param string $objectUri
264
+	 * @param string $calendarData
265
+	 *
266
+	 * @return string|null
267
+	 */
268
+	public function updateCalendarObject($calendarId, $objectUri, $calendarData)
269
+	{
270
+		$stmt = $this->pdo->prepare('UPDATE simple_calendarobjects SET calendardata = ? WHERE calendarid = ? AND uri = ?');
271
+		$stmt->execute([$calendarData, $calendarId, $objectUri]);
272 272
 
273
-        return '"'.md5($calendarData).'"';
274
-    }
273
+		return '"'.md5($calendarData).'"';
274
+	}
275 275
 
276
-    /**
277
-     * Deletes an existing calendar object.
278
-     *
279
-     * The object uri is only the basename, or filename and not a full path.
280
-     *
281
-     * @param string $calendarId
282
-     * @param string $objectUri
283
-     */
284
-    public function deleteCalendarObject($calendarId, $objectUri)
285
-    {
286
-        $stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?');
287
-        $stmt->execute([$calendarId, $objectUri]);
288
-    }
276
+	/**
277
+	 * Deletes an existing calendar object.
278
+	 *
279
+	 * The object uri is only the basename, or filename and not a full path.
280
+	 *
281
+	 * @param string $calendarId
282
+	 * @param string $objectUri
283
+	 */
284
+	public function deleteCalendarObject($calendarId, $objectUri)
285
+	{
286
+		$stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?');
287
+		$stmt->execute([$calendarId, $objectUri]);
288
+	}
289 289
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SubscriptionSupport.php 1 patch
Indentation   +68 added lines, -68 removed lines patch added patch discarded remove patch
@@ -15,75 +15,75 @@
 block discarded – undo
15 15
  */
16 16
 interface SubscriptionSupport extends BackendInterface
17 17
 {
18
-    /**
19
-     * Returns a list of subscriptions for a principal.
20
-     *
21
-     * Every subscription is an array with the following keys:
22
-     *  * id, a unique id that will be used by other functions to modify the
23
-     *    subscription. This can be the same as the uri or a database key.
24
-     *  * uri. This is just the 'base uri' or 'filename' of the subscription.
25
-     *  * principaluri. The owner of the subscription. Almost always the same as
26
-     *    principalUri passed to this method.
27
-     *
28
-     * Furthermore, all the subscription info must be returned too:
29
-     *
30
-     * 1. {DAV:}displayname
31
-     * 2. {http://apple.com/ns/ical/}refreshrate
32
-     * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
33
-     *    should not be stripped).
34
-     * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
35
-     *    should not be stripped).
36
-     * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
37
-     *    attachments should not be stripped).
38
-     * 6. {http://calendarserver.org/ns/}source (Must be a
39
-     *     Sabre\DAV\Property\Href).
40
-     * 7. {http://apple.com/ns/ical/}calendar-color
41
-     * 8. {http://apple.com/ns/ical/}calendar-order
42
-     * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
43
-     *    (should just be an instance of
44
-     *    Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
45
-     *    default components).
46
-     *
47
-     * @param string $principalUri
48
-     *
49
-     * @return array
50
-     */
51
-    public function getSubscriptionsForUser($principalUri);
18
+	/**
19
+	 * Returns a list of subscriptions for a principal.
20
+	 *
21
+	 * Every subscription is an array with the following keys:
22
+	 *  * id, a unique id that will be used by other functions to modify the
23
+	 *    subscription. This can be the same as the uri or a database key.
24
+	 *  * uri. This is just the 'base uri' or 'filename' of the subscription.
25
+	 *  * principaluri. The owner of the subscription. Almost always the same as
26
+	 *    principalUri passed to this method.
27
+	 *
28
+	 * Furthermore, all the subscription info must be returned too:
29
+	 *
30
+	 * 1. {DAV:}displayname
31
+	 * 2. {http://apple.com/ns/ical/}refreshrate
32
+	 * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
33
+	 *    should not be stripped).
34
+	 * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
35
+	 *    should not be stripped).
36
+	 * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
37
+	 *    attachments should not be stripped).
38
+	 * 6. {http://calendarserver.org/ns/}source (Must be a
39
+	 *     Sabre\DAV\Property\Href).
40
+	 * 7. {http://apple.com/ns/ical/}calendar-color
41
+	 * 8. {http://apple.com/ns/ical/}calendar-order
42
+	 * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
43
+	 *    (should just be an instance of
44
+	 *    Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
45
+	 *    default components).
46
+	 *
47
+	 * @param string $principalUri
48
+	 *
49
+	 * @return array
50
+	 */
51
+	public function getSubscriptionsForUser($principalUri);
52 52
 
53
-    /**
54
-     * Creates a new subscription for a principal.
55
-     *
56
-     * If the creation was a success, an id must be returned that can be used to reference
57
-     * this subscription in other methods, such as updateSubscription.
58
-     *
59
-     * @param string $principalUri
60
-     * @param string $uri
61
-     *
62
-     * @return mixed
63
-     */
64
-    public function createSubscription($principalUri, $uri, array $properties);
53
+	/**
54
+	 * Creates a new subscription for a principal.
55
+	 *
56
+	 * If the creation was a success, an id must be returned that can be used to reference
57
+	 * this subscription in other methods, such as updateSubscription.
58
+	 *
59
+	 * @param string $principalUri
60
+	 * @param string $uri
61
+	 *
62
+	 * @return mixed
63
+	 */
64
+	public function createSubscription($principalUri, $uri, array $properties);
65 65
 
66
-    /**
67
-     * Updates a subscription.
68
-     *
69
-     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
70
-     * To do the actual updates, you must tell this object which properties
71
-     * you're going to process with the handle() method.
72
-     *
73
-     * Calling the handle method is like telling the PropPatch object "I
74
-     * promise I can handle updating this property".
75
-     *
76
-     * Read the PropPatch documentation for more info and examples.
77
-     *
78
-     * @param mixed                $subscriptionId
79
-     * @param \Sabre\DAV\PropPatch $propPatch
80
-     */
81
-    public function updateSubscription($subscriptionId, DAV\PropPatch $propPatch);
66
+	/**
67
+	 * Updates a subscription.
68
+	 *
69
+	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
70
+	 * To do the actual updates, you must tell this object which properties
71
+	 * you're going to process with the handle() method.
72
+	 *
73
+	 * Calling the handle method is like telling the PropPatch object "I
74
+	 * promise I can handle updating this property".
75
+	 *
76
+	 * Read the PropPatch documentation for more info and examples.
77
+	 *
78
+	 * @param mixed                $subscriptionId
79
+	 * @param \Sabre\DAV\PropPatch $propPatch
80
+	 */
81
+	public function updateSubscription($subscriptionId, DAV\PropPatch $propPatch);
82 82
 
83
-    /**
84
-     * Deletes a subscription.
85
-     *
86
-     * @param mixed $subscriptionId
87
-     */
88
-    public function deleteSubscription($subscriptionId);
83
+	/**
84
+	 * Deletes a subscription.
85
+	 *
86
+	 * @param mixed $subscriptionId
87
+	 */
88
+	public function deleteSubscription($subscriptionId);
89 89
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SharingSupport.php 1 patch
Indentation   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -24,37 +24,37 @@
 block discarded – undo
24 24
  */
25 25
 interface SharingSupport extends BackendInterface
26 26
 {
27
-    /**
28
-     * Updates the list of shares.
29
-     *
30
-     * @param mixed                           $calendarId
31
-     * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees
32
-     */
33
-    public function updateInvites($calendarId, array $sharees);
27
+	/**
28
+	 * Updates the list of shares.
29
+	 *
30
+	 * @param mixed                           $calendarId
31
+	 * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees
32
+	 */
33
+	public function updateInvites($calendarId, array $sharees);
34 34
 
35
-    /**
36
-     * Returns the list of people whom this calendar is shared with.
37
-     *
38
-     * Every item in the returned list must be a Sharee object with at
39
-     * least the following properties set:
40
-     *   $href
41
-     *   $shareAccess
42
-     *   $inviteStatus
43
-     *
44
-     * and optionally:
45
-     *   $properties
46
-     *
47
-     * @param mixed $calendarId
48
-     *
49
-     * @return \Sabre\DAV\Xml\Element\Sharee[]
50
-     */
51
-    public function getInvites($calendarId);
35
+	/**
36
+	 * Returns the list of people whom this calendar is shared with.
37
+	 *
38
+	 * Every item in the returned list must be a Sharee object with at
39
+	 * least the following properties set:
40
+	 *   $href
41
+	 *   $shareAccess
42
+	 *   $inviteStatus
43
+	 *
44
+	 * and optionally:
45
+	 *   $properties
46
+	 *
47
+	 * @param mixed $calendarId
48
+	 *
49
+	 * @return \Sabre\DAV\Xml\Element\Sharee[]
50
+	 */
51
+	public function getInvites($calendarId);
52 52
 
53
-    /**
54
-     * Publishes a calendar.
55
-     *
56
-     * @param mixed $calendarId
57
-     * @param bool  $value
58
-     */
59
-    public function setPublishStatus($calendarId, $value);
53
+	/**
54
+	 * Publishes a calendar.
55
+	 *
56
+	 * @param mixed $calendarId
57
+	 * @param bool  $value
58
+	 */
59
+	public function setPublishStatus($calendarId, $value);
60 60
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/PDO.php 3 patches
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
         $supportedProperties = array_keys($this->propertyMap);
301 301
         $supportedProperties[] = '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp';
302 302
 
303
-        $propPatch->handle($supportedProperties, function ($mutations) use ($calendarId, $instanceId) {
303
+        $propPatch->handle($supportedProperties, function($mutations) use ($calendarId, $instanceId) {
304 304
             $newValues = [];
305 305
             foreach ($mutations as $propertyName => $propertyValue) {
306 306
                 switch ($propertyName) {
@@ -1185,7 +1185,7 @@  discard block
 block discarded – undo
1185 1185
         $supportedProperties = array_keys($this->subscriptionPropertyMap);
1186 1186
         $supportedProperties[] = '{http://calendarserver.org/ns/}source';
1187 1187
 
1188
-        $propPatch->handle($supportedProperties, function ($mutations) use ($subscriptionId) {
1188
+        $propPatch->handle($supportedProperties, function($mutations) use ($subscriptionId) {
1189 1189
             $newValues = [];
1190 1190
 
1191 1191
             foreach ($mutations as $propertyName => $propertyValue) {
Please login to merge, or discard this patch.
Upper-Lower-Casing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -163,11 +163,11 @@  discard block
 block discarded – undo
163 163
         // Making fields a comma-delimited list
164 164
         $fields = implode(', ', $fields);
165 165
         $stmt = $this->pdo->prepare(<<<SQL
166
-SELECT {$this->calendarInstancesTableName}.id as id, $fields FROM {$this->calendarInstancesTableName}
166
+select {$this->calendarInstancesTableName}.id as id, $fields FROM {$this->calendarInstancesTableName}
167 167
     LEFT JOIN {$this->calendarTableName} ON
168 168
         {$this->calendarInstancesTableName}.calendarid = {$this->calendarTableName}.id
169 169
 WHERE principaluri = ? ORDER BY calendarorder ASC
170
-SQL
170
+sql
171 171
         );
172 172
         $stmt->execute([$principalUri]);
173 173
 
@@ -874,7 +874,7 @@  discard block
 block discarded – undo
874 874
     calendarobjects.uid = ?
875 875
     AND
876 876
     calendar_instances.access = 1
877
-SQL;
877
+sql;
878 878
 
879 879
         $stmt = $this->pdo->prepare($query);
880 880
         $stmt->execute([$principalUri, $uid]);
@@ -1452,7 +1452,7 @@  discard block
 block discarded – undo
1452 1452
 FROM {$this->calendarInstancesTableName}
1453 1453
 WHERE
1454 1454
     calendarid = ?
1455
-SQL;
1455
+sql;
1456 1456
 
1457 1457
         $stmt = $this->pdo->prepare($query);
1458 1458
         $stmt->execute([$calendarId]);
Please login to merge, or discard this patch.
Indentation   +1398 added lines, -1398 removed lines patch added patch discarded remove patch
@@ -23,844 +23,844 @@  discard block
 block discarded – undo
23 23
  */
24 24
 class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, SchedulingSupport, SharingSupport
25 25
 {
26
-    /**
27
-     * We need to specify a max date, because we need to stop *somewhere*.
28
-     *
29
-     * On 32 bit system the maximum for a signed integer is 2147483647, so
30
-     * MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
31
-     * in 2038-01-19 to avoid problems when the date is converted
32
-     * to a unix timestamp.
33
-     */
34
-    const MAX_DATE = '2038-01-01';
35
-
36
-    /**
37
-     * pdo.
38
-     *
39
-     * @var \PDO
40
-     */
41
-    protected $pdo;
42
-
43
-    /**
44
-     * The table name that will be used for calendars.
45
-     *
46
-     * @var string
47
-     */
48
-    public $calendarTableName = 'calendars';
49
-
50
-    /**
51
-     * The table name that will be used for calendars instances.
52
-     *
53
-     * A single calendar can have multiple instances, if the calendar is
54
-     * shared.
55
-     *
56
-     * @var string
57
-     */
58
-    public $calendarInstancesTableName = 'calendarinstances';
59
-
60
-    /**
61
-     * The table name that will be used for calendar objects.
62
-     *
63
-     * @var string
64
-     */
65
-    public $calendarObjectTableName = 'calendarobjects';
66
-
67
-    /**
68
-     * The table name that will be used for tracking changes in calendars.
69
-     *
70
-     * @var string
71
-     */
72
-    public $calendarChangesTableName = 'calendarchanges';
73
-
74
-    /**
75
-     * The table name that will be used inbox items.
76
-     *
77
-     * @var string
78
-     */
79
-    public $schedulingObjectTableName = 'schedulingobjects';
80
-
81
-    /**
82
-     * The table name that will be used for calendar subscriptions.
83
-     *
84
-     * @var string
85
-     */
86
-    public $calendarSubscriptionsTableName = 'calendarsubscriptions';
87
-
88
-    /**
89
-     * List of CalDAV properties, and how they map to database fieldnames
90
-     * Add your own properties by simply adding on to this array.
91
-     *
92
-     * Note that only string-based properties are supported here.
93
-     *
94
-     * @var array
95
-     */
96
-    public $propertyMap = [
97
-        '{DAV:}displayname' => 'displayname',
98
-        '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
99
-        '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
100
-        '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
101
-        '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
102
-    ];
103
-
104
-    /**
105
-     * List of subscription properties, and how they map to database fieldnames.
106
-     *
107
-     * @var array
108
-     */
109
-    public $subscriptionPropertyMap = [
110
-        '{DAV:}displayname' => 'displayname',
111
-        '{http://apple.com/ns/ical/}refreshrate' => 'refreshrate',
112
-        '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
113
-        '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
114
-        '{http://calendarserver.org/ns/}subscribed-strip-todos' => 'striptodos',
115
-        '{http://calendarserver.org/ns/}subscribed-strip-alarms' => 'stripalarms',
116
-        '{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments',
117
-    ];
118
-
119
-    /**
120
-     * Creates the backend.
121
-     */
122
-    public function __construct(\PDO $pdo)
123
-    {
124
-        $this->pdo = $pdo;
125
-    }
126
-
127
-    /**
128
-     * Returns a list of calendars for a principal.
129
-     *
130
-     * Every project is an array with the following keys:
131
-     *  * id, a unique id that will be used by other functions to modify the
132
-     *    calendar. This can be the same as the uri or a database key.
133
-     *  * uri. This is just the 'base uri' or 'filename' of the calendar.
134
-     *  * principaluri. The owner of the calendar. Almost always the same as
135
-     *    principalUri passed to this method.
136
-     *
137
-     * Furthermore it can contain webdav properties in clark notation. A very
138
-     * common one is '{DAV:}displayname'.
139
-     *
140
-     * Many clients also require:
141
-     * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
142
-     * For this property, you can just return an instance of
143
-     * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet.
144
-     *
145
-     * If you return {http://sabredav.org/ns}read-only and set the value to 1,
146
-     * ACL will automatically be put in read-only mode.
147
-     *
148
-     * @param string $principalUri
149
-     *
150
-     * @return array
151
-     */
152
-    public function getCalendarsForUser($principalUri)
153
-    {
154
-        $fields = array_values($this->propertyMap);
155
-        $fields[] = 'calendarid';
156
-        $fields[] = 'uri';
157
-        $fields[] = 'synctoken';
158
-        $fields[] = 'components';
159
-        $fields[] = 'principaluri';
160
-        $fields[] = 'transparent';
161
-        $fields[] = 'access';
162
-
163
-        // Making fields a comma-delimited list
164
-        $fields = implode(', ', $fields);
165
-        $stmt = $this->pdo->prepare(<<<SQL
26
+	/**
27
+	 * We need to specify a max date, because we need to stop *somewhere*.
28
+	 *
29
+	 * On 32 bit system the maximum for a signed integer is 2147483647, so
30
+	 * MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
31
+	 * in 2038-01-19 to avoid problems when the date is converted
32
+	 * to a unix timestamp.
33
+	 */
34
+	const MAX_DATE = '2038-01-01';
35
+
36
+	/**
37
+	 * pdo.
38
+	 *
39
+	 * @var \PDO
40
+	 */
41
+	protected $pdo;
42
+
43
+	/**
44
+	 * The table name that will be used for calendars.
45
+	 *
46
+	 * @var string
47
+	 */
48
+	public $calendarTableName = 'calendars';
49
+
50
+	/**
51
+	 * The table name that will be used for calendars instances.
52
+	 *
53
+	 * A single calendar can have multiple instances, if the calendar is
54
+	 * shared.
55
+	 *
56
+	 * @var string
57
+	 */
58
+	public $calendarInstancesTableName = 'calendarinstances';
59
+
60
+	/**
61
+	 * The table name that will be used for calendar objects.
62
+	 *
63
+	 * @var string
64
+	 */
65
+	public $calendarObjectTableName = 'calendarobjects';
66
+
67
+	/**
68
+	 * The table name that will be used for tracking changes in calendars.
69
+	 *
70
+	 * @var string
71
+	 */
72
+	public $calendarChangesTableName = 'calendarchanges';
73
+
74
+	/**
75
+	 * The table name that will be used inbox items.
76
+	 *
77
+	 * @var string
78
+	 */
79
+	public $schedulingObjectTableName = 'schedulingobjects';
80
+
81
+	/**
82
+	 * The table name that will be used for calendar subscriptions.
83
+	 *
84
+	 * @var string
85
+	 */
86
+	public $calendarSubscriptionsTableName = 'calendarsubscriptions';
87
+
88
+	/**
89
+	 * List of CalDAV properties, and how they map to database fieldnames
90
+	 * Add your own properties by simply adding on to this array.
91
+	 *
92
+	 * Note that only string-based properties are supported here.
93
+	 *
94
+	 * @var array
95
+	 */
96
+	public $propertyMap = [
97
+		'{DAV:}displayname' => 'displayname',
98
+		'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
99
+		'{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
100
+		'{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
101
+		'{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
102
+	];
103
+
104
+	/**
105
+	 * List of subscription properties, and how they map to database fieldnames.
106
+	 *
107
+	 * @var array
108
+	 */
109
+	public $subscriptionPropertyMap = [
110
+		'{DAV:}displayname' => 'displayname',
111
+		'{http://apple.com/ns/ical/}refreshrate' => 'refreshrate',
112
+		'{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
113
+		'{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
114
+		'{http://calendarserver.org/ns/}subscribed-strip-todos' => 'striptodos',
115
+		'{http://calendarserver.org/ns/}subscribed-strip-alarms' => 'stripalarms',
116
+		'{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments',
117
+	];
118
+
119
+	/**
120
+	 * Creates the backend.
121
+	 */
122
+	public function __construct(\PDO $pdo)
123
+	{
124
+		$this->pdo = $pdo;
125
+	}
126
+
127
+	/**
128
+	 * Returns a list of calendars for a principal.
129
+	 *
130
+	 * Every project is an array with the following keys:
131
+	 *  * id, a unique id that will be used by other functions to modify the
132
+	 *    calendar. This can be the same as the uri or a database key.
133
+	 *  * uri. This is just the 'base uri' or 'filename' of the calendar.
134
+	 *  * principaluri. The owner of the calendar. Almost always the same as
135
+	 *    principalUri passed to this method.
136
+	 *
137
+	 * Furthermore it can contain webdav properties in clark notation. A very
138
+	 * common one is '{DAV:}displayname'.
139
+	 *
140
+	 * Many clients also require:
141
+	 * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
142
+	 * For this property, you can just return an instance of
143
+	 * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet.
144
+	 *
145
+	 * If you return {http://sabredav.org/ns}read-only and set the value to 1,
146
+	 * ACL will automatically be put in read-only mode.
147
+	 *
148
+	 * @param string $principalUri
149
+	 *
150
+	 * @return array
151
+	 */
152
+	public function getCalendarsForUser($principalUri)
153
+	{
154
+		$fields = array_values($this->propertyMap);
155
+		$fields[] = 'calendarid';
156
+		$fields[] = 'uri';
157
+		$fields[] = 'synctoken';
158
+		$fields[] = 'components';
159
+		$fields[] = 'principaluri';
160
+		$fields[] = 'transparent';
161
+		$fields[] = 'access';
162
+
163
+		// Making fields a comma-delimited list
164
+		$fields = implode(', ', $fields);
165
+		$stmt = $this->pdo->prepare(<<<SQL
166 166
 SELECT {$this->calendarInstancesTableName}.id as id, $fields FROM {$this->calendarInstancesTableName}
167 167
     LEFT JOIN {$this->calendarTableName} ON
168 168
         {$this->calendarInstancesTableName}.calendarid = {$this->calendarTableName}.id
169 169
 WHERE principaluri = ? ORDER BY calendarorder ASC
170 170
 SQL
171
-        );
172
-        $stmt->execute([$principalUri]);
173
-
174
-        $calendars = [];
175
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
176
-            $components = [];
177
-            if ($row['components']) {
178
-                $components = explode(',', $row['components']);
179
-            }
180
-
181
-            $calendar = [
182
-                'id' => [(int) $row['calendarid'], (int) $row['id']],
183
-                'uri' => $row['uri'],
184
-                'principaluri' => $row['principaluri'],
185
-                '{'.CalDAV\Plugin::NS_CALENDARSERVER.'}getctag' => 'http://sabre.io/ns/sync/'.($row['synctoken'] ? $row['synctoken'] : '0'),
186
-                '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0',
187
-                '{'.CalDAV\Plugin::NS_CALDAV.'}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet($components),
188
-                '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'),
189
-                'share-resource-uri' => '/ns/share/'.$row['calendarid'],
190
-            ];
191
-
192
-            $calendar['share-access'] = (int) $row['access'];
193
-            // 1 = owner, 2 = readonly, 3 = readwrite
194
-            if ($row['access'] > 1) {
195
-                // We need to find more information about the original owner.
196
-                //$stmt2 = $this->pdo->prepare('SELECT principaluri FROM ' . $this->calendarInstancesTableName . ' WHERE access = 1 AND id = ?');
197
-                //$stmt2->execute([$row['id']]);
198
-
199
-                // read-only is for backwards compatbility. Might go away in
200
-                // the future.
201
-                $calendar['read-only'] = \Sabre\DAV\Sharing\Plugin::ACCESS_READ === (int) $row['access'];
202
-            }
203
-
204
-            foreach ($this->propertyMap as $xmlName => $dbName) {
205
-                $calendar[$xmlName] = $row[$dbName];
206
-            }
207
-
208
-            $calendars[] = $calendar;
209
-        }
210
-
211
-        return $calendars;
212
-    }
213
-
214
-    /**
215
-     * Creates a new calendar for a principal.
216
-     *
217
-     * If the creation was a success, an id must be returned that can be used
218
-     * to reference this calendar in other methods, such as updateCalendar.
219
-     *
220
-     * @param string $principalUri
221
-     * @param string $calendarUri
222
-     *
223
-     * @return string
224
-     */
225
-    public function createCalendar($principalUri, $calendarUri, array $properties)
226
-    {
227
-        $fieldNames = [
228
-            'principaluri',
229
-            'uri',
230
-            'transparent',
231
-            'calendarid',
232
-        ];
233
-        $values = [
234
-            ':principaluri' => $principalUri,
235
-            ':uri' => $calendarUri,
236
-            ':transparent' => 0,
237
-        ];
238
-
239
-        $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
240
-        if (!isset($properties[$sccs])) {
241
-            // Default value
242
-            $components = 'VEVENT,VTODO';
243
-        } else {
244
-            if (!($properties[$sccs] instanceof CalDAV\Xml\Property\SupportedCalendarComponentSet)) {
245
-                throw new DAV\Exception('The '.$sccs.' property must be of type: \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet');
246
-            }
247
-            $components = implode(',', $properties[$sccs]->getValue());
248
-        }
249
-        $transp = '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp';
250
-        if (isset($properties[$transp])) {
251
-            $values[':transparent'] = 'transparent' === $properties[$transp]->getValue() ? 1 : 0;
252
-        }
253
-        $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarTableName.' (synctoken, components) VALUES (1, ?)');
254
-        $stmt->execute([$components]);
255
-
256
-        $calendarId = $this->pdo->lastInsertId(
257
-            $this->calendarTableName.'_id_seq'
258
-        );
259
-
260
-        $values[':calendarid'] = $calendarId;
261
-
262
-        foreach ($this->propertyMap as $xmlName => $dbName) {
263
-            if (isset($properties[$xmlName])) {
264
-                $values[':'.$dbName] = $properties[$xmlName];
265
-                $fieldNames[] = $dbName;
266
-            }
267
-        }
268
-
269
-        $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarInstancesTableName.' ('.implode(', ', $fieldNames).') VALUES ('.implode(', ', array_keys($values)).')');
270
-
271
-        $stmt->execute($values);
272
-
273
-        return [
274
-            $calendarId,
275
-            $this->pdo->lastInsertId($this->calendarInstancesTableName.'_id_seq'),
276
-        ];
277
-    }
278
-
279
-    /**
280
-     * Updates properties for a calendar.
281
-     *
282
-     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
283
-     * To do the actual updates, you must tell this object which properties
284
-     * you're going to process with the handle() method.
285
-     *
286
-     * Calling the handle method is like telling the PropPatch object "I
287
-     * promise I can handle updating this property".
288
-     *
289
-     * Read the PropPatch documentation for more info and examples.
290
-     *
291
-     * @param mixed $calendarId
292
-     */
293
-    public function updateCalendar($calendarId, PropPatch $propPatch)
294
-    {
295
-        if (!is_array($calendarId)) {
296
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
297
-        }
298
-        list($calendarId, $instanceId) = $calendarId;
299
-
300
-        $supportedProperties = array_keys($this->propertyMap);
301
-        $supportedProperties[] = '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp';
302
-
303
-        $propPatch->handle($supportedProperties, function ($mutations) use ($calendarId, $instanceId) {
304
-            $newValues = [];
305
-            foreach ($mutations as $propertyName => $propertyValue) {
306
-                switch ($propertyName) {
307
-                    case '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp':
308
-                        $fieldName = 'transparent';
309
-                        $newValues[$fieldName] = 'transparent' === $propertyValue->getValue();
310
-                        break;
311
-                    default:
312
-                        $fieldName = $this->propertyMap[$propertyName];
313
-                        $newValues[$fieldName] = $propertyValue;
314
-                        break;
315
-                }
316
-            }
317
-            $valuesSql = [];
318
-            foreach ($newValues as $fieldName => $value) {
319
-                $valuesSql[] = $fieldName.' = ?';
320
-            }
321
-
322
-            $stmt = $this->pdo->prepare('UPDATE '.$this->calendarInstancesTableName.' SET '.implode(', ', $valuesSql).' WHERE id = ?');
323
-            $newValues['id'] = $instanceId;
324
-            $stmt->execute(array_values($newValues));
325
-
326
-            $this->addChange($calendarId, '', 2);
327
-
328
-            return true;
329
-        });
330
-    }
331
-
332
-    /**
333
-     * Delete a calendar and all it's objects.
334
-     *
335
-     * @param mixed $calendarId
336
-     */
337
-    public function deleteCalendar($calendarId)
338
-    {
339
-        if (!is_array($calendarId)) {
340
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
341
-        }
342
-        list($calendarId, $instanceId) = $calendarId;
343
-
344
-        $stmt = $this->pdo->prepare('SELECT access FROM '.$this->calendarInstancesTableName.' where id = ?');
345
-        $stmt->execute([$instanceId]);
346
-        $access = (int) $stmt->fetchColumn();
347
-
348
-        if (\Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER === $access) {
349
-            /**
350
-             * If the user is the owner of the calendar, we delete all data and all
351
-             * instances.
352
-             **/
353
-            $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
354
-            $stmt->execute([$calendarId]);
355
-
356
-            $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarChangesTableName.' WHERE calendarid = ?');
357
-            $stmt->execute([$calendarId]);
358
-
359
-            $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarInstancesTableName.' WHERE calendarid = ?');
360
-            $stmt->execute([$calendarId]);
361
-
362
-            $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?');
363
-            $stmt->execute([$calendarId]);
364
-        } else {
365
-            /**
366
-             * If it was an instance of a shared calendar, we only delete that
367
-             * instance.
368
-             */
369
-            $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarInstancesTableName.' WHERE id = ?');
370
-            $stmt->execute([$instanceId]);
371
-        }
372
-    }
373
-
374
-    /**
375
-     * Returns all calendar objects within a calendar.
376
-     *
377
-     * Every item contains an array with the following keys:
378
-     *   * calendardata - The iCalendar-compatible calendar data
379
-     *   * uri - a unique key which will be used to construct the uri. This can
380
-     *     be any arbitrary string, but making sure it ends with '.ics' is a
381
-     *     good idea. This is only the basename, or filename, not the full
382
-     *     path.
383
-     *   * lastmodified - a timestamp of the last modification time
384
-     *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
385
-     *   '  "abcdef"')
386
-     *   * size - The size of the calendar objects, in bytes.
387
-     *   * component - optional, a string containing the type of object, such
388
-     *     as 'vevent' or 'vtodo'. If specified, this will be used to populate
389
-     *     the Content-Type header.
390
-     *
391
-     * Note that the etag is optional, but it's highly encouraged to return for
392
-     * speed reasons.
393
-     *
394
-     * The calendardata is also optional. If it's not returned
395
-     * 'getCalendarObject' will be called later, which *is* expected to return
396
-     * calendardata.
397
-     *
398
-     * If neither etag or size are specified, the calendardata will be
399
-     * used/fetched to determine these numbers. If both are specified the
400
-     * amount of times this is needed is reduced by a great degree.
401
-     *
402
-     * @param mixed $calendarId
403
-     *
404
-     * @return array
405
-     */
406
-    public function getCalendarObjects($calendarId)
407
-    {
408
-        if (!is_array($calendarId)) {
409
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
410
-        }
411
-        list($calendarId, $instanceId) = $calendarId;
412
-
413
-        $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
414
-        $stmt->execute([$calendarId]);
415
-
416
-        $result = [];
417
-        foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
418
-            $result[] = [
419
-                'id' => $row['id'],
420
-                'uri' => $row['uri'],
421
-                'lastmodified' => (int) $row['lastmodified'],
422
-                'etag' => '"'.$row['etag'].'"',
423
-                'size' => (int) $row['size'],
424
-                'component' => strtolower($row['componenttype']),
425
-            ];
426
-        }
427
-
428
-        return $result;
429
-    }
430
-
431
-    /**
432
-     * Returns information from a single calendar object, based on it's object
433
-     * uri.
434
-     *
435
-     * The object uri is only the basename, or filename and not a full path.
436
-     *
437
-     * The returned array must have the same keys as getCalendarObjects. The
438
-     * 'calendardata' object is required here though, while it's not required
439
-     * for getCalendarObjects.
440
-     *
441
-     * This method must return null if the object did not exist.
442
-     *
443
-     * @param mixed  $calendarId
444
-     * @param string $objectUri
445
-     *
446
-     * @return array|null
447
-     */
448
-    public function getCalendarObject($calendarId, $objectUri)
449
-    {
450
-        if (!is_array($calendarId)) {
451
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
452
-        }
453
-        list($calendarId, $instanceId) = $calendarId;
454
-
455
-        $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
456
-        $stmt->execute([$calendarId, $objectUri]);
457
-        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
458
-
459
-        if (!$row) {
460
-            return null;
461
-        }
462
-
463
-        return [
464
-            'id' => $row['id'],
465
-            'uri' => $row['uri'],
466
-            'lastmodified' => (int) $row['lastmodified'],
467
-            'etag' => '"'.$row['etag'].'"',
468
-            'size' => (int) $row['size'],
469
-            'calendardata' => $row['calendardata'],
470
-            'component' => strtolower($row['componenttype']),
471
-         ];
472
-    }
473
-
474
-    /**
475
-     * Returns a list of calendar objects.
476
-     *
477
-     * This method should work identical to getCalendarObject, but instead
478
-     * return all the calendar objects in the list as an array.
479
-     *
480
-     * If the backend supports this, it may allow for some speed-ups.
481
-     *
482
-     * @param mixed $calendarId
483
-     *
484
-     * @return array
485
-     */
486
-    public function getMultipleCalendarObjects($calendarId, array $uris)
487
-    {
488
-        if (!is_array($calendarId)) {
489
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
490
-        }
491
-        list($calendarId, $instanceId) = $calendarId;
492
-
493
-        $result = [];
494
-        foreach (array_chunk($uris, 900) as $chunk) {
495
-            $query = 'SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri IN (';
496
-            // Inserting a whole bunch of question marks
497
-            $query .= implode(',', array_fill(0, count($chunk), '?'));
498
-            $query .= ')';
499
-
500
-            $stmt = $this->pdo->prepare($query);
501
-            $stmt->execute(array_merge([$calendarId], $chunk));
502
-
503
-            while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
504
-                $result[] = [
505
-                    'id' => $row['id'],
506
-                    'uri' => $row['uri'],
507
-                    'lastmodified' => (int) $row['lastmodified'],
508
-                    'etag' => '"'.$row['etag'].'"',
509
-                    'size' => (int) $row['size'],
510
-                    'calendardata' => $row['calendardata'],
511
-                    'component' => strtolower($row['componenttype']),
512
-                ];
513
-            }
514
-        }
515
-
516
-        return $result;
517
-    }
518
-
519
-    /**
520
-     * Creates a new calendar object.
521
-     *
522
-     * The object uri is only the basename, or filename and not a full path.
523
-     *
524
-     * It is possible return an etag from this function, which will be used in
525
-     * the response to this PUT request. Note that the ETag must be surrounded
526
-     * by double-quotes.
527
-     *
528
-     * However, you should only really return this ETag if you don't mangle the
529
-     * calendar-data. If the result of a subsequent GET to this object is not
530
-     * the exact same as this request body, you should omit the ETag.
531
-     *
532
-     * @param mixed  $calendarId
533
-     * @param string $objectUri
534
-     * @param string $calendarData
535
-     *
536
-     * @return string|null
537
-     */
538
-    public function createCalendarObject($calendarId, $objectUri, $calendarData)
539
-    {
540
-        if (!is_array($calendarId)) {
541
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
542
-        }
543
-        list($calendarId, $instanceId) = $calendarId;
544
-
545
-        $extraData = $this->getDenormalizedData($calendarData);
546
-
547
-        $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)');
548
-        $stmt->execute([
549
-            $calendarId,
550
-            $objectUri,
551
-            $calendarData,
552
-            time(),
553
-            $extraData['etag'],
554
-            $extraData['size'],
555
-            $extraData['componentType'],
556
-            $extraData['firstOccurence'],
557
-            $extraData['lastOccurence'],
558
-            $extraData['uid'],
559
-        ]);
560
-        $this->addChange($calendarId, $objectUri, 1);
561
-
562
-        return '"'.$extraData['etag'].'"';
563
-    }
564
-
565
-    /**
566
-     * Updates an existing calendarobject, based on it's uri.
567
-     *
568
-     * The object uri is only the basename, or filename and not a full path.
569
-     *
570
-     * It is possible return an etag from this function, which will be used in
571
-     * the response to this PUT request. Note that the ETag must be surrounded
572
-     * by double-quotes.
573
-     *
574
-     * However, you should only really return this ETag if you don't mangle the
575
-     * calendar-data. If the result of a subsequent GET to this object is not
576
-     * the exact same as this request body, you should omit the ETag.
577
-     *
578
-     * @param mixed  $calendarId
579
-     * @param string $objectUri
580
-     * @param string $calendarData
581
-     *
582
-     * @return string|null
583
-     */
584
-    public function updateCalendarObject($calendarId, $objectUri, $calendarData)
585
-    {
586
-        if (!is_array($calendarId)) {
587
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
588
-        }
589
-        list($calendarId, $instanceId) = $calendarId;
590
-
591
-        $extraData = $this->getDenormalizedData($calendarData);
592
-
593
-        $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?');
594
-        $stmt->execute([$calendarData, time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'], $extraData['uid'], $calendarId, $objectUri]);
595
-
596
-        $this->addChange($calendarId, $objectUri, 2);
597
-
598
-        return '"'.$extraData['etag'].'"';
599
-    }
600
-
601
-    /**
602
-     * Parses some information from calendar objects, used for optimized
603
-     * calendar-queries.
604
-     *
605
-     * Returns an array with the following keys:
606
-     *   * etag - An md5 checksum of the object without the quotes.
607
-     *   * size - Size of the object in bytes
608
-     *   * componentType - VEVENT, VTODO or VJOURNAL
609
-     *   * firstOccurence
610
-     *   * lastOccurence
611
-     *   * uid - value of the UID property
612
-     *
613
-     * @param string $calendarData
614
-     *
615
-     * @return array
616
-     */
617
-    protected function getDenormalizedData($calendarData)
618
-    {
619
-        $vObject = VObject\Reader::read($calendarData);
620
-        $componentType = null;
621
-        $component = null;
622
-        $firstOccurence = null;
623
-        $lastOccurence = null;
624
-        $uid = null;
625
-        foreach ($vObject->getComponents() as $component) {
626
-            if ('VTIMEZONE' !== $component->name) {
627
-                $componentType = $component->name;
628
-                $uid = (string) $component->UID;
629
-                break;
630
-            }
631
-        }
632
-        if (!$componentType) {
633
-            throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
634
-        }
635
-        if ('VEVENT' === $componentType) {
636
-            $firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
637
-            // Finding the last occurence is a bit harder
638
-            if (!isset($component->RRULE)) {
639
-                if (isset($component->DTEND)) {
640
-                    $lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
641
-                } elseif (isset($component->DURATION)) {
642
-                    $endDate = clone $component->DTSTART->getDateTime();
643
-                    $endDate = $endDate->add(VObject\DateTimeParser::parse($component->DURATION->getValue()));
644
-                    $lastOccurence = $endDate->getTimeStamp();
645
-                } elseif (!$component->DTSTART->hasTime()) {
646
-                    $endDate = clone $component->DTSTART->getDateTime();
647
-                    $endDate = $endDate->modify('+1 day');
648
-                    $lastOccurence = $endDate->getTimeStamp();
649
-                } else {
650
-                    $lastOccurence = $firstOccurence;
651
-                }
652
-            } else {
653
-                $it = new VObject\Recur\EventIterator($vObject, (string) $component->UID);
654
-                $maxDate = new \DateTime(self::MAX_DATE);
655
-                if ($it->isInfinite()) {
656
-                    $lastOccurence = $maxDate->getTimeStamp();
657
-                } else {
658
-                    $end = $it->getDtEnd();
659
-                    while ($it->valid() && $end < $maxDate) {
660
-                        $end = $it->getDtEnd();
661
-                        $it->next();
662
-                    }
663
-                    $lastOccurence = $end->getTimeStamp();
664
-                }
665
-            }
666
-
667
-            // Ensure Occurence values are positive
668
-            if ($firstOccurence < 0) {
669
-                $firstOccurence = 0;
670
-            }
671
-            if ($lastOccurence < 0) {
672
-                $lastOccurence = 0;
673
-            }
674
-        }
675
-
676
-        // Destroy circular references to PHP will GC the object.
677
-        $vObject->destroy();
678
-
679
-        return [
680
-            'etag' => md5($calendarData),
681
-            'size' => strlen($calendarData),
682
-            'componentType' => $componentType,
683
-            'firstOccurence' => $firstOccurence,
684
-            'lastOccurence' => $lastOccurence,
685
-            'uid' => $uid,
686
-        ];
687
-    }
688
-
689
-    /**
690
-     * Deletes an existing calendar object.
691
-     *
692
-     * The object uri is only the basename, or filename and not a full path.
693
-     *
694
-     * @param mixed  $calendarId
695
-     * @param string $objectUri
696
-     */
697
-    public function deleteCalendarObject($calendarId, $objectUri)
698
-    {
699
-        if (!is_array($calendarId)) {
700
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
701
-        }
702
-        list($calendarId, $instanceId) = $calendarId;
703
-
704
-        $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
705
-        $stmt->execute([$calendarId, $objectUri]);
706
-
707
-        $this->addChange($calendarId, $objectUri, 3);
708
-    }
709
-
710
-    /**
711
-     * Performs a calendar-query on the contents of this calendar.
712
-     *
713
-     * The calendar-query is defined in RFC4791 : CalDAV. Using the
714
-     * calendar-query it is possible for a client to request a specific set of
715
-     * object, based on contents of iCalendar properties, date-ranges and
716
-     * iCalendar component types (VTODO, VEVENT).
717
-     *
718
-     * This method should just return a list of (relative) urls that match this
719
-     * query.
720
-     *
721
-     * The list of filters are specified as an array. The exact array is
722
-     * documented by \Sabre\CalDAV\CalendarQueryParser.
723
-     *
724
-     * Note that it is extremely likely that getCalendarObject for every path
725
-     * returned from this method will be called almost immediately after. You
726
-     * may want to anticipate this to speed up these requests.
727
-     *
728
-     * This method provides a default implementation, which parses *all* the
729
-     * iCalendar objects in the specified calendar.
730
-     *
731
-     * This default may well be good enough for personal use, and calendars
732
-     * that aren't very large. But if you anticipate high usage, big calendars
733
-     * or high loads, you are strongly adviced to optimize certain paths.
734
-     *
735
-     * The best way to do so is override this method and to optimize
736
-     * specifically for 'common filters'.
737
-     *
738
-     * Requests that are extremely common are:
739
-     *   * requests for just VEVENTS
740
-     *   * requests for just VTODO
741
-     *   * requests with a time-range-filter on a VEVENT.
742
-     *
743
-     * ..and combinations of these requests. It may not be worth it to try to
744
-     * handle every possible situation and just rely on the (relatively
745
-     * easy to use) CalendarQueryValidator to handle the rest.
746
-     *
747
-     * Note that especially time-range-filters may be difficult to parse. A
748
-     * time-range filter specified on a VEVENT must for instance also handle
749
-     * recurrence rules correctly.
750
-     * A good example of how to interpret all these filters can also simply
751
-     * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
752
-     * as possible, so it gives you a good idea on what type of stuff you need
753
-     * to think of.
754
-     *
755
-     * This specific implementation (for the PDO) backend optimizes filters on
756
-     * specific components, and VEVENT time-ranges.
757
-     *
758
-     * @param mixed $calendarId
759
-     *
760
-     * @return array
761
-     */
762
-    public function calendarQuery($calendarId, array $filters)
763
-    {
764
-        if (!is_array($calendarId)) {
765
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
766
-        }
767
-        list($calendarId, $instanceId) = $calendarId;
768
-
769
-        $componentType = null;
770
-        $requirePostFilter = true;
771
-        $timeRange = null;
772
-
773
-        // if no filters were specified, we don't need to filter after a query
774
-        if (!$filters['prop-filters'] && !$filters['comp-filters']) {
775
-            $requirePostFilter = false;
776
-        }
777
-
778
-        // Figuring out if there's a component filter
779
-        if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) {
780
-            $componentType = $filters['comp-filters'][0]['name'];
781
-
782
-            // Checking if we need post-filters
783
-            $has_time_range = array_key_exists('time-range', $filters['comp-filters'][0]) && $filters['comp-filters'][0]['time-range'];
784
-            if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$has_time_range && !$filters['comp-filters'][0]['prop-filters']) {
785
-                $requirePostFilter = false;
786
-            }
787
-            // There was a time-range filter
788
-            if ('VEVENT' == $componentType && $has_time_range) {
789
-                $timeRange = $filters['comp-filters'][0]['time-range'];
790
-
791
-                // If start time OR the end time is not specified, we can do a
792
-                // 100% accurate mysql query.
793
-                if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && $timeRange) {
794
-                    if ((array_key_exists('start', $timeRange) && !$timeRange['start']) || (array_key_exists('end', $timeRange) && !$timeRange['end'])) {
795
-                        $requirePostFilter = false;
796
-                    }
797
-                }
798
-            }
799
-        }
800
-
801
-        if ($requirePostFilter) {
802
-            $query = 'SELECT uri, calendardata FROM '.$this->calendarObjectTableName.' WHERE calendarid = :calendarid';
803
-        } else {
804
-            $query = 'SELECT uri FROM '.$this->calendarObjectTableName.' WHERE calendarid = :calendarid';
805
-        }
806
-
807
-        $values = [
808
-            'calendarid' => $calendarId,
809
-        ];
810
-
811
-        if ($componentType) {
812
-            $query .= ' AND componenttype = :componenttype';
813
-            $values['componenttype'] = $componentType;
814
-        }
815
-
816
-        if ($timeRange && array_key_exists('start', $timeRange) && $timeRange['start']) {
817
-            $query .= ' AND lastoccurence > :startdate';
818
-            $values['startdate'] = $timeRange['start']->getTimeStamp();
819
-        }
820
-        if ($timeRange && array_key_exists('end', $timeRange) && $timeRange['end']) {
821
-            $query .= ' AND firstoccurence < :enddate';
822
-            $values['enddate'] = $timeRange['end']->getTimeStamp();
823
-        }
824
-
825
-        $stmt = $this->pdo->prepare($query);
826
-        $stmt->execute($values);
827
-
828
-        $result = [];
829
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
830
-            if ($requirePostFilter) {
831
-                if (!$this->validateFilterForObject($row, $filters)) {
832
-                    continue;
833
-                }
834
-            }
835
-            $result[] = $row['uri'];
836
-        }
837
-
838
-        return $result;
839
-    }
840
-
841
-    /**
842
-     * Searches through all of a users calendars and calendar objects to find
843
-     * an object with a specific UID.
844
-     *
845
-     * This method should return the path to this object, relative to the
846
-     * calendar home, so this path usually only contains two parts:
847
-     *
848
-     * calendarpath/objectpath.ics
849
-     *
850
-     * If the uid is not found, return null.
851
-     *
852
-     * This method should only consider * objects that the principal owns, so
853
-     * any calendars owned by other principals that also appear in this
854
-     * collection should be ignored.
855
-     *
856
-     * @param string $principalUri
857
-     * @param string $uid
858
-     *
859
-     * @return string|null
860
-     */
861
-    public function getCalendarObjectByUID($principalUri, $uid)
862
-    {
863
-        $query = <<<SQL
171
+		);
172
+		$stmt->execute([$principalUri]);
173
+
174
+		$calendars = [];
175
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
176
+			$components = [];
177
+			if ($row['components']) {
178
+				$components = explode(',', $row['components']);
179
+			}
180
+
181
+			$calendar = [
182
+				'id' => [(int) $row['calendarid'], (int) $row['id']],
183
+				'uri' => $row['uri'],
184
+				'principaluri' => $row['principaluri'],
185
+				'{'.CalDAV\Plugin::NS_CALENDARSERVER.'}getctag' => 'http://sabre.io/ns/sync/'.($row['synctoken'] ? $row['synctoken'] : '0'),
186
+				'{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0',
187
+				'{'.CalDAV\Plugin::NS_CALDAV.'}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet($components),
188
+				'{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'),
189
+				'share-resource-uri' => '/ns/share/'.$row['calendarid'],
190
+			];
191
+
192
+			$calendar['share-access'] = (int) $row['access'];
193
+			// 1 = owner, 2 = readonly, 3 = readwrite
194
+			if ($row['access'] > 1) {
195
+				// We need to find more information about the original owner.
196
+				//$stmt2 = $this->pdo->prepare('SELECT principaluri FROM ' . $this->calendarInstancesTableName . ' WHERE access = 1 AND id = ?');
197
+				//$stmt2->execute([$row['id']]);
198
+
199
+				// read-only is for backwards compatbility. Might go away in
200
+				// the future.
201
+				$calendar['read-only'] = \Sabre\DAV\Sharing\Plugin::ACCESS_READ === (int) $row['access'];
202
+			}
203
+
204
+			foreach ($this->propertyMap as $xmlName => $dbName) {
205
+				$calendar[$xmlName] = $row[$dbName];
206
+			}
207
+
208
+			$calendars[] = $calendar;
209
+		}
210
+
211
+		return $calendars;
212
+	}
213
+
214
+	/**
215
+	 * Creates a new calendar for a principal.
216
+	 *
217
+	 * If the creation was a success, an id must be returned that can be used
218
+	 * to reference this calendar in other methods, such as updateCalendar.
219
+	 *
220
+	 * @param string $principalUri
221
+	 * @param string $calendarUri
222
+	 *
223
+	 * @return string
224
+	 */
225
+	public function createCalendar($principalUri, $calendarUri, array $properties)
226
+	{
227
+		$fieldNames = [
228
+			'principaluri',
229
+			'uri',
230
+			'transparent',
231
+			'calendarid',
232
+		];
233
+		$values = [
234
+			':principaluri' => $principalUri,
235
+			':uri' => $calendarUri,
236
+			':transparent' => 0,
237
+		];
238
+
239
+		$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
240
+		if (!isset($properties[$sccs])) {
241
+			// Default value
242
+			$components = 'VEVENT,VTODO';
243
+		} else {
244
+			if (!($properties[$sccs] instanceof CalDAV\Xml\Property\SupportedCalendarComponentSet)) {
245
+				throw new DAV\Exception('The '.$sccs.' property must be of type: \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet');
246
+			}
247
+			$components = implode(',', $properties[$sccs]->getValue());
248
+		}
249
+		$transp = '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp';
250
+		if (isset($properties[$transp])) {
251
+			$values[':transparent'] = 'transparent' === $properties[$transp]->getValue() ? 1 : 0;
252
+		}
253
+		$stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarTableName.' (synctoken, components) VALUES (1, ?)');
254
+		$stmt->execute([$components]);
255
+
256
+		$calendarId = $this->pdo->lastInsertId(
257
+			$this->calendarTableName.'_id_seq'
258
+		);
259
+
260
+		$values[':calendarid'] = $calendarId;
261
+
262
+		foreach ($this->propertyMap as $xmlName => $dbName) {
263
+			if (isset($properties[$xmlName])) {
264
+				$values[':'.$dbName] = $properties[$xmlName];
265
+				$fieldNames[] = $dbName;
266
+			}
267
+		}
268
+
269
+		$stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarInstancesTableName.' ('.implode(', ', $fieldNames).') VALUES ('.implode(', ', array_keys($values)).')');
270
+
271
+		$stmt->execute($values);
272
+
273
+		return [
274
+			$calendarId,
275
+			$this->pdo->lastInsertId($this->calendarInstancesTableName.'_id_seq'),
276
+		];
277
+	}
278
+
279
+	/**
280
+	 * Updates properties for a calendar.
281
+	 *
282
+	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
283
+	 * To do the actual updates, you must tell this object which properties
284
+	 * you're going to process with the handle() method.
285
+	 *
286
+	 * Calling the handle method is like telling the PropPatch object "I
287
+	 * promise I can handle updating this property".
288
+	 *
289
+	 * Read the PropPatch documentation for more info and examples.
290
+	 *
291
+	 * @param mixed $calendarId
292
+	 */
293
+	public function updateCalendar($calendarId, PropPatch $propPatch)
294
+	{
295
+		if (!is_array($calendarId)) {
296
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
297
+		}
298
+		list($calendarId, $instanceId) = $calendarId;
299
+
300
+		$supportedProperties = array_keys($this->propertyMap);
301
+		$supportedProperties[] = '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp';
302
+
303
+		$propPatch->handle($supportedProperties, function ($mutations) use ($calendarId, $instanceId) {
304
+			$newValues = [];
305
+			foreach ($mutations as $propertyName => $propertyValue) {
306
+				switch ($propertyName) {
307
+					case '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp':
308
+						$fieldName = 'transparent';
309
+						$newValues[$fieldName] = 'transparent' === $propertyValue->getValue();
310
+						break;
311
+					default:
312
+						$fieldName = $this->propertyMap[$propertyName];
313
+						$newValues[$fieldName] = $propertyValue;
314
+						break;
315
+				}
316
+			}
317
+			$valuesSql = [];
318
+			foreach ($newValues as $fieldName => $value) {
319
+				$valuesSql[] = $fieldName.' = ?';
320
+			}
321
+
322
+			$stmt = $this->pdo->prepare('UPDATE '.$this->calendarInstancesTableName.' SET '.implode(', ', $valuesSql).' WHERE id = ?');
323
+			$newValues['id'] = $instanceId;
324
+			$stmt->execute(array_values($newValues));
325
+
326
+			$this->addChange($calendarId, '', 2);
327
+
328
+			return true;
329
+		});
330
+	}
331
+
332
+	/**
333
+	 * Delete a calendar and all it's objects.
334
+	 *
335
+	 * @param mixed $calendarId
336
+	 */
337
+	public function deleteCalendar($calendarId)
338
+	{
339
+		if (!is_array($calendarId)) {
340
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
341
+		}
342
+		list($calendarId, $instanceId) = $calendarId;
343
+
344
+		$stmt = $this->pdo->prepare('SELECT access FROM '.$this->calendarInstancesTableName.' where id = ?');
345
+		$stmt->execute([$instanceId]);
346
+		$access = (int) $stmt->fetchColumn();
347
+
348
+		if (\Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER === $access) {
349
+			/**
350
+			 * If the user is the owner of the calendar, we delete all data and all
351
+			 * instances.
352
+			 **/
353
+			$stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
354
+			$stmt->execute([$calendarId]);
355
+
356
+			$stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarChangesTableName.' WHERE calendarid = ?');
357
+			$stmt->execute([$calendarId]);
358
+
359
+			$stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarInstancesTableName.' WHERE calendarid = ?');
360
+			$stmt->execute([$calendarId]);
361
+
362
+			$stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarTableName.' WHERE id = ?');
363
+			$stmt->execute([$calendarId]);
364
+		} else {
365
+			/**
366
+			 * If it was an instance of a shared calendar, we only delete that
367
+			 * instance.
368
+			 */
369
+			$stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarInstancesTableName.' WHERE id = ?');
370
+			$stmt->execute([$instanceId]);
371
+		}
372
+	}
373
+
374
+	/**
375
+	 * Returns all calendar objects within a calendar.
376
+	 *
377
+	 * Every item contains an array with the following keys:
378
+	 *   * calendardata - The iCalendar-compatible calendar data
379
+	 *   * uri - a unique key which will be used to construct the uri. This can
380
+	 *     be any arbitrary string, but making sure it ends with '.ics' is a
381
+	 *     good idea. This is only the basename, or filename, not the full
382
+	 *     path.
383
+	 *   * lastmodified - a timestamp of the last modification time
384
+	 *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
385
+	 *   '  "abcdef"')
386
+	 *   * size - The size of the calendar objects, in bytes.
387
+	 *   * component - optional, a string containing the type of object, such
388
+	 *     as 'vevent' or 'vtodo'. If specified, this will be used to populate
389
+	 *     the Content-Type header.
390
+	 *
391
+	 * Note that the etag is optional, but it's highly encouraged to return for
392
+	 * speed reasons.
393
+	 *
394
+	 * The calendardata is also optional. If it's not returned
395
+	 * 'getCalendarObject' will be called later, which *is* expected to return
396
+	 * calendardata.
397
+	 *
398
+	 * If neither etag or size are specified, the calendardata will be
399
+	 * used/fetched to determine these numbers. If both are specified the
400
+	 * amount of times this is needed is reduced by a great degree.
401
+	 *
402
+	 * @param mixed $calendarId
403
+	 *
404
+	 * @return array
405
+	 */
406
+	public function getCalendarObjects($calendarId)
407
+	{
408
+		if (!is_array($calendarId)) {
409
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
410
+		}
411
+		list($calendarId, $instanceId) = $calendarId;
412
+
413
+		$stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?');
414
+		$stmt->execute([$calendarId]);
415
+
416
+		$result = [];
417
+		foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
418
+			$result[] = [
419
+				'id' => $row['id'],
420
+				'uri' => $row['uri'],
421
+				'lastmodified' => (int) $row['lastmodified'],
422
+				'etag' => '"'.$row['etag'].'"',
423
+				'size' => (int) $row['size'],
424
+				'component' => strtolower($row['componenttype']),
425
+			];
426
+		}
427
+
428
+		return $result;
429
+	}
430
+
431
+	/**
432
+	 * Returns information from a single calendar object, based on it's object
433
+	 * uri.
434
+	 *
435
+	 * The object uri is only the basename, or filename and not a full path.
436
+	 *
437
+	 * The returned array must have the same keys as getCalendarObjects. The
438
+	 * 'calendardata' object is required here though, while it's not required
439
+	 * for getCalendarObjects.
440
+	 *
441
+	 * This method must return null if the object did not exist.
442
+	 *
443
+	 * @param mixed  $calendarId
444
+	 * @param string $objectUri
445
+	 *
446
+	 * @return array|null
447
+	 */
448
+	public function getCalendarObject($calendarId, $objectUri)
449
+	{
450
+		if (!is_array($calendarId)) {
451
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
452
+		}
453
+		list($calendarId, $instanceId) = $calendarId;
454
+
455
+		$stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
456
+		$stmt->execute([$calendarId, $objectUri]);
457
+		$row = $stmt->fetch(\PDO::FETCH_ASSOC);
458
+
459
+		if (!$row) {
460
+			return null;
461
+		}
462
+
463
+		return [
464
+			'id' => $row['id'],
465
+			'uri' => $row['uri'],
466
+			'lastmodified' => (int) $row['lastmodified'],
467
+			'etag' => '"'.$row['etag'].'"',
468
+			'size' => (int) $row['size'],
469
+			'calendardata' => $row['calendardata'],
470
+			'component' => strtolower($row['componenttype']),
471
+		 ];
472
+	}
473
+
474
+	/**
475
+	 * Returns a list of calendar objects.
476
+	 *
477
+	 * This method should work identical to getCalendarObject, but instead
478
+	 * return all the calendar objects in the list as an array.
479
+	 *
480
+	 * If the backend supports this, it may allow for some speed-ups.
481
+	 *
482
+	 * @param mixed $calendarId
483
+	 *
484
+	 * @return array
485
+	 */
486
+	public function getMultipleCalendarObjects($calendarId, array $uris)
487
+	{
488
+		if (!is_array($calendarId)) {
489
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
490
+		}
491
+		list($calendarId, $instanceId) = $calendarId;
492
+
493
+		$result = [];
494
+		foreach (array_chunk($uris, 900) as $chunk) {
495
+			$query = 'SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri IN (';
496
+			// Inserting a whole bunch of question marks
497
+			$query .= implode(',', array_fill(0, count($chunk), '?'));
498
+			$query .= ')';
499
+
500
+			$stmt = $this->pdo->prepare($query);
501
+			$stmt->execute(array_merge([$calendarId], $chunk));
502
+
503
+			while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
504
+				$result[] = [
505
+					'id' => $row['id'],
506
+					'uri' => $row['uri'],
507
+					'lastmodified' => (int) $row['lastmodified'],
508
+					'etag' => '"'.$row['etag'].'"',
509
+					'size' => (int) $row['size'],
510
+					'calendardata' => $row['calendardata'],
511
+					'component' => strtolower($row['componenttype']),
512
+				];
513
+			}
514
+		}
515
+
516
+		return $result;
517
+	}
518
+
519
+	/**
520
+	 * Creates a new calendar object.
521
+	 *
522
+	 * The object uri is only the basename, or filename and not a full path.
523
+	 *
524
+	 * It is possible return an etag from this function, which will be used in
525
+	 * the response to this PUT request. Note that the ETag must be surrounded
526
+	 * by double-quotes.
527
+	 *
528
+	 * However, you should only really return this ETag if you don't mangle the
529
+	 * calendar-data. If the result of a subsequent GET to this object is not
530
+	 * the exact same as this request body, you should omit the ETag.
531
+	 *
532
+	 * @param mixed  $calendarId
533
+	 * @param string $objectUri
534
+	 * @param string $calendarData
535
+	 *
536
+	 * @return string|null
537
+	 */
538
+	public function createCalendarObject($calendarId, $objectUri, $calendarData)
539
+	{
540
+		if (!is_array($calendarId)) {
541
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
542
+		}
543
+		list($calendarId, $instanceId) = $calendarId;
544
+
545
+		$extraData = $this->getDenormalizedData($calendarData);
546
+
547
+		$stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)');
548
+		$stmt->execute([
549
+			$calendarId,
550
+			$objectUri,
551
+			$calendarData,
552
+			time(),
553
+			$extraData['etag'],
554
+			$extraData['size'],
555
+			$extraData['componentType'],
556
+			$extraData['firstOccurence'],
557
+			$extraData['lastOccurence'],
558
+			$extraData['uid'],
559
+		]);
560
+		$this->addChange($calendarId, $objectUri, 1);
561
+
562
+		return '"'.$extraData['etag'].'"';
563
+	}
564
+
565
+	/**
566
+	 * Updates an existing calendarobject, based on it's uri.
567
+	 *
568
+	 * The object uri is only the basename, or filename and not a full path.
569
+	 *
570
+	 * It is possible return an etag from this function, which will be used in
571
+	 * the response to this PUT request. Note that the ETag must be surrounded
572
+	 * by double-quotes.
573
+	 *
574
+	 * However, you should only really return this ETag if you don't mangle the
575
+	 * calendar-data. If the result of a subsequent GET to this object is not
576
+	 * the exact same as this request body, you should omit the ETag.
577
+	 *
578
+	 * @param mixed  $calendarId
579
+	 * @param string $objectUri
580
+	 * @param string $calendarData
581
+	 *
582
+	 * @return string|null
583
+	 */
584
+	public function updateCalendarObject($calendarId, $objectUri, $calendarData)
585
+	{
586
+		if (!is_array($calendarId)) {
587
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
588
+		}
589
+		list($calendarId, $instanceId) = $calendarId;
590
+
591
+		$extraData = $this->getDenormalizedData($calendarData);
592
+
593
+		$stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?');
594
+		$stmt->execute([$calendarData, time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'], $extraData['uid'], $calendarId, $objectUri]);
595
+
596
+		$this->addChange($calendarId, $objectUri, 2);
597
+
598
+		return '"'.$extraData['etag'].'"';
599
+	}
600
+
601
+	/**
602
+	 * Parses some information from calendar objects, used for optimized
603
+	 * calendar-queries.
604
+	 *
605
+	 * Returns an array with the following keys:
606
+	 *   * etag - An md5 checksum of the object without the quotes.
607
+	 *   * size - Size of the object in bytes
608
+	 *   * componentType - VEVENT, VTODO or VJOURNAL
609
+	 *   * firstOccurence
610
+	 *   * lastOccurence
611
+	 *   * uid - value of the UID property
612
+	 *
613
+	 * @param string $calendarData
614
+	 *
615
+	 * @return array
616
+	 */
617
+	protected function getDenormalizedData($calendarData)
618
+	{
619
+		$vObject = VObject\Reader::read($calendarData);
620
+		$componentType = null;
621
+		$component = null;
622
+		$firstOccurence = null;
623
+		$lastOccurence = null;
624
+		$uid = null;
625
+		foreach ($vObject->getComponents() as $component) {
626
+			if ('VTIMEZONE' !== $component->name) {
627
+				$componentType = $component->name;
628
+				$uid = (string) $component->UID;
629
+				break;
630
+			}
631
+		}
632
+		if (!$componentType) {
633
+			throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
634
+		}
635
+		if ('VEVENT' === $componentType) {
636
+			$firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
637
+			// Finding the last occurence is a bit harder
638
+			if (!isset($component->RRULE)) {
639
+				if (isset($component->DTEND)) {
640
+					$lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
641
+				} elseif (isset($component->DURATION)) {
642
+					$endDate = clone $component->DTSTART->getDateTime();
643
+					$endDate = $endDate->add(VObject\DateTimeParser::parse($component->DURATION->getValue()));
644
+					$lastOccurence = $endDate->getTimeStamp();
645
+				} elseif (!$component->DTSTART->hasTime()) {
646
+					$endDate = clone $component->DTSTART->getDateTime();
647
+					$endDate = $endDate->modify('+1 day');
648
+					$lastOccurence = $endDate->getTimeStamp();
649
+				} else {
650
+					$lastOccurence = $firstOccurence;
651
+				}
652
+			} else {
653
+				$it = new VObject\Recur\EventIterator($vObject, (string) $component->UID);
654
+				$maxDate = new \DateTime(self::MAX_DATE);
655
+				if ($it->isInfinite()) {
656
+					$lastOccurence = $maxDate->getTimeStamp();
657
+				} else {
658
+					$end = $it->getDtEnd();
659
+					while ($it->valid() && $end < $maxDate) {
660
+						$end = $it->getDtEnd();
661
+						$it->next();
662
+					}
663
+					$lastOccurence = $end->getTimeStamp();
664
+				}
665
+			}
666
+
667
+			// Ensure Occurence values are positive
668
+			if ($firstOccurence < 0) {
669
+				$firstOccurence = 0;
670
+			}
671
+			if ($lastOccurence < 0) {
672
+				$lastOccurence = 0;
673
+			}
674
+		}
675
+
676
+		// Destroy circular references to PHP will GC the object.
677
+		$vObject->destroy();
678
+
679
+		return [
680
+			'etag' => md5($calendarData),
681
+			'size' => strlen($calendarData),
682
+			'componentType' => $componentType,
683
+			'firstOccurence' => $firstOccurence,
684
+			'lastOccurence' => $lastOccurence,
685
+			'uid' => $uid,
686
+		];
687
+	}
688
+
689
+	/**
690
+	 * Deletes an existing calendar object.
691
+	 *
692
+	 * The object uri is only the basename, or filename and not a full path.
693
+	 *
694
+	 * @param mixed  $calendarId
695
+	 * @param string $objectUri
696
+	 */
697
+	public function deleteCalendarObject($calendarId, $objectUri)
698
+	{
699
+		if (!is_array($calendarId)) {
700
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
701
+		}
702
+		list($calendarId, $instanceId) = $calendarId;
703
+
704
+		$stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarObjectTableName.' WHERE calendarid = ? AND uri = ?');
705
+		$stmt->execute([$calendarId, $objectUri]);
706
+
707
+		$this->addChange($calendarId, $objectUri, 3);
708
+	}
709
+
710
+	/**
711
+	 * Performs a calendar-query on the contents of this calendar.
712
+	 *
713
+	 * The calendar-query is defined in RFC4791 : CalDAV. Using the
714
+	 * calendar-query it is possible for a client to request a specific set of
715
+	 * object, based on contents of iCalendar properties, date-ranges and
716
+	 * iCalendar component types (VTODO, VEVENT).
717
+	 *
718
+	 * This method should just return a list of (relative) urls that match this
719
+	 * query.
720
+	 *
721
+	 * The list of filters are specified as an array. The exact array is
722
+	 * documented by \Sabre\CalDAV\CalendarQueryParser.
723
+	 *
724
+	 * Note that it is extremely likely that getCalendarObject for every path
725
+	 * returned from this method will be called almost immediately after. You
726
+	 * may want to anticipate this to speed up these requests.
727
+	 *
728
+	 * This method provides a default implementation, which parses *all* the
729
+	 * iCalendar objects in the specified calendar.
730
+	 *
731
+	 * This default may well be good enough for personal use, and calendars
732
+	 * that aren't very large. But if you anticipate high usage, big calendars
733
+	 * or high loads, you are strongly adviced to optimize certain paths.
734
+	 *
735
+	 * The best way to do so is override this method and to optimize
736
+	 * specifically for 'common filters'.
737
+	 *
738
+	 * Requests that are extremely common are:
739
+	 *   * requests for just VEVENTS
740
+	 *   * requests for just VTODO
741
+	 *   * requests with a time-range-filter on a VEVENT.
742
+	 *
743
+	 * ..and combinations of these requests. It may not be worth it to try to
744
+	 * handle every possible situation and just rely on the (relatively
745
+	 * easy to use) CalendarQueryValidator to handle the rest.
746
+	 *
747
+	 * Note that especially time-range-filters may be difficult to parse. A
748
+	 * time-range filter specified on a VEVENT must for instance also handle
749
+	 * recurrence rules correctly.
750
+	 * A good example of how to interpret all these filters can also simply
751
+	 * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
752
+	 * as possible, so it gives you a good idea on what type of stuff you need
753
+	 * to think of.
754
+	 *
755
+	 * This specific implementation (for the PDO) backend optimizes filters on
756
+	 * specific components, and VEVENT time-ranges.
757
+	 *
758
+	 * @param mixed $calendarId
759
+	 *
760
+	 * @return array
761
+	 */
762
+	public function calendarQuery($calendarId, array $filters)
763
+	{
764
+		if (!is_array($calendarId)) {
765
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
766
+		}
767
+		list($calendarId, $instanceId) = $calendarId;
768
+
769
+		$componentType = null;
770
+		$requirePostFilter = true;
771
+		$timeRange = null;
772
+
773
+		// if no filters were specified, we don't need to filter after a query
774
+		if (!$filters['prop-filters'] && !$filters['comp-filters']) {
775
+			$requirePostFilter = false;
776
+		}
777
+
778
+		// Figuring out if there's a component filter
779
+		if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) {
780
+			$componentType = $filters['comp-filters'][0]['name'];
781
+
782
+			// Checking if we need post-filters
783
+			$has_time_range = array_key_exists('time-range', $filters['comp-filters'][0]) && $filters['comp-filters'][0]['time-range'];
784
+			if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$has_time_range && !$filters['comp-filters'][0]['prop-filters']) {
785
+				$requirePostFilter = false;
786
+			}
787
+			// There was a time-range filter
788
+			if ('VEVENT' == $componentType && $has_time_range) {
789
+				$timeRange = $filters['comp-filters'][0]['time-range'];
790
+
791
+				// If start time OR the end time is not specified, we can do a
792
+				// 100% accurate mysql query.
793
+				if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && $timeRange) {
794
+					if ((array_key_exists('start', $timeRange) && !$timeRange['start']) || (array_key_exists('end', $timeRange) && !$timeRange['end'])) {
795
+						$requirePostFilter = false;
796
+					}
797
+				}
798
+			}
799
+		}
800
+
801
+		if ($requirePostFilter) {
802
+			$query = 'SELECT uri, calendardata FROM '.$this->calendarObjectTableName.' WHERE calendarid = :calendarid';
803
+		} else {
804
+			$query = 'SELECT uri FROM '.$this->calendarObjectTableName.' WHERE calendarid = :calendarid';
805
+		}
806
+
807
+		$values = [
808
+			'calendarid' => $calendarId,
809
+		];
810
+
811
+		if ($componentType) {
812
+			$query .= ' AND componenttype = :componenttype';
813
+			$values['componenttype'] = $componentType;
814
+		}
815
+
816
+		if ($timeRange && array_key_exists('start', $timeRange) && $timeRange['start']) {
817
+			$query .= ' AND lastoccurence > :startdate';
818
+			$values['startdate'] = $timeRange['start']->getTimeStamp();
819
+		}
820
+		if ($timeRange && array_key_exists('end', $timeRange) && $timeRange['end']) {
821
+			$query .= ' AND firstoccurence < :enddate';
822
+			$values['enddate'] = $timeRange['end']->getTimeStamp();
823
+		}
824
+
825
+		$stmt = $this->pdo->prepare($query);
826
+		$stmt->execute($values);
827
+
828
+		$result = [];
829
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
830
+			if ($requirePostFilter) {
831
+				if (!$this->validateFilterForObject($row, $filters)) {
832
+					continue;
833
+				}
834
+			}
835
+			$result[] = $row['uri'];
836
+		}
837
+
838
+		return $result;
839
+	}
840
+
841
+	/**
842
+	 * Searches through all of a users calendars and calendar objects to find
843
+	 * an object with a specific UID.
844
+	 *
845
+	 * This method should return the path to this object, relative to the
846
+	 * calendar home, so this path usually only contains two parts:
847
+	 *
848
+	 * calendarpath/objectpath.ics
849
+	 *
850
+	 * If the uid is not found, return null.
851
+	 *
852
+	 * This method should only consider * objects that the principal owns, so
853
+	 * any calendars owned by other principals that also appear in this
854
+	 * collection should be ignored.
855
+	 *
856
+	 * @param string $principalUri
857
+	 * @param string $uid
858
+	 *
859
+	 * @return string|null
860
+	 */
861
+	public function getCalendarObjectByUID($principalUri, $uid)
862
+	{
863
+		$query = <<<SQL
864 864
 SELECT
865 865
     calendar_instances.uri AS calendaruri, calendarobjects.uri as objecturi
866 866
 FROM
@@ -876,468 +876,468 @@  discard block
 block discarded – undo
876 876
     calendar_instances.access = 1
877 877
 SQL;
878 878
 
879
-        $stmt = $this->pdo->prepare($query);
880
-        $stmt->execute([$principalUri, $uid]);
881
-
882
-        if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
883
-            return $row['calendaruri'].'/'.$row['objecturi'];
884
-        }
885
-    }
886
-
887
-    /**
888
-     * The getChanges method returns all the changes that have happened, since
889
-     * the specified syncToken in the specified calendar.
890
-     *
891
-     * This function should return an array, such as the following:
892
-     *
893
-     * [
894
-     *   'syncToken' => 'The current synctoken',
895
-     *   'added'   => [
896
-     *      'new.txt',
897
-     *   ],
898
-     *   'modified'   => [
899
-     *      'modified.txt',
900
-     *   ],
901
-     *   'deleted' => [
902
-     *      'foo.php.bak',
903
-     *      'old.txt'
904
-     *   ]
905
-     * ];
906
-     *
907
-     * The returned syncToken property should reflect the *current* syncToken
908
-     * of the calendar, as reported in the {http://sabredav.org/ns}sync-token
909
-     * property this is needed here too, to ensure the operation is atomic.
910
-     *
911
-     * If the $syncToken argument is specified as null, this is an initial
912
-     * sync, and all members should be reported.
913
-     *
914
-     * The modified property is an array of nodenames that have changed since
915
-     * the last token.
916
-     *
917
-     * The deleted property is an array with nodenames, that have been deleted
918
-     * from collection.
919
-     *
920
-     * The $syncLevel argument is basically the 'depth' of the report. If it's
921
-     * 1, you only have to report changes that happened only directly in
922
-     * immediate descendants. If it's 2, it should also include changes from
923
-     * the nodes below the child collections. (grandchildren)
924
-     *
925
-     * The $limit argument allows a client to specify how many results should
926
-     * be returned at most. If the limit is not specified, it should be treated
927
-     * as infinite.
928
-     *
929
-     * If the limit (infinite or not) is higher than you're willing to return,
930
-     * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
931
-     *
932
-     * If the syncToken is expired (due to data cleanup) or unknown, you must
933
-     * return null.
934
-     *
935
-     * The limit is 'suggestive'. You are free to ignore it.
936
-     *
937
-     * @param mixed  $calendarId
938
-     * @param string $syncToken
939
-     * @param int    $syncLevel
940
-     * @param int    $limit
941
-     *
942
-     * @return array|null
943
-     */
944
-    public function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null)
945
-    {
946
-        if (!is_array($calendarId)) {
947
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
948
-        }
949
-        list($calendarId, $instanceId) = $calendarId;
950
-
951
-        $result = [
952
-            'added' => [],
953
-            'modified' => [],
954
-            'deleted' => [],
955
-        ];
956
-
957
-        if ($syncToken) {
958
-            $query = 'SELECT uri, operation, synctoken FROM '.$this->calendarChangesTableName.' WHERE synctoken >= ?  AND calendarid = ? ORDER BY synctoken';
959
-            if ($limit > 0) {
960
-                // Fetch one more raw to detect result truncation
961
-                $query .= ' LIMIT '.((int) $limit + 1);
962
-            }
963
-
964
-            // Fetching all changes
965
-            $stmt = $this->pdo->prepare($query);
966
-            $stmt->execute([$syncToken, $calendarId]);
967
-
968
-            $changes = [];
969
-
970
-            // This loop ensures that any duplicates are overwritten, only the
971
-            // last change on a node is relevant.
972
-            while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
973
-                $changes[$row['uri']] = $row;
974
-            }
975
-            $currentToken = null;
976
-
977
-            $result_count = 0;
978
-            foreach ($changes as $uri => $operation) {
979
-                if (!is_null($limit) && $result_count >= $limit) {
980
-                    $result['result_truncated'] = true;
981
-                    break;
982
-                }
983
-
984
-                if (null === $currentToken || $currentToken < $operation['synctoken'] + 1) {
985
-                    // SyncToken in CalDAV perspective is consistently the next number of the last synced change event in this class.
986
-                    $currentToken = $operation['synctoken'] + 1;
987
-                }
988
-
989
-                ++$result_count;
990
-                switch ($operation['operation']) {
991
-                    case 1:
992
-                        $result['added'][] = $uri;
993
-                        break;
994
-                    case 2:
995
-                        $result['modified'][] = $uri;
996
-                        break;
997
-                    case 3:
998
-                        $result['deleted'][] = $uri;
999
-                        break;
1000
-                }
1001
-            }
1002
-
1003
-            if (!is_null($currentToken)) {
1004
-                $result['syncToken'] = $currentToken;
1005
-            } else {
1006
-                // This means returned value is equivalent to syncToken
1007
-                $result['syncToken'] = $syncToken;
1008
-            }
1009
-        } else {
1010
-            // Current synctoken
1011
-            $stmt = $this->pdo->prepare('SELECT synctoken FROM '.$this->calendarTableName.' WHERE id = ?');
1012
-            $stmt->execute([$calendarId]);
1013
-            $currentToken = $stmt->fetchColumn(0);
1014
-
1015
-            if (is_null($currentToken)) {
1016
-                return null;
1017
-            }
1018
-            $result['syncToken'] = $currentToken;
1019
-
1020
-            // No synctoken supplied, this is the initial sync.
1021
-            $query = 'SELECT uri FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?';
1022
-            $stmt = $this->pdo->prepare($query);
1023
-            $stmt->execute([$calendarId]);
1024
-
1025
-            $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
1026
-        }
1027
-
1028
-        return $result;
1029
-    }
1030
-
1031
-    /**
1032
-     * Adds a change record to the calendarchanges table.
1033
-     *
1034
-     * @param mixed  $calendarId
1035
-     * @param string $objectUri
1036
-     * @param int    $operation  1 = add, 2 = modify, 3 = delete
1037
-     */
1038
-    protected function addChange($calendarId, $objectUri, $operation)
1039
-    {
1040
-        $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarChangesTableName.' (uri, synctoken, calendarid, operation) SELECT ?, synctoken, ?, ? FROM '.$this->calendarTableName.' WHERE id = ?');
1041
-        $stmt->execute([
1042
-            $objectUri,
1043
-            $calendarId,
1044
-            $operation,
1045
-            $calendarId,
1046
-        ]);
1047
-        $stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET synctoken = synctoken + 1 WHERE id = ?');
1048
-        $stmt->execute([
1049
-            $calendarId,
1050
-        ]);
1051
-    }
1052
-
1053
-    /**
1054
-     * Returns a list of subscriptions for a principal.
1055
-     *
1056
-     * Every subscription is an array with the following keys:
1057
-     *  * id, a unique id that will be used by other functions to modify the
1058
-     *    subscription. This can be the same as the uri or a database key.
1059
-     *  * uri. This is just the 'base uri' or 'filename' of the subscription.
1060
-     *  * principaluri. The owner of the subscription. Almost always the same as
1061
-     *    principalUri passed to this method.
1062
-     *  * source. Url to the actual feed
1063
-     *
1064
-     * Furthermore, all the subscription info must be returned too:
1065
-     *
1066
-     * 1. {DAV:}displayname
1067
-     * 2. {http://apple.com/ns/ical/}refreshrate
1068
-     * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
1069
-     *    should not be stripped).
1070
-     * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
1071
-     *    should not be stripped).
1072
-     * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
1073
-     *    attachments should not be stripped).
1074
-     * 7. {http://apple.com/ns/ical/}calendar-color
1075
-     * 8. {http://apple.com/ns/ical/}calendar-order
1076
-     * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
1077
-     *    (should just be an instance of
1078
-     *    Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
1079
-     *    default components).
1080
-     *
1081
-     * @param string $principalUri
1082
-     *
1083
-     * @return array
1084
-     */
1085
-    public function getSubscriptionsForUser($principalUri)
1086
-    {
1087
-        $fields = array_values($this->subscriptionPropertyMap);
1088
-        $fields[] = 'id';
1089
-        $fields[] = 'uri';
1090
-        $fields[] = 'source';
1091
-        $fields[] = 'principaluri';
1092
-        $fields[] = 'lastmodified';
1093
-
1094
-        // Making fields a comma-delimited list
1095
-        $fields = implode(', ', $fields);
1096
-        $stmt = $this->pdo->prepare('SELECT '.$fields.' FROM '.$this->calendarSubscriptionsTableName.' WHERE principaluri = ? ORDER BY calendarorder ASC');
1097
-        $stmt->execute([$principalUri]);
1098
-
1099
-        $subscriptions = [];
1100
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1101
-            $subscription = [
1102
-                'id' => $row['id'],
1103
-                'uri' => $row['uri'],
1104
-                'principaluri' => $row['principaluri'],
1105
-                'source' => $row['source'],
1106
-                'lastmodified' => $row['lastmodified'],
1107
-
1108
-                '{'.CalDAV\Plugin::NS_CALDAV.'}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VTODO', 'VEVENT']),
1109
-            ];
1110
-
1111
-            foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) {
1112
-                if (!is_null($row[$dbName])) {
1113
-                    $subscription[$xmlName] = $row[$dbName];
1114
-                }
1115
-            }
1116
-
1117
-            $subscriptions[] = $subscription;
1118
-        }
1119
-
1120
-        return $subscriptions;
1121
-    }
1122
-
1123
-    /**
1124
-     * Creates a new subscription for a principal.
1125
-     *
1126
-     * If the creation was a success, an id must be returned that can be used to reference
1127
-     * this subscription in other methods, such as updateSubscription.
1128
-     *
1129
-     * @param string $principalUri
1130
-     * @param string $uri
1131
-     *
1132
-     * @return mixed
1133
-     */
1134
-    public function createSubscription($principalUri, $uri, array $properties)
1135
-    {
1136
-        $fieldNames = [
1137
-            'principaluri',
1138
-            'uri',
1139
-            'source',
1140
-            'lastmodified',
1141
-        ];
1142
-
1143
-        if (!isset($properties['{http://calendarserver.org/ns/}source'])) {
1144
-            throw new Forbidden('The {http://calendarserver.org/ns/}source property is required when creating subscriptions');
1145
-        }
1146
-
1147
-        $values = [
1148
-            ':principaluri' => $principalUri,
1149
-            ':uri' => $uri,
1150
-            ':source' => $properties['{http://calendarserver.org/ns/}source']->getHref(),
1151
-            ':lastmodified' => time(),
1152
-        ];
1153
-
1154
-        foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) {
1155
-            if (isset($properties[$xmlName])) {
1156
-                $values[':'.$dbName] = $properties[$xmlName];
1157
-                $fieldNames[] = $dbName;
1158
-            }
1159
-        }
1160
-
1161
-        $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarSubscriptionsTableName.' ('.implode(', ', $fieldNames).') VALUES ('.implode(', ', array_keys($values)).')');
1162
-        $stmt->execute($values);
1163
-
1164
-        return $this->pdo->lastInsertId(
1165
-            $this->calendarSubscriptionsTableName.'_id_seq'
1166
-        );
1167
-    }
1168
-
1169
-    /**
1170
-     * Updates a subscription.
1171
-     *
1172
-     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
1173
-     * To do the actual updates, you must tell this object which properties
1174
-     * you're going to process with the handle() method.
1175
-     *
1176
-     * Calling the handle method is like telling the PropPatch object "I
1177
-     * promise I can handle updating this property".
1178
-     *
1179
-     * Read the PropPatch documentation for more info and examples.
1180
-     *
1181
-     * @param mixed $subscriptionId
1182
-     */
1183
-    public function updateSubscription($subscriptionId, PropPatch $propPatch)
1184
-    {
1185
-        $supportedProperties = array_keys($this->subscriptionPropertyMap);
1186
-        $supportedProperties[] = '{http://calendarserver.org/ns/}source';
1187
-
1188
-        $propPatch->handle($supportedProperties, function ($mutations) use ($subscriptionId) {
1189
-            $newValues = [];
1190
-
1191
-            foreach ($mutations as $propertyName => $propertyValue) {
1192
-                if ('{http://calendarserver.org/ns/}source' === $propertyName) {
1193
-                    $newValues['source'] = $propertyValue->getHref();
1194
-                } else {
1195
-                    $fieldName = $this->subscriptionPropertyMap[$propertyName];
1196
-                    $newValues[$fieldName] = $propertyValue;
1197
-                }
1198
-            }
1199
-
1200
-            // Now we're generating the sql query.
1201
-            $valuesSql = [];
1202
-            foreach ($newValues as $fieldName => $value) {
1203
-                $valuesSql[] = $fieldName.' = ?';
1204
-            }
1205
-
1206
-            $stmt = $this->pdo->prepare('UPDATE '.$this->calendarSubscriptionsTableName.' SET '.implode(', ', $valuesSql).', lastmodified = ? WHERE id = ?');
1207
-            $newValues['lastmodified'] = time();
1208
-            $newValues['id'] = $subscriptionId;
1209
-            $stmt->execute(array_values($newValues));
1210
-
1211
-            return true;
1212
-        });
1213
-    }
1214
-
1215
-    /**
1216
-     * Deletes a subscription.
1217
-     *
1218
-     * @param mixed $subscriptionId
1219
-     */
1220
-    public function deleteSubscription($subscriptionId)
1221
-    {
1222
-        $stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarSubscriptionsTableName.' WHERE id = ?');
1223
-        $stmt->execute([$subscriptionId]);
1224
-    }
1225
-
1226
-    /**
1227
-     * Returns a single scheduling object.
1228
-     *
1229
-     * The returned array should contain the following elements:
1230
-     *   * uri - A unique basename for the object. This will be used to
1231
-     *           construct a full uri.
1232
-     *   * calendardata - The iCalendar object
1233
-     *   * lastmodified - The last modification date. Can be an int for a unix
1234
-     *                    timestamp, or a PHP DateTime object.
1235
-     *   * etag - A unique token that must change if the object changed.
1236
-     *   * size - The size of the object, in bytes.
1237
-     *
1238
-     * @param string $principalUri
1239
-     * @param string $objectUri
1240
-     *
1241
-     * @return array
1242
-     */
1243
-    public function getSchedulingObject($principalUri, $objectUri)
1244
-    {
1245
-        $stmt = $this->pdo->prepare('SELECT uri, calendardata, lastmodified, etag, size FROM '.$this->schedulingObjectTableName.' WHERE principaluri = ? AND uri = ?');
1246
-        $stmt->execute([$principalUri, $objectUri]);
1247
-        $row = $stmt->fetch(\PDO::FETCH_ASSOC);
1248
-
1249
-        if (!$row) {
1250
-            return null;
1251
-        }
1252
-
1253
-        return [
1254
-            'uri' => $row['uri'],
1255
-            'calendardata' => $row['calendardata'],
1256
-            'lastmodified' => $row['lastmodified'],
1257
-            'etag' => '"'.$row['etag'].'"',
1258
-            'size' => (int) $row['size'],
1259
-         ];
1260
-    }
1261
-
1262
-    /**
1263
-     * Returns all scheduling objects for the inbox collection.
1264
-     *
1265
-     * These objects should be returned as an array. Every item in the array
1266
-     * should follow the same structure as returned from getSchedulingObject.
1267
-     *
1268
-     * The main difference is that 'calendardata' is optional.
1269
-     *
1270
-     * @param string $principalUri
1271
-     *
1272
-     * @return array
1273
-     */
1274
-    public function getSchedulingObjects($principalUri)
1275
-    {
1276
-        $stmt = $this->pdo->prepare('SELECT id, calendardata, uri, lastmodified, etag, size FROM '.$this->schedulingObjectTableName.' WHERE principaluri = ?');
1277
-        $stmt->execute([$principalUri]);
1278
-
1279
-        $result = [];
1280
-        foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
1281
-            $result[] = [
1282
-                'calendardata' => $row['calendardata'],
1283
-                'uri' => $row['uri'],
1284
-                'lastmodified' => $row['lastmodified'],
1285
-                'etag' => '"'.$row['etag'].'"',
1286
-                'size' => (int) $row['size'],
1287
-            ];
1288
-        }
1289
-
1290
-        return $result;
1291
-    }
1292
-
1293
-    /**
1294
-     * Deletes a scheduling object.
1295
-     *
1296
-     * @param string $principalUri
1297
-     * @param string $objectUri
1298
-     */
1299
-    public function deleteSchedulingObject($principalUri, $objectUri)
1300
-    {
1301
-        $stmt = $this->pdo->prepare('DELETE FROM '.$this->schedulingObjectTableName.' WHERE principaluri = ? AND uri = ?');
1302
-        $stmt->execute([$principalUri, $objectUri]);
1303
-    }
1304
-
1305
-    /**
1306
-     * Creates a new scheduling object. This should land in a users' inbox.
1307
-     *
1308
-     * @param string          $principalUri
1309
-     * @param string          $objectUri
1310
-     * @param string|resource $objectData
1311
-     */
1312
-    public function createSchedulingObject($principalUri, $objectUri, $objectData)
1313
-    {
1314
-        $stmt = $this->pdo->prepare('INSERT INTO '.$this->schedulingObjectTableName.' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (?, ?, ?, ?, ?, ?)');
1315
-
1316
-        if (is_resource($objectData)) {
1317
-            $objectData = stream_get_contents($objectData);
1318
-        }
1319
-
1320
-        $stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData)]);
1321
-    }
1322
-
1323
-    /**
1324
-     * Updates the list of shares.
1325
-     *
1326
-     * @param mixed                           $calendarId
1327
-     * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees
1328
-     */
1329
-    public function updateInvites($calendarId, array $sharees)
1330
-    {
1331
-        if (!is_array($calendarId)) {
1332
-            throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
1333
-        }
1334
-        $currentInvites = $this->getInvites($calendarId);
1335
-        list($calendarId, $instanceId) = $calendarId;
1336
-
1337
-        $removeStmt = $this->pdo->prepare('DELETE FROM '.$this->calendarInstancesTableName.' WHERE calendarid = ? AND share_href = ? AND access IN (2,3)');
1338
-        $updateStmt = $this->pdo->prepare('UPDATE '.$this->calendarInstancesTableName.' SET access = ?, share_displayname = ?, share_invitestatus = ? WHERE calendarid = ? AND share_href = ?');
1339
-
1340
-        $insertStmt = $this->pdo->prepare('
879
+		$stmt = $this->pdo->prepare($query);
880
+		$stmt->execute([$principalUri, $uid]);
881
+
882
+		if ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
883
+			return $row['calendaruri'].'/'.$row['objecturi'];
884
+		}
885
+	}
886
+
887
+	/**
888
+	 * The getChanges method returns all the changes that have happened, since
889
+	 * the specified syncToken in the specified calendar.
890
+	 *
891
+	 * This function should return an array, such as the following:
892
+	 *
893
+	 * [
894
+	 *   'syncToken' => 'The current synctoken',
895
+	 *   'added'   => [
896
+	 *      'new.txt',
897
+	 *   ],
898
+	 *   'modified'   => [
899
+	 *      'modified.txt',
900
+	 *   ],
901
+	 *   'deleted' => [
902
+	 *      'foo.php.bak',
903
+	 *      'old.txt'
904
+	 *   ]
905
+	 * ];
906
+	 *
907
+	 * The returned syncToken property should reflect the *current* syncToken
908
+	 * of the calendar, as reported in the {http://sabredav.org/ns}sync-token
909
+	 * property this is needed here too, to ensure the operation is atomic.
910
+	 *
911
+	 * If the $syncToken argument is specified as null, this is an initial
912
+	 * sync, and all members should be reported.
913
+	 *
914
+	 * The modified property is an array of nodenames that have changed since
915
+	 * the last token.
916
+	 *
917
+	 * The deleted property is an array with nodenames, that have been deleted
918
+	 * from collection.
919
+	 *
920
+	 * The $syncLevel argument is basically the 'depth' of the report. If it's
921
+	 * 1, you only have to report changes that happened only directly in
922
+	 * immediate descendants. If it's 2, it should also include changes from
923
+	 * the nodes below the child collections. (grandchildren)
924
+	 *
925
+	 * The $limit argument allows a client to specify how many results should
926
+	 * be returned at most. If the limit is not specified, it should be treated
927
+	 * as infinite.
928
+	 *
929
+	 * If the limit (infinite or not) is higher than you're willing to return,
930
+	 * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
931
+	 *
932
+	 * If the syncToken is expired (due to data cleanup) or unknown, you must
933
+	 * return null.
934
+	 *
935
+	 * The limit is 'suggestive'. You are free to ignore it.
936
+	 *
937
+	 * @param mixed  $calendarId
938
+	 * @param string $syncToken
939
+	 * @param int    $syncLevel
940
+	 * @param int    $limit
941
+	 *
942
+	 * @return array|null
943
+	 */
944
+	public function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null)
945
+	{
946
+		if (!is_array($calendarId)) {
947
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
948
+		}
949
+		list($calendarId, $instanceId) = $calendarId;
950
+
951
+		$result = [
952
+			'added' => [],
953
+			'modified' => [],
954
+			'deleted' => [],
955
+		];
956
+
957
+		if ($syncToken) {
958
+			$query = 'SELECT uri, operation, synctoken FROM '.$this->calendarChangesTableName.' WHERE synctoken >= ?  AND calendarid = ? ORDER BY synctoken';
959
+			if ($limit > 0) {
960
+				// Fetch one more raw to detect result truncation
961
+				$query .= ' LIMIT '.((int) $limit + 1);
962
+			}
963
+
964
+			// Fetching all changes
965
+			$stmt = $this->pdo->prepare($query);
966
+			$stmt->execute([$syncToken, $calendarId]);
967
+
968
+			$changes = [];
969
+
970
+			// This loop ensures that any duplicates are overwritten, only the
971
+			// last change on a node is relevant.
972
+			while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
973
+				$changes[$row['uri']] = $row;
974
+			}
975
+			$currentToken = null;
976
+
977
+			$result_count = 0;
978
+			foreach ($changes as $uri => $operation) {
979
+				if (!is_null($limit) && $result_count >= $limit) {
980
+					$result['result_truncated'] = true;
981
+					break;
982
+				}
983
+
984
+				if (null === $currentToken || $currentToken < $operation['synctoken'] + 1) {
985
+					// SyncToken in CalDAV perspective is consistently the next number of the last synced change event in this class.
986
+					$currentToken = $operation['synctoken'] + 1;
987
+				}
988
+
989
+				++$result_count;
990
+				switch ($operation['operation']) {
991
+					case 1:
992
+						$result['added'][] = $uri;
993
+						break;
994
+					case 2:
995
+						$result['modified'][] = $uri;
996
+						break;
997
+					case 3:
998
+						$result['deleted'][] = $uri;
999
+						break;
1000
+				}
1001
+			}
1002
+
1003
+			if (!is_null($currentToken)) {
1004
+				$result['syncToken'] = $currentToken;
1005
+			} else {
1006
+				// This means returned value is equivalent to syncToken
1007
+				$result['syncToken'] = $syncToken;
1008
+			}
1009
+		} else {
1010
+			// Current synctoken
1011
+			$stmt = $this->pdo->prepare('SELECT synctoken FROM '.$this->calendarTableName.' WHERE id = ?');
1012
+			$stmt->execute([$calendarId]);
1013
+			$currentToken = $stmt->fetchColumn(0);
1014
+
1015
+			if (is_null($currentToken)) {
1016
+				return null;
1017
+			}
1018
+			$result['syncToken'] = $currentToken;
1019
+
1020
+			// No synctoken supplied, this is the initial sync.
1021
+			$query = 'SELECT uri FROM '.$this->calendarObjectTableName.' WHERE calendarid = ?';
1022
+			$stmt = $this->pdo->prepare($query);
1023
+			$stmt->execute([$calendarId]);
1024
+
1025
+			$result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
1026
+		}
1027
+
1028
+		return $result;
1029
+	}
1030
+
1031
+	/**
1032
+	 * Adds a change record to the calendarchanges table.
1033
+	 *
1034
+	 * @param mixed  $calendarId
1035
+	 * @param string $objectUri
1036
+	 * @param int    $operation  1 = add, 2 = modify, 3 = delete
1037
+	 */
1038
+	protected function addChange($calendarId, $objectUri, $operation)
1039
+	{
1040
+		$stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarChangesTableName.' (uri, synctoken, calendarid, operation) SELECT ?, synctoken, ?, ? FROM '.$this->calendarTableName.' WHERE id = ?');
1041
+		$stmt->execute([
1042
+			$objectUri,
1043
+			$calendarId,
1044
+			$operation,
1045
+			$calendarId,
1046
+		]);
1047
+		$stmt = $this->pdo->prepare('UPDATE '.$this->calendarTableName.' SET synctoken = synctoken + 1 WHERE id = ?');
1048
+		$stmt->execute([
1049
+			$calendarId,
1050
+		]);
1051
+	}
1052
+
1053
+	/**
1054
+	 * Returns a list of subscriptions for a principal.
1055
+	 *
1056
+	 * Every subscription is an array with the following keys:
1057
+	 *  * id, a unique id that will be used by other functions to modify the
1058
+	 *    subscription. This can be the same as the uri or a database key.
1059
+	 *  * uri. This is just the 'base uri' or 'filename' of the subscription.
1060
+	 *  * principaluri. The owner of the subscription. Almost always the same as
1061
+	 *    principalUri passed to this method.
1062
+	 *  * source. Url to the actual feed
1063
+	 *
1064
+	 * Furthermore, all the subscription info must be returned too:
1065
+	 *
1066
+	 * 1. {DAV:}displayname
1067
+	 * 2. {http://apple.com/ns/ical/}refreshrate
1068
+	 * 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
1069
+	 *    should not be stripped).
1070
+	 * 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
1071
+	 *    should not be stripped).
1072
+	 * 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
1073
+	 *    attachments should not be stripped).
1074
+	 * 7. {http://apple.com/ns/ical/}calendar-color
1075
+	 * 8. {http://apple.com/ns/ical/}calendar-order
1076
+	 * 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
1077
+	 *    (should just be an instance of
1078
+	 *    Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
1079
+	 *    default components).
1080
+	 *
1081
+	 * @param string $principalUri
1082
+	 *
1083
+	 * @return array
1084
+	 */
1085
+	public function getSubscriptionsForUser($principalUri)
1086
+	{
1087
+		$fields = array_values($this->subscriptionPropertyMap);
1088
+		$fields[] = 'id';
1089
+		$fields[] = 'uri';
1090
+		$fields[] = 'source';
1091
+		$fields[] = 'principaluri';
1092
+		$fields[] = 'lastmodified';
1093
+
1094
+		// Making fields a comma-delimited list
1095
+		$fields = implode(', ', $fields);
1096
+		$stmt = $this->pdo->prepare('SELECT '.$fields.' FROM '.$this->calendarSubscriptionsTableName.' WHERE principaluri = ? ORDER BY calendarorder ASC');
1097
+		$stmt->execute([$principalUri]);
1098
+
1099
+		$subscriptions = [];
1100
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1101
+			$subscription = [
1102
+				'id' => $row['id'],
1103
+				'uri' => $row['uri'],
1104
+				'principaluri' => $row['principaluri'],
1105
+				'source' => $row['source'],
1106
+				'lastmodified' => $row['lastmodified'],
1107
+
1108
+				'{'.CalDAV\Plugin::NS_CALDAV.'}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet(['VTODO', 'VEVENT']),
1109
+			];
1110
+
1111
+			foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) {
1112
+				if (!is_null($row[$dbName])) {
1113
+					$subscription[$xmlName] = $row[$dbName];
1114
+				}
1115
+			}
1116
+
1117
+			$subscriptions[] = $subscription;
1118
+		}
1119
+
1120
+		return $subscriptions;
1121
+	}
1122
+
1123
+	/**
1124
+	 * Creates a new subscription for a principal.
1125
+	 *
1126
+	 * If the creation was a success, an id must be returned that can be used to reference
1127
+	 * this subscription in other methods, such as updateSubscription.
1128
+	 *
1129
+	 * @param string $principalUri
1130
+	 * @param string $uri
1131
+	 *
1132
+	 * @return mixed
1133
+	 */
1134
+	public function createSubscription($principalUri, $uri, array $properties)
1135
+	{
1136
+		$fieldNames = [
1137
+			'principaluri',
1138
+			'uri',
1139
+			'source',
1140
+			'lastmodified',
1141
+		];
1142
+
1143
+		if (!isset($properties['{http://calendarserver.org/ns/}source'])) {
1144
+			throw new Forbidden('The {http://calendarserver.org/ns/}source property is required when creating subscriptions');
1145
+		}
1146
+
1147
+		$values = [
1148
+			':principaluri' => $principalUri,
1149
+			':uri' => $uri,
1150
+			':source' => $properties['{http://calendarserver.org/ns/}source']->getHref(),
1151
+			':lastmodified' => time(),
1152
+		];
1153
+
1154
+		foreach ($this->subscriptionPropertyMap as $xmlName => $dbName) {
1155
+			if (isset($properties[$xmlName])) {
1156
+				$values[':'.$dbName] = $properties[$xmlName];
1157
+				$fieldNames[] = $dbName;
1158
+			}
1159
+		}
1160
+
1161
+		$stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarSubscriptionsTableName.' ('.implode(', ', $fieldNames).') VALUES ('.implode(', ', array_keys($values)).')');
1162
+		$stmt->execute($values);
1163
+
1164
+		return $this->pdo->lastInsertId(
1165
+			$this->calendarSubscriptionsTableName.'_id_seq'
1166
+		);
1167
+	}
1168
+
1169
+	/**
1170
+	 * Updates a subscription.
1171
+	 *
1172
+	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
1173
+	 * To do the actual updates, you must tell this object which properties
1174
+	 * you're going to process with the handle() method.
1175
+	 *
1176
+	 * Calling the handle method is like telling the PropPatch object "I
1177
+	 * promise I can handle updating this property".
1178
+	 *
1179
+	 * Read the PropPatch documentation for more info and examples.
1180
+	 *
1181
+	 * @param mixed $subscriptionId
1182
+	 */
1183
+	public function updateSubscription($subscriptionId, PropPatch $propPatch)
1184
+	{
1185
+		$supportedProperties = array_keys($this->subscriptionPropertyMap);
1186
+		$supportedProperties[] = '{http://calendarserver.org/ns/}source';
1187
+
1188
+		$propPatch->handle($supportedProperties, function ($mutations) use ($subscriptionId) {
1189
+			$newValues = [];
1190
+
1191
+			foreach ($mutations as $propertyName => $propertyValue) {
1192
+				if ('{http://calendarserver.org/ns/}source' === $propertyName) {
1193
+					$newValues['source'] = $propertyValue->getHref();
1194
+				} else {
1195
+					$fieldName = $this->subscriptionPropertyMap[$propertyName];
1196
+					$newValues[$fieldName] = $propertyValue;
1197
+				}
1198
+			}
1199
+
1200
+			// Now we're generating the sql query.
1201
+			$valuesSql = [];
1202
+			foreach ($newValues as $fieldName => $value) {
1203
+				$valuesSql[] = $fieldName.' = ?';
1204
+			}
1205
+
1206
+			$stmt = $this->pdo->prepare('UPDATE '.$this->calendarSubscriptionsTableName.' SET '.implode(', ', $valuesSql).', lastmodified = ? WHERE id = ?');
1207
+			$newValues['lastmodified'] = time();
1208
+			$newValues['id'] = $subscriptionId;
1209
+			$stmt->execute(array_values($newValues));
1210
+
1211
+			return true;
1212
+		});
1213
+	}
1214
+
1215
+	/**
1216
+	 * Deletes a subscription.
1217
+	 *
1218
+	 * @param mixed $subscriptionId
1219
+	 */
1220
+	public function deleteSubscription($subscriptionId)
1221
+	{
1222
+		$stmt = $this->pdo->prepare('DELETE FROM '.$this->calendarSubscriptionsTableName.' WHERE id = ?');
1223
+		$stmt->execute([$subscriptionId]);
1224
+	}
1225
+
1226
+	/**
1227
+	 * Returns a single scheduling object.
1228
+	 *
1229
+	 * The returned array should contain the following elements:
1230
+	 *   * uri - A unique basename for the object. This will be used to
1231
+	 *           construct a full uri.
1232
+	 *   * calendardata - The iCalendar object
1233
+	 *   * lastmodified - The last modification date. Can be an int for a unix
1234
+	 *                    timestamp, or a PHP DateTime object.
1235
+	 *   * etag - A unique token that must change if the object changed.
1236
+	 *   * size - The size of the object, in bytes.
1237
+	 *
1238
+	 * @param string $principalUri
1239
+	 * @param string $objectUri
1240
+	 *
1241
+	 * @return array
1242
+	 */
1243
+	public function getSchedulingObject($principalUri, $objectUri)
1244
+	{
1245
+		$stmt = $this->pdo->prepare('SELECT uri, calendardata, lastmodified, etag, size FROM '.$this->schedulingObjectTableName.' WHERE principaluri = ? AND uri = ?');
1246
+		$stmt->execute([$principalUri, $objectUri]);
1247
+		$row = $stmt->fetch(\PDO::FETCH_ASSOC);
1248
+
1249
+		if (!$row) {
1250
+			return null;
1251
+		}
1252
+
1253
+		return [
1254
+			'uri' => $row['uri'],
1255
+			'calendardata' => $row['calendardata'],
1256
+			'lastmodified' => $row['lastmodified'],
1257
+			'etag' => '"'.$row['etag'].'"',
1258
+			'size' => (int) $row['size'],
1259
+		 ];
1260
+	}
1261
+
1262
+	/**
1263
+	 * Returns all scheduling objects for the inbox collection.
1264
+	 *
1265
+	 * These objects should be returned as an array. Every item in the array
1266
+	 * should follow the same structure as returned from getSchedulingObject.
1267
+	 *
1268
+	 * The main difference is that 'calendardata' is optional.
1269
+	 *
1270
+	 * @param string $principalUri
1271
+	 *
1272
+	 * @return array
1273
+	 */
1274
+	public function getSchedulingObjects($principalUri)
1275
+	{
1276
+		$stmt = $this->pdo->prepare('SELECT id, calendardata, uri, lastmodified, etag, size FROM '.$this->schedulingObjectTableName.' WHERE principaluri = ?');
1277
+		$stmt->execute([$principalUri]);
1278
+
1279
+		$result = [];
1280
+		foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
1281
+			$result[] = [
1282
+				'calendardata' => $row['calendardata'],
1283
+				'uri' => $row['uri'],
1284
+				'lastmodified' => $row['lastmodified'],
1285
+				'etag' => '"'.$row['etag'].'"',
1286
+				'size' => (int) $row['size'],
1287
+			];
1288
+		}
1289
+
1290
+		return $result;
1291
+	}
1292
+
1293
+	/**
1294
+	 * Deletes a scheduling object.
1295
+	 *
1296
+	 * @param string $principalUri
1297
+	 * @param string $objectUri
1298
+	 */
1299
+	public function deleteSchedulingObject($principalUri, $objectUri)
1300
+	{
1301
+		$stmt = $this->pdo->prepare('DELETE FROM '.$this->schedulingObjectTableName.' WHERE principaluri = ? AND uri = ?');
1302
+		$stmt->execute([$principalUri, $objectUri]);
1303
+	}
1304
+
1305
+	/**
1306
+	 * Creates a new scheduling object. This should land in a users' inbox.
1307
+	 *
1308
+	 * @param string          $principalUri
1309
+	 * @param string          $objectUri
1310
+	 * @param string|resource $objectData
1311
+	 */
1312
+	public function createSchedulingObject($principalUri, $objectUri, $objectData)
1313
+	{
1314
+		$stmt = $this->pdo->prepare('INSERT INTO '.$this->schedulingObjectTableName.' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (?, ?, ?, ?, ?, ?)');
1315
+
1316
+		if (is_resource($objectData)) {
1317
+			$objectData = stream_get_contents($objectData);
1318
+		}
1319
+
1320
+		$stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData)]);
1321
+	}
1322
+
1323
+	/**
1324
+	 * Updates the list of shares.
1325
+	 *
1326
+	 * @param mixed                           $calendarId
1327
+	 * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees
1328
+	 */
1329
+	public function updateInvites($calendarId, array $sharees)
1330
+	{
1331
+		if (!is_array($calendarId)) {
1332
+			throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId');
1333
+		}
1334
+		$currentInvites = $this->getInvites($calendarId);
1335
+		list($calendarId, $instanceId) = $calendarId;
1336
+
1337
+		$removeStmt = $this->pdo->prepare('DELETE FROM '.$this->calendarInstancesTableName.' WHERE calendarid = ? AND share_href = ? AND access IN (2,3)');
1338
+		$updateStmt = $this->pdo->prepare('UPDATE '.$this->calendarInstancesTableName.' SET access = ?, share_displayname = ?, share_invitestatus = ? WHERE calendarid = ? AND share_href = ?');
1339
+
1340
+		$insertStmt = $this->pdo->prepare('
1341 1341
 INSERT INTO '.$this->calendarInstancesTableName.'
1342 1342
     (
1343 1343
         calendarid,
@@ -1370,79 +1370,79 @@  discard block
 block discarded – undo
1370 1370
         ?
1371 1371
     FROM '.$this->calendarInstancesTableName.' WHERE id = ?');
1372 1372
 
1373
-        foreach ($sharees as $sharee) {
1374
-            if (\Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS === $sharee->access) {
1375
-                // if access was set no NOACCESS, it means access for an
1376
-                // existing sharee was removed.
1377
-                $removeStmt->execute([$calendarId, $sharee->href]);
1378
-                continue;
1379
-            }
1380
-
1381
-            if (is_null($sharee->principal)) {
1382
-                // If the server could not determine the principal automatically,
1383
-                // we will mark the invite status as invalid.
1384
-                $sharee->inviteStatus = \Sabre\DAV\Sharing\Plugin::INVITE_INVALID;
1385
-            } else {
1386
-                // Because sabre/dav does not yet have an invitation system,
1387
-                // every invite is automatically accepted for now.
1388
-                $sharee->inviteStatus = \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED;
1389
-            }
1390
-
1391
-            foreach ($currentInvites as $oldSharee) {
1392
-                if ($oldSharee->href === $sharee->href) {
1393
-                    // This is an update
1394
-                    $sharee->properties = array_merge(
1395
-                        $oldSharee->properties,
1396
-                        $sharee->properties
1397
-                    );
1398
-                    $updateStmt->execute([
1399
-                        $sharee->access,
1400
-                        isset($sharee->properties['{DAV:}displayname']) ? $sharee->properties['{DAV:}displayname'] : null,
1401
-                        $sharee->inviteStatus ?: $oldSharee->inviteStatus,
1402
-                        $calendarId,
1403
-                        $sharee->href,
1404
-                    ]);
1405
-                    continue 2;
1406
-                }
1407
-            }
1408
-            // If we got here, it means it was a new sharee
1409
-            $insertStmt->execute([
1410
-                $calendarId,
1411
-                $sharee->principal,
1412
-                $sharee->access,
1413
-                \Sabre\DAV\UUIDUtil::getUUID(),
1414
-                $sharee->href,
1415
-                isset($sharee->properties['{DAV:}displayname']) ? $sharee->properties['{DAV:}displayname'] : null,
1416
-                $sharee->inviteStatus ?: \Sabre\DAV\Sharing\Plugin::INVITE_NORESPONSE,
1417
-                $instanceId,
1418
-            ]);
1419
-        }
1420
-    }
1421
-
1422
-    /**
1423
-     * Returns the list of people whom a calendar is shared with.
1424
-     *
1425
-     * Every item in the returned list must be a Sharee object with at
1426
-     * least the following properties set:
1427
-     *   $href
1428
-     *   $shareAccess
1429
-     *   $inviteStatus
1430
-     *
1431
-     * and optionally:
1432
-     *   $properties
1433
-     *
1434
-     * @param mixed $calendarId
1435
-     *
1436
-     * @return \Sabre\DAV\Xml\Element\Sharee[]
1437
-     */
1438
-    public function getInvites($calendarId)
1439
-    {
1440
-        if (!is_array($calendarId)) {
1441
-            throw new \InvalidArgumentException('The value passed to getInvites() is expected to be an array with a calendarId and an instanceId');
1442
-        }
1443
-        list($calendarId, $instanceId) = $calendarId;
1444
-
1445
-        $query = <<<SQL
1373
+		foreach ($sharees as $sharee) {
1374
+			if (\Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS === $sharee->access) {
1375
+				// if access was set no NOACCESS, it means access for an
1376
+				// existing sharee was removed.
1377
+				$removeStmt->execute([$calendarId, $sharee->href]);
1378
+				continue;
1379
+			}
1380
+
1381
+			if (is_null($sharee->principal)) {
1382
+				// If the server could not determine the principal automatically,
1383
+				// we will mark the invite status as invalid.
1384
+				$sharee->inviteStatus = \Sabre\DAV\Sharing\Plugin::INVITE_INVALID;
1385
+			} else {
1386
+				// Because sabre/dav does not yet have an invitation system,
1387
+				// every invite is automatically accepted for now.
1388
+				$sharee->inviteStatus = \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED;
1389
+			}
1390
+
1391
+			foreach ($currentInvites as $oldSharee) {
1392
+				if ($oldSharee->href === $sharee->href) {
1393
+					// This is an update
1394
+					$sharee->properties = array_merge(
1395
+						$oldSharee->properties,
1396
+						$sharee->properties
1397
+					);
1398
+					$updateStmt->execute([
1399
+						$sharee->access,
1400
+						isset($sharee->properties['{DAV:}displayname']) ? $sharee->properties['{DAV:}displayname'] : null,
1401
+						$sharee->inviteStatus ?: $oldSharee->inviteStatus,
1402
+						$calendarId,
1403
+						$sharee->href,
1404
+					]);
1405
+					continue 2;
1406
+				}
1407
+			}
1408
+			// If we got here, it means it was a new sharee
1409
+			$insertStmt->execute([
1410
+				$calendarId,
1411
+				$sharee->principal,
1412
+				$sharee->access,
1413
+				\Sabre\DAV\UUIDUtil::getUUID(),
1414
+				$sharee->href,
1415
+				isset($sharee->properties['{DAV:}displayname']) ? $sharee->properties['{DAV:}displayname'] : null,
1416
+				$sharee->inviteStatus ?: \Sabre\DAV\Sharing\Plugin::INVITE_NORESPONSE,
1417
+				$instanceId,
1418
+			]);
1419
+		}
1420
+	}
1421
+
1422
+	/**
1423
+	 * Returns the list of people whom a calendar is shared with.
1424
+	 *
1425
+	 * Every item in the returned list must be a Sharee object with at
1426
+	 * least the following properties set:
1427
+	 *   $href
1428
+	 *   $shareAccess
1429
+	 *   $inviteStatus
1430
+	 *
1431
+	 * and optionally:
1432
+	 *   $properties
1433
+	 *
1434
+	 * @param mixed $calendarId
1435
+	 *
1436
+	 * @return \Sabre\DAV\Xml\Element\Sharee[]
1437
+	 */
1438
+	public function getInvites($calendarId)
1439
+	{
1440
+		if (!is_array($calendarId)) {
1441
+			throw new \InvalidArgumentException('The value passed to getInvites() is expected to be an array with a calendarId and an instanceId');
1442
+		}
1443
+		list($calendarId, $instanceId) = $calendarId;
1444
+
1445
+		$query = <<<SQL
1446 1446
 SELECT
1447 1447
     principaluri,
1448 1448
     access,
@@ -1454,34 +1454,34 @@  discard block
 block discarded – undo
1454 1454
     calendarid = ?
1455 1455
 SQL;
1456 1456
 
1457
-        $stmt = $this->pdo->prepare($query);
1458
-        $stmt->execute([$calendarId]);
1459
-
1460
-        $result = [];
1461
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1462
-            $result[] = new Sharee([
1463
-                'href' => isset($row['share_href']) ? $row['share_href'] : \Sabre\HTTP\encodePath($row['principaluri']),
1464
-                'access' => (int) $row['access'],
1465
-                /// Everyone is always immediately accepted, for now.
1466
-                'inviteStatus' => (int) $row['share_invitestatus'],
1467
-                'properties' => !empty($row['share_displayname'])
1468
-                    ? ['{DAV:}displayname' => $row['share_displayname']]
1469
-                    : [],
1470
-                'principal' => $row['principaluri'],
1471
-            ]);
1472
-        }
1473
-
1474
-        return $result;
1475
-    }
1476
-
1477
-    /**
1478
-     * Publishes a calendar.
1479
-     *
1480
-     * @param mixed $calendarId
1481
-     * @param bool  $value
1482
-     */
1483
-    public function setPublishStatus($calendarId, $value)
1484
-    {
1485
-        throw new DAV\Exception\NotImplemented('Not implemented');
1486
-    }
1457
+		$stmt = $this->pdo->prepare($query);
1458
+		$stmt->execute([$calendarId]);
1459
+
1460
+		$result = [];
1461
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
1462
+			$result[] = new Sharee([
1463
+				'href' => isset($row['share_href']) ? $row['share_href'] : \Sabre\HTTP\encodePath($row['principaluri']),
1464
+				'access' => (int) $row['access'],
1465
+				/// Everyone is always immediately accepted, for now.
1466
+				'inviteStatus' => (int) $row['share_invitestatus'],
1467
+				'properties' => !empty($row['share_displayname'])
1468
+					? ['{DAV:}displayname' => $row['share_displayname']]
1469
+					: [],
1470
+				'principal' => $row['principaluri'],
1471
+			]);
1472
+		}
1473
+
1474
+		return $result;
1475
+	}
1476
+
1477
+	/**
1478
+	 * Publishes a calendar.
1479
+	 *
1480
+	 * @param mixed $calendarId
1481
+	 * @param bool  $value
1482
+	 */
1483
+	public function setPublishStatus($calendarId, $value)
1484
+	{
1485
+		throw new DAV\Exception\NotImplemented('Not implemented');
1486
+	}
1487 1487
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/SchedulingSupport.php 1 patch
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -14,53 +14,53 @@
 block discarded – undo
14 14
  */
15 15
 interface SchedulingSupport extends BackendInterface
16 16
 {
17
-    /**
18
-     * Returns a single scheduling object for the inbox collection.
19
-     *
20
-     * The returned array should contain the following elements:
21
-     *   * uri - A unique basename for the object. This will be used to
22
-     *           construct a full uri.
23
-     *   * calendardata - The iCalendar object
24
-     *   * lastmodified - The last modification date. Can be an int for a unix
25
-     *                    timestamp, or a PHP DateTime object.
26
-     *   * etag - A unique token that must change if the object changed.
27
-     *   * size - The size of the object, in bytes.
28
-     *
29
-     * @param string $principalUri
30
-     * @param string $objectUri
31
-     *
32
-     * @return array
33
-     */
34
-    public function getSchedulingObject($principalUri, $objectUri);
17
+	/**
18
+	 * Returns a single scheduling object for the inbox collection.
19
+	 *
20
+	 * The returned array should contain the following elements:
21
+	 *   * uri - A unique basename for the object. This will be used to
22
+	 *           construct a full uri.
23
+	 *   * calendardata - The iCalendar object
24
+	 *   * lastmodified - The last modification date. Can be an int for a unix
25
+	 *                    timestamp, or a PHP DateTime object.
26
+	 *   * etag - A unique token that must change if the object changed.
27
+	 *   * size - The size of the object, in bytes.
28
+	 *
29
+	 * @param string $principalUri
30
+	 * @param string $objectUri
31
+	 *
32
+	 * @return array
33
+	 */
34
+	public function getSchedulingObject($principalUri, $objectUri);
35 35
 
36
-    /**
37
-     * Returns all scheduling objects for the inbox collection.
38
-     *
39
-     * These objects should be returned as an array. Every item in the array
40
-     * should follow the same structure as returned from getSchedulingObject.
41
-     *
42
-     * The main difference is that 'calendardata' is optional.
43
-     *
44
-     * @param string $principalUri
45
-     *
46
-     * @return array
47
-     */
48
-    public function getSchedulingObjects($principalUri);
36
+	/**
37
+	 * Returns all scheduling objects for the inbox collection.
38
+	 *
39
+	 * These objects should be returned as an array. Every item in the array
40
+	 * should follow the same structure as returned from getSchedulingObject.
41
+	 *
42
+	 * The main difference is that 'calendardata' is optional.
43
+	 *
44
+	 * @param string $principalUri
45
+	 *
46
+	 * @return array
47
+	 */
48
+	public function getSchedulingObjects($principalUri);
49 49
 
50
-    /**
51
-     * Deletes a scheduling object from the inbox collection.
52
-     *
53
-     * @param string $principalUri
54
-     * @param string $objectUri
55
-     */
56
-    public function deleteSchedulingObject($principalUri, $objectUri);
50
+	/**
51
+	 * Deletes a scheduling object from the inbox collection.
52
+	 *
53
+	 * @param string $principalUri
54
+	 * @param string $objectUri
55
+	 */
56
+	public function deleteSchedulingObject($principalUri, $objectUri);
57 57
 
58
-    /**
59
-     * Creates a new scheduling object. This should land in a users' inbox.
60
-     *
61
-     * @param string          $principalUri
62
-     * @param string          $objectUri
63
-     * @param string|resource $objectData
64
-     */
65
-    public function createSchedulingObject($principalUri, $objectUri, $objectData);
58
+	/**
59
+	 * Creates a new scheduling object. This should land in a users' inbox.
60
+	 *
61
+	 * @param string          $principalUri
62
+	 * @param string          $objectUri
63
+	 * @param string|resource $objectData
64
+	 */
65
+	public function createSchedulingObject($principalUri, $objectUri, $objectData);
66 66
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/CalendarRoot.php 1 patch
Indentation   +48 added lines, -48 removed lines patch added patch discarded remove patch
@@ -21,55 +21,55 @@
 block discarded – undo
21 21
  */
22 22
 class CalendarRoot extends \Sabre\DAVACL\AbstractPrincipalCollection
23 23
 {
24
-    /**
25
-     * CalDAV backend.
26
-     *
27
-     * @var Backend\BackendInterface
28
-     */
29
-    protected $caldavBackend;
24
+	/**
25
+	 * CalDAV backend.
26
+	 *
27
+	 * @var Backend\BackendInterface
28
+	 */
29
+	protected $caldavBackend;
30 30
 
31
-    /**
32
-     * Constructor.
33
-     *
34
-     * This constructor needs both an authentication and a caldav backend.
35
-     *
36
-     * By default this class will show a list of calendar collections for
37
-     * principals in the 'principals' collection. If your main principals are
38
-     * actually located in a different path, use the $principalPrefix argument
39
-     * to override this.
40
-     *
41
-     * @param string $principalPrefix
42
-     */
43
-    public function __construct(PrincipalBackend\BackendInterface $principalBackend, Backend\BackendInterface $caldavBackend, $principalPrefix = 'principals')
44
-    {
45
-        parent::__construct($principalBackend, $principalPrefix);
46
-        $this->caldavBackend = $caldavBackend;
47
-    }
31
+	/**
32
+	 * Constructor.
33
+	 *
34
+	 * This constructor needs both an authentication and a caldav backend.
35
+	 *
36
+	 * By default this class will show a list of calendar collections for
37
+	 * principals in the 'principals' collection. If your main principals are
38
+	 * actually located in a different path, use the $principalPrefix argument
39
+	 * to override this.
40
+	 *
41
+	 * @param string $principalPrefix
42
+	 */
43
+	public function __construct(PrincipalBackend\BackendInterface $principalBackend, Backend\BackendInterface $caldavBackend, $principalPrefix = 'principals')
44
+	{
45
+		parent::__construct($principalBackend, $principalPrefix);
46
+		$this->caldavBackend = $caldavBackend;
47
+	}
48 48
 
49
-    /**
50
-     * Returns the nodename.
51
-     *
52
-     * We're overriding this, because the default will be the 'principalPrefix',
53
-     * and we want it to be Sabre\CalDAV\Plugin::CALENDAR_ROOT
54
-     *
55
-     * @return string
56
-     */
57
-    public function getName()
58
-    {
59
-        return Plugin::CALENDAR_ROOT;
60
-    }
49
+	/**
50
+	 * Returns the nodename.
51
+	 *
52
+	 * We're overriding this, because the default will be the 'principalPrefix',
53
+	 * and we want it to be Sabre\CalDAV\Plugin::CALENDAR_ROOT
54
+	 *
55
+	 * @return string
56
+	 */
57
+	public function getName()
58
+	{
59
+		return Plugin::CALENDAR_ROOT;
60
+	}
61 61
 
62
-    /**
63
-     * This method returns a node for a principal.
64
-     *
65
-     * The passed array contains principal information, and is guaranteed to
66
-     * at least contain a uri item. Other properties may or may not be
67
-     * supplied by the authentication backend.
68
-     *
69
-     * @return \Sabre\DAV\INode
70
-     */
71
-    public function getChildForPrincipal(array $principal)
72
-    {
73
-        return new CalendarHome($this->caldavBackend, $principal);
74
-    }
62
+	/**
63
+	 * This method returns a node for a principal.
64
+	 *
65
+	 * The passed array contains principal information, and is guaranteed to
66
+	 * at least contain a uri item. Other properties may or may not be
67
+	 * supplied by the authentication backend.
68
+	 *
69
+	 * @return \Sabre\DAV\INode
70
+	 */
71
+	public function getChildForPrincipal(array $principal)
72
+	{
73
+		return new CalendarHome($this->caldavBackend, $principal);
74
+	}
75 75
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php 1 patch
Indentation   +77 added lines, -77 removed lines patch added patch discarded remove patch
@@ -21,88 +21,88 @@
 block discarded – undo
21 21
  */
22 22
 class Plugin extends ServerPlugin
23 23
 {
24
-    /**
25
-     * This initializes the plugin.
26
-     *
27
-     * This function is called by Sabre\DAV\Server, after
28
-     * addPlugin is called.
29
-     *
30
-     * This method should set up the required event subscriptions.
31
-     */
32
-    public function initialize(Server $server)
33
-    {
34
-        $server->resourceTypeMapping['Sabre\\CalDAV\\Subscriptions\\ISubscription'] =
35
-            '{http://calendarserver.org/ns/}subscribed';
24
+	/**
25
+	 * This initializes the plugin.
26
+	 *
27
+	 * This function is called by Sabre\DAV\Server, after
28
+	 * addPlugin is called.
29
+	 *
30
+	 * This method should set up the required event subscriptions.
31
+	 */
32
+	public function initialize(Server $server)
33
+	{
34
+		$server->resourceTypeMapping['Sabre\\CalDAV\\Subscriptions\\ISubscription'] =
35
+			'{http://calendarserver.org/ns/}subscribed';
36 36
 
37
-        $server->xml->elementMap['{http://calendarserver.org/ns/}source'] =
38
-            'Sabre\\DAV\\Xml\\Property\\Href';
37
+		$server->xml->elementMap['{http://calendarserver.org/ns/}source'] =
38
+			'Sabre\\DAV\\Xml\\Property\\Href';
39 39
 
40
-        $server->on('propFind', [$this, 'propFind'], 150);
41
-    }
40
+		$server->on('propFind', [$this, 'propFind'], 150);
41
+	}
42 42
 
43
-    /**
44
-     * This method should return a list of server-features.
45
-     *
46
-     * This is for example 'versioning' and is added to the DAV: header
47
-     * in an OPTIONS response.
48
-     *
49
-     * @return array
50
-     */
51
-    public function getFeatures()
52
-    {
53
-        return ['calendarserver-subscribed'];
54
-    }
43
+	/**
44
+	 * This method should return a list of server-features.
45
+	 *
46
+	 * This is for example 'versioning' and is added to the DAV: header
47
+	 * in an OPTIONS response.
48
+	 *
49
+	 * @return array
50
+	 */
51
+	public function getFeatures()
52
+	{
53
+		return ['calendarserver-subscribed'];
54
+	}
55 55
 
56
-    /**
57
-     * Triggered after properties have been fetched.
58
-     */
59
-    public function propFind(PropFind $propFind, INode $node)
60
-    {
61
-        // There's a bunch of properties that must appear as a self-closing
62
-        // xml-element. This event handler ensures that this will be the case.
63
-        $props = [
64
-            '{http://calendarserver.org/ns/}subscribed-strip-alarms',
65
-            '{http://calendarserver.org/ns/}subscribed-strip-attachments',
66
-            '{http://calendarserver.org/ns/}subscribed-strip-todos',
67
-        ];
56
+	/**
57
+	 * Triggered after properties have been fetched.
58
+	 */
59
+	public function propFind(PropFind $propFind, INode $node)
60
+	{
61
+		// There's a bunch of properties that must appear as a self-closing
62
+		// xml-element. This event handler ensures that this will be the case.
63
+		$props = [
64
+			'{http://calendarserver.org/ns/}subscribed-strip-alarms',
65
+			'{http://calendarserver.org/ns/}subscribed-strip-attachments',
66
+			'{http://calendarserver.org/ns/}subscribed-strip-todos',
67
+		];
68 68
 
69
-        foreach ($props as $prop) {
70
-            if (200 === $propFind->getStatus($prop)) {
71
-                $propFind->set($prop, '', 200);
72
-            }
73
-        }
74
-    }
69
+		foreach ($props as $prop) {
70
+			if (200 === $propFind->getStatus($prop)) {
71
+				$propFind->set($prop, '', 200);
72
+			}
73
+		}
74
+	}
75 75
 
76
-    /**
77
-     * Returns a plugin name.
78
-     *
79
-     * Using this name other plugins will be able to access other plugins
80
-     * using \Sabre\DAV\Server::getPlugin
81
-     *
82
-     * @return string
83
-     */
84
-    public function getPluginName()
85
-    {
86
-        return 'subscriptions';
87
-    }
76
+	/**
77
+	 * Returns a plugin name.
78
+	 *
79
+	 * Using this name other plugins will be able to access other plugins
80
+	 * using \Sabre\DAV\Server::getPlugin
81
+	 *
82
+	 * @return string
83
+	 */
84
+	public function getPluginName()
85
+	{
86
+		return 'subscriptions';
87
+	}
88 88
 
89
-    /**
90
-     * Returns a bunch of meta-data about the plugin.
91
-     *
92
-     * Providing this information is optional, and is mainly displayed by the
93
-     * Browser plugin.
94
-     *
95
-     * The description key in the returned array may contain html and will not
96
-     * be sanitized.
97
-     *
98
-     * @return array
99
-     */
100
-    public function getPluginInfo()
101
-    {
102
-        return [
103
-            'name' => $this->getPluginName(),
104
-            'description' => 'This plugin allows users to store iCalendar subscriptions in their calendar-home.',
105
-            'link' => null,
106
-        ];
107
-    }
89
+	/**
90
+	 * Returns a bunch of meta-data about the plugin.
91
+	 *
92
+	 * Providing this information is optional, and is mainly displayed by the
93
+	 * Browser plugin.
94
+	 *
95
+	 * The description key in the returned array may contain html and will not
96
+	 * be sanitized.
97
+	 *
98
+	 * @return array
99
+	 */
100
+	public function getPluginInfo()
101
+	{
102
+		return [
103
+			'name' => $this->getPluginName(),
104
+			'description' => 'This plugin allows users to store iCalendar subscriptions in their calendar-home.',
105
+			'link' => null,
106
+		];
107
+	}
108 108
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Subscriptions/Subscription.php 1 patch
Indentation   +179 added lines, -179 removed lines patch added patch discarded remove patch
@@ -22,183 +22,183 @@
 block discarded – undo
22 22
  */
23 23
 class Subscription extends Collection implements ISubscription, IACL
24 24
 {
25
-    use ACLTrait;
26
-
27
-    /**
28
-     * caldavBackend.
29
-     *
30
-     * @var SubscriptionSupport
31
-     */
32
-    protected $caldavBackend;
33
-
34
-    /**
35
-     * subscriptionInfo.
36
-     *
37
-     * @var array
38
-     */
39
-    protected $subscriptionInfo;
40
-
41
-    /**
42
-     * Constructor.
43
-     */
44
-    public function __construct(SubscriptionSupport $caldavBackend, array $subscriptionInfo)
45
-    {
46
-        $this->caldavBackend = $caldavBackend;
47
-        $this->subscriptionInfo = $subscriptionInfo;
48
-
49
-        $required = [
50
-            'id',
51
-            'uri',
52
-            'principaluri',
53
-            'source',
54
-            ];
55
-
56
-        foreach ($required as $r) {
57
-            if (!isset($subscriptionInfo[$r])) {
58
-                throw new \InvalidArgumentException('The '.$r.' field is required when creating a subscription node');
59
-            }
60
-        }
61
-    }
62
-
63
-    /**
64
-     * Returns the name of the node.
65
-     *
66
-     * This is used to generate the url.
67
-     *
68
-     * @return string
69
-     */
70
-    public function getName()
71
-    {
72
-        return $this->subscriptionInfo['uri'];
73
-    }
74
-
75
-    /**
76
-     * Returns the last modification time.
77
-     *
78
-     * @return int|null
79
-     */
80
-    public function getLastModified()
81
-    {
82
-        if (isset($this->subscriptionInfo['lastmodified'])) {
83
-            return $this->subscriptionInfo['lastmodified'];
84
-        }
85
-    }
86
-
87
-    /**
88
-     * Deletes the current node.
89
-     */
90
-    public function delete()
91
-    {
92
-        $this->caldavBackend->deleteSubscription(
93
-            $this->subscriptionInfo['id']
94
-        );
95
-    }
96
-
97
-    /**
98
-     * Returns an array with all the child nodes.
99
-     *
100
-     * @return \Sabre\DAV\INode[]
101
-     */
102
-    public function getChildren()
103
-    {
104
-        return [];
105
-    }
106
-
107
-    /**
108
-     * Updates properties on this node.
109
-     *
110
-     * This method received a PropPatch object, which contains all the
111
-     * information about the update.
112
-     *
113
-     * To update specific properties, call the 'handle' method on this object.
114
-     * Read the PropPatch documentation for more information.
115
-     */
116
-    public function propPatch(PropPatch $propPatch)
117
-    {
118
-        return $this->caldavBackend->updateSubscription(
119
-            $this->subscriptionInfo['id'],
120
-            $propPatch
121
-        );
122
-    }
123
-
124
-    /**
125
-     * Returns a list of properties for this nodes.
126
-     *
127
-     * The properties list is a list of propertynames the client requested,
128
-     * encoded in clark-notation {xmlnamespace}tagname.
129
-     *
130
-     * If the array is empty, it means 'all properties' were requested.
131
-     *
132
-     * Note that it's fine to liberally give properties back, instead of
133
-     * conforming to the list of requested properties.
134
-     * The Server class will filter out the extra.
135
-     *
136
-     * @param array $properties
137
-     *
138
-     * @return array
139
-     */
140
-    public function getProperties($properties)
141
-    {
142
-        $r = [];
143
-
144
-        foreach ($properties as $prop) {
145
-            switch ($prop) {
146
-                case '{http://calendarserver.org/ns/}source':
147
-                    $r[$prop] = new Href($this->subscriptionInfo['source']);
148
-                    break;
149
-                default:
150
-                    if (array_key_exists($prop, $this->subscriptionInfo)) {
151
-                        $r[$prop] = $this->subscriptionInfo[$prop];
152
-                    }
153
-                    break;
154
-            }
155
-        }
156
-
157
-        return $r;
158
-    }
159
-
160
-    /**
161
-     * Returns the owner principal.
162
-     *
163
-     * This must be a url to a principal, or null if there's no owner
164
-     *
165
-     * @return string|null
166
-     */
167
-    public function getOwner()
168
-    {
169
-        return $this->subscriptionInfo['principaluri'];
170
-    }
171
-
172
-    /**
173
-     * Returns a list of ACE's for this node.
174
-     *
175
-     * Each ACE has the following properties:
176
-     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
177
-     *     currently the only supported privileges
178
-     *   * 'principal', a url to the principal who owns the node
179
-     *   * 'protected' (optional), indicating that this ACE is not allowed to
180
-     *      be updated.
181
-     *
182
-     * @return array
183
-     */
184
-    public function getACL()
185
-    {
186
-        return [
187
-            [
188
-                'privilege' => '{DAV:}all',
189
-                'principal' => $this->getOwner(),
190
-                'protected' => true,
191
-            ],
192
-            [
193
-                'privilege' => '{DAV:}all',
194
-                'principal' => $this->getOwner().'/calendar-proxy-write',
195
-                'protected' => true,
196
-            ],
197
-            [
198
-                'privilege' => '{DAV:}read',
199
-                'principal' => $this->getOwner().'/calendar-proxy-read',
200
-                'protected' => true,
201
-            ],
202
-        ];
203
-    }
25
+	use ACLTrait;
26
+
27
+	/**
28
+	 * caldavBackend.
29
+	 *
30
+	 * @var SubscriptionSupport
31
+	 */
32
+	protected $caldavBackend;
33
+
34
+	/**
35
+	 * subscriptionInfo.
36
+	 *
37
+	 * @var array
38
+	 */
39
+	protected $subscriptionInfo;
40
+
41
+	/**
42
+	 * Constructor.
43
+	 */
44
+	public function __construct(SubscriptionSupport $caldavBackend, array $subscriptionInfo)
45
+	{
46
+		$this->caldavBackend = $caldavBackend;
47
+		$this->subscriptionInfo = $subscriptionInfo;
48
+
49
+		$required = [
50
+			'id',
51
+			'uri',
52
+			'principaluri',
53
+			'source',
54
+			];
55
+
56
+		foreach ($required as $r) {
57
+			if (!isset($subscriptionInfo[$r])) {
58
+				throw new \InvalidArgumentException('The '.$r.' field is required when creating a subscription node');
59
+			}
60
+		}
61
+	}
62
+
63
+	/**
64
+	 * Returns the name of the node.
65
+	 *
66
+	 * This is used to generate the url.
67
+	 *
68
+	 * @return string
69
+	 */
70
+	public function getName()
71
+	{
72
+		return $this->subscriptionInfo['uri'];
73
+	}
74
+
75
+	/**
76
+	 * Returns the last modification time.
77
+	 *
78
+	 * @return int|null
79
+	 */
80
+	public function getLastModified()
81
+	{
82
+		if (isset($this->subscriptionInfo['lastmodified'])) {
83
+			return $this->subscriptionInfo['lastmodified'];
84
+		}
85
+	}
86
+
87
+	/**
88
+	 * Deletes the current node.
89
+	 */
90
+	public function delete()
91
+	{
92
+		$this->caldavBackend->deleteSubscription(
93
+			$this->subscriptionInfo['id']
94
+		);
95
+	}
96
+
97
+	/**
98
+	 * Returns an array with all the child nodes.
99
+	 *
100
+	 * @return \Sabre\DAV\INode[]
101
+	 */
102
+	public function getChildren()
103
+	{
104
+		return [];
105
+	}
106
+
107
+	/**
108
+	 * Updates properties on this node.
109
+	 *
110
+	 * This method received a PropPatch object, which contains all the
111
+	 * information about the update.
112
+	 *
113
+	 * To update specific properties, call the 'handle' method on this object.
114
+	 * Read the PropPatch documentation for more information.
115
+	 */
116
+	public function propPatch(PropPatch $propPatch)
117
+	{
118
+		return $this->caldavBackend->updateSubscription(
119
+			$this->subscriptionInfo['id'],
120
+			$propPatch
121
+		);
122
+	}
123
+
124
+	/**
125
+	 * Returns a list of properties for this nodes.
126
+	 *
127
+	 * The properties list is a list of propertynames the client requested,
128
+	 * encoded in clark-notation {xmlnamespace}tagname.
129
+	 *
130
+	 * If the array is empty, it means 'all properties' were requested.
131
+	 *
132
+	 * Note that it's fine to liberally give properties back, instead of
133
+	 * conforming to the list of requested properties.
134
+	 * The Server class will filter out the extra.
135
+	 *
136
+	 * @param array $properties
137
+	 *
138
+	 * @return array
139
+	 */
140
+	public function getProperties($properties)
141
+	{
142
+		$r = [];
143
+
144
+		foreach ($properties as $prop) {
145
+			switch ($prop) {
146
+				case '{http://calendarserver.org/ns/}source':
147
+					$r[$prop] = new Href($this->subscriptionInfo['source']);
148
+					break;
149
+				default:
150
+					if (array_key_exists($prop, $this->subscriptionInfo)) {
151
+						$r[$prop] = $this->subscriptionInfo[$prop];
152
+					}
153
+					break;
154
+			}
155
+		}
156
+
157
+		return $r;
158
+	}
159
+
160
+	/**
161
+	 * Returns the owner principal.
162
+	 *
163
+	 * This must be a url to a principal, or null if there's no owner
164
+	 *
165
+	 * @return string|null
166
+	 */
167
+	public function getOwner()
168
+	{
169
+		return $this->subscriptionInfo['principaluri'];
170
+	}
171
+
172
+	/**
173
+	 * Returns a list of ACE's for this node.
174
+	 *
175
+	 * Each ACE has the following properties:
176
+	 *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
177
+	 *     currently the only supported privileges
178
+	 *   * 'principal', a url to the principal who owns the node
179
+	 *   * 'protected' (optional), indicating that this ACE is not allowed to
180
+	 *      be updated.
181
+	 *
182
+	 * @return array
183
+	 */
184
+	public function getACL()
185
+	{
186
+		return [
187
+			[
188
+				'privilege' => '{DAV:}all',
189
+				'principal' => $this->getOwner(),
190
+				'protected' => true,
191
+			],
192
+			[
193
+				'privilege' => '{DAV:}all',
194
+				'principal' => $this->getOwner().'/calendar-proxy-write',
195
+				'protected' => true,
196
+			],
197
+			[
198
+				'privilege' => '{DAV:}read',
199
+				'principal' => $this->getOwner().'/calendar-proxy-read',
200
+				'protected' => true,
201
+			],
202
+		];
203
+	}
204 204
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Xml/Notification/Invite.php 1 patch
Indentation   +266 added lines, -266 removed lines patch added patch discarded remove patch
@@ -21,270 +21,270 @@
 block discarded – undo
21 21
  */
22 22
 class Invite implements NotificationInterface
23 23
 {
24
-    /**
25
-     * A unique id for the message.
26
-     *
27
-     * @var string
28
-     */
29
-    protected $id;
30
-
31
-    /**
32
-     * Timestamp of the notification.
33
-     *
34
-     * @var \DateTime
35
-     */
36
-    protected $dtStamp;
37
-
38
-    /**
39
-     * A url to the recipient of the notification. This can be an email
40
-     * address (mailto:), or a principal url.
41
-     *
42
-     * @var string
43
-     */
44
-    protected $href;
45
-
46
-    /**
47
-     * The type of message, see the SharingPlugin::STATUS_* constants.
48
-     *
49
-     * @var int
50
-     */
51
-    protected $type;
52
-
53
-    /**
54
-     * True if access to a calendar is read-only.
55
-     *
56
-     * @var bool
57
-     */
58
-    protected $readOnly;
59
-
60
-    /**
61
-     * A url to the shared calendar.
62
-     *
63
-     * @var string
64
-     */
65
-    protected $hostUrl;
66
-
67
-    /**
68
-     * Url to the sharer of the calendar.
69
-     *
70
-     * @var string
71
-     */
72
-    protected $organizer;
73
-
74
-    /**
75
-     * The name of the sharer.
76
-     *
77
-     * @var string
78
-     */
79
-    protected $commonName;
80
-
81
-    /**
82
-     * The name of the sharer.
83
-     *
84
-     * @var string
85
-     */
86
-    protected $firstName;
87
-
88
-    /**
89
-     * The name of the sharer.
90
-     *
91
-     * @var string
92
-     */
93
-    protected $lastName;
94
-
95
-    /**
96
-     * A description of the share request.
97
-     *
98
-     * @var string
99
-     */
100
-    protected $summary;
101
-
102
-    /**
103
-     * The Etag for the notification.
104
-     *
105
-     * @var string
106
-     */
107
-    protected $etag;
108
-
109
-    /**
110
-     * The list of supported components.
111
-     *
112
-     * @var CalDAV\Xml\Property\SupportedCalendarComponentSet
113
-     */
114
-    protected $supportedComponents;
115
-
116
-    /**
117
-     * Creates the Invite notification.
118
-     *
119
-     * This constructor receives an array with the following elements:
120
-     *
121
-     *   * id           - A unique id
122
-     *   * etag         - The etag
123
-     *   * dtStamp      - A DateTime object with a timestamp for the notification.
124
-     *   * type         - The type of notification, see SharingPlugin::STATUS_*
125
-     *                    constants for details.
126
-     *   * readOnly     - This must be set to true, if this is an invite for
127
-     *                    read-only access to a calendar.
128
-     *   * hostUrl      - A url to the shared calendar.
129
-     *   * organizer    - Url to the sharer principal.
130
-     *   * commonName   - The real name of the sharer (optional).
131
-     *   * firstName    - The first name of the sharer (optional).
132
-     *   * lastName     - The last name of the sharer (optional).
133
-     *   * summary      - Description of the share, can be the same as the
134
-     *                    calendar, but may also be modified (optional).
135
-     *   * supportedComponents - An instance of
136
-     *                    Sabre\CalDAV\Property\SupportedCalendarComponentSet.
137
-     *                    This allows the client to determine which components
138
-     *                    will be supported in the shared calendar. This is
139
-     *                    also optional.
140
-     *
141
-     * @param array $values All the options
142
-     */
143
-    public function __construct(array $values)
144
-    {
145
-        $required = [
146
-            'id',
147
-            'etag',
148
-            'href',
149
-            'dtStamp',
150
-            'type',
151
-            'readOnly',
152
-            'hostUrl',
153
-            'organizer',
154
-        ];
155
-        foreach ($required as $item) {
156
-            if (!isset($values[$item])) {
157
-                throw new \InvalidArgumentException($item.' is a required constructor option');
158
-            }
159
-        }
160
-
161
-        foreach ($values as $key => $value) {
162
-            if (!property_exists($this, $key)) {
163
-                throw new \InvalidArgumentException('Unknown option: '.$key);
164
-            }
165
-            $this->$key = $value;
166
-        }
167
-    }
168
-
169
-    /**
170
-     * The xmlSerialize method is called during xml writing.
171
-     *
172
-     * Use the $writer argument to write its own xml serialization.
173
-     *
174
-     * An important note: do _not_ create a parent element. Any element
175
-     * implementing XmlSerializable should only ever write what's considered
176
-     * its 'inner xml'.
177
-     *
178
-     * The parent of the current element is responsible for writing a
179
-     * containing element.
180
-     *
181
-     * This allows serializers to be re-used for different element names.
182
-     *
183
-     * If you are opening new elements, you must also close them again.
184
-     */
185
-    public function xmlSerialize(Writer $writer)
186
-    {
187
-        $writer->writeElement('{'.CalDAV\Plugin::NS_CALENDARSERVER.'}invite-notification');
188
-    }
189
-
190
-    /**
191
-     * This method serializes the entire notification, as it is used in the
192
-     * response body.
193
-     */
194
-    public function xmlSerializeFull(Writer $writer)
195
-    {
196
-        $cs = '{'.CalDAV\Plugin::NS_CALENDARSERVER.'}';
197
-
198
-        $this->dtStamp->setTimezone(new \DateTimeZone('GMT'));
199
-        $writer->writeElement($cs.'dtstamp', $this->dtStamp->format('Ymd\\THis\\Z'));
200
-
201
-        $writer->startElement($cs.'invite-notification');
202
-
203
-        $writer->writeElement($cs.'uid', $this->id);
204
-        $writer->writeElement('{DAV:}href', $this->href);
205
-
206
-        switch ($this->type) {
207
-            case DAV\Sharing\Plugin::INVITE_ACCEPTED:
208
-                $writer->writeElement($cs.'invite-accepted');
209
-                break;
210
-            case DAV\Sharing\Plugin::INVITE_NORESPONSE:
211
-                $writer->writeElement($cs.'invite-noresponse');
212
-                break;
213
-        }
214
-
215
-        $writer->writeElement($cs.'hosturl', [
216
-            '{DAV:}href' => $writer->contextUri.$this->hostUrl,
217
-            ]);
218
-
219
-        if ($this->summary) {
220
-            $writer->writeElement($cs.'summary', $this->summary);
221
-        }
222
-
223
-        $writer->startElement($cs.'access');
224
-        if ($this->readOnly) {
225
-            $writer->writeElement($cs.'read');
226
-        } else {
227
-            $writer->writeElement($cs.'read-write');
228
-        }
229
-        $writer->endElement(); // access
230
-
231
-        $writer->startElement($cs.'organizer');
232
-        // If the organizer contains a 'mailto:' part, it means it should be
233
-        // treated as absolute.
234
-        if ('mailto:' === strtolower(substr($this->organizer, 0, 7))) {
235
-            $writer->writeElement('{DAV:}href', $this->organizer);
236
-        } else {
237
-            $writer->writeElement('{DAV:}href', $writer->contextUri.$this->organizer);
238
-        }
239
-        if ($this->commonName) {
240
-            $writer->writeElement($cs.'common-name', $this->commonName);
241
-        }
242
-        if ($this->firstName) {
243
-            $writer->writeElement($cs.'first-name', $this->firstName);
244
-        }
245
-        if ($this->lastName) {
246
-            $writer->writeElement($cs.'last-name', $this->lastName);
247
-        }
248
-        $writer->endElement(); // organizer
249
-
250
-        if ($this->commonName) {
251
-            $writer->writeElement($cs.'organizer-cn', $this->commonName);
252
-        }
253
-        if ($this->firstName) {
254
-            $writer->writeElement($cs.'organizer-first', $this->firstName);
255
-        }
256
-        if ($this->lastName) {
257
-            $writer->writeElement($cs.'organizer-last', $this->lastName);
258
-        }
259
-        if ($this->supportedComponents) {
260
-            $writer->writeElement('{'.CalDAV\Plugin::NS_CALDAV.'}supported-calendar-component-set', $this->supportedComponents);
261
-        }
262
-
263
-        $writer->endElement(); // invite-notification
264
-    }
265
-
266
-    /**
267
-     * Returns a unique id for this notification.
268
-     *
269
-     * This is just the base url. This should generally be some kind of unique
270
-     * id.
271
-     *
272
-     * @return string
273
-     */
274
-    public function getId()
275
-    {
276
-        return $this->id;
277
-    }
278
-
279
-    /**
280
-     * Returns the ETag for this notification.
281
-     *
282
-     * The ETag must be surrounded by literal double-quotes.
283
-     *
284
-     * @return string
285
-     */
286
-    public function getETag()
287
-    {
288
-        return $this->etag;
289
-    }
24
+	/**
25
+	 * A unique id for the message.
26
+	 *
27
+	 * @var string
28
+	 */
29
+	protected $id;
30
+
31
+	/**
32
+	 * Timestamp of the notification.
33
+	 *
34
+	 * @var \DateTime
35
+	 */
36
+	protected $dtStamp;
37
+
38
+	/**
39
+	 * A url to the recipient of the notification. This can be an email
40
+	 * address (mailto:), or a principal url.
41
+	 *
42
+	 * @var string
43
+	 */
44
+	protected $href;
45
+
46
+	/**
47
+	 * The type of message, see the SharingPlugin::STATUS_* constants.
48
+	 *
49
+	 * @var int
50
+	 */
51
+	protected $type;
52
+
53
+	/**
54
+	 * True if access to a calendar is read-only.
55
+	 *
56
+	 * @var bool
57
+	 */
58
+	protected $readOnly;
59
+
60
+	/**
61
+	 * A url to the shared calendar.
62
+	 *
63
+	 * @var string
64
+	 */
65
+	protected $hostUrl;
66
+
67
+	/**
68
+	 * Url to the sharer of the calendar.
69
+	 *
70
+	 * @var string
71
+	 */
72
+	protected $organizer;
73
+
74
+	/**
75
+	 * The name of the sharer.
76
+	 *
77
+	 * @var string
78
+	 */
79
+	protected $commonName;
80
+
81
+	/**
82
+	 * The name of the sharer.
83
+	 *
84
+	 * @var string
85
+	 */
86
+	protected $firstName;
87
+
88
+	/**
89
+	 * The name of the sharer.
90
+	 *
91
+	 * @var string
92
+	 */
93
+	protected $lastName;
94
+
95
+	/**
96
+	 * A description of the share request.
97
+	 *
98
+	 * @var string
99
+	 */
100
+	protected $summary;
101
+
102
+	/**
103
+	 * The Etag for the notification.
104
+	 *
105
+	 * @var string
106
+	 */
107
+	protected $etag;
108
+
109
+	/**
110
+	 * The list of supported components.
111
+	 *
112
+	 * @var CalDAV\Xml\Property\SupportedCalendarComponentSet
113
+	 */
114
+	protected $supportedComponents;
115
+
116
+	/**
117
+	 * Creates the Invite notification.
118
+	 *
119
+	 * This constructor receives an array with the following elements:
120
+	 *
121
+	 *   * id           - A unique id
122
+	 *   * etag         - The etag
123
+	 *   * dtStamp      - A DateTime object with a timestamp for the notification.
124
+	 *   * type         - The type of notification, see SharingPlugin::STATUS_*
125
+	 *                    constants for details.
126
+	 *   * readOnly     - This must be set to true, if this is an invite for
127
+	 *                    read-only access to a calendar.
128
+	 *   * hostUrl      - A url to the shared calendar.
129
+	 *   * organizer    - Url to the sharer principal.
130
+	 *   * commonName   - The real name of the sharer (optional).
131
+	 *   * firstName    - The first name of the sharer (optional).
132
+	 *   * lastName     - The last name of the sharer (optional).
133
+	 *   * summary      - Description of the share, can be the same as the
134
+	 *                    calendar, but may also be modified (optional).
135
+	 *   * supportedComponents - An instance of
136
+	 *                    Sabre\CalDAV\Property\SupportedCalendarComponentSet.
137
+	 *                    This allows the client to determine which components
138
+	 *                    will be supported in the shared calendar. This is
139
+	 *                    also optional.
140
+	 *
141
+	 * @param array $values All the options
142
+	 */
143
+	public function __construct(array $values)
144
+	{
145
+		$required = [
146
+			'id',
147
+			'etag',
148
+			'href',
149
+			'dtStamp',
150
+			'type',
151
+			'readOnly',
152
+			'hostUrl',
153
+			'organizer',
154
+		];
155
+		foreach ($required as $item) {
156
+			if (!isset($values[$item])) {
157
+				throw new \InvalidArgumentException($item.' is a required constructor option');
158
+			}
159
+		}
160
+
161
+		foreach ($values as $key => $value) {
162
+			if (!property_exists($this, $key)) {
163
+				throw new \InvalidArgumentException('Unknown option: '.$key);
164
+			}
165
+			$this->$key = $value;
166
+		}
167
+	}
168
+
169
+	/**
170
+	 * The xmlSerialize method is called during xml writing.
171
+	 *
172
+	 * Use the $writer argument to write its own xml serialization.
173
+	 *
174
+	 * An important note: do _not_ create a parent element. Any element
175
+	 * implementing XmlSerializable should only ever write what's considered
176
+	 * its 'inner xml'.
177
+	 *
178
+	 * The parent of the current element is responsible for writing a
179
+	 * containing element.
180
+	 *
181
+	 * This allows serializers to be re-used for different element names.
182
+	 *
183
+	 * If you are opening new elements, you must also close them again.
184
+	 */
185
+	public function xmlSerialize(Writer $writer)
186
+	{
187
+		$writer->writeElement('{'.CalDAV\Plugin::NS_CALENDARSERVER.'}invite-notification');
188
+	}
189
+
190
+	/**
191
+	 * This method serializes the entire notification, as it is used in the
192
+	 * response body.
193
+	 */
194
+	public function xmlSerializeFull(Writer $writer)
195
+	{
196
+		$cs = '{'.CalDAV\Plugin::NS_CALENDARSERVER.'}';
197
+
198
+		$this->dtStamp->setTimezone(new \DateTimeZone('GMT'));
199
+		$writer->writeElement($cs.'dtstamp', $this->dtStamp->format('Ymd\\THis\\Z'));
200
+
201
+		$writer->startElement($cs.'invite-notification');
202
+
203
+		$writer->writeElement($cs.'uid', $this->id);
204
+		$writer->writeElement('{DAV:}href', $this->href);
205
+
206
+		switch ($this->type) {
207
+			case DAV\Sharing\Plugin::INVITE_ACCEPTED:
208
+				$writer->writeElement($cs.'invite-accepted');
209
+				break;
210
+			case DAV\Sharing\Plugin::INVITE_NORESPONSE:
211
+				$writer->writeElement($cs.'invite-noresponse');
212
+				break;
213
+		}
214
+
215
+		$writer->writeElement($cs.'hosturl', [
216
+			'{DAV:}href' => $writer->contextUri.$this->hostUrl,
217
+			]);
218
+
219
+		if ($this->summary) {
220
+			$writer->writeElement($cs.'summary', $this->summary);
221
+		}
222
+
223
+		$writer->startElement($cs.'access');
224
+		if ($this->readOnly) {
225
+			$writer->writeElement($cs.'read');
226
+		} else {
227
+			$writer->writeElement($cs.'read-write');
228
+		}
229
+		$writer->endElement(); // access
230
+
231
+		$writer->startElement($cs.'organizer');
232
+		// If the organizer contains a 'mailto:' part, it means it should be
233
+		// treated as absolute.
234
+		if ('mailto:' === strtolower(substr($this->organizer, 0, 7))) {
235
+			$writer->writeElement('{DAV:}href', $this->organizer);
236
+		} else {
237
+			$writer->writeElement('{DAV:}href', $writer->contextUri.$this->organizer);
238
+		}
239
+		if ($this->commonName) {
240
+			$writer->writeElement($cs.'common-name', $this->commonName);
241
+		}
242
+		if ($this->firstName) {
243
+			$writer->writeElement($cs.'first-name', $this->firstName);
244
+		}
245
+		if ($this->lastName) {
246
+			$writer->writeElement($cs.'last-name', $this->lastName);
247
+		}
248
+		$writer->endElement(); // organizer
249
+
250
+		if ($this->commonName) {
251
+			$writer->writeElement($cs.'organizer-cn', $this->commonName);
252
+		}
253
+		if ($this->firstName) {
254
+			$writer->writeElement($cs.'organizer-first', $this->firstName);
255
+		}
256
+		if ($this->lastName) {
257
+			$writer->writeElement($cs.'organizer-last', $this->lastName);
258
+		}
259
+		if ($this->supportedComponents) {
260
+			$writer->writeElement('{'.CalDAV\Plugin::NS_CALDAV.'}supported-calendar-component-set', $this->supportedComponents);
261
+		}
262
+
263
+		$writer->endElement(); // invite-notification
264
+	}
265
+
266
+	/**
267
+	 * Returns a unique id for this notification.
268
+	 *
269
+	 * This is just the base url. This should generally be some kind of unique
270
+	 * id.
271
+	 *
272
+	 * @return string
273
+	 */
274
+	public function getId()
275
+	{
276
+		return $this->id;
277
+	}
278
+
279
+	/**
280
+	 * Returns the ETag for this notification.
281
+	 *
282
+	 * The ETag must be surrounded by literal double-quotes.
283
+	 *
284
+	 * @return string
285
+	 */
286
+	public function getETag()
287
+	{
288
+		return $this->etag;
289
+	}
290 290
 }
Please login to merge, or discard this patch.