Completed
Push — master ( f78db1...a155f8 )
by Morris
34:10 queued 10s
created

ApiController   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 380
Duplicated Lines 6.58 %

Coupling/Cohesion

Components 2
Dependencies 18

Importance

Changes 0
Metric Value
dl 25
loc 380
rs 9.0399
c 0
b 0
f 0
wmc 42
lcom 2
cbo 18

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 16 1
B getThumbnail() 0 21 6
A updateFileTags() 0 23 5
A formatNodes() 0 17 3
A getRecentFiles() 0 5 1
A setSortingStrategy() 0 4 1
A getSortingStrategy() 0 3 1
A setReverseQuickaccess() 0 4 1
A getReverseQuickaccess() 0 6 2
A setShowQuickaccessSettings() 0 4 1
A getShowQuickaccessSettings() 0 6 2
A setSortingOrder() 0 4 1
A getFavoritesFolder() 0 16 2
A getShareTypes() 25 25 3
A updateFileSorting() 0 12 3
A showHiddenFiles() 0 4 1
A toggleShowFolder() 0 14 6
A getSortingOrder() 0 3 1
A getNodeType() 0 4 1

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 ApiController 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 ApiController, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bjoern Schiessle <[email protected]>
6
 * @author Christoph Wurst <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Tobias Kaminsky <[email protected]>
13
 * @author Vincent Petry <[email protected]>
14
 * @author Felix Nüsse <[email protected]>
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License, version 3,
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
31
namespace OCA\Files\Controller;
32
33
use OCP\AppFramework\Http;
34
use OCP\AppFramework\Controller;
35
use OCP\Files\File;
36
use OCP\Files\Folder;
37
use OCP\Files\NotFoundException;
38
use OCP\IConfig;
39
use OCP\IRequest;
40
use OCP\AppFramework\Http\DataResponse;
41
use OCP\AppFramework\Http\FileDisplayResponse;
42
use OCP\AppFramework\Http\Response;
43
use OCA\Files\Service\TagService;
44
use OCP\IPreview;
45
use OCP\Share\IManager;
46
use OC\Files\Node\Node;
47
use OCP\IUserSession;
48
use Sabre\VObject\Property\Boolean;
49
50
/**
51
 * Class ApiController
52
 *
53
 * @package OCA\Files\Controller
54
 */
55
class ApiController extends Controller {
56
	/** @var TagService */
57
	private $tagService;
58
	/** @var IManager * */
59
	private $shareManager;
60
	/** @var IPreview */
61
	private $previewManager;
62
	/** IUserSession */
63
	private $userSession;
64
	/** IConfig */
65
	private $config;
66
	/** @var Folder */
67
	private $userFolder;
68
69
	/**
70
	 * @param string $appName
71
	 * @param IRequest $request
72
	 * @param IUserSession $userSession
73
	 * @param TagService $tagService
74
	 * @param IPreview $previewManager
75
	 * @param IManager $shareManager
76
	 * @param IConfig $config
77
	 * @param Folder $userFolder
78
	 */
79
	public function __construct($appName,
80
								IRequest $request,
81
								IUserSession $userSession,
82
								TagService $tagService,
83
								IPreview $previewManager,
84
								IManager $shareManager,
85
								IConfig $config,
86
								Folder $userFolder) {
87
		parent::__construct($appName, $request);
88
		$this->userSession = $userSession;
89
		$this->tagService = $tagService;
90
		$this->previewManager = $previewManager;
91
		$this->shareManager = $shareManager;
92
		$this->config = $config;
93
		$this->userFolder = $userFolder;
94
	}
95
96
	/**
97
	 * Gets a thumbnail of the specified file
98
	 *
99
	 * @since API version 1.0
100
	 *
101
	 * @NoAdminRequired
102
	 * @NoCSRFRequired
103
	 * @StrictCookieRequired
104
	 *
105
	 * @param int $x
106
	 * @param int $y
107
	 * @param string $file URL-encoded filename
108
	 * @return DataResponse|FileDisplayResponse
109
	 */
110
	public function getThumbnail($x, $y, $file) {
111
		if ($x < 1 || $y < 1) {
112
			return new DataResponse(['message' => 'Requested size must be numeric and a positive value.'], Http::STATUS_BAD_REQUEST);
113
		}
114
115
		try {
116
			$file = $this->userFolder->get($file);
117
			if ($file instanceof Folder) {
118
				throw new NotFoundException();
119
			}
120
121
			/** @var File $file */
122
			$preview = $this->previewManager->getPreview($file, $x, $y, true);
123
124
			return new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => $preview->getMimeType()]);
125
		} catch (NotFoundException $e) {
126
			return new DataResponse(['message' => 'File not found.'], Http::STATUS_NOT_FOUND);
127
		} catch (\Exception $e) {
128
			return new DataResponse([], Http::STATUS_BAD_REQUEST);
129
		}
