Completed
Branch develop (aa6d31)
by
unknown
24:05
created
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Principal/ProxyWrite.php 1 patch
Indentation   +138 added lines, -138 removed lines patch added patch discarded remove patch
@@ -20,142 +20,142 @@
 block discarded – undo
20 20
  */
21 21
 class ProxyWrite implements IProxyWrite
22 22
 {
23
-    /**
24
-     * Parent principal information.
25
-     *
26
-     * @var array
27
-     */
28
-    protected $principalInfo;
29
-
30
-    /**
31
-     * Principal Backend.
32
-     *
33
-     * @var DAVACL\PrincipalBackend\BackendInterface
34
-     */
35
-    protected $principalBackend;
36
-
37
-    /**
38
-     * Creates the object.
39
-     *
40
-     * Note that you MUST supply the parent principal information.
41
-     */
42
-    public function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, array $principalInfo)
43
-    {
44
-        $this->principalInfo = $principalInfo;
45
-        $this->principalBackend = $principalBackend;
46
-    }
47
-
48
-    /**
49
-     * Returns this principals name.
50
-     *
51
-     * @return string
52
-     */
53
-    public function getName()
54
-    {
55
-        return 'calendar-proxy-write';
56
-    }
57
-
58
-    /**
59
-     * Returns the last modification time.
60
-     */
61
-    public function getLastModified()
62
-    {
63
-        return null;
64
-    }
65
-
66
-    /**
67
-     * Deletes the current node.
68
-     *
69
-     * @throws DAV\Exception\Forbidden
70
-     */
71
-    public function delete()
72
-    {
73
-        throw new DAV\Exception\Forbidden('Permission denied to delete node');
74
-    }
75
-
76
-    /**
77
-     * Renames the node.
78
-     *
79
-     * @param string $name The new name
80
-     *
81
-     * @throws DAV\Exception\Forbidden
82
-     */
83
-    public function setName($name)
84
-    {
85
-        throw new DAV\Exception\Forbidden('Permission denied to rename file');
86
-    }
87
-
88
-    /**
89
-     * Returns a list of alternative urls for a principal.
90
-     *
91
-     * This can for example be an email address, or ldap url.
92
-     *
93
-     * @return array
94
-     */
95
-    public function getAlternateUriSet()
96
-    {
97
-        return [];
98
-    }
99
-
100
-    /**
101
-     * Returns the full principal url.
102
-     *
103
-     * @return string
104
-     */
105
-    public function getPrincipalUrl()
106
-    {
107
-        return $this->principalInfo['uri'].'/'.$this->getName();
108
-    }
109
-
110
-    /**
111
-     * Returns the list of group members.
112
-     *
113
-     * If this principal is a group, this function should return
114
-     * all member principal uri's for the group.
115
-     *
116
-     * @return array
117
-     */
118
-    public function getGroupMemberSet()
119
-    {
120
-        return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
121
-    }
122
-
123
-    /**
124
-     * Returns the list of groups this principal is member of.
125
-     *
126
-     * If this principal is a member of a (list of) groups, this function
127
-     * should return a list of principal uri's for it's members.
128
-     *
129
-     * @return array
130
-     */
131
-    public function getGroupMembership()
132
-    {
133
-        return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
134
-    }
135
-
136
-    /**
137
-     * Sets a list of group members.
138
-     *
139
-     * If this principal is a group, this method sets all the group members.
140
-     * The list of members is always overwritten, never appended to.
141
-     *
142
-     * This method should throw an exception if the members could not be set.
143
-     */
144
-    public function setGroupMemberSet(array $principals)
145
-    {
146
-        $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
147
-    }
148
-
149
-    /**
150
-     * Returns the displayname.
151
-     *
152
-     * This should be a human readable name for the principal.
153
-     * If none is available, return the nodename.
154
-     *
155
-     * @return string
156
-     */
157
-    public function getDisplayName()
158
-    {
159
-        return $this->getName();
160
-    }
23
+	/**
24
+	 * Parent principal information.
25
+	 *
26
+	 * @var array
27
+	 */
28
+	protected $principalInfo;
29
+
30
+	/**
31
+	 * Principal Backend.
32
+	 *
33
+	 * @var DAVACL\PrincipalBackend\BackendInterface
34
+	 */
35
+	protected $principalBackend;
36
+
37
+	/**
38
+	 * Creates the object.
39
+	 *
40
+	 * Note that you MUST supply the parent principal information.
41
+	 */
42
+	public function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, array $principalInfo)
43
+	{
44
+		$this->principalInfo = $principalInfo;
45
+		$this->principalBackend = $principalBackend;
46
+	}
47
+
48
+	/**
49
+	 * Returns this principals name.
50
+	 *
51
+	 * @return string
52
+	 */
53
+	public function getName()
54
+	{
55
+		return 'calendar-proxy-write';
56
+	}
57
+
58
+	/**
59
+	 * Returns the last modification time.
60
+	 */
61
+	public function getLastModified()
62
+	{
63
+		return null;
64
+	}
65
+
66
+	/**
67
+	 * Deletes the current node.
68
+	 *
69
+	 * @throws DAV\Exception\Forbidden
70
+	 */
71
+	public function delete()
72
+	{
73
+		throw new DAV\Exception\Forbidden('Permission denied to delete node');
74
+	}
75
+
76
+	/**
77
+	 * Renames the node.
78
+	 *
79
+	 * @param string $name The new name
80
+	 *
81
+	 * @throws DAV\Exception\Forbidden
82
+	 */
83
+	public function setName($name)
84
+	{
85
+		throw new DAV\Exception\Forbidden('Permission denied to rename file');
86
+	}
87
+
88
+	/**
89
+	 * Returns a list of alternative urls for a principal.
90
+	 *
91
+	 * This can for example be an email address, or ldap url.
92
+	 *
93
+	 * @return array
94
+	 */
95
+	public function getAlternateUriSet()
96
+	{
97
+		return [];
98
+	}
99
+
100
+	/**
101
+	 * Returns the full principal url.
102
+	 *
103
+	 * @return string
104
+	 */
105
+	public function getPrincipalUrl()
106
+	{
107
+		return $this->principalInfo['uri'].'/'.$this->getName();
108
+	}
109
+
110
+	/**
111
+	 * Returns the list of group members.
112
+	 *
113
+	 * If this principal is a group, this function should return
114
+	 * all member principal uri's for the group.
115
+	 *
116
+	 * @return array
117
+	 */
118
+	public function getGroupMemberSet()
119
+	{
120
+		return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl());
121
+	}
122
+
123
+	/**
124
+	 * Returns the list of groups this principal is member of.
125
+	 *
126
+	 * If this principal is a member of a (list of) groups, this function
127
+	 * should return a list of principal uri's for it's members.
128
+	 *
129
+	 * @return array
130
+	 */
131
+	public function getGroupMembership()
132
+	{
133
+		return $this->principalBackend->getGroupMembership($this->getPrincipalUrl());
134
+	}
135
+
136
+	/**
137
+	 * Sets a list of group members.
138
+	 *
139
+	 * If this principal is a group, this method sets all the group members.
140
+	 * The list of members is always overwritten, never appended to.
141
+	 *
142
+	 * This method should throw an exception if the members could not be set.
143
+	 */
144
+	public function setGroupMemberSet(array $principals)
145
+	{
146
+		$this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
147
+	}
148
+
149
+	/**
150
+	 * Returns the displayname.
151
+	 *
152
+	 * This should be a human readable name for the principal.
153
+	 * If none is available, return the nodename.
154
+	 *
155
+	 * @return string
156
+	 */
157
+	public function getDisplayName()
158
+	{
159
+		return $this->getName();
160
+	}
161 161
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/ICSExportPlugin.php 2 patches
Indentation   +326 added lines, -326 removed lines patch added patch discarded remove patch
@@ -48,330 +48,330 @@
 block discarded – undo
48 48
  */
49 49
 class ICSExportPlugin extends DAV\ServerPlugin
