Blame view

sources/apps/contacts/lib/addressbook.php 11.8 KB
d1bafeea1   Kload   [fix] Upgrade to ...
1
2
3
4
5
  <?php
  /**
   * ownCloud - Addressbook
   *
   * @author Thomas Tanghus
6d9380f96   Cédric Dupont   Update sources OC...
6
   * @copyright 2013-2014 Thomas Tanghus (thomas@tanghus.net)
d1bafeea1   Kload   [fix] Upgrade to ...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
   * License as published by the Free Software Foundation; either
   * version 3 of the License, or any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
   *
   * You should have received a copy of the GNU Affero General Public
   * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
   *
   */
  
  namespace OCA\Contacts;
6d9380f96   Cédric Dupont   Update sources OC...
24
25
26
  use OC_L10N;
  use OCA\Contacts\Backend\AbstractBackend;
  use OCP\AppFramework\Http;
d1bafeea1   Kload   [fix] Upgrade to ...
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  /**
   * This class manages our addressbooks.
   */
  
  class Addressbook extends AbstractPIMCollection {
  
  	/**
  	 * @brief language object
  	 *
  	 * @var OC_L10N
  	 */
  	public static $l10n;
  
  	protected $_count;
6d9380f96   Cédric Dupont   Update sources OC...
41

d1bafeea1   Kload   [fix] Upgrade to ...
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  	/**
  	 * @var Backend\AbstractBackend
  	 */
  	protected $backend;
  
  	/**
  	 * An array containing the mandatory:
  	 * 	'displayname'
  	 * 	'discription'
  	 * 	'permissions'
  	 *
  	 * And the optional:
  	 * 	'Etag'
  	 * 	'lastModified'
  	 *
  	 * @var array
  	 */
  	protected $addressBookInfo;
  
  	/**
  	 * @param AbstractBackend $backend The storage backend
  	 * @param array $addressBookInfo
6d9380f96   Cédric Dupont   Update sources OC...
64
  	 * @throws \Exception
d1bafeea1   Kload   [fix] Upgrade to ...
65
66
67
68
69
  	 */
  	public function __construct(Backend\AbstractBackend $backend, array $addressBookInfo) {
  		self::$l10n = \OCP\Util::getL10N('contacts');
  		$this->backend = $backend;
  		$this->addressBookInfo = $addressBookInfo;
6d9380f96   Cédric Dupont   Update sources OC...
70
  		if (is_null($this->getId())) {
d1bafeea1   Kload   [fix] Upgrade to ...
71
  			$id = $this->backend->createAddressBook($addressBookInfo);
6d9380f96   Cédric Dupont   Update sources OC...
72
73
  			if ($id === false) {
  				throw new \Exception('Error creating address book.', Http::STATUS_INTERNAL_SERVER_ERROR);
d1bafeea1   Kload   [fix] Upgrade to ...
74
  			}
6d9380f96   Cédric Dupont   Update sources OC...
75

d1bafeea1   Kload   [fix] Upgrade to ...
76
  			$this->addressBookInfo = $this->backend->getAddressBook($id);
d1bafeea1   Kload   [fix] Upgrade to ...
77
  		}
6d9380f96   Cédric Dupont   Update sources OC...
78

d1bafeea1   Kload   [fix] Upgrade to ...
79
80
81
82
  		//\OCP\Util::writeLog('contacts', __METHOD__.' backend: ' . print_r($this->backend, true), \OCP\Util::DEBUG);
  	}
  
  	/**
6d9380f96   Cédric Dupont   Update sources OC...
83
84
85
86
87
88
89
  	 * @return AbstractBackend
  	 */
  	public function getBackend() {
  		return $this->backend;
  	}
  
  	/**
d1bafeea1   Kload   [fix] Upgrade to ...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  	 * @return string|null
  	 */
  	public function getId() {
  		return isset($this->addressBookInfo['id'])
  			? $this->addressBookInfo['id']
  			: null;
  	}
  
  	/**
  	 * @return array
  	 */
  	public function getMetaData() {
  		$metadata = $this->addressBookInfo;
  		$metadata['lastmodified'] = $this->lastModified();
  		$metadata['active'] = $this->isActive();
  		$metadata['backend'] = $this->getBackend()->name;
6d9380f96   Cédric Dupont   Update sources OC...
106
  		$metadata['owner'] = $this->getOwner();
d1bafeea1   Kload   [fix] Upgrade to ...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  		return $metadata;
  	}
  
  	/**
  	 * @return string
  	 */
  	public function getDisplayName() {
  		return $this->addressBookInfo['displayname'];
  	}
  
  	/**
  	 * @return string
  	 */
  	public function getURI() {
  		return $this->addressBookInfo['uri'];
  	}
  
  	/**
  	 * @return string
  	 */
  	public function getOwner() {
6d9380f96   Cédric Dupont   Update sources OC...
128
129
130
  		return isset($this->addressBookInfo['owner'])
  			? $this->addressBookInfo['owner']
  			: \OCP\User::getUser();
d1bafeea1   Kload   [fix] Upgrade to ...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  	}
  
  	/**
  	 * Returns the lowest permission of what the backend allows and what it supports.
  	 * @return int
  	 */
  	public function getPermissions() {
  		return $this->addressBookInfo['permissions'];
  	}
  
  	/**
  	 * @brief Query whether an address book is active
  	 * @return boolean
  	 */
  	public function isActive() {
  		return $this->backend->isActive($this->getId());
  	}
  
  	/**
  	 * @brief Activate an address book
6d9380f96   Cédric Dupont   Update sources OC...
151
  	 * @param bool $active
d1bafeea1   Kload   [fix] Upgrade to ...
152
153
154
155
156
157
158
159
160
161
162
  	 * @return void
  	 */
  	public function setActive($active) {
  		$this->backend->setActive($active, $this->getId());
  	}
  
  	/**
  	* Returns a specific child node, referenced by its id
  	*
  	* @param string $id
  	* @return Contact|null
6d9380f96   Cédric Dupont   Update sources OC...
163
  	* @throws \Exception On not found
d1bafeea1   Kload   [fix] Upgrade to ...
164
165
166
  	*/
  	public function getChild($id) {
  		//\OCP\Util::writeLog('contacts', __METHOD__.' id: '.$id, \OCP\Util::DEBUG);
6d9380f96   Cédric Dupont   Update sources OC...
167
168
169
170
171
  		if (!$this->hasPermission(\OCP\PERMISSION_READ)) {
  			throw new \Exception(
  				self::$l10n->t('You do not have permissions to see this contact'),
  				Http::STATUS_FORBIDDEN
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
172
  		}
6d9380f96   Cédric Dupont   Update sources OC...
173
174
  
  		if (!isset($this->objects[(string)$id])) {
d1bafeea1   Kload   [fix] Upgrade to ...
175
  			$contact = $this->backend->getContact($this->getId(), $id);
6d9380f96   Cédric Dupont   Update sources OC...
176
177
  			if ($contact) {
  				$this->objects[(string)$id] = new Contact($this, $this->backend, $contact);
d1bafeea1   Kload   [fix] Upgrade to ...
178
  			} else {
6d9380f96   Cédric Dupont   Update sources OC...
179
180
181
182
  				throw new \Exception(
  					self::$l10n->t('Contact not found'),
  					Http::STATUS_NOT_FOUND
  				);
d1bafeea1   Kload   [fix] Upgrade to ...
183
184
  			}
  		}
6d9380f96   Cédric Dupont   Update sources OC...
185

d1bafeea1   Kload   [fix] Upgrade to ...
186
  		// When requesting a single contact we preparse it
6d9380f96   Cédric Dupont   Update sources OC...
187
188
189
  		if (isset($this->objects[(string)$id])) {
  			$this->objects[(string)$id]->retrieve();
  			return $this->objects[(string)$id];
d1bafeea1   Kload   [fix] Upgrade to ...
190
191
192
193
194
195
196
197
198
199
  		}
  	}
  
  	/**
  	* Checks if a child-node with the specified id exists
  	*
  	* @param string $id
  	* @return bool
  	*/
  	public function childExists($id) {
6d9380f96   Cédric Dupont   Update sources OC...
200
201
202
  		if(isset($this->objects[$id])) {
  			return true;
  		}
d1bafeea1   Kload   [fix] Upgrade to ...
203
204
205
206
207
208
  		return ($this->getChild($id) !== null);
  	}
  
  	/**
  	* Returns an array with all the child nodes
  	*
6d9380f96   Cédric Dupont   Update sources OC...
209
210
211
  	* @param int $limit
  	* @param int $offset
  	* @param bool $omitdata
d1bafeea1   Kload   [fix] Upgrade to ...
212
213
214
  	* @return Contact[]
  	*/
  	public function getChildren($limit = null, $offset = null, $omitdata = false) {
6d9380f96   Cédric Dupont   Update sources OC...
215
216
217
218
219
  		if (!$this->hasPermission(\OCP\PERMISSION_READ)) {
  			throw new \Exception(
  				self::$l10n->t('You do not have permissions to see these contacts'),
  				Http::STATUS_FORBIDDEN
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
220
221
222
223
224
  		}
  
  		$contacts = array();
  
  		$options = array('limit' => $limit, 'offset' => $offset, 'omitdata' => $omitdata);
6d9380f96   Cédric Dupont   Update sources OC...
225
  		foreach ($this->backend->getContacts($this->getId(), $options) as $contact) {
d1bafeea1   Kload   [fix] Upgrade to ...
226
  			//\OCP\Util::writeLog('contacts', __METHOD__.' id: '.$contact['id'], \OCP\Util::DEBUG);
6d9380f96   Cédric Dupont   Update sources OC...
227
  			if (!isset($this->objects[$contact['id']])) {
d1bafeea1   Kload   [fix] Upgrade to ...
228
229
  				$this->objects[$contact['id']] = new Contact($this, $this->backend, $contact);
  			}
6d9380f96   Cédric Dupont   Update sources OC...
230

d1bafeea1   Kload   [fix] Upgrade to ...
231
232
  			$contacts[] = $this->objects[$contact['id']];
  		}
6d9380f96   Cédric Dupont   Update sources OC...
233

d1bafeea1   Kload   [fix] Upgrade to ...
234
235
236
237
238
239
240
241
242
243
244
  		//\OCP\Util::writeLog('contacts', __METHOD__.' children: '.count($contacts), \OCP\Util::DEBUG);
  		return $contacts;
  	}
  
  	/**
  	 * Add a contact to the address book
  	 * This takes an array or a VCard|Contact and return
  	 * the ID or false.
  	 *
  	 * @param array|VObject\VCard $data
  	 * @return int|bool
6d9380f96   Cédric Dupont   Update sources OC...
245
  	 * @throws \Exception on missing permissions
d1bafeea1   Kload   [fix] Upgrade to ...
246
247
  	 */
  	public function addChild($data = null) {
6d9380f96   Cédric Dupont   Update sources OC...
248
249
250
251
252
  		if (!$this->hasPermission(\OCP\PERMISSION_CREATE)) {
  			throw new \Exception(
  				self::$l10n->t('You do not have permissions add contacts to the address book'),
  				Http::STATUS_FORBIDDEN
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
253
  		}
6d9380f96   Cédric Dupont   Update sources OC...
254
255
256
257
258
259
  
  		if (!$this->getBackend()->hasContactMethodFor(\OCP\PERMISSION_CREATE)) {
  			throw new \Exception(
  				self::$l10n->t('The backend for this address book does not support adding contacts'),
  				Http::STATUS_NOT_IMPLEMENTED
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
260
  		}
6d9380f96   Cédric Dupont   Update sources OC...
261

d1bafeea1   Kload   [fix] Upgrade to ...
262
  		$contact = new Contact($this, $this->backend, $data);
6d9380f96   Cédric Dupont   Update sources OC...
263
264
265
266
267
268
269
  
  		if (is_null($data)) {
  			// A new Contact, don't try to load from backend
  			$contact->setRetrieved(true);
  		}
  
  		if ($contact->save() === false) {
d1bafeea1   Kload   [fix] Upgrade to ...
270
271
  			return false;
  		}
6d9380f96   Cédric Dupont   Update sources OC...
272

d1bafeea1   Kload   [fix] Upgrade to ...
273
  		$id = $contact->getId();
6d9380f96   Cédric Dupont   Update sources OC...
274
275
276
277
278
279
280
281
282
283
  
  		// If this method is called directly the index isn't set.
  		if (!isset($this->objects[$id])) {
  			$this->objects[$id] = $contact;
  		}
  
  		/* If count() hasn't been called yet don't _count hasn't been initialized
  		 * so incrementing it would give a misleading value.
  		 */
  		if (isset($this->_count)) {
d1bafeea1   Kload   [fix] Upgrade to ...
284
285
  			$this->_count += 1;
  		}
6d9380f96   Cédric Dupont   Update sources OC...
286
287
  
  		//\OCP\Util::writeLog('contacts', __METHOD__.' id: ' . $id, \OCP\Util::DEBUG);
d1bafeea1   Kload   [fix] Upgrade to ...
288
289
290
291
292
293
294
295
296
297
298
299
  		return $id;
  	}
  
  	/**
  	 * Delete a contact from the address book
  	 *
  	 * @param string $id
  	 * @param array $options
  	 * @return bool
  	 * @throws \Exception on missing permissions
  	 */
  	public function deleteChild($id, $options = array()) {
6d9380f96   Cédric Dupont   Update sources OC...
300
301
302
303
304
  		if (!$this->hasPermission(\OCP\PERMISSION_DELETE)) {
  			throw new \Exception(
  				self::$l10n->t('You do not have permissions to delete this contact'),
  				Http::STATUS_FORBIDDEN
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
305
  		}
6d9380f96   Cédric Dupont   Update sources OC...
306
307
308
309
310
311
  
  		if (!$this->getBackend()->hasContactMethodFor(\OCP\PERMISSION_DELETE)) {
  			throw new \Exception(
  				self::$l10n->t('The backend for this address book does not support deleting contacts'),
  				Http::STATUS_NOT_IMPLEMENTED
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
312
  		}
6d9380f96   Cédric Dupont   Update sources OC...
313
314
315
  
  		if ($this->backend->deleteContact($this->getId(), $id, $options)) {
  			if (isset($this->objects[$id])) {
d1bafeea1   Kload   [fix] Upgrade to ...
316
317
  				unset($this->objects[$id]);
  			}
6d9380f96   Cédric Dupont   Update sources OC...
318
319
320
321
322
  
  			/* If count() hasn't been called yet don't _count hasn't been initialized
  			* so decrementing it would give a misleading value.
  			*/
  			if (isset($this->_count)) {
d1bafeea1   Kload   [fix] Upgrade to ...
323
324
  				$this->_count -= 1;
  			}
6d9380f96   Cédric Dupont   Update sources OC...
325

d1bafeea1   Kload   [fix] Upgrade to ...
326
327
  			return true;
  		}
6d9380f96   Cédric Dupont   Update sources OC...
328

d1bafeea1   Kload   [fix] Upgrade to ...
329
330
331
332
333
334
335
336
337
338
339
  		return false;
  	}
  
  	/**
  	 * Delete a list of contacts from the address book
  	 *
  	 * @param array $ids
  	 * @return array containing the status
  	 * @throws \Exception on missing permissions
  	 */
  	public function deleteChildren($ids) {
6d9380f96   Cédric Dupont   Update sources OC...
340
341
342
343
344
  		if (!$this->hasPermission(\OCP\PERMISSION_DELETE)) {
  			throw new \Exception(
  				self::$l10n->t('You do not have permissions to delete this contact'),
  				Http::STATUS_FORBIDDEN
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
345
  		}
6d9380f96   Cédric Dupont   Update sources OC...
346
347
348
349
350
351
  
  		if (!$this->getBackend()->hasContactMethodFor(\OCP\PERMISSION_DELETE)) {
  			throw new \Exception(
  				self::$l10n->t('The backend for this address book does not support deleting contacts'),
  				Http::STATUS_NOT_IMPLEMENTED
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
352
353
354
355
356
357
358
  		}
  
  		$response = array();
  
  		\OCP\Util::emitHook('OCA\Contacts', 'pre_deleteContact',
  			array('id' => $ids)
  		);
6d9380f96   Cédric Dupont   Update sources OC...
359
  		foreach ($ids as $id) {
d1bafeea1   Kload   [fix] Upgrade to ...
360
  			try {
6d9380f96   Cédric Dupont   Update sources OC...
361
  				if (!$this->deleteChild($id, array('isBatch' => true))) {
d1bafeea1   Kload   [fix] Upgrade to ...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  					\OCP\Util::writeLog(
  						'contacts', __METHOD__.' Error deleting contact: '
  						. $this->getBackend()->name . '::'
  						. $this->getId() . '::' . $id,
  						\OCP\Util::ERROR
  					);
  					$response[] = array(
  						'id' => (string)$id,
  						'status' => 'error',
  						'message' => self::$l10n->t('Unknown error')
  					);
  				} else {
  					$response[] = array(
  						'id' => (string)$id,
  						'status' => 'success'
  					);
  				}
  			} catch(\Exception $e) {
  				$response[] = array(
  					'id' => (string)$id,
  					'status' => 'error',
  					'message' => $e->getMessage()
  				);
  			}
  		}
  		return $response;
  	}
  
  	/**
  	 * @internal implements Countable
  	 * @return int|null
  	 */
  	public function count() {
6d9380f96   Cédric Dupont   Update sources OC...
395
  		if (!isset($this->_count)) {
d1bafeea1   Kload   [fix] Upgrade to ...
396
397
  			$this->_count = $this->backend->numContacts($this->getId());
  		}
6d9380f96   Cédric Dupont   Update sources OC...
398

d1bafeea1   Kload   [fix] Upgrade to ...
399
400
401
402
403
404
405
406
407
408
409
  		return $this->_count;
  	}
  
  	/**
  	 * Update and save the address book data to backend
  	 * NOTE: @see IPIMObject::update for consistency considerations.
  	 *
  	 * @param array $data
  	 * @return bool
  	 */
  	public function update(array $data) {
6d9380f96   Cédric Dupont   Update sources OC...
410
411
412
413
414
  		if (!$this->hasPermission(\OCP\PERMISSION_UPDATE)) {
  			throw new \Exception(
  				self::$l10n->t('Access denied'),
  				Http::STATUS_FORBIDDEN
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
415
  		}
6d9380f96   Cédric Dupont   Update sources OC...
416
417
418
419
420
421
  
  		if (!$this->getBackend()->hasContactMethodFor(\OCP\PERMISSION_UPDATE)) {
  			throw new \Exception(
  				self::$l10n->t('The backend for this address book does not support updating'),
  				Http::STATUS_NOT_IMPLEMENTED
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
422
  		}
6d9380f96   Cédric Dupont   Update sources OC...
423
424
  
  		if (count($data) === 0) {
d1bafeea1   Kload   [fix] Upgrade to ...
425
426
  			return false;
  		}
6d9380f96   Cédric Dupont   Update sources OC...
427
428
429
  
  		foreach ($data as $key => $value) {
  			switch ($key) {
d1bafeea1   Kload   [fix] Upgrade to ...
430
431
432
433
434
435
436
437
  				case 'displayname':
  					$this->addressBookInfo['displayname'] = $value;
  					break;
  				case 'description':
  					$this->addressBookInfo['description'] = $value;
  					break;
  			}
  		}
d1bafeea1   Kload   [fix] Upgrade to ...
438

6d9380f96   Cédric Dupont   Update sources OC...
439
  		return $this->backend->updateAddressBook($this->getId(), $data);
d1bafeea1   Kload   [fix] Upgrade to ...
440
441
442
443
444
445
446
447
  	}
  
  	/**
  	 * Delete the address book from backend
  	 *
  	 * @return bool
  	 */
  	public function delete() {
6d9380f96   Cédric Dupont   Update sources OC...
448
449
450
451
452
  		if (!$this->hasPermission(\OCP\PERMISSION_DELETE)) {
  			throw new \Exception(
  				self::$l10n->t('You don\'t have permissions to delete the address book.'),
  				Http::STATUS_FORBIDDEN
  			);
d1bafeea1   Kload   [fix] Upgrade to ...
453
  		}
6d9380f96   Cédric Dupont   Update sources OC...
454

d1bafeea1   Kload   [fix] Upgrade to ...
455
456
457
458
459
460
461
462
463
  		return $this->backend->deleteAddressBook($this->getId());
  	}
  
  	/**
  	 * @brief Get the last modification time for the object.
  	 *
  	 * Must return a UNIX time stamp or null if the backend
  	 * doesn't support it.
  	 *
6d9380f96   Cédric Dupont   Update sources OC...
464
  	 * @return int | null
d1bafeea1   Kload   [fix] Upgrade to ...
465
466
467
468
469
470
471
472
473
474
475
476
477
  	 */
  	public function lastModified() {
  		return $this->backend->lastModifiedAddressBook($this->getId());
  	}
  
  	/**
  	 * Get an array of birthday events for contacts in this address book.
  	 *
  	 * @return \Sabre\VObject\Component\VEvent[]
  	 */
  	public function getBirthdayEvents() {
  
  		$events = array();
6d9380f96   Cédric Dupont   Update sources OC...
478
479
480
  
  		foreach ($this->getChildren() as $contact) {
  			if ($event = $contact->getBirthdayEvent()) {
d1bafeea1   Kload   [fix] Upgrade to ...
481
482
483
  				$events[] = $event;
  			}
  		}
6d9380f96   Cédric Dupont   Update sources OC...
484

d1bafeea1   Kload   [fix] Upgrade to ...
485
486
  		return $events;
  	}
6d9380f96   Cédric Dupont   Update sources OC...
487
488
489
490
491
492
493
494
495
  
  	/**
  	 * Returns the searchProvider for a specific backend.
  	 *
  	 * @return \OCP\IAddressBook
  	 */
  	public function getSearchProvider() {
  		return $this->backend->getSearchProvider($this);
  	}
d1bafeea1   Kload   [fix] Upgrade to ...
496
  }