130
	}
131
132
	/**
133
	 * Updates the info of the specified file path
134
	 * The passed tags are absolute, which means they will
135
	 * replace the actual tag selection.
136
	 *
137
	 * @NoAdminRequired
138
	 *
139
	 * @param string $path path
140
	 * @param array|string $tags array of tags
141
	 * @return DataResponse
142
	 */
143
	public function updateFileTags($path, $tags = null) {
144
		$result = [];
145
		// if tags specified or empty array, update tags
146
		if (!is_null($tags)) {
147
			try {
148
				$this->tagService->updateFileTags($path, $tags);
0 ignored issues
show
Bug introduced by
It seems like $tags defined by parameter $tags on line 143 can also be of type string; however, OCA\Files\Service\TagService::updateFileTags() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
149
			} catch (\OCP\Files\NotFoundException $e) {
150
				return new DataResponse([
151
					'message' => $e->getMessage()
152
				], Http::STATUS_NOT_FOUND);
153
			} catch (\OCP\Files\StorageNotAvailableException $e) {
154
				return new DataResponse([
155
					'message' => $e->getMessage()
156
				], Http::STATUS_SERVICE_UNAVAILABLE);
157
			} catch (\Exception $e) {
158
				return new DataResponse([
159
					'message' => $e->getMessage()
160
				], Http::STATUS_NOT_FOUND);
161
			}
162
			$result['tags'] = $tags;
163
		}
164
		return new DataResponse($result);
165
	}
166
167
	/**
168
	 * @param \OCP\Files\Node[] $nodes
169
	 * @return array
170
	 */
171
	private function formatNodes(array $nodes) {
172
		return array_values(array_map(function (Node $node) {
173
			/** @var \OC\Files\Node\Node $shareTypes */
174
			$shareTypes = $this->getShareTypes($node);
175
			$file = \OCA\Files\Helper::formatFileInfo($node->getFileInfo());
176
			$parts = explode('/', dirname($node->getPath()), 4);
177
			if (isset($parts[3])) {
178
				$file['path'] = '/' . $parts[3];
179
			} else {
180
				$file['path'] = '/';
181
			}
182
			if (!empty($shareTypes)) {
183
				$file['shareTypes'] = $shareTypes;
184
			}
185
			return $file;
186
		}, $nodes));
187
	}
188
189
	/**
190
	 * Returns a list of recently modifed files.
191
	 *
192
	 * @NoAdminRequired
193
	 *
194
	 * @return DataResponse
195
	 */
196
	public function getRecentFiles() {
197
		$nodes = $this->userFolder->getRecent(100);
198
		$files = $this->formatNodes($nodes);
199
		return new DataResponse(['files' => $files]);
200
	}
201
202
	/**
203
	 * Returns a list of favorites modifed folder.
204
	 *
205
	 * @NoAdminRequired
206
	 *
207
	 * @return DataResponse
208
	 */
209
	public function getFavoritesFolder() {
210
		$nodes = $this->userFolder->searchByTag('_$!<Favorite>!$_', $this->userSession->getUser()->getUID());
211
212
		$favorites = [];
213
		$i = 0;
214
		foreach ($nodes as &$node) {
215
216
			$favorites[$i]['id'] = $node->getId();
217
			$favorites[$i]['name'] = $node->getName();
218
			$favorites[$i]['path'] = $node->getInternalPath();
219
			$favorites[$i]['mtime'] = $node->getMTime();
220
			$i++;
221
		}
222
223
		return new DataResponse(['favoriteFolders' => $favorites]);
224
	}
225
226
	/**
227
	 * Return a list of share types for outgoing shares
228
	 *
229
	 * @param Node $node file node
230
	 *
231
	 * @return int[] array of share types
232
	 */
233 View Code Duplication
	private function getShareTypes(Node $node) {
234
		$userId = $this->userSession->getUser()->getUID();
235
		$shareTypes = [];
236
		$requestedShareTypes = [
237
			\OCP\Share::SHARE_TYPE_USER,
238
			\OCP\Share::SHARE_TYPE_GROUP,
239
			\OCP\Share::SHARE_TYPE_LINK,
240
			\OCP\Share::SHARE_TYPE_REMOTE,
241
			\OCP\Share::SHARE_TYPE_EMAIL
242
		];
243
		foreach ($requestedShareTypes as $requestedShareType) {
244
			// one of each type is enough to find out about the types
245
			$shares = $this->shareManager->getSharesBy(
246
				$userId,
247
				$requestedShareType,
248
				$node,
249
				false,
250
				1
251
			);
252
			if (!empty($shares)) {
253
				$shareTypes[] = $requestedShareType;
254
			}
255
		}
256
		return $shareTypes;
257
	}
258
259
	/**
260
	 * Change the default sort mode
261
	 *
262
	 * @NoAdminRequired
263
	 *
264
	 * @param string $mode
265
	 * @param string $direction
266
	 * @return Response
267
	 */