50 50
 {
51
-    /**
52
-     * Reference to Server class.
53
-     *
54
-     * @var \Sabre\DAV\Server
55
-     */
56
-    protected $server;
57
-
58
-    /**
59
-     * Initializes the plugin and registers event handlers.
60
-     *
61
-     * @param \Sabre\DAV\Server $server
62
-     */
63
-    public function initialize(DAV\Server $server)
64
-    {
65
-        $this->server = $server;
66
-        $server->on('method:GET', [$this, 'httpGet'], 90);
67
-        $server->on('browserButtonActions', function ($path, $node, &$actions) {
68
-            if ($node instanceof ICalendar) {
69
-                $actions .= '<a href="'.htmlspecialchars($path, ENT_QUOTES, 'UTF-8').'?export"><span class="oi" data-glyph="calendar"></span></a>';
70
-            }
71
-        });
72
-    }
73
-
74
-    /**
75
-     * Intercepts GET requests on calendar urls ending with ?export.
76
-     *
77
-     * @throws BadRequest
78
-     * @throws DAV\Exception\NotFound
79
-     * @throws VObject\InvalidDataException
80
-     *
81
-     * @return bool
82
-     */
83
-    public function httpGet(RequestInterface $request, ResponseInterface $response)
84
-    {
85
-        $queryParams = $request->getQueryParameters();
86
-        if (!array_key_exists('export', $queryParams)) {
87
-            return;
88
-        }
89
-
90
-        $path = $request->getPath();
91
-
92
-        $node = $this->server->getProperties($path, [
93
-            '{DAV:}resourcetype',
94
-            '{DAV:}displayname',
95
-            '{http://sabredav.org/ns}sync-token',
96
-            '{DAV:}sync-token',
97
-            '{http://apple.com/ns/ical/}calendar-color',
98
-        ]);
99
-
100
-        if (!isset($node['{DAV:}resourcetype']) || !$node['{DAV:}resourcetype']->is('{'.Plugin::NS_CALDAV.'}calendar')) {
101
-            return;
102
-        }
103
-        // Marking the transactionType, for logging purposes.
104
-        $this->server->transactionType = 'get-calendar-export';
105
-
106
-        $properties = $node;
107
-
108
-        $start = null;
109
-        $end = null;
110
-        $expand = false;
111
-        $componentType = false;
112
-        if (isset($queryParams['start'])) {
113
-            if (!ctype_digit($queryParams['start'])) {
114
-                throw new BadRequest('The start= parameter must contain a unix timestamp');
115
-            }
116
-            $start = DateTime::createFromFormat('U', $queryParams['start']);
117
-        }
118
-        if (isset($queryParams['end'])) {
119
-            if (!ctype_digit($queryParams['end'])) {
120
-                throw new BadRequest('The end= parameter must contain a unix timestamp');
121
-            }
122
-            $end = DateTime::createFromFormat('U', $queryParams['end']);
123
-        }
124
-        if (isset($queryParams['expand']) && (bool) $queryParams['expand']) {
125
-            if (!$start || !$end) {
126
-                throw new BadRequest('If you\'d like to expand recurrences, you must specify both a start= and end= parameter.');
127
-            }
128
-            $expand = true;
129
-            $componentType = 'VEVENT';
130
-        }
131
-        if (isset($queryParams['componentType'])) {
132
-            if (!in_array($queryParams['componentType'], ['VEVENT', 'VTODO', 'VJOURNAL'])) {
133
-                throw new BadRequest('You are not allowed to search for components of type: '.$queryParams['componentType'].' here');
134
-            }
135
-            $componentType = $queryParams['componentType'];
136
-        }
137
-
138
-        $format = \Sabre\HTTP\negotiateContentType(
139
-            $request->getHeader('Accept'),
140
-            [
141
-                'text/calendar',
142
-                'application/calendar+json',
143
-            ]
144
-        );
145
-
146
-        if (isset($queryParams['accept'])) {
147
-            if ('application/calendar+json' === $queryParams['accept'] || 'jcal' === $queryParams['accept']) {
148
-                $format = 'application/calendar+json';
149
-            }
150
-        }
151
-        if (!$format) {
152
-            $format = 'text/calendar';
153
-        }
154
-
155
-        $this->generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, $response);
156
-
157
-        // Returning false to break the event chain
158
-        return false;
159
-    }
160
-
161
-    /**
162
-     * This method is responsible for generating the actual, full response.
163
-     *
164
-     * @param string        $path
165
-     * @param DateTime|null $start
166
-     * @param DateTime|null $end
167
-     * @param bool          $expand
168
-     * @param string        $componentType
169
-     * @param string        $format
170
-     * @param array         $properties
171
-     *
172
-     * @throws DAV\Exception\NotFound
173
-     * @throws VObject\InvalidDataException
174
-     */
175
-    protected function generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response)
176
-    {
177
-        $calDataProp = '{'.Plugin::NS_CALDAV.'}calendar-data';
178
-        $calendarNode = $this->server->tree->getNodeForPath($path);
179
-
180
-        $blobs = [];
181
-        if ($start || $end || $componentType) {
182
-            // If there was a start or end filter, we need to enlist
183
-            // calendarQuery for speed.
184
-            $queryResult = $calendarNode->calendarQuery([
185
-                'name' => 'VCALENDAR',
186
-                'comp-filters' => [
187
-                    [
188
-                        'name' => $componentType,
189
-                        'comp-filters' => [],
190
-                        'prop-filters' => [],
191
-                        'is-not-defined' => false,
192
-                        'time-range' => [
193
-                            'start' => $start,
194
-                            'end' => $end,
195
-                        ],
196
-                    ],
197
-                ],
198
-                'prop-filters' => [],
199
-                'is-not-defined' => false,
200
-                'time-range' => null,
201
-            ]);
202
-
203
-            // queryResult is just a list of base urls. We need to prefix the
204
-            // calendar path.
205
-            $queryResult = array_map(
206
-                function ($item) use ($path) {
207
-                    return $path.'/'.$item;
208
-                },
209
-                $queryResult
210
-            );
211
-            $nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]);
212
-            unset($queryResult);
213
-        } else {
214
-            $nodes = $this->server->getPropertiesForPath($path, [$calDataProp], 1);
215
-        }
216
-
217
-        // Flattening the arrays
218
-        foreach ($nodes as $node) {
219
-            if (isset($node[200][$calDataProp])) {
220
-                $blobs[$node['href']] = $node[200][$calDataProp];
221
-            }
222
-        }
223
-        unset($nodes);
224
-
225
-        $mergedCalendar = $this->mergeObjects(
226
-            $properties,
227
-            $blobs
228
-        );
229
-
230
-        if ($expand) {
231
-            $calendarTimeZone = null;
232
-            // We're expanding, and for that we need to figure out the
233
-            // calendar's timezone.
234
-            $tzProp = '{'.Plugin::NS_CALDAV.'}calendar-timezone';
235
-            $tzResult = $this->server->getProperties($path, [$tzProp]);
236
-            if (isset($tzResult[$tzProp])) {
237
-                // This property contains a VCALENDAR with a single
238
-                // VTIMEZONE.
239
-                $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]);
240
-                $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
241
-                // Destroy circular references to PHP will GC the object.
242
-                $vtimezoneObj->destroy();
243
-                unset($vtimezoneObj);
244
-            } else {
245
-                // Defaulting to UTC.
246
-                $calendarTimeZone = new DateTimeZone('UTC');
247
-            }
248
-
249
-            $mergedCalendar = $mergedCalendar->expand($start, $end, $calendarTimeZone);
250
-        }
251
-
252
-        $filenameExtension = '.ics';
253
-
254
-        switch ($format) {
255
-            case 'text/calendar':
256
-                $mergedCalendar = $mergedCalendar->serialize();
257
-                $filenameExtension = '.ics';
258
-                break;
259
-            case 'application/calendar+json':
260
-                $mergedCalendar = json_encode($mergedCalendar->jsonSerialize());
261
-                $filenameExtension = '.json';
262
-                break;
263
-        }
264
-
265
-        $filename = preg_replace(
266
-            '/[^a-zA-Z0-9-_ ]/um',
267
-            '',
268
-            $calendarNode->getName()
269
-        );
270
-        $filename .= '-'.date('Y-m-d').$filenameExtension;
271
-
272
-        $response->setHeader('Content-Disposition', 'attachment; filename="'.$filename.'"');
273
-        $response->setHeader('Content-Type', $format);
274
-
275
-        $response->setStatus(200);
276
-        $response->setBody($mergedCalendar);
277
-    }
278
-
279
-    /**
280
-     * Merges all calendar objects, and builds one big iCalendar blob.
281
-     *
282
-     * @param array $properties Some CalDAV properties
283
-     *
284
-     * @return VObject\Component\VCalendar
285
-     */
286
-    public function mergeObjects(array $properties, array $inputObjects)
287
-    {
288
-        $calendar = new VObject\Component\VCalendar();
289
-        $calendar->VERSION = '2.0';
290
-        if (DAV\Server::$exposeVersion) {
291
-            $calendar->PRODID = '-//SabreDAV//SabreDAV '.DAV\Version::VERSION.'//EN';
292
-        } else {
293
-            $calendar->PRODID = '-//SabreDAV//SabreDAV//EN';
294
-        }
295
-        if (isset($properties['{DAV:}displayname'])) {
296
-            $calendar->{'X-WR-CALNAME'} = $properties['{DAV:}displayname'];
297
-        }
298
-        if (isset($properties['{http://apple.com/ns/ical/}calendar-color'])) {
299
-            $calendar->{'X-APPLE-CALENDAR-COLOR'} = $properties['{http://apple.com/ns/ical/}calendar-color'];
300
-        }
301
-
302
-        $collectedTimezones = [];
303
-
304
-        $timezones = [];
305
-        $objects = [];
306
-
307
-        foreach ($inputObjects as $href => $inputObject) {
308
-            $nodeComp = VObject\Reader::read($inputObject);
309
-
310
-            foreach ($nodeComp->children() as $child) {
311
-                switch ($child->name) {
312
-                    case 'VEVENT':
313
-                    case 'VTODO':
314
-                    case 'VJOURNAL':
315
-                        $objects[] = clone $child;
316
-                        break;
317
-
318
-                    // VTIMEZONE is special, because we need to filter out the duplicates
319
-                    case 'VTIMEZONE':
320
-                        // Naively just checking tzid.
321
-                        if (in_array((string) $child->TZID, $collectedTimezones)) {
322
-                            break;
323
-                        }
324
-
325
-                        $timezones[] = clone $child;
326
-                        $collectedTimezones[] = $child->TZID;
327
-                        break;
328
-                }
329
-            }
330
-            // Destroy circular references to PHP will GC the object.
331
-            $nodeComp->destroy();
332
-            unset($nodeComp);
333
-        }
334
-
335
-        foreach ($timezones as $tz) {
336
-            $calendar->add($tz);
337
-        }
338
-        foreach ($objects as $obj) {
339
-            $calendar->add($obj);
340
-        }
341
-
342
-        return $calendar;
343
-    }
344
-
345
-    /**
346
-     * Returns a plugin name.
347
-     *
348
-     * Using this name other plugins will be able to access other plugins
349
-     * using \Sabre\DAV\Server::getPlugin
350
-     *
351
-     * @return string
352
-     */
353
-    public function getPluginName()
354
-    {
355
-        return 'ics-export';
356
-    }
357
-
358
-    /**
359
-     * Returns a bunch of meta-data about the plugin.
360
-     *
361
-     * Providing this information is optional, and is mainly displayed by the
362
-     * Browser plugin.
363
-     *
364
-     * The description key in the returned array may contain html and will not
365
-     * be sanitized.
366
-     *
367
-     * @return array
368
-     */
369
-    public function getPluginInfo()
370
-    {
371
-        return [
372
-            'name' => $this->getPluginName(),
373
-            'description' => 'Adds the ability to export CalDAV calendars as a single iCalendar file.',
374
-            'link' => 'http://sabre.io/dav/ics-export-plugin/',
375
-        ];
376
-    }
51
+	/**
52
+	 * Reference to Server class.
53
+	 *
54
+	 * @var \Sabre\DAV\Server
55
+	 */
56
+	protected $server;
57
+
58
+	/**
59
+	 * Initializes the plugin and registers event handlers.
60
+	 *
61
+	 * @param \Sabre\DAV\Server $server
62
+	 */
63
+	public function initialize(DAV\Server $server)
64
+	{
65
+		$this->server = $server;
66
+		$server->on('method:GET', [$this, 'httpGet'], 90);
67
+		$server->on('browserButtonActions', function ($path, $node, &$actions) {
68
+			if ($node instanceof ICalendar) {
69
+				$actions .= '<a href="'.htmlspecialchars($path, ENT_QUOTES, 'UTF-8').'?export"><span class="oi" data-glyph="calendar"></span></a>';
70
+			}
71
+		});
72
+	}
73
+
74
+	/**
75
+	 * Intercepts GET requests on calendar urls ending with ?export.
76
+	 *
77
+	 * @throws BadRequest
78
+	 * @throws DAV\Exception\NotFound
79
+	 * @throws VObject\InvalidDataException
80
+	 *
81
+	 * @return bool
82
+	 */
83
+	public function httpGet(RequestInterface $request, ResponseInterface $response)
84
+	{
85
+		$queryParams = $request->getQueryParameters();
86
+		if (!array_key_exists('export', $queryParams)) {
87
+			return;
88
+		}
89
+
90
+		$path = $request->getPath();
91
+
92
+		$node = $this->server->getProperties($path, [
93
+			'{DAV:}resourcetype',
94
+			'{DAV:}displayname',
95
+			'{http://sabredav.org/ns}sync-token',
96
+			'{DAV:}sync-token',
97
+			'{http://apple.com/ns/ical/}calendar-color',
98
+		]);
99
+
100
+		if (!isset($node['{DAV:}resourcetype']) || !$node['{DAV:}resourcetype']->is('{'.Plugin::NS_CALDAV.'}calendar')) {
101
+			return;
102
+		}
103
+		// Marking the transactionType, for logging purposes.
104
+		$this->server->transactionType = 'get-calendar-export';
105
+
106
+		$properties = $node;
107
+
108
+		$start = null;
109
+		$end = null;
110
+		$expand = false;
111
+		$componentType = false;
112
+		if (isset($queryParams['start'])) {
113
+			if (!ctype_digit($queryParams['start'])) {
114
+				throw new BadRequest('The start= parameter must contain a unix timestamp');
115
+			}
116
+			$start = DateTime::createFromFormat('U', $queryParams['start']);
117
+		}
118
+		if (isset($queryParams['end'])) {
119
+			if (!ctype_digit($queryParams['end'])) {
120
+				throw new BadRequest('The end= parameter must contain a unix timestamp');
121
+			}
122
+			$end = DateTime::createFromFormat('U', $queryParams['end']);
123
+		}
124
+		if (isset($queryParams['expand']) && (bool) $queryParams['expand']) {
125
+			if (!$start || !$end) {
126
+				throw new BadRequest('If you\'d like to expand recurrences, you must specify both a start= and end= parameter.');
127
+			}
128
+			$expand = true;
129
+			$componentType = 'VEVENT';
130
+		}
131
+		if (isset($queryParams['componentType'])) {
132
+			if (!in_array($queryParams['componentType'], ['VEVENT', 'VTODO', 'VJOURNAL'])) {
133
+				throw new BadRequest('You are not allowed to search for components of type: '.$queryParams['componentType'].' here');
134
+			}
135
+			$componentType = $queryParams['componentType'];
136
+		}
137
+
138
+		$format = \Sabre\HTTP\negotiateContentType(
139
+			$request->getHeader('Accept'),
140
+			[
141
+				'text/calendar',
142
+				'application/calendar+json',
143
+			]
144
+		);
145
+
146
+		if (isset($queryParams['accept'])) {
147
+			if ('application/calendar+json' === $queryParams['accept'] || 'jcal' === $queryParams['accept']) {
148
+				$format = 'application/calendar+json';
149
+			}
150
+		}
151
+		if (!$format) {
152
+			$format = 'text/calendar';
153
+		}
154
+
155
+		$this->generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, $response);
156
+
157
+		// Returning false to break the event chain
158
+		return false;
159
+	}
160
+
161
+	/**
162
+	 * This method is responsible for generating the actual, full response.
163
+	 *
164
+	 * @param string        $path
165
+	 * @param DateTime|null $start
166
+	 * @param DateTime|null $end
167
+	 * @param bool          $expand
168
+	 * @param string        $componentType
169
+	 * @param string        $format
170
+	 * @param array         $properties
171
+	 *
172
+	 * @throws DAV\Exception\NotFound
173
+	 * @throws VObject\InvalidDataException
174
+	 */
175
+	protected function generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response)
176
+	{
177
+		$calDataProp = '{'.Plugin::NS_CALDAV.'}calendar-data';
178
+		$calendarNode = $this->server->tree->getNodeForPath($path);
179
+
180
+		$blobs = [];
181
+		if ($start || $end || $componentType) {
182
+			// If there was a start or end filter, we need to enlist
183
+			// calendarQuery for speed.
184
+			$queryResult = $calendarNode->calendarQuery([
185
+				'name' => 'VCALENDAR',
186
+				'comp-filters' => [
187
+					[
188
+						'name' => $componentType,
189
+						'comp-filters' => [],
190
+						'prop-filters' => [],
191
+						'is-not-defined' => false,
192
+						'time-range' => [
193
+							'start' => $start,
194
+							'end' => $end,
195
+						],
196
+					],
197
+				],
198
+				'prop-filters' => [],
199
+				'is-not-defined' => false,
200
+				'time-range' => null,
201
+			]);
202
+
203
+			// queryResult is just a list of base urls. We need to prefix the
204
+			// calendar path.
205
+			$queryResult = array_map(
206
+				function ($item) use ($path) {
207
+					return $path.'/'.$item;
208
+				},
209
+				$queryResult
210
+			);
211
+			$nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]);
212
+			unset($queryResult);
213
+		} else {
214
+			$nodes = $this->server->getPropertiesForPath($path, [$calDataProp], 1);
215
+		}
216
+
217
+		// Flattening the arrays
218
+		foreach ($nodes as $node) {
219
+			if (isset($node[200][$calDataProp])) {
220
+				$blobs[$node['href']] = $node[200][$calDataProp];
221
+			}
222
+		}
223
+		unset($nodes);
224
+
225
+		$mergedCalendar = $this->mergeObjects(
226
+			$properties,
227
+			$blobs
228
+		);
229
+
230
+		if ($expand) {
231
+			$calendarTimeZone = null;
232
+			// We're expanding, and for that we need to figure out the
233
+			// calendar's timezone.
234
+			$tzProp = '{'.Plugin::NS_CALDAV.'}calendar-timezone';
235
+			$tzResult = $this->server->getProperties($path, [$tzProp]);
236
+			if (isset($tzResult[$tzProp])) {
237
+				// This property contains a VCALENDAR with a single
238
+				// VTIMEZONE.
239
+				$vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]);
240
+				$calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
241
+				// Destroy circular references to PHP will GC the object.
242
+				$vtimezoneObj->destroy();
243
+				unset($vtimezoneObj);
244
+			} else {
245
+				// Defaulting to UTC.
246
+				$calendarTimeZone = new DateTimeZone('UTC');
247
+			}
248
+
249
+			$mergedCalendar = $mergedCalendar->expand($start, $end, $calendarTimeZone);
250
+		}
251
+
252
+		$filenameExtension = '.ics';
253
+
254
+		switch ($format) {
255
+			case 'text/calendar':
256
+				$mergedCalendar = $mergedCalendar->serialize();
257
+				$filenameExtension = '.ics';
258
+				break;
259
+			case 'application/calendar+json':
260
+				$mergedCalendar = json_encode($mergedCalendar->jsonSerialize());
261
+				$filenameExtension = '.json';
262
+				break;
263
+		}
264
+
265
+		$filename = preg_replace(
266
+			'/[^a-zA-Z0-9-_ ]/um',
267
+			'',
268
+			$calendarNode->getName()
269
+		);
270
+		$filename .= '-'.date('Y-m-d').$filenameExtension;
271
+
272
+		$response->setHeader('Content-Disposition', 'attachment; filename="'.$filename.'"');
273
+		$response->setHeader('Content-Type', $format);
274
+
275
+		$response->setStatus(200);
276
+		$response->setBody($mergedCalendar);
277
+	}
278
+
279
+	/**
280
+	 * Merges all calendar objects, and builds one big iCalendar blob.
281
+	 *
282
+	 * @param array $properties Some CalDAV properties
283
+	 *
284
+	 * @return VObject\Component\VCalendar
285
+	 */
286
+	public function mergeObjects(array $properties, array $inputObjects)
287
+	{
288
+		$calendar = new VObject\Component\VCalendar();
289
+		$calendar->VERSION = '2.0';
290
+		if (DAV\Server::$exposeVersion) {
291
+			$calendar->PRODID = '-//SabreDAV//SabreDAV '.DAV\Version::VERSION.'//EN';
292
+		} else {
293
+			$calendar->PRODID = '-//SabreDAV//SabreDAV//EN';
294
+		}
295
+		if (isset($properties['{DAV:}displayname'])) {
296
+			$calendar->{'X-WR-CALNAME'} = $properties['{DAV:}displayname'];
297
+		}
298
+		if (isset($properties['{http://apple.com/ns/ical/}calendar-color'])) {
299
+			$calendar->{'X-APPLE-CALENDAR-COLOR'} = $properties['{http://apple.com/ns/ical/}calendar-color'];
300
+		}
301
+
302
+		$collectedTimezones = [];
303
+
304
+		$timezones = [];
305
+		$objects = [];
306
+
307
+		foreach ($inputObjects as $href => $inputObject) {
308
+			$nodeComp = VObject\Reader::read($inputObject);
309
+
310
+			foreach ($nodeComp->children() as $child) {
311
+				switch ($child->name) {
312
+					case 'VEVENT':
313
+					case 'VTODO':
314
+					case 'VJOURNAL':
315
+						$objects[] = clone $child;
316
+						break;
317
+
318
+					// VTIMEZONE is special, because we need to filter out the duplicates
319
+					case 'VTIMEZONE':
320
+						// Naively just checking tzid.
321
+						if (in_array((string) $child->TZID, $collectedTimezones)) {
322
+							break;
323
+						}
324
+
325
+						$timezones[] = clone $child;
326
+						$collectedTimezones[] = $child->TZID;
327
+						break;
328
+				}
329
+			}
330
+			// Destroy circular references to PHP will GC the object.
331
+			$nodeComp->destroy();
332
+			unset($nodeComp);
333
+		}
334
+
335
+		foreach ($timezones as $tz) {
336
+			$calendar->add($tz);
337
+		}
338
+		foreach ($objects as $obj) {
339
+			$calendar->add($obj);
340
+		}
341
+
342
+		return $calendar;
343
+	}
344
+
345
+	/**
346
+	 * Returns a plugin name.
347
+	 *
348
+	 * Using this name other plugins will be able to access other plugins
349
+	 * using \Sabre\DAV\Server::getPlugin
350
+	 *
351
+	 * @return string
352
+	 */
353
+	public function getPluginName()
354
+	{
355
+		return 'ics-export';
356
+	}
357
+
358
+	/**
359
+	 * Returns a bunch of meta-data about the plugin.
360
+	 *
361
+	 * Providing this information is optional, and is mainly displayed by the
362
+	 * Browser plugin.
363
+	 *
364
+	 * The description key in the returned array may contain html and will not
365
+	 * be sanitized.
366
+	 *
367
+	 * @return array
368
+	 */
369
+	public function getPluginInfo()
370
+	{
371
+		return [
372
+			'name' => $this->getPluginName(),
373
+			'description' => 'Adds the ability to export CalDAV calendars as a single iCalendar file.',
374
+			'link' => 'http://sabre.io/dav/ics-export-plugin/',
375
+		];
376
+	}
377 377
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -64,7 +64,7 @@  discard block
 block discarded – undo
