AddressBookController   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 349
Duplicated Lines 9.46 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 43
lcom 1
cbo 5
dl 33
loc 349
ccs 0
cts 216
cp 0
rs 8.3157
c 2
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B userAddressBooks() 0 35 3
A getAddressBook() 6 17 3
C getContacts() 6 51 14
B addAddressBook() 0 23 4
A updateAddressBook() 10 10 1
B deleteAddressBook() 0 31 4
A activateAddressBook() 11 11 1
B addChild() 0 40 4
A deleteChild() 0 17 2
A deleteChildren() 0 12 1
B moveChild() 0 44 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AddressBookController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AddressBookController, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Thomas Tanghus
4
 * @copyright 2013-2014 Thomas Tanghus ([email protected])
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later.
7
 * See the COPYING-README file.
8
 */
9
10
namespace OCA\Contacts\Controller;
11
12
use OCA\Contacts\App,
13
	OCA\Contacts\JSONResponse,
14
	OCA\Contacts\Utils\JSONSerializer,
15
	OCA\Contacts\Controller,
16
	OCP\AppFramework\Http,
17
	OCP\IRequest;
18
19
/**
20
 * Controller class For Address Books
21
 */
22
class AddressBookController extends Controller {
23
24
	/** @var string */
25
	protected $userId;
26
27
	public function __construct($appName, IRequest $request, App $app, $userId) {
28
		parent::__construct($appName, $request, $app);
29
		$this->userId = $userId;
30
	}
31
32
	/**
33
	 * @NoAdminRequired
34
	 * @NoCSRFRequired
35
	 */
36
	public function userAddressBooks() {
37
		$addressBooks = $this->app->getAddressBooksForUser();
38
		$result = array();
39
		$lastModified = 0;
40
41
		foreach($addressBooks as $addressBook) {
42
43
			$data = $addressBook->getMetaData();
44
			$result[] = $data;
45
46
			if (!is_null($data['lastmodified'])) {
47
				$lastModified = max($lastModified, $data['lastmodified']);
48
			}
49
50
		}
51
52
		// To avoid invalid cache deletion time is saved
53
		/*$lastModified = max(
54
			$lastModified,
55
			\OCP\Config::getUserValue($this->userId, 'contacts', 'last_address_book_deleted', 0)
56
		);*/
57
58
		$response = new JSONResponse(array(
59
			'addressbooks' => $result,
60
		));
61
62
		/** FIXME: Caching is currently disabled
63
		if($lastModified > 0) {
64
			$response->setLastModified(\DateTime::createFromFormat('U', $lastModified) ?: null);
65
			$response->setETag(md5($lastModified));
66
		}
67
		*/
68
69
		return $response;
70
	}
71
72
	/**
73
	 * @NoAdminRequired
74
	 * @NoCSRFRequired
75
	 */
76
	public function getAddressBook() {
77
		$params = $this->request->urlParams;
78
79
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
80
		$lastModified = $addressBook->lastModified();
81
		$response = new JSONResponse();
82
		$response->setData(array('data' => $addressBook->getMetaData()));
83
84 View Code Duplication
		if (!is_null($lastModified)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
85
			$response->addHeader('Cache-Control', 'private, must-revalidate');
86
			$response->setLastModified(\DateTime::createFromFormat('U', $lastModified) ?: null);
87
			$etag = md5($lastModified);
88
			$response->setETag($etag);
89
		}
90
91
		return $response;
92
	}
93
94
	/**
95
	 * @NoAdminRequired
96
	 * @NoCSRFRequired
97
	 */
98
	public function getContacts() {
99
		$params = $this->request->urlParams;
100
101
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
102
		$lastModified = $addressBook->lastModified();
103
		$etag = null;
104
		$response = new JSONResponse();
105
106 View Code Duplication
		if (!is_null($lastModified)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
			//$response->addHeader('Cache-Control', 'private, must-revalidate');
108
			$response->setLastModified(\DateTime::createFromFormat('U', $lastModified) ?: null);
109
			$etag = md5($lastModified);
110
			$response->setETag($etag);
111
		}
112
113
		if (!is_null($etag)
114
			&& $this->request->getHeader('If-None-Match') === '"'.$etag.'"')
115
		{
116
			return $response->setStatus(Http::STATUS_NOT_MODIFIED);
117
		} else {
118
			switch ($this->request->method) {
119
				case 'OPTIONS':
120
					$options = array('GET', 'HEAD', 'OPTIONS');
121
					if ($addressBook->hasPermission(\OCP\PERMISSION_DELETE)
122
						&& $addressBook->getBackend()->hasAddressBookMethodFor(\OCP\PERMISSION_DELETE))
123
					{
124
						$options[] = 'DELETE';
125
					}
126
					if ($addressBook->hasPermission(\OCP\PERMISSION_UPDATE)
127
						&& $addressBook->getBackend()->hasAddressBookMethodFor(\OCP\PERMISSION_UPDATE))
128
					{
129
						$options[] = 'POST';
130
					}
131
					$response->addHeader('Allow' , implode(',', $options));
132
					return $response;
133
				case 'HEAD':
134
					return $response;
135
				case 'GET':
136
					$contacts = array();
137
138
					foreach ($addressBook->getChildren() as $i => $contact) {
139
						$result = JSONSerializer::serializeContact($contact);
140
						if ($result !== null) {
141
							$contacts[] = $result;
142
						}
143
					}
144
145
					return $response->setData(array('contacts' => $contacts));
146
			}
147
		}
148
	}
149
150
	/**
151
	 * @NoAdminRequired
152
	 */
153
	public function addAddressBook() {
154
		$params = $this->request->urlParams;
155
156
		$response = new JSONResponse();
157
158
		$backend = $this->app->getBackend($params['backend']);
159
160
		if (!$backend->hasAddressBookMethodFor(\OCP\PERMISSION_CREATE)) {
161
			throw new \Exception('This backend does not support adding address books', 501);
162
		}
163
164
		try {
165
			$id = $backend->createAddressBook($this->request->post);
166
		} catch(\Exception $e) {
167
			return $response->bailOut($e->getMessage());
168
		}
169
170
		if ($id === false) {
171
			return $response->bailOut(App::$l10n->t('Error creating address book'));
172
		}
173
174
		return $response->setStatus('201')->setParams($backend->getAddressBook($id));
175
	}
176
177
	/**
178
	 * @NoAdminRequired
179
	 */
180 View Code Duplication
	public function updateAddressBook() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
181
		$params = $this->request->urlParams;
182
183
		$response = new JSONResponse();
184
185
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
186
		$addressBook->update($this->request['properties']);
187
188
		return $response->setParams($addressBook->getMetaData());
189
	}
