Completed
Pull Request — master (#434)
by Joas
04:25
created

OCSEndPoint::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 21
ccs 12
cts 12
cp 1
rs 9.3143
cc 1
eloc 20
nc 1
nop 10
crap 1

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @author Joas Schilling <[email protected]>
4
 *
5
 * @copyright Copyright (c) 2015, ownCloud, Inc.
6
 * @license AGPL-3.0
7
 *
8
 * This code is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Affero General Public License, version 3,
10
 * as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License, version 3,
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19
 *
20
 */
21
22
namespace OCA\Activity\Controller;
23
24
25
use OC\Files\View;
26
use OCA\Activity\Data;
27
use OCA\Activity\Exception\InvalidFilterException;
28
use OCA\Activity\GroupHelper;
29
use OCA\Activity\UserSettings;
30
use OCA\Activity\ViewInfoCache;
31
use OCP\AppFramework\Http;
32
use OCP\Files\IMimeTypeDetector;
33
use OCP\IPreview;
34
use OCP\IRequest;
35
use OCP\IURLGenerator;
36
use OCP\IUser;
37
use OCP\IUserSession;
38
39
class OCSEndPoint {
40
41
	/** @var string */
42
	protected $filter;
43
44
	/** @var int */
45
	protected $since;
46
47
	/** @var int */
48
	protected $limit;
49
50
	/** @var string */
51
	protected $sort;
52
53
	/** @var string */
54
	protected $objectType;
55
56
	/** @var int */
57
	protected $objectId;
58
59
	/** @var string */
60
	protected $user;
61
62
	/** @var bool */
63
	protected $loadPreviews;
64
65
66
	/** @var Data */
67
	protected $data;
68
69
	/** @var GroupHelper */
70
	protected $helper;
71
72
	/** @var UserSettings */
73
	protected $settings;
74
75
	/** @var IRequest */
76
	protected $request;
77
78
	/** @var IURLGenerator */
79
	protected $urlGenerator;
80
81
	/** @var IUserSession */
82
	protected $userSession;
83
84
	/** @var IPreview */
85
	protected $preview;
86
87
	/** @var IMimeTypeDetector */
88
	protected $mimeTypeDetector;
89
90
	/** @var View */
91
	protected $view;
92
93
	/** @var ViewInfoCache */
94
	protected $infoCache;
95
96
	/**
97
	 * OCSEndPoint constructor.
98
	 *
99
	 * @param Data $data
100
	 * @param GroupHelper $helper
101
	 * @param UserSettings $settings
102
	 * @param IRequest $request
103
	 * @param IURLGenerator $urlGenerator
104
	 * @param IUserSession $userSession
105
	 * @param IPreview $preview
106
	 * @param IMimeTypeDetector $mimeTypeDetector
107
	 * @param View $view
108
	 * @param ViewInfoCache $infoCache
109
	 */
110 52
	public function __construct(Data $data,
111
								GroupHelper $helper,
112
								UserSettings $settings,
113
								IRequest $request,
114
								IURLGenerator $urlGenerator,
115
								IUserSession $userSession,
116
								IPreview $preview,
117
								IMimeTypeDetector $mimeTypeDetector,
118
								View $view,
119
								ViewInfoCache $infoCache) {
120 52
		$this->data = $data;
121 52
		$this->helper = $helper;
122 52
		$this->settings = $settings;
123 52
		$this->request = $request;
124 52
		$this->urlGenerator = $urlGenerator;
125 52
		$this->userSession = $userSession;
126 52
		$this->preview = $preview;
127 52
		$this->mimeTypeDetector = $mimeTypeDetector;
128 52
		$this->view = $view;
129 52
		$this->infoCache = $infoCache;
130 52
	}
131
132
	/**
133
	 * @param array $parameters
134
	 * @throws InvalidFilterException when the filter is invalid
135
	 * @throws \OutOfBoundsException when no user is given
136
	 */
137 23
	protected function readParameters(array $parameters) {
138 23
		$this->filter = isset($parameters['filter']) && is_string($parameters['filter']) ? (string) $parameters['filter'] : 'all';
139 23
		if ($this->filter !== $this->data->validateFilter($this->filter)) {
140 2
			throw new InvalidFilterException();
141
		}
142 21
		$this->since = (int) $this->request->getParam('since', 0);
143 21
		$this->limit = (int) $this->request->getParam('limit', 50);
144 21
		$this->loadPreviews = $this->request->getParam('previews', 'false') === 'true';
145 21
		$this->objectType = (string) $this->request->getParam('object_type', '');
146 21
		$this->objectId = (int) $this->request->getParam('object_id', 0);
147 21
		$this->sort = (string) $this->request->getParam('sort', '');
148 21
		$this->sort = in_array($this->sort, ['asc', 'desc']) ? $this->sort : 'desc';
149
150 21
		if ($this->objectType !== '' && $this->objectId === 0 || $this->objectType === '' && $this->objectId !== 0) {
151
			// Only allowed together
152 2
			$this->objectType = '';
153 2
			$this->objectId = 0;
154 2
		}
155
156 21
		$user = $this->userSession->getUser();
157 21
		if ($user instanceof IUser) {
0 ignored issues
show
Bug introduced by
The class OCP\IUser does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
158 20
			$this->user = $user->getUID();
159 20
		} else {
160
			// No user logged in
161 1
			throw new \OutOfBoundsException();
162
		}
163 20
	}
164
165
	/**
166
	 * @param array $parameters
167
	 * @return \OC_OCS_Result
168
	 */
169 3
	public function getDefault(array $parameters) {
170 3
		return $this->get(array_merge($parameters, [
171 3
			'filter' => 'all',
172 3
		]));
173
	}
174
175
	/**
176
	 * @param array $parameters
177
	 * @return \OC_OCS_Result
178
	 */
179 3
	public function getFilter(array $parameters) {
180 3
		return $this->get($parameters);
181
	}
182
183
	/**
184
	 * @param array $parameters
185
	 * @return \OC_OCS_Result
186
	 */
187
	protected function get(array $parameters) {
188
		try {
189
			$this->readParameters($parameters);
190
		} catch (InvalidFilterException $e) {
191
			return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND);
192
		} catch (\OutOfBoundsException $e) {
193
			return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN);
194
		}
195
196
		try {
197
			$response = $this->data->get(
198
				$this->helper,
199
				$this->settings,
200
				$this->user,
201
202
				$this->since,
203
				$this->limit,
204
				$this->sort,
205
206
				$this->filter,
207
				$this->objectType,
208
				$this->objectId
209
			);
210
		} catch (\OutOfBoundsException $e) {
211
			// Invalid since argument
212
			return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN);
213
		} catch (\BadMethodCallException $e) {
214
			// No activity settings enabled
215
			return new \OC_OCS_Result(null, Http::STATUS_NO_CONTENT);
216
		}
