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