Completed
Branch develop (c24f26)
by
unknown
26:13
created
htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/BackendInterface.php 1 patch
Indentation   +163 added lines, -163 removed lines patch added patch discarded remove patch
@@ -19,176 +19,176 @@
 block discarded – undo
19 19
  */
20 20
 interface BackendInterface
21 21
 {
22
-    /**
23
-     * Returns the list of addressbooks for a specific user.
24
-     *
25
-     * Every addressbook should have the following properties:
26
-     *   id - an arbitrary unique id
27
-     *   uri - the 'basename' part of the url
28
-     *   principaluri - Same as the passed parameter
29
-     *
30
-     * Any additional clark-notation property may be passed besides this. Some
31
-     * common ones are :
32
-     *   {DAV:}displayname
33
-     *   {urn:ietf:params:xml:ns:carddav}addressbook-description
34
-     *   {http://calendarserver.org/ns/}getctag
35
-     *
36
-     * @param string $principalUri
37
-     *
38
-     * @return array
39
-     */
40
-    public function getAddressBooksForUser($principalUri);
22
+	/**
23
+	 * Returns the list of addressbooks for a specific user.
24
+	 *
25
+	 * Every addressbook should have the following properties:
26
+	 *   id - an arbitrary unique id
27
+	 *   uri - the 'basename' part of the url
28
+	 *   principaluri - Same as the passed parameter
29
+	 *
30
+	 * Any additional clark-notation property may be passed besides this. Some
31
+	 * common ones are :
32
+	 *   {DAV:}displayname
33
+	 *   {urn:ietf:params:xml:ns:carddav}addressbook-description
34
+	 *   {http://calendarserver.org/ns/}getctag
35
+	 *
36
+	 * @param string $principalUri
37
+	 *
38
+	 * @return array
39
+	 */
40
+	public function getAddressBooksForUser($principalUri);
41 41
 
42
-    /**
43
-     * Updates properties for an address book.
44
-     *
45
-     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
46
-     * To do the actual updates, you must tell this object which properties
47
-     * you're going to process with the handle() method.
48
-     *
49
-     * Calling the handle method is like telling the PropPatch object "I
50
-     * promise I can handle updating this property".
51
-     *
52
-     * Read the PropPatch documentation for more info and examples.
53
-     *
54
-     * @param string $addressBookId
55
-     */
56
-    public function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch);
42
+	/**
43
+	 * Updates properties for an address book.
44
+	 *
45
+	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
46
+	 * To do the actual updates, you must tell this object which properties
47
+	 * you're going to process with the handle() method.
48
+	 *
49
+	 * Calling the handle method is like telling the PropPatch object "I
50
+	 * promise I can handle updating this property".
51
+	 *
52
+	 * Read the PropPatch documentation for more info and examples.
53
+	 *
54
+	 * @param string $addressBookId
55
+	 */
56
+	public function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch);
57 57
 
58
-    /**
59
-     * Creates a new address book.
60
-     *
61
-     * This method should return the id of the new address book. The id can be
62
-     * in any format, including ints, strings, arrays or objects.
63
-     *
64
-     * @param string $principalUri
65
-     * @param string $url          just the 'basename' of the url
66
-     *
67
-     * @return mixed
68
-     */
69
-    public function createAddressBook($principalUri, $url, array $properties);
58
+	/**
59
+	 * Creates a new address book.
60
+	 *
61
+	 * This method should return the id of the new address book. The id can be
62
+	 * in any format, including ints, strings, arrays or objects.
63
+	 *
64
+	 * @param string $principalUri
65
+	 * @param string $url          just the 'basename' of the url
66
+	 *
67
+	 * @return mixed
68
+	 */
69
+	public function createAddressBook($principalUri, $url, array $properties);
70 70
 
71
-    /**
72
-     * Deletes an entire addressbook and all its contents.
73
-     *
74
-     * @param mixed $addressBookId
75
-     */
76
-    public function deleteAddressBook($addressBookId);
71
+	/**
72
+	 * Deletes an entire addressbook and all its contents.
73
+	 *
74
+	 * @param mixed $addressBookId
75
+	 */
76
+	public function deleteAddressBook($addressBookId);
77 77
 
78
-    /**
79
-     * Returns all cards for a specific addressbook id.
80
-     *
81
-     * This method should return the following properties for each card:
82
-     *   * carddata - raw vcard data
83
-     *   * uri - Some unique url
84
-     *   * lastmodified - A unix timestamp
85
-     *
86
-     * It's recommended to also return the following properties:
87
-     *   * etag - A unique etag. This must change every time the card changes.
88
-     *   * size - The size of the card in bytes.
89
-     *
90
-     * If these last two properties are provided, less time will be spent
91
-     * calculating them. If they are specified, you can also ommit carddata.
92
-     * This may speed up certain requests, especially with large cards.
93
-     *
94
-     * @param mixed $addressbookId
95
-     *
96
-     * @return array
97
-     */
98
-    public function getCards($addressbookId);
78
+	/**
79
+	 * Returns all cards for a specific addressbook id.
80
+	 *
81
+	 * This method should return the following properties for each card:
82
+	 *   * carddata - raw vcard data
83
+	 *   * uri - Some unique url
84
+	 *   * lastmodified - A unix timestamp
85
+	 *
86
+	 * It's recommended to also return the following properties:
87
+	 *   * etag - A unique etag. This must change every time the card changes.
88
+	 *   * size - The size of the card in bytes.
89
+	 *
90
+	 * If these last two properties are provided, less time will be spent
91
+	 * calculating them. If they are specified, you can also ommit carddata.
92
+	 * This may speed up certain requests, especially with large cards.
93
+	 *
94
+	 * @param mixed $addressbookId
95
+	 *
96
+	 * @return array
97
+	 */
98
+	public function getCards($addressbookId);
99 99
 
100
-    /**
101
-     * Returns a specfic card.
102
-     *
103
-     * The same set of properties must be returned as with getCards. The only
104
-     * exception is that 'carddata' is absolutely required.
105
-     *
106
-     * If the card does not exist, you must return false.
107
-     *
108
-     * @param mixed  $addressBookId
109
-     * @param string $cardUri
110
-     *
111
-     * @return array
112
-     */
113
-    public function getCard($addressBookId, $cardUri);
100
+	/**
101
+	 * Returns a specfic card.
102
+	 *
103
+	 * The same set of properties must be returned as with getCards. The only
104
+	 * exception is that 'carddata' is absolutely required.
105
+	 *
106
+	 * If the card does not exist, you must return false.
107
+	 *
108
+	 * @param mixed  $addressBookId
109
+	 * @param string $cardUri
110
+	 *
111
+	 * @return array
112
+	 */
113
+	public function getCard($addressBookId, $cardUri);
114 114
 
115
-    /**
116
-     * Returns a list of cards.
117
-     *
118
-     * This method should work identical to getCard, but instead return all the
119
-     * cards in the list as an array.
120
-     *
121
-     * If the backend supports this, it may allow for some speed-ups.
122
-     *
123
-     * @param mixed $addressBookId
124
-     *
125
-     * @return array
126
-     */
127
-    public function getMultipleCards($addressBookId, array $uris);
115
+	/**
116
+	 * Returns a list of cards.
117
+	 *
118
+	 * This method should work identical to getCard, but instead return all the
119
+	 * cards in the list as an array.
120
+	 *
121
+	 * If the backend supports this, it may allow for some speed-ups.
122
+	 *
123
+	 * @param mixed $addressBookId
124
+	 *
125
+	 * @return array
126
+	 */
127
+	public function getMultipleCards($addressBookId, array $uris);
128 128
 
129
-    /**
130
-     * Creates a new card.
131
-     *
132
-     * The addressbook id will be passed as the first argument. This is the
133
-     * same id as it is returned from the getAddressBooksForUser method.
134
-     *
135
-     * The cardUri is a base uri, and doesn't include the full path. The
136
-     * cardData argument is the vcard body, and is passed as a string.
137
-     *
138
-     * It is possible to return an ETag from this method. This ETag is for the
139
-     * newly created resource, and must be enclosed with double quotes (that
140
-     * is, the string itself must contain the double quotes).
141
-     *
142
-     * You should only return the ETag if you store the carddata as-is. If a
143
-     * subsequent GET request on the same card does not have the same body,
144
-     * byte-by-byte and you did return an ETag here, clients tend to get
145
-     * confused.
146
-     *
147
-     * If you don't return an ETag, you can just return null.
148
-     *
149
-     * @param mixed  $addressBookId
150
-     * @param string $cardUri
151
-     * @param string $cardData
152
-     *
153
-     * @return string|null
154
-     */
155
-    public function createCard($addressBookId, $cardUri, $cardData);
129
+	/**
130
+	 * Creates a new card.
131
+	 *
132
+	 * The addressbook id will be passed as the first argument. This is the
133
+	 * same id as it is returned from the getAddressBooksForUser method.
134
+	 *
135
+	 * The cardUri is a base uri, and doesn't include the full path. The
136
+	 * cardData argument is the vcard body, and is passed as a string.
137
+	 *
138
+	 * It is possible to return an ETag from this method. This ETag is for the
139
+	 * newly created resource, and must be enclosed with double quotes (that
140
+	 * is, the string itself must contain the double quotes).
141
+	 *
142
+	 * You should only return the ETag if you store the carddata as-is. If a
143
+	 * subsequent GET request on the same card does not have the same body,
144
+	 * byte-by-byte and you did return an ETag here, clients tend to get
145
+	 * confused.
146
+	 *
147
+	 * If you don't return an ETag, you can just return null.
148
+	 *
149
+	 * @param mixed  $addressBookId
150
+	 * @param string $cardUri
151
+	 * @param string $cardData
152
+	 *
153
+	 * @return string|null
154
+	 */
155
+	public function createCard($addressBookId, $cardUri, $cardData);
156 156
 
157
-    /**
158
-     * Updates a card.
159
-     *
160
-     * The addressbook id will be passed as the first argument. This is the
161
-     * same id as it is returned from the getAddressBooksForUser method.
162
-     *
163
-     * The cardUri is a base uri, and doesn't include the full path. The
164
-     * cardData argument is the vcard body, and is passed as a string.
165
-     *
166
-     * It is possible to return an ETag from this method. This ETag should
167
-     * match that of the updated resource, and must be enclosed with double
168
-     * quotes (that is: the string itself must contain the actual quotes).
169
-     *
170
-     * You should only return the ETag if you store the carddata as-is. If a
171
-     * subsequent GET request on the same card does not have the same body,
172
-     * byte-by-byte and you did return an ETag here, clients tend to get
173
-     * confused.
174
-     *
175
-     * If you don't return an ETag, you can just return null.
176
-     *
177
-     * @param mixed  $addressBookId
178
-     * @param string $cardUri
179
-     * @param string $cardData
180
-     *
181
-     * @return string|null
182
-     */
183
-    public function updateCard($addressBookId, $cardUri, $cardData);
157
+	/**
158
+	 * Updates a card.
159
+	 *
160
+	 * The addressbook id will be passed as the first argument. This is the
161
+	 * same id as it is returned from the getAddressBooksForUser method.
162
+	 *
163
+	 * The cardUri is a base uri, and doesn't include the full path. The
164
+	 * cardData argument is the vcard body, and is passed as a string.
165
+	 *
166
+	 * It is possible to return an ETag from this method. This ETag should
167
+	 * match that of the updated resource, and must be enclosed with double
168
+	 * quotes (that is: the string itself must contain the actual quotes).
169
+	 *
170
+	 * You should only return the ETag if you store the carddata as-is. If a
171
+	 * subsequent GET request on the same card does not have the same body,
172
+	 * byte-by-byte and you did return an ETag here, clients tend to get
173
+	 * confused.
174
+	 *
175
+	 * If you don't return an ETag, you can just return null.
176
+	 *
177
+	 * @param mixed  $addressBookId
178
+	 * @param string $cardUri
179
+	 * @param string $cardData
180
+	 *
181
+	 * @return string|null
182
+	 */
183
+	public function updateCard($addressBookId, $cardUri, $cardData);
184 184
 
