Completed
Pull Request — master (#434)
by Joas
03:41
created

OCSEndPoint::generateHeaders()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
ccs 0
cts 21
cp 0
rs 8.439
cc 5
eloc 18
nc 5
nop 2
crap 30
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 1
	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 1
		$this->data = $data;
121 1
		$this->helper = $helper;
122 1
		$this->settings = $settings;
123 1
		$this->request = $request;
124 1
		$this->urlGenerator = $urlGenerator;
125 1
		$this->userSession = $userSession;
126 1
		$this->preview = $preview;
127 1
		$this->mimeTypeDetector = $mimeTypeDetector;
128 1
		$this->view = $view;
129 1
		$this->infoCache = $infoCache;
130 1
	}
131
132
	/**
133
	 * @param array $parameters
134
	 * @throws InvalidFilterException when the filter is invalid
135
	 * @throws \OutOfBoundsException when no user is given
136
	 */
137
	protected function readParameters(array $parameters) {
138
		$this->filter = isset($parameters['filter']) && is_string($parameters['filter']) ? (string) $parameters['filter'] : 'all';
139
		if ($this->filter !== $this->data->validateFilter($this->filter)) {
140
			throw new InvalidFilterException();
141
		}
142
		$this->since = (int) $this->request->getParam('since', 0);
143
		$this->limit = (int) $this->request->getParam('limit', 50);
144
		$this->loadPreviews = $this->request->getParam('previews', 'false') === 'true';
145
		$this->objectType = (string) $this->request->getParam('object_type', '');
146
		$this->objectId = (int) $this->request->getParam('object_id', 0);
147
		$this->sort = (string) $this->request->getParam('sort', '');
148
		$this->sort = in_array($this->sort, ['asc', 'desc']) ? $this->sort : 'desc';
149
150
		if ($this->objectType !== '' && $this->objectId === 0 || $this->objectType === '' && $this->objectId !== 0) {
151
			// Only allowed together
152
			$this->objectType = '';
153
			$this->objectId = 0;
154
		}
155
156
		$user = $this->userSession->getUser();
157
		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
			$this->user = $user->getUID();
159
		} else {
160
			// No user logged in
161
			throw new \OutOfBoundsException();
162
		}
163
	}
164
165
	/**
166
	 * @param array $parameters
167
	 * @return \OC_OCS_Result
168
	 */
169
	public function getDefault(array $parameters) {
170
		return $this->get(array_merge($parameters, [
171
			'filter' => 'all',
172
		]));
173
	}
174
175
	/**
176
	 * @param array $parameters
177
	 * @return \OC_OCS_Result
178
	 */
179
	public function getFilter(array $parameters) {
180
		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
			return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN);
212
		}
213
214
		$headers = $this->generateHeaders($response['headers'], $response['has_more']);
215
		if (empty($response['data'])) {
216
			return new \OC_OCS_Result([], Http::STATUS_NOT_MODIFIED, null, $headers);
217
		}
218
219
		$preparedActivities = [];
220
		foreach ($response['data'] as $activity) {
221
			$activity['datetime'] = date('c', $activity['timestamp']);
222
			unset($activity['timestamp']);
223
224
			if ($this->loadPreviews) {
225
				$activity['previews'] = [];
226
				if ($activity['object_type'] === 'files' && !empty($activity['files'])) {
227
					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...
228
						if (((int) $objectId) === 0 || $objectName === '') {
229
							// No file, no preview
230
							continue;
231
						}
232
233
						$activity['previews'][] = $this->getPreview($activity['affecteduser'], (int) $objectId, $objectName);
234
					}
235
				} else if ($activity['object_type'] === 'files' && $activity['object_id']) {
236
					$activity['previews'][] = $this->getPreview($activity['affecteduser'], (int) $activity['object_id'], $activity['file']);
237
				}
238
			}
239
240
			$preparedActivities[] = $activity;
241
		}
242
243
		return new \OC_OCS_Result($preparedActivities, 100, null, $headers);
244
	}
245
246
	/**
247
	 * @param array $headers
248
	 * @param bool $hasMoreActivities
249
	 * @return array
250
	 */