190
191
	/**
192
	 * @NoAdminRequired
193
	 */
194
	public function deleteAddressBook() {
195
		$params = $this->request->urlParams;
196
197
		$response = new JSONResponse();
198
199
		$backend = $this->app->getBackend($params['backend']);
200
201
		if (!$backend->hasAddressBookMethodFor(\OCP\PERMISSION_DELETE)) {
202
			throw new \Exception(App::$l10n->t(
203
				'The "%s" backend does not support deleting address books', array($backend->name)
204
			), 501);
205
		}
206
207
		$addressBookInfo = $backend->getAddressBook($params['addressBookId']);
208
209
		if (!$addressBookInfo['permissions'] & \OCP\PERMISSION_DELETE) {
210
			throw new \Exception(App::$l10n->t(
211
				'You do not have permissions to delete the "%s" address book',
212
				array($addressBookInfo['displayname'])
213
			), 403);
214
		}
215
216
		if (!$backend->deleteAddressBook($params['addressBookId'])) {
217
			throw new \Exception(App::$l10n->t(
218
				'Error deleting address book'
219
			), 500);
220
		}
221
222
		\OCP\Config::setUserValue($this->userId, 'contacts', 'last_address_book_deleted', time());
223
		return $response;
224
	}
225
226
	/**
227
	 * @NoAdminRequired
228
	 */