185
-    /**
186
-     * Deletes a card.
187
-     *
188
-     * @param mixed  $addressBookId
189
-     * @param string $cardUri
190
-     *
191
-     * @return bool
192
-     */
193
-    public function deleteCard($addressBookId, $cardUri);
185
+	/**
186
+	 * Deletes a card.
187
+	 *
188
+	 * @param mixed  $addressBookId
189
+	 * @param string $cardUri
190
+	 *
191
+	 * @return bool
192
+	 */
193
+	public function deleteCard($addressBookId, $cardUri);
194 194
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/AbstractBackend.php 2 patches
Indentation   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -17,22 +17,22 @@
 block discarded – undo
17 17
  */
18 18
 abstract class AbstractBackend implements BackendInterface
19 19
 {
20
-    /**
21
-     * Returns a list of cards.
22
-     *
23
-     * This method should work identical to getCard, but instead return all the
24
-     * cards in the list as an array.
25
-     *
26
-     * If the backend supports this, it may allow for some speed-ups.
27
-     *
28
-     * @param mixed $addressBookId
29
-     *
30
-     * @return array
31
-     */
32
-    public function getMultipleCards($addressBookId, array $uris)
33
-    {
34
-        return array_map(function ($uri) use ($addressBookId) {
35
-            return $this->getCard($addressBookId, $uri);
36
-        }, $uris);
37
-    }
20
+	/**
21
+	 * Returns a list of cards.
22
+	 *
23
+	 * This method should work identical to getCard, but instead return all the
24
+	 * cards in the list as an array.
25
+	 *
26
+	 * If the backend supports this, it may allow for some speed-ups.
27
+	 *
28
+	 * @param mixed $addressBookId
29
+	 *
30
+	 * @return array
31
+	 */
32
+	public function getMultipleCards($addressBookId, array $uris)
33
+	{
34
+		return array_map(function ($uri) use ($addressBookId) {
35
+			return $this->getCard($addressBookId, $uri);
36
+		}, $uris);
37
+	}
38 38
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -31,7 +31,7 @@
 block discarded – undo
31 31
      */
32 32
     public function getMultipleCards($addressBookId, array $uris)
33 33
     {
34
-        return array_map(function ($uri) use ($addressBookId) {
34
+        return array_map(function($uri) use ($addressBookId) {
35 35
             return $this->getCard($addressBookId, $uri);
36 36
         }, $uris);
37 37
     }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CardDAV/Backend/PDO.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -101,7 +101,7 @@
 block discarded – undo
101 101
             '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description',
102 102
         ];
103 103
 
104
-        $propPatch->handle($supportedProperties, function ($mutations) use ($addressBookId) {
104
+        $propPatch->handle($supportedProperties, function($mutations) use ($addressBookId) {
105 105
             $updates = [];
106 106
             foreach ($mutations as $property => $newValue) {
107 107
                 switch ($property) {
Please login to merge, or discard this patch.
Indentation   +517 added lines, -517 removed lines patch added patch discarded remove patch
@@ -19,521 +19,521 @@
 block discarded – undo
19 19
  */
20 20
 class PDO extends AbstractBackend implements SyncSupport
21 21
 {
22
-    /**
23
-     * PDO connection.
24
-     *
25
-     * @var PDO
26
-     */
27
-    protected $pdo;
28
-
29
-    /**
30
-     * The PDO table name used to store addressbooks.
31
-     */
32
-    public $addressBooksTableName = 'addressbooks';
33
-
34
-    /**
35
-     * The PDO table name used to store cards.
36
-     */
37
-    public $cardsTableName = 'cards';
38
-
39
-    /**
40
-     * The table name that will be used for tracking changes in address books.
41
-     *
42
-     * @var string
43
-     */
44
-    public $addressBookChangesTableName = 'addressbookchanges';
45
-
46
-    /**
47
-     * Sets up the object.
48
-     */
49
-    public function __construct(\PDO $pdo)
50
-    {
51
-        $this->pdo = $pdo;
52
-    }
53
-
54
-    /**
55
-     * Returns the list of addressbooks for a specific user.
56
-     *
57
-     * @param string $principalUri
58
-     *
59
-     * @return array
60
-     */
61
-    public function getAddressBooksForUser($principalUri)
62
-    {
63
-        $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, synctoken FROM '.$this->addressBooksTableName.' WHERE principaluri = ?');
64
-        $stmt->execute([$principalUri]);
65
-
66
-        $addressBooks = [];
67
-
68
-        foreach ($stmt->fetchAll() as $row) {
69
-            $addressBooks[] = [
70
-                'id' => $row['id'],
71
-                'uri' => $row['uri'],
72
-                'principaluri' => $row['principaluri'],
73
-                '{DAV:}displayname' => $row['displayname'],
74
-                '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description' => $row['description'],
75
-                '{http://calendarserver.org/ns/}getctag' => $row['synctoken'],
76
-                '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0',
77
-            ];
78
-        }
79
-
80
-        return $addressBooks;
81
-    }
82
-
83
-    /**
84
-     * Updates properties for an address book.
85
-     *
86
-     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
87
-     * To do the actual updates, you must tell this object which properties
88
-     * you're going to process with the handle() method.
89
-     *
90
-     * Calling the handle method is like telling the PropPatch object "I
91
-     * promise I can handle updating this property".
92
-     *
93
-     * Read the PropPatch documentation for more info and examples.
94
-     *
95
-     * @param string $addressBookId
96
-     */
97
-    public function updateAddressBook($addressBookId, PropPatch $propPatch)
98
-    {
99
-        $supportedProperties = [
100
-            '{DAV:}displayname',
101
-            '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description',
102
-        ];
103
-
104
-        $propPatch->handle($supportedProperties, function ($mutations) use ($addressBookId) {
105
-            $updates = [];
106
-            foreach ($mutations as $property => $newValue) {
107
-                switch ($property) {
108
-                    case '{DAV:}displayname':
109
-                        $updates['displayname'] = $newValue;
110
-                        break;
111
-                    case '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description':
112
-                        $updates['description'] = $newValue;
113
-                        break;
114
-                }
115
-            }
116
-            $query = 'UPDATE '.$this->addressBooksTableName.' SET ';
117
-            $first = true;
118
-            foreach ($updates as $key => $value) {
119
-                if ($first) {
120
-                    $first = false;
121
-                } else {
122
-                    $query .= ', ';
123
-                }
124
-                $query .= ' '.$key.' = :'.$key.' ';
125
-            }
126
-            $query .= ' WHERE id = :addressbookid';
127
-
128
-            $stmt = $this->pdo->prepare($query);
129
-            $updates['addressbookid'] = $addressBookId;
130
-
131
-            $stmt->execute($updates);
132
-
133
-            $this->addChange($addressBookId, '', 2);
134
-
135
-            return true;
136
-        });
137
-    }
138
-
139
-    /**
140
-     * Creates a new address book.
141
-     *
142
-     * @param string $principalUri
143
-     * @param string $url          just the 'basename' of the url
144
-     *
145
-     * @return int Last insert id
146
-     */
147
-    public function createAddressBook($principalUri, $url, array $properties)
148
-    {
149
-        $values = [
150
-            'displayname' => null,
151
-            'description' => null,
152
-            'principaluri' => $principalUri,
153
-            'uri' => $url,
154
-        ];
155
-
156
-        foreach ($properties as $property => $newValue) {
157
-            switch ($property) {
158
-                case '{DAV:}displayname':
159
-                    $values['displayname'] = $newValue;
160
-                    break;
161
-                case '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description':
162
-                    $values['description'] = $newValue;
163
-                    break;
164
-                default:
165
-                    throw new DAV\Exception\BadRequest('Unknown property: '.$property);
166
-            }
167
-        }
168
-
169
-        $query = 'INSERT INTO '.$this->addressBooksTableName.' (uri, displayname, description, principaluri, synctoken) VALUES (:uri, :displayname, :description, :principaluri, 1)';
170
-        $stmt = $this->pdo->prepare($query);
171
-        $stmt->execute($values);
172
-
173
-        return $this->pdo->lastInsertId(
174
-            $this->addressBooksTableName.'_id_seq'
175
-        );
176
-    }
177
-
178
-    /**
179
-     * Deletes an entire addressbook and all its contents.
180
-     *
181
-     * @param int $addressBookId
182
-     */
183
-    public function deleteAddressBook($addressBookId)
184
-    {
185
-        $stmt = $this->pdo->prepare('DELETE FROM '.$this->cardsTableName.' WHERE addressbookid = ?');
186
-        $stmt->execute([$addressBookId]);
187
-
188
-        $stmt = $this->pdo->prepare('DELETE FROM '.$this->addressBooksTableName.' WHERE id = ?');
189
-        $stmt->execute([$addressBookId]);
190
-
191
-        $stmt = $this->pdo->prepare('DELETE FROM '.$this->addressBookChangesTableName.' WHERE addressbookid = ?');
192
-        $stmt->execute([$addressBookId]);
193
-    }
194
-
195
-    /**
196
-     * Returns all cards for a specific addressbook id.
197
-     *
198
-     * This method should return the following properties for each card:
199
-     *   * carddata - raw vcard data
200
-     *   * uri - Some unique url
201
-     *   * lastmodified - A unix timestamp
202
-     *
203
-     * It's recommended to also return the following properties:
204
-     *   * etag - A unique etag. This must change every time the card changes.
205
-     *   * size - The size of the card in bytes.
206
-     *
207
-     * If these last two properties are provided, less time will be spent
208
-     * calculating them. If they are specified, you can also ommit carddata.
209
-     * This may speed up certain requests, especially with large cards.
210
-     *
211
-     * @param mixed $addressbookId
212
-     *
213
-     * @return array
214
-     */
215
-    public function getCards($addressbookId)
216
-    {
217
-        $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, size FROM '.$this->cardsTableName.' WHERE addressbookid = ?');
218
-        $stmt->execute([$addressbookId]);
219
-
220
-        $result = [];
221
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
222
-            $row['etag'] = '"'.$row['etag'].'"';
223
-            $row['lastmodified'] = (int) $row['lastmodified'];
224
-            $result[] = $row;
225
-        }
226
-
227
-        return $result;
228
-    }
229
-
230
-    /**
231
-     * Returns a specific card.
232
-     *
233
-     * The same set of properties must be returned as with getCards. The only
234
-     * exception is that 'carddata' is absolutely required.
235
-     *
236
-     * If the card does not exist, you must return false.
237
-     *
238
-     * @param mixed  $addressBookId
239
-     * @param string $cardUri
240
-     *
241
-     * @return array
242
-     */
243
-    public function getCard($addressBookId, $cardUri)
244
-    {
245
-        $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified, etag, size FROM '.$this->cardsTableName.' WHERE addressbookid = ? AND uri = ? LIMIT 1');
246
-        $stmt->execute([$addressBookId, $cardUri]);
247
-
248
-        $result = $stmt->fetch(\PDO::FETCH_ASSOC);
249
-
250
-        if (!$result) {
251
-            return false;
252
-        }
253
-
254
-        $result['etag'] = '"'.$result['etag'].'"';
255
-        $result['lastmodified'] = (int) $result['lastmodified'];
256
-
257
-        return $result;
258
-    }
259
-
260
-    /**
261
-     * Returns a list of cards.
262
-     *
263
-     * This method should work identical to getCard, but instead return all the
264
-     * cards in the list as an array.
265
-     *
266
-     * If the backend supports this, it may allow for some speed-ups.
267
-     *
268
-     * @param mixed $addressBookId
269
-     *
270
-     * @return array
271
-     */
272
-    public function getMultipleCards($addressBookId, array $uris)
273
-    {
274
-        $query = 'SELECT id, uri, lastmodified, etag, size, carddata FROM '.$this->cardsTableName.' WHERE addressbookid = ? AND uri IN (';
275
-        // Inserting a whole bunch of question marks
276
-        $query .= implode(',', array_fill(0, count($uris), '?'));
277
-        $query .= ')';
278
-
279
-        $stmt = $this->pdo->prepare($query);
280
-        $stmt->execute(array_merge([$addressBookId], $uris));
281
-        $result = [];
282
-        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
283
-            $row['etag'] = '"'.$row['etag'].'"';
284
-            $row['lastmodified'] = (int) $row['lastmodified'];
285
-            $result[] = $row;
286
-        }
287
-
288
-        return $result;
289
-    }
290
-
291
-    /**
292
-     * Creates a new card.
293
-     *
294
-     * The addressbook id will be passed as the first argument. This is the
295
-     * same id as it is returned from the getAddressBooksForUser method.
296
-     *
297
-     * The cardUri is a base uri, and doesn't include the full path. The
298
-     * cardData argument is the vcard body, and is passed as a string.
299
-     *
300
-     * It is possible to return an ETag from this method. This ETag is for the
301
-     * newly created resource, and must be enclosed with double quotes (that
302
-     * is, the string itself must contain the double quotes).
303
-     *
304
-     * You should only return the ETag if you store the carddata as-is. If a
305
-     * subsequent GET request on the same card does not have the same body,
306
-     * byte-by-byte and you did return an ETag here, clients tend to get
307
-     * confused.
308
-     *
309
-     * If you don't return an ETag, you can just return null.
310
-     *
311
-     * @param mixed  $addressBookId
312
-     * @param string $cardUri
313
-     * @param string $cardData
314
-     *
315
-     * @return string|null
316
-     */
317
-    public function createCard($addressBookId, $cardUri, $cardData)
318
-    {
319
-        $stmt = $this->pdo->prepare('INSERT INTO '.$this->cardsTableName.' (carddata, uri, lastmodified, addressbookid, size, etag) VALUES (?, ?, ?, ?, ?, ?)');
320
-
321
-        $etag = md5($cardData);
322
-
323
-        $stmt->execute([
324
-            $cardData,
325
-            $cardUri,
326
-            time(),
327
-            $addressBookId,
328
-            strlen($cardData),
329
-            $etag,
330
-        ]);
331
-
332
-        $this->addChange($addressBookId, $cardUri, 1);
333
-
334
-        return '"'.$etag.'"';
335
-    }
336
-
337
-    /**
338
-     * Updates a card.
339
-     *
340
-     * The addressbook id will be passed as the first argument. This is the
341
-     * same id as it is returned from the getAddressBooksForUser method.
342
-     *
343
-     * The cardUri is a base uri, and doesn't include the full path. The
344
-     * cardData argument is the vcard body, and is passed as a string.
345
-     *
346
-     * It is possible to return an ETag from this method. This ETag should
347
-     * match that of the updated resource, and must be enclosed with double
348
-     * quotes (that is: the string itself must contain the actual quotes).
349
-     *
350
-     * You should only return the ETag if you store the carddata as-is. If a
351
-     * subsequent GET request on the same card does not have the same body,
352
-     * byte-by-byte and you did return an ETag here, clients tend to get
353
-     * confused.
354
-     *
355
-     * If you don't return an ETag, you can just return null.
356
-     *
357
-     * @param mixed  $addressBookId
358
-     * @param string $cardUri
359
-     * @param string $cardData
360
-     *
361
-     * @return string|null
362
-     */
363
-    public function updateCard($addressBookId, $cardUri, $cardData)
364
-    {
365
-        $stmt = $this->pdo->prepare('UPDATE '.$this->cardsTableName.' SET carddata = ?, lastmodified = ?, size = ?, etag = ? WHERE uri = ? AND addressbookid =?');
366
-
367
-        $etag = md5($cardData);
368
-        $stmt->execute([
369
-            $cardData,
370
-            time(),
371
-            strlen($cardData),
372
-            $etag,
373
-            $cardUri,
374
-            $addressBookId,
375
-        ]);
376
-
377
-        $this->addChange($addressBookId, $cardUri, 2);
378
-
379
-        return '"'.$etag.'"';
380
-    }
381
-
382
-    /**
383
-     * Deletes a card.
384
-     *
385
-     * @param mixed  $addressBookId
386
-     * @param string $cardUri
387
-     *
388
-     * @return bool
389
-     */
390
-    public function deleteCard($addressBookId, $cardUri)
391
-    {
392
-        $stmt = $this->pdo->prepare('DELETE FROM '.$this->cardsTableName.' WHERE addressbookid = ? AND uri = ?');
393
-        $stmt->execute([$addressBookId, $cardUri]);
394
-
395
-        $this->addChange($addressBookId, $cardUri, 3);
396
-
397
-        return 1 === $stmt->rowCount();
398
-    }
399
-
400
-    /**
401
-     * The getChanges method returns all the changes that have happened, since
402
-     * the specified syncToken in the specified address book.
403
-     *
404
-     * This function should return an array, such as the following:
405
-     *
406
-     * [
407
-     *   'syncToken' => 'The current synctoken',
408
-     *   'added'   => [
409
-     *      'new.txt',
410
-     *   ],
411
-     *   'modified'   => [
412
-     *      'updated.txt',
413
-     *   ],
414
-     *   'deleted' => [
415
-     *      'foo.php.bak',
416
-     *      'old.txt'
417
-     *   ]
418
-     * ];
419
-     *
420
-     * The returned syncToken property should reflect the *current* syncToken
421
-     * of the addressbook, as reported in the {http://sabredav.org/ns}sync-token
422
-     * property. This is needed here too, to ensure the operation is atomic.
423
-     *
424
-     * If the $syncToken argument is specified as null, this is an initial
425
-     * sync, and all members should be reported.
426
-     *
427
-     * The modified property is an array of nodenames that have changed since
428
-     * the last token.
429
-     *
430
-     * The deleted property is an array with nodenames, that have been deleted
431
-     * from collection.
432
-     *
433
-     * The $syncLevel argument is basically the 'depth' of the report. If it's
434
-     * 1, you only have to report changes that happened only directly in
435
-     * immediate descendants. If it's 2, it should also include changes from
436
-     * the nodes below the child collections. (grandchildren)
437
-     *
438
-     * The $limit argument allows a client to specify how many results should
439
-     * be returned at most. If the limit is not specified, it should be treated
440
-     * as infinite.
441
-     *
442
-     * If the limit (infinite or not) is higher than you're willing to return,
443
-     * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
444
-     *
445
-     * If the syncToken is expired (due to data cleanup) or unknown, you must
446
-     * return null.
447
-     *
448
-     * The limit is 'suggestive'. You are free to ignore it.
449
-     *
450
-     * @param string $addressBookId
451
-     * @param string $syncToken
452
-     * @param int    $syncLevel
453
-     * @param int    $limit
454
-     *
455
-     * @return array|null
456
-     */
457
-    public function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null)
458
-    {
459
-        // Current synctoken
460
-        $stmt = $this->pdo->prepare('SELECT synctoken FROM '.$this->addressBooksTableName.' WHERE id = ?');
461
-        $stmt->execute([$addressBookId]);
462
-        $currentToken = $stmt->fetchColumn(0);
463
-
464
-        if (is_null($currentToken)) {
465
-            return null;
466
-        }
467
-
468
-        $result = [
469
-            'syncToken' => $currentToken,
470
-            'added' => [],
471
-            'modified' => [],
472
-            'deleted' => [],
473
-        ];
474
-
475
-        if ($syncToken) {
476
-            $query = 'SELECT uri, operation FROM '.$this->addressBookChangesTableName.' WHERE synctoken >= ? AND synctoken < ? AND addressbookid = ? ORDER BY synctoken';
477
-            if ($limit > 0) {
478
-                $query .= ' LIMIT '.(int) $limit;
479
-            }
480
-
481
-            // Fetching all changes
482
-            $stmt = $this->pdo->prepare($query);
483
-            $stmt->execute([$syncToken, $currentToken, $addressBookId]);
484
-
485
-            $changes = [];
486
-
487
-            // This loop ensures that any duplicates are overwritten, only the
488
-            // last change on a node is relevant.
489
-            while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
490
-                $changes[$row['uri']] = $row['operation'];
491
-            }
492
-
493
-            foreach ($changes as $uri => $operation) {
494
-                switch ($operation) {
495
-                    case 1:
496
-                        $result['added'][] = $uri;
497
-                        break;
498
-                    case 2:
499
-                        $result['modified'][] = $uri;
500
-                        break;
501
-                    case 3:
502
-                        $result['deleted'][] = $uri;
503
-                        break;
504
-                }
505
-            }
506
-        } else {
507
-            // No synctoken supplied, this is the initial sync.
508
-            $query = 'SELECT uri FROM '.$this->cardsTableName.' WHERE addressbookid = ?';
509
-            $stmt = $this->pdo->prepare($query);
510
-            $stmt->execute([$addressBookId]);
511
-
512
-            $result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
513
-        }
514
-
515
-        return $result;
516
-    }
517
-
518
-    /**
519
-     * Adds a change record to the addressbookchanges table.
520
-     *
521
-     * @param mixed  $addressBookId
522
-     * @param string $objectUri
523
-     * @param int    $operation     1 = add, 2 = modify, 3 = delete
524
-     */
525
-    protected function addChange($addressBookId, $objectUri, $operation)
526
-    {
527
-        $stmt = $this->pdo->prepare('INSERT INTO '.$this->addressBookChangesTableName.' (uri, synctoken, addressbookid, operation) SELECT ?, synctoken, ?, ? FROM '.$this->addressBooksTableName.' WHERE id = ?');
528
-        $stmt->execute([
529
-            $objectUri,
530
-            $addressBookId,
531
-            $operation,
532
-            $addressBookId,
533
-        ]);
534
-        $stmt = $this->pdo->prepare('UPDATE '.$this->addressBooksTableName.' SET synctoken = synctoken + 1 WHERE id = ?');
535
-        $stmt->execute([
536
-            $addressBookId,
537
-        ]);
538
-    }
22
+	/**
23
+	 * PDO connection.
24
+	 *
25
+	 * @var PDO
26
+	 */
27
+	protected $pdo;
28
+
29
+	/**
30
+	 * The PDO table name used to store addressbooks.
31
+	 */
32
+	public $addressBooksTableName = 'addressbooks';
33
+
34
+	/**
35
+	 * The PDO table name used to store cards.
36
+	 */
37
+	public $cardsTableName = 'cards';
38
+
39
+	/**
40
+	 * The table name that will be used for tracking changes in address books.
41
+	 *
42
+	 * @var string
43
+	 */
44
+	public $addressBookChangesTableName = 'addressbookchanges';
45
+
46
+	/**
47
+	 * Sets up the object.
48
+	 */
49
+	public function __construct(\PDO $pdo)
50
+	{
51
+		$this->pdo = $pdo;
52
+	}
53
+
54
+	/**
55
+	 * Returns the list of addressbooks for a specific user.
56
+	 *
57
+	 * @param string $principalUri
58
+	 *
59
+	 * @return array
60
+	 */
61
+	public function getAddressBooksForUser($principalUri)
62
+	{
63
+		$stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, synctoken FROM '.$this->addressBooksTableName.' WHERE principaluri = ?');
64
+		$stmt->execute([$principalUri]);
65
+
66
+		$addressBooks = [];
67
+
68
+		foreach ($stmt->fetchAll() as $row) {
69
+			$addressBooks[] = [
70
+				'id' => $row['id'],
71
+				'uri' => $row['uri'],
72
+				'principaluri' => $row['principaluri'],
73
+				'{DAV:}displayname' => $row['displayname'],
74
+				'{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description' => $row['description'],
75
+				'{http://calendarserver.org/ns/}getctag' => $row['synctoken'],
76
+				'{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0',
77
+			];
78
+		}
79
+
80
+		return $addressBooks;
81
+	}
82
+
83
+	/**
84
+	 * Updates properties for an address book.
85
+	 *
86
+	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
87
+	 * To do the actual updates, you must tell this object which properties
88
+	 * you're going to process with the handle() method.
89
+	 *
90
+	 * Calling the handle method is like telling the PropPatch object "I
91
+	 * promise I can handle updating this property".
92
+	 *
93
+	 * Read the PropPatch documentation for more info and examples.
94
+	 *
95
+	 * @param string $addressBookId
96
+	 */
97
+	public function updateAddressBook($addressBookId, PropPatch $propPatch)
98
+	{
99
+		$supportedProperties = [
100
+			'{DAV:}displayname',
101
+			'{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description',
102
+		];
103
+
104
+		$propPatch->handle($supportedProperties, function ($mutations) use ($addressBookId) {
105
+			$updates = [];
106
+			foreach ($mutations as $property => $newValue) {
107
+				switch ($property) {
108
+					case '{DAV:}displayname':
109
+						$updates['displayname'] = $newValue;
110
+						break;
111
+					case '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description':
112
+						$updates['description'] = $newValue;
113
+						break;
114
+				}
115
+			}
116
+			$query = 'UPDATE '.$this->addressBooksTableName.' SET ';
117
+			$first = true;
118
+			foreach ($updates as $key => $value) {
119
+				if ($first) {
120
+					$first = false;
121
+				} else {
122
+					$query .= ', ';
123
+				}
124
+				$query .= ' '.$key.' = :'.$key.' ';
125
+			}
126
+			$query .= ' WHERE id = :addressbookid';
127
+
128
+			$stmt = $this->pdo->prepare($query);
129
+			$updates['addressbookid'] = $addressBookId;
130
+
131
+			$stmt->execute($updates);
132
+
133
+			$this->addChange($addressBookId, '', 2);
134
+
135
+			return true;
136
+		});
137
+	}
138
+
139
+	/**
140
+	 * Creates a new address book.
141
+	 *
142
+	 * @param string $principalUri
143
+	 * @param string $url          just the 'basename' of the url
144
+	 *
145
+	 * @return int Last insert id
146
+	 */
147
+	public function createAddressBook($principalUri, $url, array $properties)
148
+	{
149
+		$values = [
150
+			'displayname' => null,
151
+			'description' => null,
152
+			'principaluri' => $principalUri,
153
+			'uri' => $url,
154
+		];
155
+
156
+		foreach ($properties as $property => $newValue) {
157
+			switch ($property) {
158
+				case '{DAV:}displayname':
159
+					$values['displayname'] = $newValue;
160
+					break;
161
+				case '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description':
162
+					$values['description'] = $newValue;
163
+					break;
164
+				default:
165
+					throw new DAV\Exception\BadRequest('Unknown property: '.$property);
166
+			}
167
+		}
168
+
169
+		$query = 'INSERT INTO '.$this->addressBooksTableName.' (uri, displayname, description, principaluri, synctoken) VALUES (:uri, :displayname, :description, :principaluri, 1)';
170
+		$stmt = $this->pdo->prepare($query);
171
+		$stmt->execute($values);
172
+
173
+		return $this->pdo->lastInsertId(
174
+			$this->addressBooksTableName.'_id_seq'
175
+		);
176
+	}
177
+
178
+	/**
179
+	 * Deletes an entire addressbook and all its contents.
180
+	 *
181
+	 * @param int $addressBookId
182
+	 */
183
+	public function deleteAddressBook($addressBookId)
184
+	{
185
+		$stmt = $this->pdo->prepare('DELETE FROM '.$this->cardsTableName.' WHERE addressbookid = ?');
186
+		$stmt->execute([$addressBookId]);
187
+
188
+		$stmt = $this->pdo->prepare('DELETE FROM '.$this->addressBooksTableName.' WHERE id = ?');
189
+		$stmt->execute([$addressBookId]);
190
+
191
+		$stmt = $this->pdo->prepare('DELETE FROM '.$this->addressBookChangesTableName.' WHERE addressbookid = ?');
192
+		$stmt->execute([$addressBookId]);
193
+	}
194
+
195
+	/**
196
+	 * Returns all cards for a specific addressbook id.
197
+	 *
198
+	 * This method should return the following properties for each card:
199
+	 *   * carddata - raw vcard data
200
+	 *   * uri - Some unique url
201
+	 *   * lastmodified - A unix timestamp
202
+	 *
203
+	 * It's recommended to also return the following properties:
204
+	 *   * etag - A unique etag. This must change every time the card changes.
205
+	 *   * size - The size of the card in bytes.
206
+	 *
207
+	 * If these last two properties are provided, less time will be spent
208
+	 * calculating them. If they are specified, you can also ommit carddata.
209
+	 * This may speed up certain requests, especially with large cards.
210
+	 *
211
+	 * @param mixed $addressbookId
212
+	 *
213
+	 * @return array
214
+	 */
215
+	public function getCards($addressbookId)
216
+	{
217
+		$stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, size FROM '.$this->cardsTableName.' WHERE addressbookid = ?');
218
+		$stmt->execute([$addressbookId]);
219
+
220
+		$result = [];
221
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
222
+			$row['etag'] = '"'.$row['etag'].'"';
223
+			$row['lastmodified'] = (int) $row['lastmodified'];
224
+			$result[] = $row;
225
+		}
226
+
227
+		return $result;
228
+	}
229
+
230
+	/**
231
+	 * Returns a specific card.
232
+	 *
233
+	 * The same set of properties must be returned as with getCards. The only
234
+	 * exception is that 'carddata' is absolutely required.
235
+	 *
236
+	 * If the card does not exist, you must return false.
237
+	 *
238
+	 * @param mixed  $addressBookId
239
+	 * @param string $cardUri
240
+	 *
241
+	 * @return array
242
+	 */
243
+	public function getCard($addressBookId, $cardUri)
244
+	{
245
+		$stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified, etag, size FROM '.$this->cardsTableName.' WHERE addressbookid = ? AND uri = ? LIMIT 1');
246
+		$stmt->execute([$addressBookId, $cardUri]);
247
+
248
+		$result = $stmt->fetch(\PDO::FETCH_ASSOC);
249
+
250
+		if (!$result) {
251
+			return false;
252
+		}
253
+
254
+		$result['etag'] = '"'.$result['etag'].'"';
255
+		$result['lastmodified'] = (int) $result['lastmodified'];
256
+
257
+		return $result;
258
+	}
259
+
260
+	/**
261
+	 * Returns a list of cards.
262
+	 *
263
+	 * This method should work identical to getCard, but instead return all the
264
+	 * cards in the list as an array.
265
+	 *
266
+	 * If the backend supports this, it may allow for some speed-ups.
267
+	 *
268
+	 * @param mixed $addressBookId
269
+	 *
270
+	 * @return array
271
+	 */
272
+	public function getMultipleCards($addressBookId, array $uris)
273
+	{
274
+		$query = 'SELECT id, uri, lastmodified, etag, size, carddata FROM '.$this->cardsTableName.' WHERE addressbookid = ? AND uri IN (';
275
+		// Inserting a whole bunch of question marks
276
+		$query .= implode(',', array_fill(0, count($uris), '?'));
277
+		$query .= ')';
278
+
279
+		$stmt = $this->pdo->prepare($query);
280
+		$stmt->execute(array_merge([$addressBookId], $uris));
281
+		$result = [];
282
+		while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
283
+			$row['etag'] = '"'.$row['etag'].'"';
284
+			$row['lastmodified'] = (int) $row['lastmodified'];
285
+			$result[] = $row;
286
+		}
287
+
288
+		return $result;
289
+	}
290
+
291
+	/**
292
+	 * Creates a new card.
293
+	 *
294
+	 * The addressbook id will be passed as the first argument. This is the
295
+	 * same id as it is returned from the getAddressBooksForUser method.
296
+	 *
297
+	 * The cardUri is a base uri, and doesn't include the full path. The
298
+	 * cardData argument is the vcard body, and is passed as a string.
299
+	 *
300
+	 * It is possible to return an ETag from this method. This ETag is for the
301
+	 * newly created resource, and must be enclosed with double quotes (that
302
+	 * is, the string itself must contain the double quotes).
303
+	 *
304
+	 * You should only return the ETag if you store the carddata as-is. If a
305
+	 * subsequent GET request on the same card does not have the same body,
306
+	 * byte-by-byte and you did return an ETag here, clients tend to get
307
+	 * confused.
308
+	 *
309
+	 * If you don't return an ETag, you can just return null.
310
+	 *
311
+	 * @param mixed  $addressBookId
312
+	 * @param string $cardUri
313
+	 * @param string $cardData
314
+	 *
315
+	 * @return string|null
316
+	 */
317
+	public function createCard($addressBookId, $cardUri, $cardData)
318
+	{
319
+		$stmt = $this->pdo->prepare('INSERT INTO '.$this->cardsTableName.' (carddata, uri, lastmodified, addressbookid, size, etag) VALUES (?, ?, ?, ?, ?, ?)');
320
+
321
+		$etag = md5($cardData);
322
+
323
+		$stmt->execute([
324
+			$cardData,
325
+			$cardUri,
326
+			time(),
327
+			$addressBookId,
328
+			strlen($cardData),
329
+			$etag,
330
+		]);
331
+
332
+		$this->addChange($addressBookId, $cardUri, 1);
333
+
334
+		return '"'.$etag.'"';
335
+	}
336
+
337
+	/**
338
+	 * Updates a card.
339
+	 *
340
+	 * The addressbook id will be passed as the first argument. This is the
341
+	 * same id as it is returned from the getAddressBooksForUser method.
342
+	 *
343
+	 * The cardUri is a base uri, and doesn't include the full path. The
344
+	 * cardData argument is the vcard body, and is passed as a string.
345
+	 *
346
+	 * It is possible to return an ETag from this method. This ETag should
347
+	 * match that of the updated resource, and must be enclosed with double
348
+	 * quotes (that is: the string itself must contain the actual quotes).
349
+	 *
350
+	 * You should only return the ETag if you store the carddata as-is. If a
351
+	 * subsequent GET request on the same card does not have the same body,
352
+	 * byte-by-byte and you did return an ETag here, clients tend to get
353
+	 * confused.
354
+	 *
355
+	 * If you don't return an ETag, you can just return null.
356
+	 *
357
+	 * @param mixed  $addressBookId
358
+	 * @param string $cardUri
359
+	 * @param string $cardData
360
+	 *
361
+	 * @return string|null
362
+	 */
363
+	public function updateCard($addressBookId, $cardUri, $cardData)
364
+	{
365
+		$stmt = $this->pdo->prepare('UPDATE '.$this->cardsTableName.' SET carddata = ?, lastmodified = ?, size = ?, etag = ? WHERE uri = ? AND addressbookid =?');
366
+
367
+		$etag = md5($cardData);
368
+		$stmt->execute([
369
+			$cardData,
370
+			time(),
371
+			strlen($cardData),
372
+			$etag,
373
+			$cardUri,
374
+			$addressBookId,
375
+		]);
376
+
377
+		$this->addChange($addressBookId, $cardUri, 2);
378
+
379
+		return '"'.$etag.'"';
380
+	}
381
+
382
+	/**
383
+	 * Deletes a card.
384
+	 *
385
+	 * @param mixed  $addressBookId
386
+	 * @param string $cardUri
387
+	 *
388
+	 * @return bool
389
+	 */
390
+	public function deleteCard($addressBookId, $cardUri)
391
+	{
392
+		$stmt = $this->pdo->prepare('DELETE FROM '.$this->cardsTableName.' WHERE addressbookid = ? AND uri = ?');
393
+		$stmt->execute([$addressBookId, $cardUri]);
394
+
395
+		$this->addChange($addressBookId, $cardUri, 3);
396
+
397
+		return 1 === $stmt->rowCount();
398
+	}
399
+
400
+	/**
401
+	 * The getChanges method returns all the changes that have happened, since
402
+	 * the specified syncToken in the specified address book.
403
+	 *
404
+	 * This function should return an array, such as the following:
405
+	 *
406
+	 * [
407
+	 *   'syncToken' => 'The current synctoken',
408
+	 *   'added'   => [
409
+	 *      'new.txt',
410
+	 *   ],
411
+	 *   'modified'   => [
412
+	 *      'updated.txt',
413
+	 *   ],
414
+	 *   'deleted' => [
415
+	 *      'foo.php.bak',
416
+	 *      'old.txt'
417
+	 *   ]
418
+	 * ];
419
+	 *
420
+	 * The returned syncToken property should reflect the *current* syncToken
421
+	 * of the addressbook, as reported in the {http://sabredav.org/ns}sync-token
422
+	 * property. This is needed here too, to ensure the operation is atomic.
423
+	 *
424
+	 * If the $syncToken argument is specified as null, this is an initial
425
+	 * sync, and all members should be reported.
426
+	 *
427
+	 * The modified property is an array of nodenames that have changed since
428
+	 * the last token.
429
+	 *
430
+	 * The deleted property is an array with nodenames, that have been deleted
431
+	 * from collection.
432
+	 *
433
+	 * The $syncLevel argument is basically the 'depth' of the report. If it's
434
+	 * 1, you only have to report changes that happened only directly in
435
+	 * immediate descendants. If it's 2, it should also include changes from
436
+	 * the nodes below the child collections. (grandchildren)
437
+	 *
438
+	 * The $limit argument allows a client to specify how many results should
439
+	 * be returned at most. If the limit is not specified, it should be treated
440
+	 * as infinite.
441
+	 *
442
+	 * If the limit (infinite or not) is higher than you're willing to return,
443
+	 * you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
444
+	 *
445
+	 * If the syncToken is expired (due to data cleanup) or unknown, you must
446
+	 * return null.
447
+	 *
448
+	 * The limit is 'suggestive'. You are free to ignore it.
449
+	 *
450
+	 * @param string $addressBookId
451
+	 * @param string $syncToken
452
+	 * @param int    $syncLevel
453
+	 * @param int    $limit
454
+	 *
455
+	 * @return array|null
456
+	 */
457
+	public function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null)
458
+	{
459
+		// Current synctoken
460
+		$stmt = $this->pdo->prepare('SELECT synctoken FROM '.$this->addressBooksTableName.' WHERE id = ?');
461
+		$stmt->execute([$addressBookId]);
462
+		$currentToken = $stmt->fetchColumn(0);
463
+
464
+		if (is_null($currentToken)) {
465
+			return null;
466
+		}
467
+
468
+		$result = [
469
+			'syncToken' => $currentToken,
470
+			'added' => [],
471
+			'modified' => [],
472
+			'deleted' => [],
473
+		];
474
+
475
+		if ($syncToken) {
476
+			$query = 'SELECT uri, operation FROM '.$this->addressBookChangesTableName.' WHERE synctoken >= ? AND synctoken < ? AND addressbookid = ? ORDER BY synctoken';
477
+			if ($limit > 0) {
478
+				$query .= ' LIMIT '.(int) $limit;
479
+			}
480
+
481
+			// Fetching all changes
482
+			$stmt = $this->pdo->prepare($query);
483
+			$stmt->execute([$syncToken, $currentToken, $addressBookId]);
484
+
485
+			$changes = [];
486
+
487
+			// This loop ensures that any duplicates are overwritten, only the
488
+			// last change on a node is relevant.
489
+			while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
490
+				$changes[$row['uri']] = $row['operation'];
491
+			}
492
+
493
+			foreach ($changes as $uri => $operation) {
494
+				switch ($operation) {
495
+					case 1:
496
+						$result['added'][] = $uri;
497
+						break;
498
+					case 2:
499
+						$result['modified'][] = $uri;
500
+						break;
501
+					case 3:
502
+						$result['deleted'][] = $uri;
503
+						break;
504
+				}
505
+			}
506
+		} else {
507
+			// No synctoken supplied, this is the initial sync.
508
+			$query = 'SELECT uri FROM '.$this->cardsTableName.' WHERE addressbookid = ?';
509
+			$stmt = $this->pdo->prepare($query);
510
+			$stmt->execute([$addressBookId]);
511
+
512
+			$result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
513
+		}
514
+
515
+		return $result;
516
+	}
517
+
518
+	/**
519
+	 * Adds a change record to the addressbookchanges table.
520
+	 *
521
+	 * @param mixed  $addressBookId
522
+	 * @param string $objectUri
523
+	 * @param int    $operation     1 = add, 2 = modify, 3 = delete
524
+	 */
525
+	protected function addChange($addressBookId, $objectUri, $operation)
526
+	{
527
+		$stmt = $this->pdo->prepare('INSERT INTO '.$this->addressBookChangesTableName.' (uri, synctoken, addressbookid, operation) SELECT ?, synctoken, ?, ? FROM '.$this->addressBooksTableName.' WHERE id = ?');
528
+		$stmt->execute([
529
+			$objectUri,
530
+			$addressBookId,
531
+			$operation,
532
+			$addressBookId,
533
+		]);
534
+		$stmt = $this->pdo->prepare('UPDATE '.$this->addressBooksTableName.' SET synctoken = synctoken + 1 WHERE id = ?');
535
+		$stmt->execute([
536
+			$addressBookId,
537
+		]);
538
+	}
539 539
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CardDAV/AddressBookRoot.php 1 patch
Indentation   +51 added lines, -51 removed lines patch added patch discarded remove patch
@@ -17,59 +17,59 @@
 block discarded – undo
17 17
  */
18 18
 class AddressBookRoot extends DAVACL\AbstractPrincipalCollection
19 19
 {
20
-    /**
21
-     * Principal Backend.
22
-     *
23
-     * @var DAVACL\PrincipalBackend\BackendInterface
24
-     */
25
-    protected $principalBackend;
20
+	/**
21
+	 * Principal Backend.
22
+	 *
23
+	 * @var DAVACL\PrincipalBackend\BackendInterface
24
+	 */
25
+	protected $principalBackend;
26 26
 
27
-    /**
28
-     * CardDAV backend.
29
-     *
30
-     * @var Backend\BackendInterface
31
-     */
32
-    protected $carddavBackend;
27
+	/**
28
+	 * CardDAV backend.
29
+	 *
30
+	 * @var Backend\BackendInterface
31
+	 */
32
+	protected $carddavBackend;
33 33
 
34
-    /**
35
-     * Constructor.
36
-     *
37
-     * This constructor needs both a principal and a carddav backend.
38
-     *
39
-     * By default this class will show a list of addressbook collections for
40
-     * principals in the 'principals' collection. If your main principals are
41
-     * actually located in a different path, use the $principalPrefix argument
42
-     * to override this.
43
-     *
44
-     * @param string $principalPrefix
45
-     */
46
-    public function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, Backend\BackendInterface $carddavBackend, $principalPrefix = 'principals')
47
-    {
48
-        $this->carddavBackend = $carddavBackend;
49
-        parent::__construct($principalBackend, $principalPrefix);
50
-    }
34
+	/**
35
+	 * Constructor.
36
+	 *
37
+	 * This constructor needs both a principal and a carddav backend.
38
+	 *
39
+	 * By default this class will show a list of addressbook collections for
40
+	 * principals in the 'principals' collection. If your main principals are
41
+	 * actually located in a different path, use the $principalPrefix argument
42
+	 * to override this.
43
+	 *
44
+	 * @param string $principalPrefix
45
+	 */
46
+	public function __construct(DAVACL\PrincipalBackend\BackendInterface $principalBackend, Backend\BackendInterface $carddavBackend, $principalPrefix = 'principals')
47
+	{
48
+		$this->carddavBackend = $carddavBackend;
49
+		parent::__construct($principalBackend, $principalPrefix);
50
+	}
51 51
 
52
-    /**
53
-     * Returns the name of the node.
54
-     *
55
-     * @return string
56
-     */
57
-    public function getName()
58
-    {
59
-        return Plugin::ADDRESSBOOK_ROOT;
60
-    }
52
+	/**
53
+	 * Returns the name of the node.
54
+	 *
55
+	 * @return string
56
+	 */
57
+	public function getName()
58
+	{
59
+		return Plugin::ADDRESSBOOK_ROOT;
60
+	}
61 61
 
62
-    /**
63
-     * This method returns a node for a principal.
64
-     *
65
-     * The passed array contains principal information, and is guaranteed to
66
-     * at least contain a uri item. Other properties may or may not be
67
-     * supplied by the authentication backend.
68
-     *
69
-     * @return \Sabre\DAV\INode
70
-     */
71
-    public function getChildForPrincipal(array $principal)
72
-    {
73
-        return new AddressBookHome($this->carddavBackend, $principal['uri']);
74
-    }
62
+	/**
63
+	 * This method returns a node for a principal.
64
+	 *
65
+	 * The passed array contains principal information, and is guaranteed to
66
+	 * at least contain a uri item. Other properties may or may not be
67
+	 * supplied by the authentication backend.
68
+	 *
69
+	 * @return \Sabre\DAV\INode
70
+	 */
71
+	public function getChildForPrincipal(array $principal)
72
+	{
73
+		return new AddressBookHome($this->carddavBackend, $principal['uri']);
74
+	}
75 75
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CardDAV/VCFExportPlugin.php 2 patches
Indentation   +139 added lines, -139 removed lines patch added patch discarded remove patch
@@ -23,143 +23,143 @@
 block discarded – undo
23 23
  */
24 24
 class VCFExportPlugin extends DAV\ServerPlugin
25 25
 {
26
-    /**
27
-     * Reference to Server class.
28
-     *
29
-     * @var DAV\Server
30
-     */
31
-    protected $server;
32
-
33
-    /**
34
-     * Initializes the plugin and registers event handlers.
35
-     */
36
-    public function initialize(DAV\Server $server)
37
-    {
38
-        $this->server = $server;
39
-        $this->server->on('method:GET', [$this, 'httpGet'], 90);
40
-        $server->on('browserButtonActions', function ($path, $node, &$actions) {
41
-            if ($node instanceof IAddressBook) {
42
-                $actions .= '<a href="'.htmlspecialchars($path, ENT_QUOTES, 'UTF-8').'?export"><span class="oi" data-glyph="book"></span></a>';
43
-            }
44
-        });
45
-    }
46
-
47
-    /**
48
-     * Intercepts GET requests on addressbook urls ending with ?export.
49
-     *
50
-     * @return bool
51
-     */
52
-    public function httpGet(RequestInterface $request, ResponseInterface $response)
53
-    {
54
-        $queryParams = $request->getQueryParameters();
55
-        if (!array_key_exists('export', $queryParams)) {
56
-            return;
57
-        }
58
-
59
-        $path = $request->getPath();
60
-
61
-        $node = $this->server->tree->getNodeForPath($path);
62
-
63
-        if (!($node instanceof IAddressBook)) {
64
-            return;
65
-        }
66
-
67
-        $this->server->transactionType = 'get-addressbook-export';
68
-
69
-        // Checking ACL, if available.
70
-        if ($aclPlugin = $this->server->getPlugin('acl')) {
71
-            $aclPlugin->checkPrivileges($path, '{DAV:}read');
72
-        }
73
-
74
-        $nodes = $this->server->getPropertiesForPath($path, [
75
-            '{'.Plugin::NS_CARDDAV.'}address-data',
76
-        ], 1);
77
-
78
-        $format = 'text/directory';
79
-
80
-        $output = null;
81
-        $filenameExtension = null;
82
-
83
-        switch ($format) {
84
-            case 'text/directory':
85
-                $output = $this->generateVCF($nodes);
86
-                $filenameExtension = '.vcf';
87
-                break;
88
-        }
89
-
90
-        $filename = preg_replace(
91
-            '/[^a-zA-Z0-9-_ ]/um',
92
-            '',
93
-            $node->getName()
94
-        );
95
-        $filename .= '-'.date('Y-m-d').$filenameExtension;
96
-
97
-        $response->setHeader('Content-Disposition', 'attachment; filename="'.$filename.'"');
98
-        $response->setHeader('Content-Type', $format);
99
-
100
-        $response->setStatus(200);
101
-        $response->setBody($output);
102
-
103
-        // Returning false to break the event chain
104
-        return false;
105
-    }
106
-
107
-    /**
108
-     * Merges all vcard objects, and builds one big vcf export.
109
-     *
110
-     * @return string
111
-     */
112
-    public function generateVCF(array $nodes)
113
-    {
114
-        $output = '';
115
-
116
-        foreach ($nodes as $node) {
117
-            if (!isset($node[200]['{'.Plugin::NS_CARDDAV.'}address-data'])) {
118
-                continue;
119
-            }
120
-            $nodeData = $node[200]['{'.Plugin::NS_CARDDAV.'}address-data'];
121
-
122
-            // Parsing this node so VObject can clean up the output.
123
-            $vcard = VObject\Reader::read($nodeData);
124
-            $output .= $vcard->serialize();
125
-
126
-            // Destroy circular references to PHP will GC the object.
127
-            $vcard->destroy();
128
-        }
129
-
130
-        return $output;
131
-    }
132
-
133
-    /**
134
-     * Returns a plugin name.
135
-     *
136
-     * Using this name other plugins will be able to access other plugins
137
-     * using \Sabre\DAV\Server::getPlugin
138
-     *
139
-     * @return string
140
-     */
141
-    public function getPluginName()
142
-    {
143
-        return 'vcf-export';
144
-    }
145
-
146
-    /**
147
-     * Returns a bunch of meta-data about the plugin.
148
-     *
149
-     * Providing this information is optional, and is mainly displayed by the
150
-     * Browser plugin.
151
-     *
152
-     * The description key in the returned array may contain html and will not
153
-     * be sanitized.
154
-     *
155
-     * @return array
156
-     */
157
-    public function getPluginInfo()
158
-    {
159
-        return [
160
-            'name' => $this->getPluginName(),
161
-            'description' => 'Adds the ability to export CardDAV addressbooks as a single vCard file.',
162
-            'link' => 'http://sabre.io/dav/vcf-export-plugin/',
163
-        ];
164
-    }
26
+	/**
27
+	 * Reference to Server class.
28
+	 *
29
+	 * @var DAV\Server
30
+	 */
31
+	protected $server;
32
+
33
+	/**
34
+	 * Initializes the plugin and registers event handlers.
35
+	 */
36
+	public function initialize(DAV\Server $server)
37
+	{
38
+		$this->server = $server;
39
+		$this->server->on('method:GET', [$this, 'httpGet'], 90);
40
+		$server->on('browserButtonActions', function ($path, $node, &$actions) {
41
+			if ($node instanceof IAddressBook) {
42
+				$actions .= '<a href="'.htmlspecialchars($path, ENT_QUOTES, 'UTF-8').'?export"><span class="oi" data-glyph="book"></span></a>';
43
+			}
44
+		});
45
+	}
46
+
47
+	/**
48
+	 * Intercepts GET requests on addressbook urls ending with ?export.
49
+	 *
50
+	 * @return bool
51
+	 */
52
+	public function httpGet(RequestInterface $request, ResponseInterface $response)
53
+	{
54
+		$queryParams = $request->getQueryParameters();
55
+		if (!array_key_exists('export', $queryParams)) {
56
+			return;
57
+		}
58
+
59
+		$path = $request->getPath();
60
+
61
+		$node = $this->server->tree->getNodeForPath($path);
62
+
63
+		if (!($node instanceof IAddressBook)) {
64
+			return;
65
+		}
66
+
67
+		$this->server->transactionType = 'get-addressbook-export';
68
+
69
+		// Checking ACL, if available.
70
+		if ($aclPlugin = $this->server->getPlugin('acl')) {
71
+			$aclPlugin->checkPrivileges($path, '{DAV:}read');
72
+		}
73
+
74
+		$nodes = $this->server->getPropertiesForPath($path, [
75
+			'{'.Plugin::NS_CARDDAV.'}address-data',
76
+		], 1);
77
+
78
+		$format = 'text/directory';
79
+
80
+		$output = null;
81
+		$filenameExtension = null;
82
+
83
+		switch ($format) {
84
+			case 'text/directory':
85
+				$output = $this->generateVCF($nodes);
86
+				$filenameExtension = '.vcf';
87
+				break;
88
+		}
89
+
90
+		$filename = preg_replace(
91
+			'/[^a-zA-Z0-9-_ ]/um',
92
+			'',
93
+			$node->getName()
94
+		);
95
+		$filename .= '-'.date('Y-m-d').$filenameExtension;
96
+
97
+		$response->setHeader('Content-Disposition', 'attachment; filename="'.$filename.'"');
98
+		$response->setHeader('Content-Type', $format);
99
+
100
+		$response->setStatus(200);
101
+		$response->setBody($output);
102
+
103
+		// Returning false to break the event chain
104
+		return false;
105
+	}
106
+
107
+	/**
108
+	 * Merges all vcard objects, and builds one big vcf export.
109
+	 *
110
+	 * @return string
111
+	 */
112
+	public function generateVCF(array $nodes)
113
+	{
114
+		$output = '';
115
+
116
+		foreach ($nodes as $node) {
117
+			if (!isset($node[200]['{'.Plugin::NS_CARDDAV.'}address-data'])) {
118
+				continue;
119
+			}
120
+			$nodeData = $node[200]['{'.Plugin::NS_CARDDAV.'}address-data'];
121
+
122
+			// Parsing this node so VObject can clean up the output.
123
+			$vcard = VObject\Reader::read($nodeData);
124
+			$output .= $vcard->serialize();
125
+
126
+			// Destroy circular references to PHP will GC the object.
127
+			$vcard->destroy();
128
+		}
129
+
130
+		return $output;
131
+	}
132
+
133
+	/**
134
+	 * Returns a plugin name.
135
+	 *
136
+	 * Using this name other plugins will be able to access other plugins
137
+	 * using \Sabre\DAV\Server::getPlugin
138
+	 *
139
+	 * @return string
140
+	 */
141
+	public function getPluginName()
142
+	{
143
+		return 'vcf-export';
144
+	}
145
+
146
+	/**
147
+	 * Returns a bunch of meta-data about the plugin.
148
+	 *
149
+	 * Providing this information is optional, and is mainly displayed by the
150
+	 * Browser plugin.
151
+	 *
152
+	 * The description key in the returned array may contain html and will not
153
+	 * be sanitized.
154
+	 *
155
+	 * @return array
156
+	 */
157
+	public function getPluginInfo()
158
+	{
159
+		return [
160
+			'name' => $this->getPluginName(),
161
+			'description' => 'Adds the ability to export CardDAV addressbooks as a single vCard file.',
162
+			'link' => 'http://sabre.io/dav/vcf-export-plugin/',
163
+		];
164
+	}
165 165
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -37,7 +37,7 @@
 block discarded – undo
37 37
     {
38 38
         $this->server = $server;
39 39
         $this->server->on('method:GET', [$this, 'httpGet'], 90);
40
-        $server->on('browserButtonActions', function ($path, $node, &$actions) {
40
+        $server->on('browserButtonActions', function($path, $node, &$actions) {
41 41
             if ($node instanceof IAddressBook) {
42 42
                 $actions .= '<a href="'.htmlspecialchars($path, ENT_QUOTES, 'UTF-8').'?export"><span class="oi" data-glyph="book"></span></a>';
43 43
             }
Please login to merge, or discard this patch.
includes/sabre/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php 1 patch
Indentation   +161 added lines, -161 removed lines patch added patch discarded remove patch
@@ -23,171 +23,171 @@
 block discarded – undo
23 23
  */
24 24
 class AddressBookQueryReport implements XmlDeserializable
25 25
 {
26
-    /**
27
-     * An array with requested properties.
28
-     *
29
-     * @var array
30
-     */
31
-    public $properties;
32
-
33
-    /**
34
-     * An array with requested vcard properties.
35
-     *
36
-     * @var array
37
-     */
38
-    public $addressDataProperties = [];
39
-
40
-    /**
41
-     * List of property/component filters.
42
-     *
43
-     * This is an array with filters. Every item is a property filter. Every
44
-     * property filter has the following keys:
45
-     *   * name - name of the component to filter on
46
-     *   * test - anyof or allof
47
-     *   * is-not-defined - Test for non-existence
48
-     *   * param-filters - A list of parameter filters on the property
49
-     *   * text-matches - A list of text values the filter needs to match
50
-     *
51
-     * Each param-filter has the following keys:
52
-     *   * name - name of the parameter
53
-     *   * is-not-defined - Test for non-existence
54
-     *   * text-match - Match the parameter value
55
-     *
56
-     * Each text-match in property filters, and the single text-match in
57
-     * param-filters have the following keys:
58
-     *
59
-     *   * value - value to match
60
-     *   * match-type - contains, starts-with, ends-with, equals
61
-     *   * negate-condition - Do the opposite match
62
-     *   * collation - Usually i;unicode-casemap
63
-     *
64
-     * @var array
65
-     */
66
-    public $filters;
67
-
68
-    /**
69
-     * The number of results the client wants.
70
-     *
71
-     * null means it wasn't specified, which in most cases means 'all results'.
72
-     *
73
-     * @var int|null
74
-     */
75
-    public $limit;
76
-
77
-    /**
78
-     * Either 'anyof' or 'allof'.
79
-     *
80
-     * @var string
81
-     */
82
-    public $test;
83
-
84
-    /**
85
-     * The mimetype of the content that should be returend. Usually
86
-     * text/vcard.
87
-     *
88
-     * @var string
89
-     */
90
-    public $contentType = null;
91
-
92
-    /**
93
-     * The version of vcard data that should be returned. Usually 3.0,
94
-     * referring to vCard 3.0.
95
-     *
96
-     * @var string
97
-     */
98
-    public $version = null;
99
-
100
-    /**
101
-     * The deserialize method is called during xml parsing.
102
-     *
103
-     * This method is called statically, this is because in theory this method
104
-     * may be used as a type of constructor, or factory method.
105
-     *
106
-     * Often you want to return an instance of the current class, but you are
107
-     * free to return other data as well.
108
-     *
109
-     * You are responsible for advancing the reader to the next element. Not
110
-     * doing anything will result in a never-ending loop.
111
-     *
112
-     * If you just want to skip parsing for this element altogether, you can
113
-     * just call $reader->next();
114
-     *
115
-     * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
116
-     * the next element.
117
-     *
118
-     * @return mixed
119
-     */
120
-    public static function xmlDeserialize(Reader $reader)
121
-    {
122
-        $elems = (array) $reader->parseInnerTree([
123
-            '{urn:ietf:params:xml:ns:carddav}prop-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\PropFilter',
124
-            '{urn:ietf:params:xml:ns:carddav}param-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter',
125
-            '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData',
126
-            '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue',
127
-        ]);
128
-
129
-        $newProps = [
130
-            'filters' => null,
131
-            'properties' => [],
132
-            'test' => 'anyof',
133
-            'limit' => null,
134
-        ];
135
-
136
-        if (!is_array($elems)) {
137
-            $elems = [];
138
-        }
139
-
140
-        foreach ($elems as $elem) {
141
-            switch ($elem['name']) {
142
-                case '{DAV:}prop':
143
-                    $newProps['properties'] = array_keys($elem['value']);
144
-                    if (isset($elem['value']['{'.Plugin::NS_CARDDAV.'}address-data'])) {
145
-                        $newProps += $elem['value']['{'.Plugin::NS_CARDDAV.'}address-data'];
146
-                    }
147
-                    break;
148
-                case '{'.Plugin::NS_CARDDAV.'}filter':
149
-                    if (!is_null($newProps['filters'])) {
150
-                        throw new BadRequest('You can only include 1 {'.Plugin::NS_CARDDAV.'}filter element');
151
-                    }
152
-                    if (isset($elem['attributes']['test'])) {
153
-                        $newProps['test'] = $elem['attributes']['test'];
154
-                        if ('allof' !== $newProps['test'] && 'anyof' !== $newProps['test']) {
155
-                            throw new BadRequest('The "test" attribute must be one of "allof" or "anyof"');
156
-                        }
157
-                    }
158
-
159
-                    $newProps['filters'] = [];
160
-                    foreach ((array) $elem['value'] as $subElem) {
161
-                        if ($subElem['name'] === '{'.Plugin::NS_CARDDAV.'}prop-filter') {
162
-                            $newProps['filters'][] = $subElem['value'];
163
-                        }
164
-                    }
165
-                    break;
166
-                case '{'.Plugin::NS_CARDDAV.'}limit':
167
-                    foreach ($elem['value'] as $child) {
168
-                        if ($child['name'] === '{'.Plugin::NS_CARDDAV.'}nresults') {
169
-                            $newProps['limit'] = (int) $child['value'];
170
-                        }
171
-                    }
172
-                    break;
173
-            }
174
-        }
175
-
176
-        if (is_null($newProps['filters'])) {
177
-            /*
26
+	/**
27
+	 * An array with requested properties.
28
+	 *
29
+	 * @var array
30
+	 */
31
+	public $properties;
32
+
33
+	/**
34
+	 * An array with requested vcard properties.
35
+	 *
36
+	 * @var array
37
+	 */
38
+	public $addressDataProperties = [];
39
+
40
+	/**
41
+	 * List of property/component filters.
42
+	 *
43
+	 * This is an array with filters. Every item is a property filter. Every
44
+	 * property filter has the following keys:
45
+	 *   * name - name of the component to filter on
46
+	 *   * test - anyof or allof
47
+	 *   * is-not-defined - Test for non-existence
48
+	 *   * param-filters - A list of parameter filters on the property
49
+	 *   * text-matches - A list of text values the filter needs to match
50
+	 *
51
+	 * Each param-filter has the following keys:
52
+	 *   * name - name of the parameter
53
+	 *   * is-not-defined - Test for non-existence
54
+	 *   * text-match - Match the parameter value
55
+	 *
56
+	 * Each text-match in property filters, and the single text-match in
57
+	 * param-filters have the following keys:
58
+	 *
59
+	 *   * value - value to match
60
+	 *   * match-type - contains, starts-with, ends-with, equals
61
+	 *   * negate-condition - Do the opposite match
62
+	 *   * collation - Usually i;unicode-casemap
63
+	 *
64
+	 * @var array
65
+	 */
66
+	public $filters;
67
+
68
+	/**
69
+	 * The number of results the client wants.
70
+	 *
71
+	 * null means it wasn't specified, which in most cases means 'all results'.
72
+	 *
73
+	 * @var int|null
74
+	 */
75
+	public $limit;
76
+
77
+	/**
78
+	 * Either 'anyof' or 'allof'.
79
+	 *
80
+	 * @var string
81
+	 */
82
+	public $test;
83
+
84
+	/**
85
+	 * The mimetype of the content that should be returend. Usually
86
+	 * text/vcard.
87
+	 *
88
+	 * @var string
89
+	 */
90
+	public $contentType = null;
91
+
92
+	/**
93
+	 * The version of vcard data that should be returned. Usually 3.0,
94
+	 * referring to vCard 3.0.
95
+	 *
96
+	 * @var string
97
+	 */
98
+	public $version = null;
99
+
100
+	/**
101
+	 * The deserialize method is called during xml parsing.
102
+	 *
103
+	 * This method is called statically, this is because in theory this method
104
+	 * may be used as a type of constructor, or factory method.
105
+	 *
106
+	 * Often you want to return an instance of the current class, but you are
107
+	 * free to return other data as well.
108
+	 *
109
+	 * You are responsible for advancing the reader to the next element. Not
110
+	 * doing anything will result in a never-ending loop.
111
+	 *
112
+	 * If you just want to skip parsing for this element altogether, you can
113
+	 * just call $reader->next();
114
+	 *
115
+	 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
116
+	 * the next element.
117
+	 *
118
+	 * @return mixed
119
+	 */
120
+	public static function xmlDeserialize(Reader $reader)
121
+	{
122
+		$elems = (array) $reader->parseInnerTree([
123
+			'{urn:ietf:params:xml:ns:carddav}prop-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\PropFilter',
124
+			'{urn:ietf:params:xml:ns:carddav}param-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter',
125
+			'{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData',
126
+			'{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue',
127
+		]);
128
+
129
+		$newProps = [
130
+			'filters' => null,
131
+			'properties' => [],
132
+			'test' => 'anyof',
133
+			'limit' => null,
134
+		];
135
+
136
+		if (!is_array($elems)) {
137
+			$elems = [];
138
+		}
139
+
140
+		foreach ($elems as $elem) {
141
+			switch ($elem['name']) {
142
+				case '{DAV:}prop':
143
+					$newProps['properties'] = array_keys($elem['value']);
144
+					if (isset($elem['value']['{'.Plugin::NS_CARDDAV.'}address-data'])) {
145
+						$newProps += $elem['value']['{'.Plugin::NS_CARDDAV.'}address-data'];
146
+					}
147
+					break;
148
+				case '{'.Plugin::NS_CARDDAV.'}filter':
149
+					if (!is_null($newProps['filters'])) {
150
+						throw new BadRequest('You can only include 1 {'.Plugin::NS_CARDDAV.'}filter element');
151
+					}
152
+					if (isset($elem['attributes']['test'])) {
153
+						$newProps['test'] = $elem['attributes']['test'];
154
+						if ('allof' !== $newProps['test'] && 'anyof' !== $newProps['test']) {
155
+							throw new BadRequest('The "test" attribute must be one of "allof" or "anyof"');
156
+						}
157
+					}
158
+
159
+					$newProps['filters'] = [];
160
+					foreach ((array) $elem['value'] as $subElem) {
161
+						if ($subElem['name'] === '{'.Plugin::NS_CARDDAV.'}prop-filter') {
162
+							$newProps['filters'][] = $subElem['value'];
163
+						}
164
+					}
165
+					break;
166
+				case '{'.Plugin::NS_CARDDAV.'}limit':
167
+					foreach ($elem['value'] as $child) {
168
+						if ($child['name'] === '{'.Plugin::NS_CARDDAV.'}nresults') {
169
+							$newProps['limit'] = (int) $child['value'];
170
+						}
171
+					}
172
+					break;
173
+			}
174
+		}
175
+
176
+		if (is_null($newProps['filters'])) {
177
+			/*
178 178
              * We are supposed to throw this error, but KDE sometimes does not
179 179
              * include the filter element, and we need to treat it as if no
180 180
              * filters are supplied
181 181
              */
182
-            //throw new BadRequest('The {' . Plugin::NS_CARDDAV . '}filter element is required for this request');
183
-            $newProps['filters'] = [];
184
-        }
182
+			//throw new BadRequest('The {' . Plugin::NS_CARDDAV . '}filter element is required for this request');
183
+			$newProps['filters'] = [];
184
+		}
185 185
 
186
-        $obj = new self();
187
-        foreach ($newProps as $key => $value) {
188
-            $obj->$key = $value;
189
-        }
186
+		$obj = new self();
187
+		foreach ($newProps as $key => $value) {
188
+			$obj->$key = $value;
189
+		}
190 190
 
191
-        return $obj;
192
-    }
191
+		return $obj;
192
+	}
193 193
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/ParamFilter.php 2 patches
Indentation   +53 added lines, -53 removed lines patch added patch discarded remove patch
@@ -25,62 +25,62 @@
 block discarded – undo
25 25
  */
26 26
 abstract class ParamFilter implements Element
27 27
 {
28
-    /**
29
-     * The deserialize method is called during xml parsing.
30
-     *
31
-     * This method is called statically, this is because in theory this method
32
-     * may be used as a type of constructor, or factory method.
33
-     *
34
-     * Often you want to return an instance of the current class, but you are
35
-     * free to return other data as well.
36
-     *
37
-     * You are responsible for advancing the reader to the next element. Not
38
-     * doing anything will result in a never-ending loop.
39
-     *
40
-     * If you just want to skip parsing for this element altogether, you can
41
-     * just call $reader->next();
42
-     *
43
-     * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
44
-     * the next element.
45
-     *
46
-     * @return mixed
47
-     */
48
-    public static function xmlDeserialize(Reader $reader)
49
-    {
50
-        $result = [
51
-            'name' => null,
52
-            'is-not-defined' => false,
53
-            'text-match' => null,
54
-        ];
28
+	/**
29
+	 * The deserialize method is called during xml parsing.
30
+	 *
31
+	 * This method is called statically, this is because in theory this method
32
+	 * may be used as a type of constructor, or factory method.
33
+	 *
34
+	 * Often you want to return an instance of the current class, but you are
35
+	 * free to return other data as well.
36
+	 *
37
+	 * You are responsible for advancing the reader to the next element. Not
38
+	 * doing anything will result in a never-ending loop.
39
+	 *
40
+	 * If you just want to skip parsing for this element altogether, you can
41
+	 * just call $reader->next();
42
+	 *
43
+	 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
44
+	 * the next element.
45
+	 *
46
+	 * @return mixed
47
+	 */
48
+	public static function xmlDeserialize(Reader $reader)
49
+	{
50
+		$result = [
51
+			'name' => null,
52
+			'is-not-defined' => false,
53
+			'text-match' => null,
54
+		];
55 55
 
56
-        $att = $reader->parseAttributes();
57
-        $result['name'] = $att['name'];
56
+		$att = $reader->parseAttributes();
57
+		$result['name'] = $att['name'];
58 58
 
59
-        $elems = $reader->parseInnerTree();
59
+		$elems = $reader->parseInnerTree();
60 60
 
61
-        if (is_array($elems)) {
62
-            foreach ($elems as $elem) {
63
-                switch ($elem['name']) {
64
-                case '{'.Plugin::NS_CARDDAV.'}is-not-defined':
65
-                    $result['is-not-defined'] = true;
66
-                    break;
67
-                case '{'.Plugin::NS_CARDDAV.'}text-match':
68
-                    $matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains';
61
+		if (is_array($elems)) {
62
+			foreach ($elems as $elem) {
63
+				switch ($elem['name']) {
64
+				case '{'.Plugin::NS_CARDDAV.'}is-not-defined':
65
+					$result['is-not-defined'] = true;
66
+					break;
67
+				case '{'.Plugin::NS_CARDDAV.'}text-match':
68
+					$matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains';
69 69
 
70
-                    if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) {
71
-                        throw new BadRequest('Unknown match-type: '.$matchType);
72
-                    }
73
-                    $result['text-match'] = [
74
-                        'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'],
75
-                        'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap',
76
-                        'value' => $elem['value'],
77
-                        'match-type' => $matchType,
78
-                    ];
79
-                    break;
80
-            }
81
-            }
82
-        }
70
+					if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) {
71
+						throw new BadRequest('Unknown match-type: '.$matchType);
72
+					}
73
+					$result['text-match'] = [
74
+						'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'],
75
+						'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap',
76
+						'value' => $elem['value'],
77
+						'match-type' => $matchType,
78
+					];
79
+					break;
80
+			}
81
+			}
82
+		}
83 83
 
84
-        return $result;
85
-    }
84
+		return $result;
85
+	}
86 86
 }
Please login to merge, or discard this patch.
Switch Indentation   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -61,22 +61,22 @@
 block discarded – undo
61 61
         if (is_array($elems)) {
62 62
             foreach ($elems as $elem) {
63 63
                 switch ($elem['name']) {
64
-                case '{'.Plugin::NS_CARDDAV.'}is-not-defined':
65
-                    $result['is-not-defined'] = true;
66
-                    break;
67
-                case '{'.Plugin::NS_CARDDAV.'}text-match':
68
-                    $matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains';
64
+                	case '{'.Plugin::NS_CARDDAV.'}is-not-defined':
65
+                    	$result['is-not-defined'] = true;
66
+                    	break;
67
+                	case '{'.Plugin::NS_CARDDAV.'}text-match':
68
+                    	$matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains';
69 69
 
70
-                    if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) {
71
-                        throw new BadRequest('Unknown match-type: '.$matchType);
72
-                    }
73
-                    $result['text-match'] = [
74
-                        'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'],
75
-                        'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap',
76
-                        'value' => $elem['value'],
77
-                        'match-type' => $matchType,
78
-                    ];
79
-                    break;
70
+                    	if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) {
71
+                        	throw new BadRequest('Unknown match-type: '.$matchType);
72
+                    	}
73
+                    	$result['text-match'] = [
74
+                        	'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'],
75
+                        	'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap',
76
+                        	'value' => $elem['value'],
77
+                        	'match-type' => $matchType,
78
+                    	];
79
+                    	break;
80 80
             }
81 81
             }
82 82
         }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/PropFilter.php 2 patches