217
218
		$headers = $this->generateHeaders($response['headers'], $response['has_more']);
219
		if (empty($response['data'])) {
220
			return new \OC_OCS_Result([], Http::STATUS_NOT_MODIFIED, null, $headers);
221
		}
222
223
		$preparedActivities = [];
224
		foreach ($response['data'] as $activity) {
225
			$activity['datetime'] = date('c', $activity['timestamp']);
226
			unset($activity['timestamp']);
227
228
			if ($this->loadPreviews) {
229
				$activity['previews'] = [];
230
				if ($activity['object_type'] === 'files' && !empty($activity['files'])) {
231
					foreach ($activity['files'] as $objectId => $objectName) {
0 ignored issues
show
Bug introduced by
The expression $activity['files'] of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
232
						if (((int) $objectId) === 0 || $objectName === '') {
233
							// No file, no preview
234
							continue;
235
						}
236
237
						$activity['previews'][] = $this->getPreview($activity['affecteduser'], (int) $objectId, $objectName);
238
					}
239
				} else if ($activity['object_type'] === 'files' && $activity['object_id']) {
240
					$activity['previews'][] = $this->getPreview($activity['affecteduser'], (int) $activity['object_id'], $activity['file']);
241
				}
242
			}
243
244
			$preparedActivities[] = $activity;
245
		}
246
247
		return new \OC_OCS_Result($preparedActivities, 100, null, $headers);
248
	}
249
250
	/**
251
	 * @param array $headers
252
	 * @param bool $hasMoreActivities
253
	 * @return array
254
	 */