268
	public function updateFileSorting($mode, $direction) {
269
		$allowedMode = ['name', 'size', 'mtime'];
270
		$allowedDirection = ['asc', 'desc'];
271
		if (!in_array($mode, $allowedMode) || !in_array($direction, $allowedDirection)) {
272
			$response = new Response();
273
			$response->setStatus(Http::STATUS_UNPROCESSABLE_ENTITY);
274
			return $response;
275
		}
276
		$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'file_sorting', $mode);
277
		$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'file_sorting_direction', $direction);
278
		return new Response();
279
	}
280
281
	/**
282
	 * Toggle default for showing/hiding hidden files
283
	 *
284
	 * @NoAdminRequired
285
	 *
286
	 * @param bool $show
287
	 */
288
	public function showHiddenFiles($show) {
289
		$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', (int)$show);
290
		return new Response();
291
	}
292
293
	/**
294
	 * Toggle default for showing/hiding xxx folder
295
	 *
296
	 * @NoAdminRequired
297
	 *
298
	 * @param bool $show 
299
	 * @param bool $key the key of the folder
300
	 *
301
	 * @return Response
302
	 */
303
	public function toggleShowFolder(int $show, string $key) {
304
		// ensure the edited key exists
305
		$navItems = \OCA\Files\App::getNavigationManager()->getAll();
306
		foreach ($navItems as $item) {
307
			// check if data is valid
308
			if (($show === 0 || $show === 1) && isset($item['expandedState']) && $key === $item['expandedState']) {
309
				$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', $key, (int)$show);
310
				return new Response();
311
			}
312
		}
313
		$response = new Response();
314
		$response->setStatus(Http::STATUS_FORBIDDEN);
315
		return $response;
316
	}
317
318
	/**
319
	 * quickaccess-sorting-strategy
320
	 *
321
	 * @NoAdminRequired
322
	 *
323
	 * @param string $strategy
324
	 * @return Response
325
	 */
326
	public function setSortingStrategy($strategy) {
327
		$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'quickaccess_sorting_strategy', (String)$strategy);
328
		return new Response();
329
	}
330
331
	/**
332
	 * Get reverse-state for quickaccess-list
333
	 *
334
	 * @NoAdminRequired
335
	 *
336
	 * @return String
337
	 */
338
	public function getSortingStrategy() {
339
		return $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'quickaccess_sorting_strategy', 'alphabet');
340
	}
341
342
	/**
343
	 * Toggle for reverse quickaccess-list
344
	 *
345
	 * @NoAdminRequired
346
	 *
347
	 * @param bool $reverse
348
	 * @return Response
349
	 */
350
	public function setReverseQuickaccess($reverse) {
351
		$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'quickaccess_reverse_list', (int)$reverse);
352
		return new Response();
353
	}
354
355
	/**
356
	 * Get reverse-state for quickaccess-list
357
	 *
358
	 * @NoAdminRequired
359
	 *
360
	 * @return bool
361
	 */
362
	public function getReverseQuickaccess() {
363
		if ($this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'quickaccess_reverse_list', false)) {
364
			return true;
365
		}
366
		return false;
367
	}
368
369
	/**
370
	 * Set state for show sorting menu
371
	 *
372
	 * @NoAdminRequired
373
	 *
374
	 * @param bool $show
375
	 * @return Response
376
	 */
377
	public function setShowQuickaccessSettings($show) {
378
		$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'quickaccess_show_settings', (int)$show);
379
		return new Response();
380
	}
381
382
	/**
383
	 * Get state for show sorting menu
384
	 *
385
	 * @NoAdminRequired
386
	 *
387
	 * @return bool
388
	 */
389
	public function getShowQuickaccessSettings() {
390
		if ($this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'quickaccess_show_settings', false)) {
391
			return true;
392
		}
393
		return false;
394
	}
395
396
	/**
397
	 * Set sorting-order for custom sorting
398
	 *
399
	 * @NoAdminRequired
400
	 *
401
	 * @param String $order
402
	 * @return Response
403
	 */
404
	public function setSortingOrder($order) {
405
		$this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'quickaccess_custom_sorting_order', (String)$order);
406
		return new Response();
407
	}
408
409
	/**
410
	 * Get sorting-order for custom sorting
411
	 *
412
	 * @NoAdminRequired
413
	 *
414
	 * @return String
415
	 */
416
	public function getSortingOrder() {
417
		return $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'quickaccess_custom_sorting_order', "");
418
	}
419
420
	/**
421
	 * Get sorting-order for custom sorting
422
	 *
423
	 * @NoAdminRequired
424
	 *
425
	 * @param String
426
	 * @return String
427
	 */
428
	public function getNodeType($folderpath) {
429
		$node = $this->userFolder->get($folderpath);
430
		return $node->getType();
431
	}
432
433
434
}
435