229 View Code Duplication
	public function activateAddressBook() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
230
		$params = $this->request->urlParams;
231
232
		$response = new JSONResponse();
233
234
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
235
236
		$addressBook->setActive($this->request->post['state']);
237
238
		return $response;
239
	}
240
241
	/**
242
	 * @NoAdminRequired
243
	 */
244
	public function addChild() {
245
		$params = $this->request->urlParams;
246
247
		$response = new JSONResponse();
248
249
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
250
251
		try {
252
			$id = $addressBook->addChild();
253
		} catch(\Exception $e) {
254
			return $response->bailOut($e->getMessage());
255
		}
256
257
		if ($id === false) {
258
			return $response->bailOut(App::$l10n->t('Error creating contact.'));
259
		}
260
261
		$contact = $addressBook->getChild($id);
262
263
		$serialized = JSONSerializer::serializeContact($contact);
264
265
		if (is_null($serialized)) {
266
			throw new \Exception(App::$l10n->t(
267
				'Error creating contact'
268
			));
269
		}
270
271
		$response->setStatus('201')->setETag($contact->getETag());
272
		$response->addHeader('Location',
273
			\OCP\Util::linkToRoute(
274
				'contacts_contact_get',
275
				array(
276
					'backend' => $params['backend'],
277
					'addressBookId' => $params['addressBookId'],
278
					'contactId' => $id
279
				)
280
			)
281
		);
282
		return $response->setParams($serialized);
283
	}
284
285
	/**
286
	 * @NoAdminRequired
287
	 */
288
	public function deleteChild() {
289
		$params = $this->request->urlParams;
290
291
		$response = new JSONResponse();
292
293
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
294
295
		$result = $addressBook->deleteChild($params['contactId']);
296
297
		if ($result === false) {
298
			throw new \Exception(App::$l10n->t(
299
				'Error deleting contact'
300
			), 500);
301
		}
302
303
		return $response->setStatus('204');
304
	}
305
306
	/**
307
	 * @NoAdminRequired
308
	 */
309
	public function deleteChildren() {
310
		$params = $this->request->urlParams;
311
312
		$response = new JSONResponse();
313
314
		$addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
315
		$contacts = $this->request->post['contacts'];
316
317
		$result = $addressBook->deleteChildren($contacts);
318
319
		return $response->setParams(array('result' => $result));
320
	}
321
322
	/**
323
	 * @NoAdminRequired
324
	 */
325
	public function moveChild() {
326
		$params = $this->request->urlParams;
327
		$targetInfo = $this->request->post['target'];
328
329
		$response = new JSONResponse();
330
331
		// TODO: Check if the backend supports move (is 'local' or 'shared') and use that operation instead.
332
		// If so, set status 204 and don't return the serialized contact.
333
		$fromAddressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']);
334
		$targetAddressBook = $this->app->getAddressBook($targetInfo['backend'], $targetInfo['id']);
335
		$contact = $fromAddressBook->getChild($params['contactId']);
336
337
		if (!$contact) {
338
			throw new \Exception(App::$l10n->t(
339
				'Error retrieving contact'
340
			), 500);
341
		}
342
343
		$contactId = $targetAddressBook->addChild($contact);
344
345
		// Retrieve the contact again to be sure it's in sync
346
		$contact = $targetAddressBook->getChild($contactId);
347
348
		if (!$contact) {
349
			throw new \Exception(App::$l10n->t(
350
				'Error saving contact'
351
			), 500);
352
		}
353
354
		if (!$fromAddressBook->deleteChild($params['contactId'])) {
355
			// Don't bail out because we have to return the contact
356
			return $response->debug(App::$l10n->t('Error removing contact from other address book.'));
357
		}
358
359
		$serialized = JSONSerializer::serializeContact($contact);
360
361
		if (is_null($serialized)) {
362
			throw new \Exception(App::$l10n->t(
363
				'Error getting moved contact'
364
			));
365
		}
366
367
		return $response->setParams($serialized);
368
	}
369
370
}
371
372