Completed
Push — master ( ed4ed7...0864f5 )
by Lukas
115:48 queued 101:14
created

FilesReportPlugin::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 16
nc 1
nop 8
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0

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
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Joas Schilling <[email protected]>
6
 * @author Vincent Petry <[email protected]>
7
 *
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OCA\DAV\Connector\Sabre;
25
26
use OC\Files\View;
27
use Sabre\DAV\Exception\NotFound;
28
use Sabre\DAV\Exception\PreconditionFailed;
29
use Sabre\DAV\Exception\ReportNotSupported;
30
use Sabre\DAV\Exception\BadRequest;
31
use Sabre\DAV\ServerPlugin;
32
use Sabre\DAV\Tree;
33
use Sabre\DAV\Xml\Element\Response;
34
use Sabre\DAV\Xml\Response\MultiStatus;
35
use Sabre\DAV\PropFind;
36
use OCP\SystemTag\ISystemTagObjectMapper;
37
use OCP\IUserSession;
38
use OCP\Files\Folder;
39
use OCP\IGroupManager;
40
use OCP\SystemTag\ISystemTagManager;
41
use OCP\SystemTag\TagNotFoundException;
42
use OCP\ITagManager;
43
44
class FilesReportPlugin extends ServerPlugin {
45
46
	// namespace
47
	const NS_OWNCLOUD = 'http://owncloud.org/ns';
48
	const REPORT_NAME            = '{http://owncloud.org/ns}filter-files';
49
	const SYSTEMTAG_PROPERTYNAME = '{http://owncloud.org/ns}systemtag';
50
51
	/**
52
	 * Reference to main server object
53
	 *
54
	 * @var \Sabre\DAV\Server
55
	 */
56
	private $server;
57
58
	/**
59
	 * @var Tree
60
	 */
61
	private $tree;
62
63
	/**
64
	 * @var View
65
	 */
66
	private $fileView;
67
68
	/**
69
	 * @var ISystemTagManager
70
	 */
71
	private $tagManager;
72
73
	/**
74
	 * @var ISystemTagObjectMapper
75
	 */
76
	private $tagMapper;
77
78
	/**
79
	 * Manager for private tags
80
	 *
81
	 * @var ITagManager
82
	 */
83
	private $fileTagger;
84
85
	/**
86
	 * @var IUserSession
87
	 */
88
	private $userSession;
89
90
	/**
91
	 * @var IGroupManager
92
	 */
93
	private $groupManager;
94
95
	/**
96
	 * @var Folder
97
	 */
98
	private $userFolder;
99
100
	/**
101
	 * @param Tree $tree
102
	 * @param View $view
103
	 * @param ISystemTagManager $tagManager
104
	 * @param ISystemTagObjectMapper $tagMapper
105
	 * @param ITagManager $fileTagger manager for private tags
106
	 * @param IUserSession $userSession
107
	 * @param IGroupManager $groupManager
108
	 * @param Folder $userfolder
0 ignored issues
show
Documentation introduced by
There is no parameter named $userfolder. Did you maybe mean $userFolder?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
109
	 */
110
	public function __construct(Tree $tree,
111
								View $view,
112
								ISystemTagManager $tagManager,
113
								ISystemTagObjectMapper $tagMapper,
114
								ITagManager $fileTagger,
115
								IUserSession $userSession,
116
								IGroupManager $groupManager,
117
								Folder $userFolder
118
	) {
119
		$this->tree = $tree;
120
		$this->fileView = $view;
121
		$this->tagManager = $tagManager;
122
		$this->tagMapper = $tagMapper;
123
		$this->fileTagger = $fileTagger;
124
		$this->userSession = $userSession;
125
		$this->groupManager = $groupManager;
126
		$this->userFolder = $userFolder;
127
	}
128
129
	/**
130
	 * This initializes the plugin.
131
	 *
132
	 * This function is called by \Sabre\DAV\Server, after
133
	 * addPlugin is called.
134
	 *
135
	 * This method should set up the required event subscriptions.
136
	 *
137
	 * @param \Sabre\DAV\Server $server
138
	 * @return void
139
	 */
140
	public function initialize(\Sabre\DAV\Server $server) {
141
142
		$server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
143
144
		$this->server = $server;
145
		$this->server->on('report', array($this, 'onReport'));
146
	}
147
148
	/**
149
	 * Returns a list of reports this plugin supports.
150
	 *
151
	 * This will be used in the {DAV:}supported-report-set property.
152
	 *
153
	 * @param string $uri
154
	 * @return array
155
	 */
156
	public function getSupportedReportSet($uri) {
157
		return [self::REPORT_NAME];
158
	}
159
160
	/**
161
	 * REPORT operations to look for files
162
	 *
163
	 * @param string $reportName
164
	 * @param [] $report
0 ignored issues
show
Documentation introduced by
The doc-type [] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
165
	 * @param string $uri
166
	 * @return bool
167
	 * @throws NotFound
168
	 * @throws ReportNotSupported
169
	 */
170
	public function onReport($reportName, $report, $uri) {
171
		$reportTargetNode = $this->server->tree->getNodeForPath($uri);
172
		if (!$reportTargetNode instanceof Directory || $reportName !== self::REPORT_NAME) {
173
			throw new ReportNotSupported();
174
		}
175
176
		$ns = '{' . $this::NS_OWNCLOUD . '}';
177
		$requestedProps = [];
178
		$filterRules = [];
179
180
		// parse report properties and gather filter info
181
		foreach ($report as $reportProps) {
182
			$name = $reportProps['name'];
183
			if ($name === $ns . 'filter-rules') {
184
				$filterRules = $reportProps['value'];
185
			} else if ($name === '{DAV:}prop') {
186
				// propfind properties
187
				foreach ($reportProps['value'] as $propVal) {
188
					$requestedProps[] = $propVal['name'];
189
				}
190
			}
191
		}
192
193
		if (empty($filterRules)) {
194
			// an empty filter would return all existing files which would be slow
195
			throw new BadRequest('Missing filter-rule block in request');
196
		}
197
198
		// gather all file ids matching filter
199
		try {
200
			$resultFileIds = $this->processFilterRules($filterRules);
201
		} catch (TagNotFoundException $e) {
202
			throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e);
203
		}
204
205
		// find sabre nodes by file id, restricted to the root node path
206
		$results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds);
