1<?php
2
3namespace Sabre\CalDAV;
4
5/**
6 * The CalendarObject represents a single VEVENT or VTODO within a Calendar.
7 *
8 * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/).
9 * @author Evert Pot (http://evertpot.com/)
10 * @license http://sabre.io/license/ Modified BSD License
11 */
12class CalendarObject extends \Sabre\DAV\File implements ICalendarObject, \Sabre\DAVACL\IACL {
13
14    /**
15     * Sabre\CalDAV\Backend\BackendInterface
16     *
17     * @var Sabre\CalDAV\Backend\AbstractBackend
18     */
19    protected $caldavBackend;
20
21    /**
22     * Array with information about this CalendarObject
23     *
24     * @var array
25     */
26    protected $objectData;
27
28    /**
29     * Array with information about the containing calendar
30     *
31     * @var array
32     */
33    protected $calendarInfo;
34
35    /**
36     * Constructor
37     *
38     * The following properties may be passed within $objectData:
39     *
40     *   * calendarid - This must refer to a calendarid from a caldavBackend
41     *   * uri - A unique uri. Only the 'basename' must be passed.
42     *   * calendardata (optional) - The iCalendar data
43     *   * etag - (optional) The etag for this object, MUST be encloded with
44     *            double-quotes.
45     *   * size - (optional) The size of the data in bytes.
46     *   * lastmodified - (optional) format as a unix timestamp.
47     *   * acl - (optional) Use this to override the default ACL for the node.
48     *
49     * @param Backend\BackendInterface $caldavBackend
50     * @param array $calendarInfo
51     * @param array $objectData
52     */
53    function __construct(Backend\BackendInterface $caldavBackend, array $calendarInfo, array $objectData) {
54
55        $this->caldavBackend = $caldavBackend;
56
57        if (!isset($objectData['uri'])) {
58            throw new \InvalidArgumentException('The objectData argument must contain an \'uri\' property');
59        }
60
61        $this->calendarInfo = $calendarInfo;
62        $this->objectData = $objectData;
63
64    }
65
66    /**
67     * Returns the uri for this object
68     *
69     * @return string
70     */
71    function getName() {
72
73        return $this->objectData['uri'];
74
75    }
76
77    /**
78     * Returns the ICalendar-formatted object
79     *
80     * @return string
81     */
82    function get() {
83
84        // Pre-populating the 'calendardata' is optional, if we don't have it
85        // already we fetch it from the backend.
86        if (!isset($this->objectData['calendardata'])) {
87            $this->objectData = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $this->objectData['uri']);
88        }
89        return $this->objectData['calendardata'];
90
91    }
92
93    /**
94     * Updates the ICalendar-formatted object
95     *
96     * @param string|resource $calendarData
97     * @return string
98     */
99    function put($calendarData) {
100
101        if (is_resource($calendarData)) {
102            $calendarData = stream_get_contents($calendarData);
103        }
104        $etag = $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'], $this->objectData['uri'], $calendarData);
105        $this->objectData['calendardata'] = $calendarData;
106        $this->objectData['etag'] = $etag;
107
108        return $etag;
109
110    }
111
112    /**
113     * Deletes the calendar object
114     *
115     * @return void
116     */
117    function delete() {
118
119        $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'], $this->objectData['uri']);
120
121    }
122
123    /**
124     * Returns the mime content-type
125     *
126     * @return string
127     */
128    function getContentType() {
129
130        $mime = 'text/calendar; charset=utf-8';
131        if (isset($this->objectData['component']) && $this->objectData['component']) {
132            $mime .= '; component=' . $this->objectData['component'];
133        }
134        return $mime;
135
136    }
137
138    /**
139     * Returns an ETag for this object.
140     *
141     * The ETag is an arbitrary string, but MUST be surrounded by double-quotes.
142     *
143     * @return string
144     */
145    function getETag() {
146
147        if (isset($this->objectData['etag'])) {
148            return $this->objectData['etag'];
149        } else {
150            return '"' . md5($this->get()) . '"';
151        }
152
153    }
154
155    /**
156     * Returns the last modification date as a unix timestamp
157     *
158     * @return int
159     */
160    function getLastModified() {
161
162        return $this->objectData['lastmodified'];
163
164    }
165
166    /**
167     * Returns the size of this object in bytes
168     *
169     * @return int
170     */
171    function getSize() {
172
173        if (array_key_exists('size', $this->objectData)) {
174            return $this->objectData['size'];
175        } else {
176            return strlen($this->get());
177        }
178
179    }
180
181    /**
182     * Returns the owner principal
183     *
184     * This must be a url to a principal, or null if there's no owner
185     *
186     * @return string|null
187     */
188    function getOwner() {
189
190        return $this->calendarInfo['principaluri'];
191
192    }
193
194    /**
195     * Returns a group principal
196     *
197     * This must be a url to a principal, or null if there's no owner
198     *
199     * @return string|null
200     */
201    function getGroup() {
202
203        return null;
204
205    }
206
207    /**
208     * Returns a list of ACE's for this node.
209     *
210     * Each ACE has the following properties:
211     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
212     *     currently the only supported privileges
213     *   * 'principal', a url to the principal who owns the node
214     *   * 'protected' (optional), indicating that this ACE is not allowed to
215     *      be updated.
216     *
217     * @return array
218     */
219    function getACL() {
220
221        // An alternative acl may be specified in the object data.
222        if (isset($this->objectData['acl'])) {
223            return $this->objectData['acl'];
224        }
225
226        // The default ACL
227        return [
228            [
229                'privilege' => '{DAV:}read',
230                'principal' => $this->calendarInfo['principaluri'],
231                'protected' => true,
232            ],
233            [
234                'privilege' => '{DAV:}write',
235                'principal' => $this->calendarInfo['principaluri'],
236                'protected' => true,
237            ],
238            [
239                'privilege' => '{DAV:}read',
240                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
241                'protected' => true,
242            ],
243            [
244                'privilege' => '{DAV:}write',
245                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
246                'protected' => true,
247            ],
248            [
249                'privilege' => '{DAV:}read',
250                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
251                'protected' => true,
252            ],
253
254        ];
255
256    }
257
258    /**
259     * Updates the ACL
260     *
261     * This method will receive a list of new ACE's.
262     *
263     * @param array $acl
264     * @return void
265     */
266    function setACL(array $acl) {
267
268        throw new \Sabre\DAV\Exception\MethodNotAllowed('Changing ACL is not yet supported');
269
270    }
271
272    /**
273     * Returns the list of supported privileges for this node.
274     *
275     * The returned data structure is a list of nested privileges.
276     * See \Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
277     * standard structure.
278     *
279     * If null is returned from this method, the default privilege set is used,
280     * which is fine for most common usecases.
281     *
282     * @return array|null
283     */
284    function getSupportedPrivilegeSet() {
285
286        return null;
287
288    }
289
290}
291