255 4
	protected function generateHeaders(array $headers, $hasMoreActivities) {
256 4
		if ($hasMoreActivities && isset($headers['X-Activity-Last-Given'])) {
257
			// Set the "Link" header for the next page
258
			$nextPageParameters = [
259 3
				'since' => $headers['X-Activity-Last-Given'],
260 3
				'limit' => $this->limit,
261 3
				'sort' => $this->sort,
262 3
			];
263 3
			if ($this->objectType && $this->objectId) {
264 1
				$nextPageParameters['object_type'] = $this->objectType;
265 1
				$nextPageParameters['object_id'] = $this->objectId;
266 1
			}
267 3
			if ($this->request->getParam('format') !== null) {
268 1
				$nextPageParameters['format'] = $this->request->getParam('format');
269 1
			}
270
271 3
			$nextPage = $this->request->getServerProtocol(); # http
272 3
			$nextPage .= '://' . $this->request->getServerHost(); # localhost
273 3
			$nextPage .= $this->request->getScriptName(); # /ocs/v2.php
274 3
			$nextPage .= $this->request->getPathInfo(); # /apps/activity/api/v2/activity
275 3
			$nextPage .= '?' . http_build_query($nextPageParameters);
276 3
			$headers['Link'] = '<' . $nextPage . '>; rel="next"';
277 3
		}
278
279 4
		return $headers;
280
	}
281
282
	/**
283
	 * @param string $owner
284
	 * @param int $fileId
285
	 * @param string $filePath
286
	 * @return array
287
	 */
288 6
	protected function getPreview($owner, $fileId, $filePath) {
289 6
		$info = $this->infoCache->getInfoById($owner, $fileId, $filePath);
290
291 6
		if (!$info['exists'] || $info['view'] !== '') {
292 3
			return $this->getPreviewFromPath($filePath);
293
		}
294
295
		$preview = [
296 3
			'link'			=> $this->getPreviewLink($info['path'], $info['is_dir']),
297 3
			'source'		=> '',
298 3
			'isMimeTypeIcon' => true,
299 3
		];
300
301
		// show a preview image if the file still exists
302 3
		if ($info['is_dir']) {
303 1
			$preview['source'] = $this->getPreviewPathFromMimeType('dir');
304 1
		} else {
305 2
			$this->view->chroot('/' . $owner . '/files');
306 2
			$fileInfo = $this->view->getFileInfo($info['path']);
307 2
			if ($this->preview->isAvailable($fileInfo)) {
308 1
				$preview['isMimeTypeIcon'] = false;
309 1
				$preview['source'] = $this->urlGenerator->linkToRoute('core_ajax_preview', [
310 1
					'file' => $info['path'],
311 1
					'c' => $this->view->getETag($info['path']),
312 1
					'x' => 150,
313 1
					'y' => 150,
314 1
				]);
315 1
			} else {
316 1
				$preview['source'] = $this->getPreviewPathFromMimeType($fileInfo->getMimetype());
317
			}
318
		}
319
320 3
		return $preview;
321
	}
322
323
	/**
324
	 * @param string $filePath
325
	 * @return array
326
	 */
327 3
	protected function getPreviewFromPath($filePath) {
328 3
		$mimeType = $this->mimeTypeDetector->detectPath($filePath);
329
		$preview = [
330 3
			'link'			=> $this->getPreviewLink($filePath, false),
331 3
			'source'		=> $this->getPreviewPathFromMimeType($mimeType),
332 3
			'isMimeTypeIcon' => true,
333 3
		];
334
335 3
		return $preview;
336
	}
337
338
	/**
339
	 * @param string $mimeType
340
	 * @return string
341
	 */
342 3
	protected function getPreviewPathFromMimeType($mimeType) {
343 3
		$mimeTypeIcon = $this->mimeTypeDetector->mimeTypeIcon($mimeType);
344 3
		if (substr($mimeTypeIcon, -4) === '.png') {
345 1
			$mimeTypeIcon = substr($mimeTypeIcon, 0, -4) . '.svg';
346 1
		}
347
348 3
		return $mimeTypeIcon;
349
	}
350
351
	/**
352
	 * @param string $path
353
	 * @param bool $isDir
354
	 * @return string
355
	 */
356 6
	protected function getPreviewLink($path, $isDir) {
357 6
		if ($isDir) {
358 3
			return $this->urlGenerator->linkTo('files', 'index.php', array('dir' => $path));
359
		} else {
360 3
			$parentDir = (substr_count($path, '/') === 1) ? '/' : dirname($path);
361 3
			$fileName = basename($path);
362 3
			return $this->urlGenerator->linkTo('files', 'index.php', array(
363 3
				'dir' => $parentDir,
364 3
				'scrollto' => $fileName,
365 3
			));
366
		}
367
	}
368
}
369