207
208
		$responses = $this->prepareResponses($requestedProps, $results);
209
210
		$xml = $this->server->xml->write(
211
			'{DAV:}multistatus',
212
			new MultiStatus($responses)
213
		);
214
215
		$this->server->httpResponse->setStatus(207);
216
		$this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
217
		$this->server->httpResponse->setBody($xml);
218
219
		return false;
220
	}
221
222
	/**
223
	 * Find file ids matching the given filter rules
224
	 *
225
	 * @param array $filterRules
226
	 * @return array array of unique file id results
227
	 *
228
	 * @throws TagNotFoundException whenever a tag was not found
229
	 */
230
	protected function processFilterRules($filterRules) {
231
		$ns = '{' . $this::NS_OWNCLOUD . '}';
232
		$resultFileIds = null;
233
		$systemTagIds = [];
234
		$favoriteFilter = null;
235
		foreach ($filterRules as $filterRule) {
236
			if ($filterRule['name'] === $ns . 'systemtag') {
237
				$systemTagIds[] = $filterRule['value'];
238
			}
239
			if ($filterRule['name'] === $ns . 'favorite') {
240
				$favoriteFilter = true;
241
			}
242
		}
243
244
		if ($favoriteFilter !== null) {
245
			$resultFileIds = $this->fileTagger->load('files')->getFavorites();
246
			if (empty($resultFileIds)) {
247
				return [];
248
			}
249
		}
250
251
		if (!empty($systemTagIds)) {
252
			$fileIds = $this->getSystemTagFileIds($systemTagIds);
253
			if (empty($resultFileIds)) {
254
				$resultFileIds = $fileIds;
255
			} else {
256
				$resultFileIds = array_intersect($fileIds, $resultFileIds);
257
			}
258
		}
259
260
		return $resultFileIds;
261
	}