Indentation   +61 added lines, -61 removed lines patch added patch discarded remove patch
@@ -25,71 +25,71 @@
 block discarded – undo
25 25
  */
26 26
 class PropFilter implements XmlDeserializable
27 27
 {
28
-    /**
29
-     * The deserialize method is called during xml parsing.
30
-     *
31
-     * This method is called statically, this is because in theory this method
32
-     * may be used as a type of constructor, or factory method.
33
-     *
34
-     * Often you want to return an instance of the current class, but you are
35
-     * free to return other data as well.
36
-     *
37
-     * You are responsible for advancing the reader to the next element. Not
38
-     * doing anything will result in a never-ending loop.
39
-     *
40
-     * If you just want to skip parsing for this element altogether, you can
41
-     * just call $reader->next();
42
-     *
43
-     * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
44
-     * the next element.
45
-     *
46
-     * @return mixed
47
-     */
48
-    public static function xmlDeserialize(Reader $reader)
49
-    {
50
-        $result = [
51
-            'name' => null,
52
-            'test' => 'anyof',
53
-            'is-not-defined' => false,
54
-            'param-filters' => [],
55
-            'text-matches' => [],
56
-        ];
28
+	/**
29
+	 * The deserialize method is called during xml parsing.
30
+	 *
31
+	 * This method is called statically, this is because in theory this method
32
+	 * may be used as a type of constructor, or factory method.
33
+	 *
34
+	 * Often you want to return an instance of the current class, but you are
35
+	 * free to return other data as well.
36
+	 *
37
+	 * You are responsible for advancing the reader to the next element. Not
38
+	 * doing anything will result in a never-ending loop.
39
+	 *
40
+	 * If you just want to skip parsing for this element altogether, you can
41
+	 * just call $reader->next();
42
+	 *
43
+	 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
44
+	 * the next element.
45
+	 *
46
+	 * @return mixed
47
+	 */
48
+	public static function xmlDeserialize(Reader $reader)
49
+	{
50
+		$result = [
51
+			'name' => null,
52
+			'test' => 'anyof',
53
+			'is-not-defined' => false,
54
+			'param-filters' => [],
55
+			'text-matches' => [],
56
+		];
57 57
 
58
-        $att = $reader->parseAttributes();
59
-        $result['name'] = $att['name'];
58
+		$att = $reader->parseAttributes();
59
+		$result['name'] = $att['name'];
60 60
 
61
-        if (isset($att['test']) && 'allof' === $att['test']) {
62
-            $result['test'] = 'allof';
63
-        }
61
+		if (isset($att['test']) && 'allof' === $att['test']) {
62
+			$result['test'] = 'allof';
63
+		}
64 64
 
65
-        $elems = $reader->parseInnerTree();
65
+		$elems = $reader->parseInnerTree();
66 66
 
67
-        if (is_array($elems)) {
68
-            foreach ($elems as $elem) {
69
-                switch ($elem['name']) {
70
-                case '{'.Plugin::NS_CARDDAV.'}param-filter':
71
-                    $result['param-filters'][] = $elem['value'];
72
-                    break;
73
-                case '{'.Plugin::NS_CARDDAV.'}is-not-defined':
74
-                    $result['is-not-defined'] = true;
75
-                    break;
76
-                case '{'.Plugin::NS_CARDDAV.'}text-match':
77
-                    $matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains';
67
+		if (is_array($elems)) {
68
+			foreach ($elems as $elem) {
69
+				switch ($elem['name']) {
70
+				case '{'.Plugin::NS_CARDDAV.'}param-filter':
71
+					$result['param-filters'][] = $elem['value'];
72
+					break;
73
+				case '{'.Plugin::NS_CARDDAV.'}is-not-defined':
74
+					$result['is-not-defined'] = true;
75
+					break;
76
+				case '{'.Plugin::NS_CARDDAV.'}text-match':
77
+					$matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains';
78 78
 
79
-                    if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) {
80
-                        throw new BadRequest('Unknown match-type: '.$matchType);
81
-                    }
82
-                    $result['text-matches'][] = [
83
-                        'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'],
84
-                        'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap',
85
-                        'value' => $elem['value'],
86
-                        'match-type' => $matchType,
87
-                    ];
88
-                    break;
89
-            }
90
-            }
91
-        }
79
+					if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) {
80
+						throw new BadRequest('Unknown match-type: '.$matchType);
81
+					}
82
+					$result['text-matches'][] = [
83
+						'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'],
84
+						'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap',
85
+						'value' => $elem['value'],
86
+						'match-type' => $matchType,
87
+					];
88
+					break;
89
+			}
90
+			}
91
+		}
92 92
 