251
	protected function generateHeaders(array $headers, $hasMoreActivities) {
252
		if ($hasMoreActivities) {
253
			// Set the "Link" header for the next page
254
			$nextPageParameters = [
255
				'since' => $headers['X-Activity-Last-Given'],
256
				'limit' => $this->limit,
257
				'sort' => $this->sort,
258
			];
259
			if ($this->objectType && $this->objectId) {
260
				$nextPageParameters['object_type'] = $this->objectType;
261
				$nextPageParameters['object_id'] = $this->objectId;
262
			}
263
			if ($this->request->getParam('format') !== null) {
264
				$nextPageParameters['format'] = $this->request->getParam('format');
265
			}
266
267
			$nextPage = $this->request->getServerProtocol(); # http
268
			$nextPage .= '://' . $this->request->getServerHost(); # localhost
269
			$nextPage .= $this->request->getScriptName(); # /ocs/v2.php
270
			$nextPage .= $this->request->getPathInfo(); # /apps/activity/api/v2/activity
271
			$nextPage .= '?' . http_build_query($nextPageParameters);
272
			$headers['Link'] = '<' . $nextPage . '>; rel="next"';
273
		}
274
275
		return $headers;
276
	}
277
278
	/**
279
	 * @param string $owner
280
	 * @param int $fileId
281
	 * @param string $filePath
282
	 * @return array
283
	 */
284
	protected function getPreview($owner, $fileId, $filePath) {
285
		$info = $this->infoCache->getInfoById($owner, $fileId, $filePath);
286
287
		if (!$info['exists'] || $info['view'] !== '') {
288
			return $this->getPreviewFromPath($filePath);
289
		}
290
291
		$preview = [
292
			'link'			=> $this->getPreviewLink($info['path'], $info['is_dir']),
293
			'source'		=> '',
294
			'isMimeTypeIcon' => true,
295
		];
296
297
		// show a preview image if the file still exists
298
		if ($info['is_dir']) {
299
			$preview['source'] = $this->getPreviewPathFromMimeType('dir');
300
		} else {
301
			$this->view->chroot('/' . $owner . '/files');
302
			$fileInfo = $this->view->getFileInfo($info['path']);
303
			if ($this->preview->isAvailable($fileInfo)) {
304
				$preview['isMimeTypeIcon'] = false;
305
				$preview['source'] = $this->urlGenerator->linkToRoute('core_ajax_preview', [
306
					'file' => $info['path'],
307
					'c' => $this->view->getETag($info['path']),
308
					'x' => 150,
309
					'y' => 150,
310
				]);
311
			} else {
312
				$preview['source'] = $this->getPreviewPathFromMimeType($fileInfo->getMimetype());
313
			}
314
		}
315
316
		return $preview;
317
	}
318
319
	/**
320
	 * @param string $filePath
321
	 * @return array
322
	 */
323
	protected function getPreviewFromPath($filePath) {
324
		$mimeType = $this->mimeTypeDetector->detectPath($filePath);
325
		$preview = [
326
			'link'			=> $this->getPreviewLink($filePath, false),
327
			'source'		=> $this->getPreviewPathFromMimeType($mimeType),
328
			'isMimeTypeIcon' => true,
329
		];
330
331
		return $preview;
332
	}
333
334
	/**
335
	 * @param string $mimeType
336
	 * @return string
337
	 */
338
	protected function getPreviewPathFromMimeType($mimeType) {
339
		$mimeTypeIcon = $this->mimeTypeDetector->mimeTypeIcon($mimeType);
340
		if (substr($mimeTypeIcon, -4) === '.png') {
341
			$mimeTypeIcon = substr($mimeTypeIcon, 0, -4) . '.svg';
342
		}
343
344
		return $mimeTypeIcon;
345
	}
346
347
	/**
348
	 * @param string $path
349
	 * @param bool $isDir
350
	 * @return string
351
	 */
352
	protected function getPreviewLink($path, $isDir) {
353
		if ($isDir) {
354
			return $this->urlGenerator->linkTo('files', 'index.php', array('dir' => $path));
355
		} else {
356
			$parentDir = (substr_count($path, '/') === 1) ? '/' : dirname($path);
357
			$fileName = basename($path);
358
			return $this->urlGenerator->linkTo('files', 'index.php', array(
359
				'dir' => $parentDir,
360
				'scrollto' => $fileName,
361
			));
362
		}
363
	}
364
}
365