262
263
	private function getSystemTagFileIds($systemTagIds) {
264
		$resultFileIds = null;
265
266
		// check user permissions, if applicable
267
		if (!$this->isAdmin()) {
268
			// check visibility/permission
269
			$tags = $this->tagManager->getTagsByIds($systemTagIds);
270
			$unknownTagIds = [];
271
			foreach ($tags as $tag) {
272
				if (!$tag->isUserVisible()) {
273
					$unknownTagIds[] = $tag->getId();
274
				}
275
			}
276
277
			if (!empty($unknownTagIds)) {
278
				throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found');
279
			}
280
		}
281
282
		// fetch all file ids and intersect them
283
		foreach ($systemTagIds as $systemTagId) {
284
			$fileIds = $this->tagMapper->getObjectIdsForTags($systemTagId, 'files');
285
286
			if (empty($fileIds)) {
287
				// This tag has no files, nothing can ever show up
288
				return [];
289
			}
290
291
			// first run ?
292
			if ($resultFileIds === null) {
293
				$resultFileIds = $fileIds;
294
			} else {
295
				$resultFileIds = array_intersect($resultFileIds, $fileIds);
296
			}
297
298
			if (empty($resultFileIds)) {
299
				// Empty intersection, nothing can show up anymore
300
				return [];
301
			}
302
		}
303
		return $resultFileIds;
304
	}
305
306
	/**
307
	 * Prepare propfind response for the given nodes
308
	 *
309
	 * @param string[] $requestedProps requested properties
310
	 * @param Node[] nodes nodes for which to fetch and prepare responses
311
	 * @return Response[]
312
	 */
313
	public function prepareResponses($requestedProps, $nodes) {
314
		$responses = [];
315
		foreach ($nodes as $node) {
316
			$propFind = new PropFind($node->getPath(), $requestedProps);
317
318
			$this->server->getPropertiesByNode($propFind, $node);
319
			// copied from Sabre Server's getPropertiesForPath
320
			$result = $propFind->getResultForMultiStatus();
321
			$result['href'] = $propFind->getPath();
322
323
			$resourceType = $this->server->getResourceTypeForNode($node);
324
			if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) {
325
				$result['href'] .= '/';
326
			}
327
328
			$responses[] = new Response(
329
				rtrim($this->server->getBaseUri(), '/') . $node->getPath(),
330
				$result,
331
				200
332
			);
333
		}
334
		return $responses;
335
	}
336
337
	/**
338
	 * Find Sabre nodes by file ids
339
	 *
340
	 * @param Node $rootNode root node for search
341
	 * @param array $fileIds file ids
342
	 * @return Node[] array of Sabre nodes
343
	 */
344
	public function findNodesByFileIds($rootNode, $fileIds) {
345
		$folder = $this->userFolder;
346
		if (trim($rootNode->getPath(), '/') !== '') {
347
			$folder = $folder->get($rootNode->getPath());
348
		}
349
350
		$results = [];
351
		foreach ($fileIds as $fileId) {
352
			$entry = $folder->getById($fileId);
353
			if ($entry) {
354
				$entry = current($entry);
355
				if ($entry instanceof \OCP\Files\File) {
356
					$results[] = new File($this->fileView, $entry);
357
				} else if ($entry instanceof \OCP\Files\Folder) {
358
					$results[] = new Directory($this->fileView, $entry);
359
				}
360
			}
361
		}
362
363
		return $results;
364
	}
365
366
	/**
367
	 * Returns whether the currently logged in user is an administrator
368
	 */
369 View Code Duplication
	private function isAdmin() {
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...
370
		$user = $this->userSession->getUser();
371
		if ($user !== null) {
372
			return $this->groupManager->isAdmin($user->getUID());
373
		}
374
		return false;
375
	}
376
}
377