93
-        return $result;
94
-    }
93
+		return $result;
94
+	}
95 95
 }
Please login to merge, or discard this patch.
Switch Indentation   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -67,25 +67,25 @@
 block discarded – undo
67 67
         if (is_array($elems)) {
68 68
             foreach ($elems as $elem) {
69 69
                 switch ($elem['name']) {
70
-                case '{'.Plugin::NS_CARDDAV.'}param-filter':
71
-                    $result['param-filters'][] = $elem['value'];
72
-                    break;
73
-                case '{'.Plugin::NS_CARDDAV.'}is-not-defined':
74
-                    $result['is-not-defined'] = true;
75
-                    break;
76
-                case '{'.Plugin::NS_CARDDAV.'}text-match':
77
-                    $matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains';
70
+                	case '{'.Plugin::NS_CARDDAV.'}param-filter':
71
+                    	$result['param-filters'][] = $elem['value'];
72
+                    	break;
73
+                	case '{'.Plugin::NS_CARDDAV.'}is-not-defined':
74
+                    	$result['is-not-defined'] = true;
75
+                    	break;
76
+                	case '{'.Plugin::NS_CARDDAV.'}text-match':
77
+                    	$matchType = isset($elem['attributes']['match-type']) ? $elem['attributes']['match-type'] : 'contains';
78 78
 
79
-                    if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) {
80
-                        throw new BadRequest('Unknown match-type: '.$matchType);
81
-                    }
82
-                    $result['text-matches'][] = [
83
-                        'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'],
84
-                        'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap',
85
-                        'value' => $elem['value'],
86
-                        'match-type' => $matchType,
87
-                    ];
88
-                    break;
79
+                    	if (!in_array($matchType, ['contains', 'equals', 'starts-with', 'ends-with'])) {
80
+                        	throw new BadRequest('Unknown match-type: '.$matchType);
81
+                    	}
82
+                    	$result['text-matches'][] = [
83
+                        	'negate-condition' => isset($elem['attributes']['negate-condition']) && 'yes' === $elem['attributes']['negate-condition'],
84
+                        	'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;unicode-casemap',
85
+                        	'value' => $elem['value'],
86
+                        	'match-type' => $matchType,
87
+                    	];
88
+                    	break;
89 89
             }