64 64
     {
65 65
         $this->server = $server;
66 66
         $server->on('method:GET', [$this, 'httpGet'], 90);
67
-        $server->on('browserButtonActions', function ($path, $node, &$actions) {
67
+        $server->on('browserButtonActions', function($path, $node, &$actions) {
68 68
             if ($node instanceof ICalendar) {
69 69
                 $actions .= '<a href="'.htmlspecialchars($path, ENT_QUOTES, 'UTF-8').'?export"><span class="oi" data-glyph="calendar"></span></a>';
70 70
             }
@@ -203,7 +203,7 @@  discard block
 block discarded – undo
203 203
             // queryResult is just a list of base urls. We need to prefix the
204 204
             // calendar path.
205 205
             $queryResult = array_map(
206
-                function ($item) use ($path) {
206
+                function($item) use ($path) {
207 207
                     return $path.'/'.$item;
208 208
                 },
209 209
                 $queryResult
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/BackendInterface.php 1 patch
Indentation   +246 added lines, -246 removed lines patch added patch discarded remove patch
@@ -13,261 +13,261 @@
 block discarded – undo
13 13
  */
14 14
 interface BackendInterface
15 15
 {
16
-    /**
17
-     * Returns a list of calendars for a principal.
18
-     *
19
-     * Every project is an array with the following keys:
20
-     *  * id, a unique id that will be used by other functions to modify the
21
-     *    calendar. This can be the same as the uri or a database key.
22
-     *  * uri, which is the basename of the uri with which the calendar is
23
-     *    accessed.
24
-     *  * principaluri. The owner of the calendar. Almost always the same as
25
-     *    principalUri passed to this method.
26
-     *
27
-     * Furthermore it can contain webdav properties in clark notation. A very
28
-     * common one is '{DAV:}displayname'.
29
-     *
30
-     * Many clients also require:
31
-     * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
32
-     * For this property, you can just return an instance of
33
-     * Sabre\CalDAV\Property\SupportedCalendarComponentSet.
34
-     *
35
-     * If you return {http://sabredav.org/ns}read-only and set the value to 1,
36
-     * ACL will automatically be put in read-only mode.
37
-     *
38
-     * @param string $principalUri
39
-     *
40
-     * @return array
41
-     */
42
-    public function getCalendarsForUser($principalUri);
16
+	/**
17
+	 * Returns a list of calendars for a principal.
18
+	 *
19
+	 * Every project is an array with the following keys:
20
+	 *  * id, a unique id that will be used by other functions to modify the
21
+	 *    calendar. This can be the same as the uri or a database key.
22
+	 *  * uri, which is the basename of the uri with which the calendar is
23
+	 *    accessed.
24
+	 *  * principaluri. The owner of the calendar. Almost always the same as
25
+	 *    principalUri passed to this method.
26
+	 *
27
+	 * Furthermore it can contain webdav properties in clark notation. A very
28
+	 * common one is '{DAV:}displayname'.
29
+	 *
30
+	 * Many clients also require:
31
+	 * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
32
+	 * For this property, you can just return an instance of
33
+	 * Sabre\CalDAV\Property\SupportedCalendarComponentSet.
34
+	 *
35
+	 * If you return {http://sabredav.org/ns}read-only and set the value to 1,
36
+	 * ACL will automatically be put in read-only mode.
37
+	 *
38
+	 * @param string $principalUri
39
+	 *
40
+	 * @return array
41
+	 */
42
+	public function getCalendarsForUser($principalUri);
43 43
 
44
-    /**
45
-     * Creates a new calendar for a principal.
46
-     *
47
-     * If the creation was a success, an id must be returned that can be used to
48
-     * reference this calendar in other methods, such as updateCalendar.
49
-     *
50
-     * The id can be any type, including ints, strings, objects or array.
51
-     *
52
-     * @param string $principalUri
53
-     * @param string $calendarUri
54
-     *
55
-     * @return mixed
56
-     */
57
-    public function createCalendar($principalUri, $calendarUri, array $properties);
44
+	/**
45
+	 * Creates a new calendar for a principal.
46
+	 *
47
+	 * If the creation was a success, an id must be returned that can be used to
48
+	 * reference this calendar in other methods, such as updateCalendar.
49
+	 *
50
+	 * The id can be any type, including ints, strings, objects or array.
51
+	 *
52
+	 * @param string $principalUri
53
+	 * @param string $calendarUri
54
+	 *
55
+	 * @return mixed
56
+	 */
57
+	public function createCalendar($principalUri, $calendarUri, array $properties);
58 58
 
59
-    /**
60
-     * Updates properties for a calendar.
61
-     *
62
-     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
63
-     * To do the actual updates, you must tell this object which properties
64
-     * you're going to process with the handle() method.
65
-     *
66
-     * Calling the handle method is like telling the PropPatch object "I
67
-     * promise I can handle updating this property".
68
-     *
69
-     * Read the PropPatch documentation for more info and examples.
70
-     *
71
-     * @param mixed $calendarId
72
-     */
73
-    public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch);
59
+	/**
60
+	 * Updates properties for a calendar.
61
+	 *
62
+	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
63
+	 * To do the actual updates, you must tell this object which properties
64
+	 * you're going to process with the handle() method.
65
+	 *
66
+	 * Calling the handle method is like telling the PropPatch object "I
67
+	 * promise I can handle updating this property".
68
+	 *
69
+	 * Read the PropPatch documentation for more info and examples.
70
+	 *
71
+	 * @param mixed $calendarId
72
+	 */
73
+	public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch);
74 74
 
75
-    /**
76
-     * Delete a calendar and all its objects.
77
-     *
78
-     * @param mixed $calendarId
79
-     */
80
-    public function deleteCalendar($calendarId);
75
+	/**
76
+	 * Delete a calendar and all its objects.
77
+	 *
78
+	 * @param mixed $calendarId
79
+	 */
80
+	public function deleteCalendar($calendarId);
81 81
 
82
-    /**
83
-     * Returns all calendar objects within a calendar.
84
-     *
85
-     * Every item contains an array with the following keys:
86
-     *   * calendardata - The iCalendar-compatible calendar data
87
-     *   * uri - a unique key which will be used to construct the uri. This can
88
-     *     be any arbitrary string, but making sure it ends with '.ics' is a
89
-     *     good idea. This is only the basename, or filename, not the full
90
-     *     path.
91
-     *   * lastmodified - a timestamp of the last modification time
92
-     *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
93
-     *   '"abcdef"')
94
-     *   * size - The size of the calendar objects, in bytes.
95
-     *   * component - optional, a string containing the type of object, such
96
-     *     as 'vevent' or 'vtodo'. If specified, this will be used to populate
97
-     *     the Content-Type header.
98
-     *
99
-     * Note that the etag is optional, but it's highly encouraged to return for
100
-     * speed reasons.
101
-     *
102
-     * The calendardata is also optional. If it's not returned
103
-     * 'getCalendarObject' will be called later, which *is* expected to return
104
-     * calendardata.
105
-     *
106
-     * If neither etag or size are specified, the calendardata will be
107
-     * used/fetched to determine these numbers. If both are specified the
108
-     * amount of times this is needed is reduced by a great degree.
109
-     *
110
-     * @param mixed $calendarId
111
-     *
112
-     * @return array
113
-     */
114
-    public function getCalendarObjects($calendarId);
82
+	/**
83
+	 * Returns all calendar objects within a calendar.
84
+	 *
85
+	 * Every item contains an array with the following keys:
86
+	 *   * calendardata - The iCalendar-compatible calendar data
87
+	 *   * uri - a unique key which will be used to construct the uri. This can
88
+	 *     be any arbitrary string, but making sure it ends with '.ics' is a
89
+	 *     good idea. This is only the basename, or filename, not the full
90
+	 *     path.
91
+	 *   * lastmodified - a timestamp of the last modification time
92
+	 *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
93
+	 *   '"abcdef"')
94
+	 *   * size - The size of the calendar objects, in bytes.
95
+	 *   * component - optional, a string containing the type of object, such
96
+	 *     as 'vevent' or 'vtodo'. If specified, this will be used to populate
97
+	 *     the Content-Type header.
98
+	 *
99
+	 * Note that the etag is optional, but it's highly encouraged to return for
100
+	 * speed reasons.
101
+	 *
102
+	 * The calendardata is also optional. If it's not returned
103
+	 * 'getCalendarObject' will be called later, which *is* expected to return
104
+	 * calendardata.
105
+	 *
106
+	 * If neither etag or size are specified, the calendardata will be
107
+	 * used/fetched to determine these numbers. If both are specified the
108
+	 * amount of times this is needed is reduced by a great degree.
109
+	 *
110
+	 * @param mixed $calendarId
111
+	 *
112
+	 * @return array
113
+	 */
114
+	public function getCalendarObjects($calendarId);
115 115
 
116
-    /**
117
-     * Returns information from a single calendar object, based on it's object
118
-     * uri.
119
-     *
120
-     * The object uri is only the basename, or filename and not a full path.
121
-     *
122
-     * The returned array must have the same keys as getCalendarObjects. The
123
-     * 'calendardata' object is required here though, while it's not required
124
-     * for getCalendarObjects.
125
-     *
126
-     * This method must return null if the object did not exist.
127
-     *
128
-     * @param mixed  $calendarId
129
-     * @param string $objectUri
130
-     *
131
-     * @return array|null
132
-     */
133
-    public function getCalendarObject($calendarId, $objectUri);
116
+	/**
117
+	 * Returns information from a single calendar object, based on it's object
118
+	 * uri.
119
+	 *
120
+	 * The object uri is only the basename, or filename and not a full path.
121
+	 *
122
+	 * The returned array must have the same keys as getCalendarObjects. The
123
+	 * 'calendardata' object is required here though, while it's not required
124
+	 * for getCalendarObjects.
125
+	 *
126
+	 * This method must return null if the object did not exist.
127
+	 *
128
+	 * @param mixed  $calendarId
129
+	 * @param string $objectUri
130
+	 *
131
+	 * @return array|null
132
+	 */
133
+	public function getCalendarObject($calendarId, $objectUri);
134 134
 
135
-    /**
136
-     * Returns a list of calendar objects.
137
-     *
138
-     * This method should work identical to getCalendarObject, but instead
139
-     * return all the calendar objects in the list as an array.
140
-     *
141
-     * If the backend supports this, it may allow for some speed-ups.
142
-     *
143
-     * @param mixed $calendarId
144
-     *
145
-     * @return array
146
-     */
147
-    public function getMultipleCalendarObjects($calendarId, array $uris);
135
+	/**
136
+	 * Returns a list of calendar objects.
137
+	 *
138
+	 * This method should work identical to getCalendarObject, but instead
139
+	 * return all the calendar objects in the list as an array.
140
+	 *
141
+	 * If the backend supports this, it may allow for some speed-ups.
142
+	 *
143
+	 * @param mixed $calendarId
144
+	 *
145
+	 * @return array
146
+	 */
147
+	public function getMultipleCalendarObjects($calendarId, array $uris);
148 148
 
149
-    /**
150
-     * Creates a new calendar object.
151
-     *
152
-     * The object uri is only the basename, or filename and not a full path.
153
-     *
154
-     * It is possible to return an etag from this function, which will be used
155
-     * in the response to this PUT request. Note that the ETag must be
156
-     * surrounded by double-quotes.
157
-     *
158
-     * However, you should only really return this ETag if you don't mangle the
159
-     * calendar-data. If the result of a subsequent GET to this object is not
160
-     * the exact same as this request body, you should omit the ETag.
161
-     *
162
-     * @param mixed  $calendarId
163
-     * @param string $objectUri
164
-     * @param string $calendarData
165
-     *
166
-     * @return string|null
167
-     */
168
-    public function createCalendarObject($calendarId, $objectUri, $calendarData);
149
+	/**
150
+	 * Creates a new calendar object.
151
+	 *
152
+	 * The object uri is only the basename, or filename and not a full path.
153
+	 *
154
+	 * It is possible to return an etag from this function, which will be used
155
+	 * in the response to this PUT request. Note that the ETag must be
156
+	 * surrounded by double-quotes.
157
+	 *
158
+	 * However, you should only really return this ETag if you don't mangle the
159
+	 * calendar-data. If the result of a subsequent GET to this object is not
160
+	 * the exact same as this request body, you should omit the ETag.
161
+	 *
162
+	 * @param mixed  $calendarId
163
+	 * @param string $objectUri
164
+	 * @param string $calendarData
165
+	 *
166
+	 * @return string|null
167
+	 */
168
+	public function createCalendarObject($calendarId, $objectUri, $calendarData);
169 169
 
170
-    /**
171
-     * Updates an existing calendarobject, based on it's uri.
172
-     *
173
-     * The object uri is only the basename, or filename and not a full path.
174
-     *
175
-     * It is possible return an etag from this function, which will be used in
176
-     * the response to this PUT request. Note that the ETag must be surrounded
177
-     * by double-quotes.
178
-     *
179
-     * However, you should only really return this ETag if you don't mangle the
180
-     * calendar-data. If the result of a subsequent GET to this object is not
181
-     * the exact same as this request body, you should omit the ETag.
182
-     *
183
-     * @param mixed  $calendarId
184
-     * @param string $objectUri
185
-     * @param string $calendarData
186
-     *
187
-     * @return string|null
188
-     */
189
-    public function updateCalendarObject($calendarId, $objectUri, $calendarData);
170
+	/**
171
+	 * Updates an existing calendarobject, based on it's uri.
172
+	 *
173
+	 * The object uri is only the basename, or filename and not a full path.
174
+	 *
175
+	 * It is possible return an etag from this function, which will be used in
176
+	 * the response to this PUT request. Note that the ETag must be surrounded
177
+	 * by double-quotes.
178
+	 *
179
+	 * However, you should only really return this ETag if you don't mangle the
180
+	 * calendar-data. If the result of a subsequent GET to this object is not
181
+	 * the exact same as this request body, you should omit the ETag.
182
+	 *
183
+	 * @param mixed  $calendarId
184
+	 * @param string $objectUri
185
+	 * @param string $calendarData
186
+	 *
187
+	 * @return string|null
188
+	 */
189
+	public function updateCalendarObject($calendarId, $objectUri, $calendarData);
190 190
 
191
-    /**
192
-     * Deletes an existing calendar object.
193
-     *
194
-     * The object uri is only the basename, or filename and not a full path.
195
-     *
196
-     * @param mixed  $calendarId
197
-     * @param string $objectUri
198
-     */
199
-    public function deleteCalendarObject($calendarId, $objectUri);
191
+	/**
192
+	 * Deletes an existing calendar object.
193
+	 *
194
+	 * The object uri is only the basename, or filename and not a full path.
195
+	 *
196
+	 * @param mixed  $calendarId
197
+	 * @param string $objectUri
198
+	 */
199
+	public function deleteCalendarObject($calendarId, $objectUri);
200 200
 
201
-    /**
202
-     * Performs a calendar-query on the contents of this calendar.
203
-     *
204
-     * The calendar-query is defined in RFC4791 : CalDAV. Using the
205
-     * calendar-query it is possible for a client to request a specific set of
206
-     * object, based on contents of iCalendar properties, date-ranges and
207
-     * iCalendar component types (VTODO, VEVENT).
208
-     *
209
-     * This method should just return a list of (relative) urls that match this
210
-     * query.
211
-     *
212
-     * The list of filters are specified as an array. The exact array is
213
-     * documented by Sabre\CalDAV\CalendarQueryParser.
214
-     *
215
-     * Note that it is extremely likely that getCalendarObject for every path
216
-     * returned from this method will be called almost immediately after. You
217
-     * may want to anticipate this to speed up these requests.
218
-     *
219
-     * This method provides a default implementation, which parses *all* the
220
-     * iCalendar objects in the specified calendar.
221
-     *
222
-     * This default may well be good enough for personal use, and calendars
223
-     * that aren't very large. But if you anticipate high usage, big calendars
224
-     * or high loads, you are strongly adviced to optimize certain paths.
225
-     *
226
-     * The best way to do so is override this method and to optimize
227
-     * specifically for 'common filters'.
228
-     *
229
-     * Requests that are extremely common are:
230
-     *   * requests for just VEVENTS
231
-     *   * requests for just VTODO
232
-     *   * requests with a time-range-filter on either VEVENT or VTODO.
233
-     *
234
-     * ..and combinations of these requests. It may not be worth it to try to
235
-     * handle every possible situation and just rely on the (relatively
236
-     * easy to use) CalendarQueryValidator to handle the rest.
237
-     *
238
-     * Note that especially time-range-filters may be difficult to parse. A
239
-     * time-range filter specified on a VEVENT must for instance also handle
240
-     * recurrence rules correctly.
241
-     * A good example of how to interprete all these filters can also simply
242
-     * be found in Sabre\CalDAV\CalendarQueryFilter. This class is as correct
243
-     * as possible, so it gives you a good idea on what type of stuff you need
244
-     * to think of.
245
-     *
246
-     * @param mixed $calendarId
247
-     *
248
-     * @return array
249
-     */
250
-    public function calendarQuery($calendarId, array $filters);
201
+	/**
202
+	 * Performs a calendar-query on the contents of this calendar.
203
+	 *
204
+	 * The calendar-query is defined in RFC4791 : CalDAV. Using the
205
+	 * calendar-query it is possible for a client to request a specific set of
206
+	 * object, based on contents of iCalendar properties, date-ranges and
207
+	 * iCalendar component types (VTODO, VEVENT).
208
+	 *
209
+	 * This method should just return a list of (relative) urls that match this
210
+	 * query.
211
+	 *
212
+	 * The list of filters are specified as an array. The exact array is
213
+	 * documented by Sabre\CalDAV\CalendarQueryParser.
214
+	 *
215
+	 * Note that it is extremely likely that getCalendarObject for every path
216
+	 * returned from this method will be called almost immediately after. You
217
+	 * may want to anticipate this to speed up these requests.
218
+	 *
219
+	 * This method provides a default implementation, which parses *all* the
220
+	 * iCalendar objects in the specified calendar.
221
+	 *
222
+	 * This default may well be good enough for personal use, and calendars
223
+	 * that aren't very large. But if you anticipate high usage, big calendars
224
+	 * or high loads, you are strongly adviced to optimize certain paths.
225
+	 *
226
+	 * The best way to do so is override this method and to optimize
227
+	 * specifically for 'common filters'.
228
+	 *
229
+	 * Requests that are extremely common are:
230
+	 *   * requests for just VEVENTS
231
+	 *   * requests for just VTODO
232
+	 *   * requests with a time-range-filter on either VEVENT or VTODO.
233
+	 *
234
+	 * ..and combinations of these requests. It may not be worth it to try to
235
+	 * handle every possible situation and just rely on the (relatively
236
+	 * easy to use) CalendarQueryValidator to handle the rest.
237
+	 *
238
+	 * Note that especially time-range-filters may be difficult to parse. A
239
+	 * time-range filter specified on a VEVENT must for instance also handle
240
+	 * recurrence rules correctly.
241
+	 * A good example of how to interprete all these filters can also simply
242
+	 * be found in Sabre\CalDAV\CalendarQueryFilter. This class is as correct
243
+	 * as possible, so it gives you a good idea on what type of stuff you need
244
+	 * to think of.
245
+	 *
246
+	 * @param mixed $calendarId
247
+	 *
248
+	 * @return array
249
+	 */
250
+	public function calendarQuery($calendarId, array $filters);
251 251
 
252
-    /**
253
-     * Searches through all of a users calendars and calendar objects to find
254
-     * an object with a specific UID.
255
-     *
256
-     * This method should return the path to this object, relative to the
257
-     * calendar home, so this path usually only contains two parts:
258
-     *
259
-     * calendarpath/objectpath.ics
260
-     *
261
-     * If the uid is not found, return null.
262
-     *
263
-     * This method should only consider * objects that the principal owns, so
264
-     * any calendars owned by other principals that also appear in this
265
-     * collection should be ignored.
266
-     *
267
-     * @param string $principalUri
268
-     * @param string $uid
269
-     *
270
-     * @return string|null
271
-     */
272
-    public function getCalendarObjectByUID($principalUri, $uid);
252
+	/**
253
+	 * Searches through all of a users calendars and calendar objects to find
254
+	 * an object with a specific UID.
255
+	 *
256
+	 * This method should return the path to this object, relative to the
257
+	 * calendar home, so this path usually only contains two parts:
258
+	 *
259
+	 * calendarpath/objectpath.ics
260
+	 *
261
+	 * If the uid is not found, return null.
262
+	 *
263
+	 * This method should only consider * objects that the principal owns, so
264
+	 * any calendars owned by other principals that also appear in this
265
+	 * collection should be ignored.
266
+	 *
267
+	 * @param string $principalUri
268
+	 * @param string $uid
269
+	 *
270
+	 * @return string|null
271
+	 */
272
+	public function getCalendarObjectByUID($principalUri, $uid);
273 273
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php 1 patch
Indentation   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -26,37 +26,37 @@
 block discarded – undo
26 26
  */
27 27
 interface NotificationSupport extends BackendInterface
28 28
 {
29
-    /**
30
-     * Returns a list of notifications for a given principal url.
31
-     *
32
-     * @param string $principalUri
33
-     *
34
-     * @return NotificationInterface[]
35
-     */
36
-    public function getNotificationsForPrincipal($principalUri);
29
+	/**
30
+	 * Returns a list of notifications for a given principal url.
31
+	 *
32
+	 * @param string $principalUri
33
+	 *
34
+	 * @return NotificationInterface[]
35
+	 */
36
+	public function getNotificationsForPrincipal($principalUri);
37 37
 
38
-    /**
39
-     * This deletes a specific notifcation.
40
-     *
41
-     * This may be called by a client once it deems a notification handled.
42
-     *
43
-     * @param string $principalUri
44
-     */
45
-    public function deleteNotification($principalUri, NotificationInterface $notification);
38
+	/**
39
+	 * This deletes a specific notifcation.
40
+	 *
41
+	 * This may be called by a client once it deems a notification handled.
42
+	 *
43
+	 * @param string $principalUri
44
+	 */
45
+	public function deleteNotification($principalUri, NotificationInterface $notification);
46 46
 
47
-    /**
48
-     * This method is called when a user replied to a request to share.
49
-     *
50
-     * If the user chose to accept the share, this method should return the
51
-     * newly created calendar url.
52
-     *
53
-     * @param string $href        The sharee who is replying (often a mailto: address)
54
-     * @param int    $status      One of the SharingPlugin::STATUS_* constants
55
-     * @param string $calendarUri The url to the calendar thats being shared
56
-     * @param string $inReplyTo   The unique id this message is a response to
57
-     * @param string $summary     A description of the reply
58
-     *
59
-     * @return string|null
60
-     */
61
-    public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null);
47
+	/**
48
+	 * This method is called when a user replied to a request to share.
49
+	 *
50
+	 * If the user chose to accept the share, this method should return the
51
+	 * newly created calendar url.
52
+	 *
53
+	 * @param string $href        The sharee who is replying (often a mailto: address)
54
+	 * @param int    $status      One of the SharingPlugin::STATUS_* constants
55
+	 * @param string $calendarUri The url to the calendar thats being shared
56
+	 * @param string $inReplyTo   The unique id this message is a response to
57
+	 * @param string $summary     A description of the reply
58
+	 *
59
+	 * @return string|null
60
+	 */
61
+	public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null);
62 62
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CalDAV/Backend/AbstractBackend.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -50,7 +50,7 @@
 block discarded – undo
50 50
      */
51 51
     public function getMultipleCalendarObjects($calendarId, array $uris)
52 52
     {
53
-        return array_map(function ($uri) use ($calendarId) {
53
+        return array_map(function($uri) use ($calendarId) {
54 54
             return $this->getCalendarObject($calendarId, $uri);
55 55
         }, $uris);
56 56
     }
Please login to merge, or discard this patch.
Indentation   +195 added lines, -195 removed lines patch added patch discarded remove patch
@@ -18,199 +18,199 @@
 block discarded – undo
18 18
  */
19 19
 abstract class AbstractBackend implements BackendInterface
20 20
 {
21
-    /**
22
-     * Updates properties for a calendar.
23
-     *
24
-     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
25
-     * To do the actual updates, you must tell this object which properties
26
-     * you're going to process with the handle() method.
27
-     *
28
-     * Calling the handle method is like telling the PropPatch object "I
29
-     * promise I can handle updating this property".
30
-     *
31
-     * Read the PropPatch documentation for more info and examples.
32
-     *
33
-     * @param mixed $calendarId
34
-     */
35
-    public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch)
36
-    {
37
-    }
38
-
39
-    /**
40
-     * Returns a list of calendar objects.
41
-     *
42
-     * This method should work identical to getCalendarObject, but instead
43
-     * return all the calendar objects in the list as an array.
44
-     *
45
-     * If the backend supports this, it may allow for some speed-ups.
46
-     *
47
-     * @param mixed $calendarId
48
-     *
49
-     * @return array
50
-     */
51
-    public function getMultipleCalendarObjects($calendarId, array $uris)
52
-    {
53
-        return array_map(function ($uri) use ($calendarId) {
54
-            return $this->getCalendarObject($calendarId, $uri);
55
-        }, $uris);
56
-    }
57
-
58
-    /**
59
-     * Performs a calendar-query on the contents of this calendar.
60
-     *
61
-     * The calendar-query is defined in RFC4791 : CalDAV. Using the
62
-     * calendar-query it is possible for a client to request a specific set of
63
-     * object, based on contents of iCalendar properties, date-ranges and
64
-     * iCalendar component types (VTODO, VEVENT).
65
-     *
66
-     * This method should just return a list of (relative) urls that match this
67
-     * query.
68
-     *
69
-     * The list of filters are specified as an array. The exact array is
70
-     * documented by \Sabre\CalDAV\CalendarQueryParser.
71
-     *
72
-     * Note that it is extremely likely that getCalendarObject for every path
73
-     * returned from this method will be called almost immediately after. You
74
-     * may want to anticipate this to speed up these requests.
75
-     *
76
-     * This method provides a default implementation, which parses *all* the
77
-     * iCalendar objects in the specified calendar.
78
-     *
79
-     * This default may well be good enough for personal use, and calendars
80
-     * that aren't very large. But if you anticipate high usage, big calendars
81
-     * or high loads, you are strongly advised to optimize certain paths.
82
-     *
83
-     * The best way to do so is override this method and to optimize
84
-     * specifically for 'common filters'.
85
-     *
86
-     * Requests that are extremely common are:
87
-     *   * requests for just VEVENTS
88
-     *   * requests for just VTODO
89
-     *   * requests with a time-range-filter on either VEVENT or VTODO.
90
-     *
91
-     * ..and combinations of these requests. It may not be worth it to try to
92
-     * handle every possible situation and just rely on the (relatively
93
-     * easy to use) CalendarQueryValidator to handle the rest.
94
-     *
95
-     * Note that especially time-range-filters may be difficult to parse. A
96
-     * time-range filter specified on a VEVENT must for instance also handle
97
-     * recurrence rules correctly.
98
-     * A good example of how to interpret all these filters can also simply
99
-     * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
100
-     * as possible, so it gives you a good idea on what type of stuff you need
101
-     * to think of.
102
-     *
103
-     * @param mixed $calendarId
104
-     *
105
-     * @return array
106
-     */
107
-    public function calendarQuery($calendarId, array $filters)
108
-    {
109
-        $result = [];
110
-        $objects = $this->getCalendarObjects($calendarId);
111
-
112
-        foreach ($objects as $object) {
113
-            if ($this->validateFilterForObject($object, $filters)) {
114
-                $result[] = $object['uri'];
115
-            }
116
-        }
117
-
118
-        return $result;
119
-    }
120
-
121
-    /**
122
-     * This method validates if a filter (as passed to calendarQuery) matches
123
-     * the given object.
124
-     *
125
-     * @return bool
126
-     */
127
-    protected function validateFilterForObject(array $object, array $filters)
128
-    {
129
-        // Unfortunately, setting the 'calendardata' here is optional. If
130
-        // it was excluded, we actually need another call to get this as
131
-        // well.
132
-        if (!isset($object['calendardata'])) {
133
-            $object = $this->getCalendarObject($object['calendarid'], $object['uri']);
134
-        }
135
-
136
-        $vObject = VObject\Reader::read($object['calendardata']);
137
-
138
-        $validator = new CalDAV\CalendarQueryValidator();
139
-        $result = $validator->validate($vObject, $filters);
140
-
141
-        // Destroy circular references so PHP will GC the object.
142
-        $vObject->destroy();
143
-
144
-        return $result;
145
-    }
146
-
147
-    /**
148
-     * Searches through all of a users calendars and calendar objects to find
149
-     * an object with a specific UID.
150
-     *
151
-     * This method should return the path to this object, relative to the
152
-     * calendar home, so this path usually only contains two parts:
153
-     *
154
-     * calendarpath/objectpath.ics
155
-     *
156
-     * If the uid is not found, return null.
157
-     *
158
-     * This method should only consider * objects that the principal owns, so
159
-     * any calendars owned by other principals that also appear in this
160
-     * collection should be ignored.
161
-     *
162
-     * @param string $principalUri
163
-     * @param string $uid
164
-     *
165
-     * @return string|null
166
-     */
167
-    public function getCalendarObjectByUID($principalUri, $uid)
168
-    {
169
-        // Note: this is a super slow naive implementation of this method. You
170
-        // are highly recommended to optimize it, if your backend allows it.
171
-        foreach ($this->getCalendarsForUser($principalUri) as $calendar) {
172
-            // We must ignore calendars owned by other principals.
173
-            if ($calendar['principaluri'] !== $principalUri) {
174
-                continue;
175
-            }
176
-
177
-            // Ignore calendars that are shared.
178
-            if (isset($calendar['{http://sabredav.org/ns}owner-principal']) && $calendar['{http://sabredav.org/ns}owner-principal'] !== $principalUri) {
179
-                continue;
180
-            }
181
-
182
-            $results = $this->calendarQuery(
183
-                $calendar['id'],
184
-                [
185
-                    'name' => 'VCALENDAR',
186
-                    'prop-filters' => [],
187
-                    'comp-filters' => [
188
-                        [
189
-                            'name' => 'VEVENT',
190
-                            'is-not-defined' => false,
191
-                            'time-range' => null,
192
-                            'comp-filters' => [],
193
-                            'prop-filters' => [
194
-                                [
195
-                                    'name' => 'UID',
196
-                                    'is-not-defined' => false,
197
-                                    'time-range' => null,
198
-                                    'text-match' => [
199
-                                        'value' => $uid,
200
-                                        'negate-condition' => false,
201
-                                        'collation' => 'i;octet',
202
-                                    ],
203
-                                    'param-filters' => [],
204
-                                ],
205
-                            ],
206
-                        ],
207
-                    ],
208
-                ]
209
-            );
210
-            if ($results) {
211
-                // We have a match
212
-                return $calendar['uri'].'/'.$results[0];
213
-            }
214
-        }
215
-    }
21
+	/**
22
+	 * Updates properties for a calendar.
23
+	 *
24
+	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
25
+	 * To do the actual updates, you must tell this object which properties
26
+	 * you're going to process with the handle() method.
27
+	 *
28
+	 * Calling the handle method is like telling the PropPatch object "I
29
+	 * promise I can handle updating this property".
30
+	 *
31
+	 * Read the PropPatch documentation for more info and examples.
32
+	 *
33
+	 * @param mixed $calendarId
34
+	 */
35
+	public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch)
36
+	{
37
+	}
38
+
39
+	/**
40
+	 * Returns a list of calendar objects.
41
+	 *
42
+	 * This method should work identical to getCalendarObject, but instead
43
+	 * return all the calendar objects in the list as an array.
44
+	 *
45
+	 * If the backend supports this, it may allow for some speed-ups.
46
+	 *
47
+	 * @param mixed $calendarId
48
+	 *
49
+	 * @return array
50
+	 */
51
+	public function getMultipleCalendarObjects($calendarId, array $uris)
52
+	{
53
+		return array_map(function ($uri) use ($calendarId) {
54
+			return $this->getCalendarObject($calendarId, $uri);
55
+		}, $uris);
56
+	}
57
+
58
+	/**
59
+	 * Performs a calendar-query on the contents of this calendar.
60
+	 *
61
+	 * The calendar-query is defined in RFC4791 : CalDAV. Using the
62
+	 * calendar-query it is possible for a client to request a specific set of
63
+	 * object, based on contents of iCalendar properties, date-ranges and
64
+	 * iCalendar component types (VTODO, VEVENT).
65
+	 *
66
+	 * This method should just return a list of (relative) urls that match this
67
+	 * query.
68
+	 *
69
+	 * The list of filters are specified as an array. The exact array is
70
+	 * documented by \Sabre\CalDAV\CalendarQueryParser.
71
+	 *
72
+	 * Note that it is extremely likely that getCalendarObject for every path
73
+	 * returned from this method will be called almost immediately after. You
74
+	 * may want to anticipate this to speed up these requests.
75
+	 *
76
+	 * This method provides a default implementation, which parses *all* the
77
+	 * iCalendar objects in the specified calendar.
78
+	 *
79
+	 * This default may well be good enough for personal use, and calendars
80
+	 * that aren't very large. But if you anticipate high usage, big calendars
81
+	 * or high loads, you are strongly advised to optimize certain paths.
82
+	 *
83
+	 * The best way to do so is override this method and to optimize
84
+	 * specifically for 'common filters'.
85
+	 *
86
+	 * Requests that are extremely common are:
87
+	 *   * requests for just VEVENTS
88
+	 *   * requests for just VTODO
89
+	 *   * requests with a time-range-filter on either VEVENT or VTODO.
90
+	 *
91
+	 * ..and combinations of these requests. It may not be worth it to try to
92
+	 * handle every possible situation and just rely on the (relatively
93
+	 * easy to use) CalendarQueryValidator to handle the rest.
94
+	 *
95
+	 * Note that especially time-range-filters may be difficult to parse. A
96
+	 * time-range filter specified on a VEVENT must for instance also handle
97
+	 * recurrence rules correctly.
98
+	 * A good example of how to interpret all these filters can also simply
99
+	 * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct
100
+	 * as possible, so it gives you a good idea on what type of stuff you need
101
+	 * to think of.
102
+	 *
103
+	 * @param mixed $calendarId
104
+	 *
105
+	 * @return array
106
+	 */
107
+	public function calendarQuery($calendarId, array $filters)
108
+	{
109
+		$result = [];
110
+		$objects = $this->getCalendarObjects($calendarId);
111
+
112
+		foreach ($objects as $object) {
113
+			if ($this->validateFilterForObject($object, $filters)) {
114
+				$result[] = $object['uri'];
115
+			}
116
+		}
117
+
118
+		return $result;
119
+	}
120
+
121
+	/**
122
+	 * This method validates if a filter (as passed to calendarQuery) matches
123
+	 * the given object.
124
+	 *
125
+	 * @return bool
126
+	 */
127
+	protected function validateFilterForObject(array $object, array $filters)
128
+	{
129
+		// Unfortunately, setting the 'calendardata' here is optional. If
130
+		// it was excluded, we actually need another call to get this as
131
+		// well.
132
+		if (!isset($object['calendardata'])) {
133
+			$object = $this->getCalendarObject($object['calendarid'], $object['uri']);
134
+		}
135
+
136
+		$vObject = VObject\Reader::read($object['calendardata']);
137
+
138
+		$validator = new CalDAV\CalendarQueryValidator();
139
+		$result = $validator->validate($vObject, $filters);
140
+
141
+		// Destroy circular references so PHP will GC the object.
142
+		$vObject->destroy();
143
+
144
+		return $result;
145
+	}
146
+
147
+	/**
148
+	 * Searches through all of a users calendars and calendar objects to find
149
+	 * an object with a specific UID.
150
+	 *
151
+	 * This method should return the path to this object, relative to the
152
+	 * calendar home, so this path usually only contains two parts:
153
+	 *
154
+	 * calendarpath/objectpath.ics
155
+	 *
156
+	 * If the uid is not found, return null.
157
+	 *
158
+	 * This method should only consider * objects that the principal owns, so
159
+	 * any calendars owned by other principals that also appear in this
160
+	 * collection should be ignored.
161
+	 *
162
+	 * @param string $principalUri
163
+	 * @param string $uid
164
+	 *
165
+	 * @return string|null
166
+	 */
167
+	public function getCalendarObjectByUID($principalUri, $uid)
168
+	{
169
+		// Note: this is a super slow naive implementation of this method. You
170
+		// are highly recommended to optimize it, if your backend allows it.
171
+		foreach ($this->getCalendarsForUser($principalUri) as $calendar) {
172
+			// We must ignore calendars owned by other principals.
173
+			if ($calendar['principaluri'] !== $principalUri) {
174
+				continue;
175
+			}
176
+
177
+			// Ignore calendars that are shared.
178
+			if (isset($calendar['{http://sabredav.org/ns}owner-principal']) && $calendar['{http://sabredav.org/ns}owner-principal'] !== $principalUri) {
179
+				continue;
180
+			}
181
+
182
+			$results = $this->calendarQuery(
183
+				$calendar['id'],
184
+				[
185
+					'name' => 'VCALENDAR',
186
+					'prop-filters' => [],
187
+					'comp-filters' => [
188
+						[
189
+							'name' => 'VEVENT',
190
+							'is-not-defined' => false,
191
+							'time-range' => null,
192
+							'comp-filters' => [],
193
+							'prop-filters' => [
194
+								[
195
+									'name' => 'UID',
196
+									'is-not-defined' => false,
197
+									'time-range' => null,
198
+									'text-match' => [
199
+										'value' => $uid,
200
+										'negate-condition' => false,
201
+										'collation' => 'i;octet',
202
+									],
203
+									'param-filters' => [],
204
+								],
205
+							],
206
+						],
207
+					],
208
+				]
209
+			);
210
+			if ($results) {
211
+				// We have a match
212
+				return $calendar['uri'].'/'.$results[0];
213
+			}
214
+		}
215
+	}
216 216
 }
Please login to merge, or discard this patch.
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.