90 90
             }
91 91
         }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/dav/lib/CardDAV/Xml/Filter/AddressData.php 2 patches
Indentation   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -25,42 +25,42 @@
 block discarded – undo
25 25
  */
26 26
 class AddressData implements XmlDeserializable
27 27
 {
28
-    /**
29
-     * The deserialize method is called during xml parsing.
30
-     *
31
-     * This method is called statically, this is because in theory this method
32
-     * may be used as a type of constructor, or factory method.
33
-     *
34
-     * Often you want to return an instance of the current class, but you are
35
-     * free to return other data as well.
36
-     *
37
-     * You are responsible for advancing the reader to the next element. Not
38
-     * doing anything will result in a never-ending loop.
39
-     *
40
-     * If you just want to skip parsing for this element altogether, you can
41
-     * just call $reader->next();
42
-     *
43
-     * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
44
-     * the next element.
45
-     *
46
-     * @return mixed
47
-     */
48
-    public static function xmlDeserialize(Reader $reader)
49
-    {
50
-        $result = [
51
-            'contentType' => $reader->getAttribute('content-type') ?: 'text/vcard',
52
-            'version' => $reader->getAttribute('version') ?: '3.0',
53
-        ];
28
+	/**
29
+	 * The deserialize method is called during xml parsing.
30
+	 *
31
+	 * This method is called statically, this is because in theory this method
32
+	 * may be used as a type of constructor, or factory method.
33
+	 *
34
+	 * Often you want to return an instance of the current class, but you are
35
+	 * free to return other data as well.
36
+	 *
37
+	 * You are responsible for advancing the reader to the next element. Not
38
+	 * doing anything will result in a never-ending loop.
39
+	 *
40
+	 * If you just want to skip parsing for this element altogether, you can
41
+	 * just call $reader->next();
42
+	 *
43
+	 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
44
+	 * the next element.
45
+	 *
46
+	 * @return mixed
47
+	 */
48
+	public static function xmlDeserialize(Reader $reader)
49
+	{
50
+		$result = [
51
+			'contentType' => $reader->getAttribute('content-type') ?: 'text/vcard',
52
+			'version' => $reader->getAttribute('version') ?: '3.0',
53
+		];
54 54
 
55
-        $elems = (array) $reader->parseInnerTree();
56
-        $elems = array_filter($elems, function ($element) {
57
-            return '{urn:ietf:params:xml:ns:carddav}prop' === $element['name'] &&
58
-                isset($element['attributes']['name']);
59
-        });
60
-        $result['addressDataProperties'] = array_map(function ($element) {
61
-            return $element['attributes']['name'];
62
-        }, $elems);
55
+		$elems = (array) $reader->parseInnerTree();
56
+		$elems = array_filter($elems, function ($element) {
57
+			return '{urn:ietf:params:xml:ns:carddav}prop' === $element['name'] &&
58
+				isset($element['attributes']['name']);
59
+		});
60
+		$result['addressDataProperties'] = array_map(function ($element) {
61
+			return $element['attributes']['name'];
62
+		}, $elems);
63 63
 
64
-        return $result;
65
-    }
64
+		return $result;
65
+	}
66 66
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -53,11 +53,11 @@
 block discarded – undo
53 53
         ];
54 54
 
55 55
         $elems = (array) $reader->parseInnerTree();
56
-        $elems = array_filter($elems, function ($element) {
56
+        $elems = array_filter($elems, function($element) {
57 57
             return '{urn:ietf:params:xml:ns:carddav}prop' === $element['name'] &&
58 58
                 isset($element['attributes']['name']);
59 59
         });
60
-        $result['addressDataProperties'] = array_map(function ($element) {
60
+        $result['addressDataProperties'] = array_map(function($element) {
61 61
             return $element['attributes']['name'];
62 62
         }, $elems);
63 63
 
Please login to merge, or discard this patch.