Completed
Pull Request — master (#4530)
by Robin
24:41 queued 46s
created
apps/dav/lib/Files/Xml/FilterRequest.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -45,7 +45,7 @@
 block discarded – undo
45 45
 	 * the next element.
46 46
 	 *
47 47
 	 * @param Reader $reader
48
-	 * @return mixed
48
+	 * @return FilterRequest
49 49
 	 */
50 50
 	static function xmlDeserialize(Reader $reader) {
51 51
 		$elems = (array)$reader->parseInnerTree([
Please login to merge, or discard this patch.
Indentation   +87 added lines, -87 removed lines patch added patch discarded remove patch
@@ -9,102 +9,102 @@
 block discarded – undo
9 9
 
10 10
 class FilterRequest implements XmlDeserializable {
11 11
 
12
-	/**
13
-	 * An array with requested properties.
14
-	 *
15
-	 * @var array
16
-	 */
17
-	public $properties;
12
+    /**
13
+     * An array with requested properties.
14
+     *
15
+     * @var array
16
+     */
17
+    public $properties;
18 18
 
19
-	/**
20
-	 * @var array
21
-	 */
22
-	public $filters;
19
+    /**
20
+     * @var array
21
+     */
22
+    public $filters;
23 23
 
24
-	/**
25
-	 * @var array
26
-	 */
27
-	public $search;
24
+    /**
25
+     * @var array
26
+     */
27
+    public $search;
28 28
 
29
-	/**
30
-	 * The deserialize method is called during xml parsing.
31
-	 *
32
-	 * This method is called statically, this is because in theory this method
33
-	 * may be used as a type of constructor, or factory method.
34
-	 *
35
-	 * Often you want to return an instance of the current class, but you are
36
-	 * free to return other data as well.
37
-	 *
38
-	 * You are responsible for advancing the reader to the next element. Not
39
-	 * doing anything will result in a never-ending loop.
40
-	 *
41
-	 * If you just want to skip parsing for this element altogether, you can
42
-	 * just call $reader->next();
43
-	 *
44
-	 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
45
-	 * the next element.
46
-	 *
47
-	 * @param Reader $reader
48
-	 * @return mixed
49
-	 */
50
-	static function xmlDeserialize(Reader $reader) {
51
-		$elems = (array)$reader->parseInnerTree([
52
-			'{DAV:}prop' => KeyValue::class,
53
-			'{http://owncloud.org/ns}filter-rules' => Base::class,
54
-			'{http://owncloud.org/ns}search' => KeyValue::class,
55
-		]);
29
+    /**
30
+     * The deserialize method is called during xml parsing.
31
+     *
32
+     * This method is called statically, this is because in theory this method
33
+     * may be used as a type of constructor, or factory method.
34
+     *
35
+     * Often you want to return an instance of the current class, but you are
36
+     * free to return other data as well.
37
+     *
38
+     * You are responsible for advancing the reader to the next element. Not
39
+     * doing anything will result in a never-ending loop.
40
+     *
41
+     * If you just want to skip parsing for this element altogether, you can
42
+     * just call $reader->next();
43
+     *
44
+     * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
45
+     * the next element.
46
+     *
47
+     * @param Reader $reader
48
+     * @return mixed
49
+     */
50
+    static function xmlDeserialize(Reader $reader) {
51
+        $elems = (array)$reader->parseInnerTree([
52
+            '{DAV:}prop' => KeyValue::class,
53
+            '{http://owncloud.org/ns}filter-rules' => Base::class,
54
+            '{http://owncloud.org/ns}search' => KeyValue::class,
55
+        ]);
56 56
 
57
-		$newProps = [
58
-			'filters'    => [
59
-				'systemtag' => [],
60
-				'favorite' => null
61
-			],
62
-			'properties' => [],
63
-			'search' => null,
64
-		];
57
+        $newProps = [
58
+            'filters'    => [
59
+                'systemtag' => [],
60
+                'favorite' => null
61
+            ],
62
+            'properties' => [],
63
+            'search' => null,
64
+        ];
65 65
 
66
-		if (!is_array($elems)) {
67
-			$elems = [];
68
-		}
66
+        if (!is_array($elems)) {
67
+            $elems = [];
68
+        }
69 69
 
70
-		foreach ($elems as $elem) {
70
+        foreach ($elems as $elem) {
71 71
 
72
-			switch ($elem['name']) {
72
+            switch ($elem['name']) {
73 73
 
74
-				case '{DAV:}prop' :
75
-					$newProps['properties'] = array_keys($elem['value']);
76
-					break;
77
-				case '{http://owncloud.org/ns}filter-rules' :
74
+                case '{DAV:}prop' :
75
+                    $newProps['properties'] = array_keys($elem['value']);
76
+                    break;
77
+                case '{http://owncloud.org/ns}filter-rules' :
78 78
 
79
-					foreach ($elem['value'] as $tag) {
80
-						if ($tag['name'] === '{http://owncloud.org/ns}systemtag') {
81
-							$newProps['filters']['systemtag'][] = $tag['value'];
82
-						}
83
-						if ($tag['name'] === '{http://owncloud.org/ns}favorite') {
84
-							$newProps['filters']['favorite'] = true;
85
-						}
86
-					}
87
-					break;
88
-				case '{http://owncloud.org/ns}search' :
89
-					$value = $elem['value'];
90
-					if (isset($value['{http://owncloud.org/ns}pattern'])) {
91
-						$newProps['search']['pattern'] = $value['{http://owncloud.org/ns}pattern'];
92
-					}
93
-					if (isset($value['{http://owncloud.org/ns}limit'])) {
94
-						$newProps['search']['limit'] = (int)$value['{http://owncloud.org/ns}limit'];
95
-					}
96
-					if (isset($value['{http://owncloud.org/ns}offset'])) {
97
-						$newProps['search']['offset'] = (int)$value['{http://owncloud.org/ns}offset'];
98
-					}
99
-					break;
100
-			}
101
-		}
79
+                    foreach ($elem['value'] as $tag) {
80
+                        if ($tag['name'] === '{http://owncloud.org/ns}systemtag') {
81
+                            $newProps['filters']['systemtag'][] = $tag['value'];
82
+                        }
83
+                        if ($tag['name'] === '{http://owncloud.org/ns}favorite') {
84
+                            $newProps['filters']['favorite'] = true;
85
+                        }
86
+                    }
87
+                    break;
88
+                case '{http://owncloud.org/ns}search' :
89
+                    $value = $elem['value'];
90
+                    if (isset($value['{http://owncloud.org/ns}pattern'])) {
91
+                        $newProps['search']['pattern'] = $value['{http://owncloud.org/ns}pattern'];
92
+                    }
93
+                    if (isset($value['{http://owncloud.org/ns}limit'])) {
94
+                        $newProps['search']['limit'] = (int)$value['{http://owncloud.org/ns}limit'];
95
+                    }
96
+                    if (isset($value['{http://owncloud.org/ns}offset'])) {
97
+                        $newProps['search']['offset'] = (int)$value['{http://owncloud.org/ns}offset'];
98
+                    }
99
+                    break;
100
+            }
101
+        }
102 102
 
103
-		$obj = new self();
104
-		foreach ($newProps as $key => $value) {
105
-			$obj->$key = $value;
106
-		}
103
+        $obj = new self();
104
+        foreach ($newProps as $key => $value) {
105
+            $obj->$key = $value;
106
+        }
107 107
 
108
-		return $obj;
109
-	}
108
+        return $obj;
109
+    }
110 110
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -48,7 +48,7 @@  discard block
 block discarded – undo
48 48
 	 * @return mixed
49 49
 	 */
50 50
 	static function xmlDeserialize(Reader $reader) {
51
-		$elems = (array)$reader->parseInnerTree([
51
+		$elems = (array) $reader->parseInnerTree([
52 52
 			'{DAV:}prop' => KeyValue::class,
53 53
 			'{http://owncloud.org/ns}filter-rules' => Base::class,
54 54
 			'{http://owncloud.org/ns}search' => KeyValue::class,
@@ -91,10 +91,10 @@  discard block
 block discarded – undo
91 91
 						$newProps['search']['pattern'] = $value['{http://owncloud.org/ns}pattern'];
92 92
 					}
93 93
 					if (isset($value['{http://owncloud.org/ns}limit'])) {
94
-						$newProps['search']['limit'] = (int)$value['{http://owncloud.org/ns}limit'];
94
+						$newProps['search']['limit'] = (int) $value['{http://owncloud.org/ns}limit'];
95 95
 					}
96 96
 					if (isset($value['{http://owncloud.org/ns}offset'])) {
97
-						$newProps['search']['offset'] = (int)$value['{http://owncloud.org/ns}offset'];
97
+						$newProps['search']['offset'] = (int) $value['{http://owncloud.org/ns}offset'];
98 98
 					}
99 99
 					break;
100 100
 			}
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/FilesReportPlugin.php 2 patches
Indentation   +353 added lines, -353 removed lines patch added patch discarded remove patch
@@ -42,357 +42,357 @@
 block discarded – undo
42 42
 
43 43
 class FilesReportPlugin extends ServerPlugin {
44 44
 
45
-	// namespace
46
-	const NS_OWNCLOUD = 'http://owncloud.org/ns';
47
-	const REPORT_NAME            = '{http://owncloud.org/ns}filter-files';
48
-	const SYSTEMTAG_PROPERTYNAME = '{http://owncloud.org/ns}systemtag';
49
-
50
-	/**
51
-	 * Reference to main server object
52
-	 *
53
-	 * @var \Sabre\DAV\Server
54
-	 */
55
-	private $server;
56
-
57
-	/**
58
-	 * @var Tree
59
-	 */
60
-	private $tree;
61
-
62
-	/**
63
-	 * @var View
64
-	 */
65
-	private $fileView;
66
-
67
-	/**
68
-	 * @var ISystemTagManager
69
-	 */
70
-	private $tagManager;
71
-
72
-	/**
73
-	 * @var ISystemTagObjectMapper
74
-	 */
75
-	private $tagMapper;
76
-
77
-	/**
78
-	 * Manager for private tags
79
-	 *
80
-	 * @var ITagManager
81
-	 */
82
-	private $fileTagger;
83
-
84
-	/**
85
-	 * @var IUserSession
86
-	 */
87
-	private $userSession;
88
-
89
-	/**
90
-	 * @var IGroupManager
91
-	 */
92
-	private $groupManager;
93
-
94
-	/**
95
-	 * @var Folder
96
-	 */
97
-	private $userFolder;
98
-
99
-	/**
100
-	 * @param Tree $tree
101
-	 * @param View $view
102
-	 * @param ISystemTagManager $tagManager
103
-	 * @param ISystemTagObjectMapper $tagMapper
104
-	 * @param ITagManager $fileTagger manager for private tags
105
-	 * @param IUserSession $userSession
106
-	 * @param IGroupManager $groupManager
107
-	 * @param Folder $userFolder
108
-	 */
109
-	public function __construct(Tree $tree,
110
-								View $view,
111
-								ISystemTagManager $tagManager,
112
-								ISystemTagObjectMapper $tagMapper,
113
-								ITagManager $fileTagger,
114
-								IUserSession $userSession,
115
-								IGroupManager $groupManager,
116
-								Folder $userFolder
117
-	) {
118
-		$this->tree = $tree;
119
-		$this->fileView = $view;
120
-		$this->tagManager = $tagManager;
121
-		$this->tagMapper = $tagMapper;
122
-		$this->fileTagger = $fileTagger;
123
-		$this->userSession = $userSession;
124
-		$this->groupManager = $groupManager;
125
-		$this->userFolder = $userFolder;
126
-	}
127
-
128
-	/**
129
-	 * This initializes the plugin.
130
-	 *
131
-	 * This function is called by \Sabre\DAV\Server, after
132
-	 * addPlugin is called.
133
-	 *
134
-	 * This method should set up the required event subscriptions.
135
-	 *
136
-	 * @param \Sabre\DAV\Server $server
137
-	 * @return void
138
-	 */
139
-	public function initialize(\Sabre\DAV\Server $server) {
140
-
141
-		$server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
142
-
143
-		$server->xml->elementMap[self::REPORT_NAME] = FilterRequest::class;
144
-
145
-		$this->server = $server;
146
-		$this->server->on('report', array($this, 'onReport'));
147
-	}
148
-
149
-	/**
150
-	 * Returns a list of reports this plugin supports.
151
-	 *
152
-	 * This will be used in the {DAV:}supported-report-set property.
153
-	 *
154
-	 * @param string $uri
155
-	 * @return array
156
-	 */
157
-	public function getSupportedReportSet($uri) {
158
-		return [self::REPORT_NAME];
159
-	}
160
-
161
-	/**
162
-	 * REPORT operations to look for files
163
-	 *
164
-	 * @param string $reportName
165
-	 * @param mixed $report
166
-	 * @param string $uri
167
-	 * @return bool
168
-	 * @throws BadRequest
169
-	 * @throws PreconditionFailed
170
-	 * @internal param $ [] $report
171
-	 */
172
-	public function onReport($reportName, $report, $uri) {
173
-
174
-		$reportTargetNode = $this->server->tree->getNodeForPath($uri);
175
-		if (!$reportTargetNode instanceof Directory || $reportName !== self::REPORT_NAME) {
176
-			return;
177
-		}
178
-
179
-		$requestedProps = $report->properties;
180
-		$filterRules = $report->filters;
181
-
182
-		// "systemtag" is always an array of tags, favorite a string/int/null
183
-		if (empty($filterRules['systemtag']) && is_null($filterRules['favorite'])) {
184
-			// FIXME: search currently not possible because results are missing properties!
185
-			throw new BadRequest('No filter criteria specified');
186
-		} else {
187
-			if (isset($report->search['pattern'])) {
188
-				// TODO: implement this at some point...
189
-				throw new BadRequest('Search pattern cannot be combined with filter');
190
-			}
191
-
192
-			// gather all file ids matching filter
193
-			try {
194
-				$resultFileIds = $this->processFilterRules($filterRules);
195
-			} catch (TagNotFoundException $e) {
196
-				throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e);
197
-			}
198
-
199
-			// pre-slice the results if needed for pagination to not waste
200
-			// time resolving nodes that will not be returned anyway
201
-			$resultFileIds = $this->slice($resultFileIds, $report);
202
-
203
-			// find sabre nodes by file id, restricted to the root node path
204
-			$results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds);
205
-		}
206
-
207
-		$filesUri = $this->getFilesBaseUri($uri, $reportTargetNode->getPath());
208
-		$results = $this->prepareResponses($filesUri, $requestedProps, $results);
209
-
210
-		$xml = $this->server->generateMultiStatus($results);
211
-
212
-		$this->server->httpResponse->setStatus(207);
213
-		$this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
214
-		$this->server->httpResponse->setBody($xml);
215
-
216
-		return false;
217
-	}
218
-
219
-	private function slice($results, $report) {
220
-		if (!is_null($report->search)) {
221
-			$length = $report->search['limit'];
222
-			$offset = $report->search['offset'];
223
-			$results = array_slice($results, $offset, $length);
224
-		}
225
-		return $results;
226
-	}
227
-
228
-	/**
229
-	 * Returns the base uri of the files root by removing
230
-	 * the subpath from the URI
231
-	 *
232
-	 * @param string $uri URI from this request
233
-	 * @param string $subPath subpath to remove from the URI
234
-	 *
235
-	 * @return string files base uri
236
-	 */
237
-	private function getFilesBaseUri($uri, $subPath) {
238
-		$uri = trim($uri, '/');
239
-		$subPath = trim($subPath, '/');
240
-		if (empty($subPath)) {
241
-			$filesUri = $uri;
242
-		} else {
243
-			$filesUri = substr($uri, 0, strlen($uri) - strlen($subPath));
244
-		}
245
-		$filesUri = trim($filesUri, '/');
246
-		if (empty($filesUri)) {
247
-			return '';
248
-		}
249
-		return '/' . $filesUri;
250
-	}
251
-
252
-	/**
253
-	 * Find file ids matching the given filter rules
254
-	 *
255
-	 * @param array $filterRules
256
-	 * @return array array of unique file id results
257
-	 *
258
-	 * @throws TagNotFoundException whenever a tag was not found
259
-	 */
260
-	protected function processFilterRules($filterRules) {
261
-		$resultFileIds = null;
262
-		$systemTagIds = $filterRules['systemtag'];
263
-		$favoriteFilter = $filterRules['favorite'];
264
-
265
-		if ($favoriteFilter !== null) {
266
-			$resultFileIds = $this->fileTagger->load('files')->getFavorites();
267
-			if (empty($resultFileIds)) {
268
-				return [];
269
-			}
270
-		}
271
-
272
-		if (!empty($systemTagIds)) {
273
-			$fileIds = $this->getSystemTagFileIds($systemTagIds);
274
-			if (empty($resultFileIds)) {
275
-				$resultFileIds = $fileIds;
276
-			} else {
277
-				$resultFileIds = array_intersect($fileIds, $resultFileIds);
278
-			}
279
-		}
280
-
281
-		return $resultFileIds;
282
-	}
283
-
284
-	private function getSystemTagFileIds($systemTagIds) {
285
-		$resultFileIds = null;
286
-
287
-		// check user permissions, if applicable
288
-		if (!$this->isAdmin()) {
289
-			// check visibility/permission
290
-			$tags = $this->tagManager->getTagsByIds($systemTagIds);
291
-			$unknownTagIds = [];
292
-			foreach ($tags as $tag) {
293
-				if (!$tag->isUserVisible()) {
294
-					$unknownTagIds[] = $tag->getId();
295
-				}
296
-			}
297
-
298
-			if (!empty($unknownTagIds)) {
299
-				throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found');
300
-			}
301
-		}
302
-
303
-		// fetch all file ids and intersect them
304
-		foreach ($systemTagIds as $systemTagId) {
305
-			$fileIds = $this->tagMapper->getObjectIdsForTags($systemTagId, 'files');
306
-
307
-			if (empty($fileIds)) {
308
-				// This tag has no files, nothing can ever show up
309
-				return [];
310
-			}
311
-
312
-			// first run ?
313
-			if ($resultFileIds === null) {
314
-				$resultFileIds = $fileIds;
315
-			} else {
316
-				$resultFileIds = array_intersect($resultFileIds, $fileIds);
317
-			}
318
-
319
-			if (empty($resultFileIds)) {
320
-				// Empty intersection, nothing can show up anymore
321
-				return [];
322
-			}
323
-		}
324
-		return $resultFileIds;
325
-	}
326
-
327
-	/**
328
-	 * Prepare propfind response for the given nodes
329
-	 *
330
-	 * @param string $filesUri $filesUri URI leading to root of the files URI,
331
-	 * with a leading slash but no trailing slash
332
-	 * @param string[] $requestedProps requested properties
333
-	 * @param Node[] nodes nodes for which to fetch and prepare responses
334
-	 * @return Response[]
335
-	 */
336
-	public function prepareResponses($filesUri, $requestedProps, $nodes) {
337
-		$results = [];
338
-		foreach ($nodes as $node) {
339
-			$propFind = new PropFind($filesUri . $node->getPath(), $requestedProps);
340
-
341
-			$this->server->getPropertiesByNode($propFind, $node);
342
-			// copied from Sabre Server's getPropertiesForPath
343
-			$result = $propFind->getResultForMultiStatus();
344
-			$result['href'] = $propFind->getPath();
345
-
346
-			$results[] = $result;
347
-		}
348
-		return $results;
349
-	}
350
-
351
-	/**
352
-	 * Find Sabre nodes by file ids
353
-	 *
354
-	 * @param Node $rootNode root node for search
355
-	 * @param array $fileIds file ids
356
-	 * @return Node[] array of Sabre nodes
357
-	 */
358
-	public function findNodesByFileIds($rootNode, $fileIds) {
359
-		$folder = $this->userFolder;
360
-		if (trim($rootNode->getPath(), '/') !== '') {
361
-			$folder = $folder->get($rootNode->getPath());
362
-		}
363
-
364
-		$results = [];
365
-		foreach ($fileIds as $fileId) {
366
-			$entry = $folder->getById($fileId);
367
-			if ($entry) {
368
-				$entry = current($entry);
369
-				$node = $this->makeSabreNode($entry);
370
-				if ($node) {
371
-					$results[] = $node;
372
-				}
373
-			}
374
-		}
375
-
376
-		return $results;
377
-	}
378
-
379
-	private function makeSabreNode(\OCP\Files\Node $filesNode) {
380
-		if ($filesNode instanceof \OCP\Files\File) {
381
-			return new File($this->fileView, $filesNode);
382
-		} else if ($filesNode instanceof \OCP\Files\Folder) {
383
-			return new Directory($this->fileView, $filesNode);
384
-		}
385
-		throw new \Exception('Unrecognized Files API node returned, aborting');
386
-	}
387
-
388
-	/**
389
-	 * Returns whether the currently logged in user is an administrator
390
-	 */
391
-	private function isAdmin() {
392
-		$user = $this->userSession->getUser();
393
-		if ($user !== null) {
394
-			return $this->groupManager->isAdmin($user->getUID());
395
-		}
396
-		return false;
397
-	}
45
+    // namespace
46
+    const NS_OWNCLOUD = 'http://owncloud.org/ns';
47
+    const REPORT_NAME            = '{http://owncloud.org/ns}filter-files';
48
+    const SYSTEMTAG_PROPERTYNAME = '{http://owncloud.org/ns}systemtag';
49
+
50
+    /**
51
+     * Reference to main server object
52
+     *
53
+     * @var \Sabre\DAV\Server
54
+     */
55
+    private $server;
56
+
57
+    /**
58
+     * @var Tree
59
+     */
60
+    private $tree;
61
+
62
+    /**
63
+     * @var View
64
+     */
65
+    private $fileView;
66
+
67
+    /**
68
+     * @var ISystemTagManager
69
+     */
70
+    private $tagManager;
71
+
72
+    /**
73
+     * @var ISystemTagObjectMapper
74
+     */
75
+    private $tagMapper;
76
+
77
+    /**
78
+     * Manager for private tags
79
+     *
80
+     * @var ITagManager
81
+     */
82
+    private $fileTagger;
83
+
84
+    /**
85
+     * @var IUserSession
86
+     */
87
+    private $userSession;
88
+
89
+    /**
90
+     * @var IGroupManager
91
+     */
92
+    private $groupManager;
93
+
94
+    /**
95
+     * @var Folder
96
+     */
97
+    private $userFolder;
98
+
99
+    /**
100
+     * @param Tree $tree
101
+     * @param View $view
102
+     * @param ISystemTagManager $tagManager
103
+     * @param ISystemTagObjectMapper $tagMapper
104
+     * @param ITagManager $fileTagger manager for private tags
105
+     * @param IUserSession $userSession
106
+     * @param IGroupManager $groupManager
107
+     * @param Folder $userFolder
108
+     */
109
+    public function __construct(Tree $tree,
110
+                                View $view,
111
+                                ISystemTagManager $tagManager,
112
+                                ISystemTagObjectMapper $tagMapper,
113
+                                ITagManager $fileTagger,
114
+                                IUserSession $userSession,
115
+                                IGroupManager $groupManager,
116
+                                Folder $userFolder
117
+    ) {
118
+        $this->tree = $tree;
119
+        $this->fileView = $view;
120
+        $this->tagManager = $tagManager;
121
+        $this->tagMapper = $tagMapper;
122
+        $this->fileTagger = $fileTagger;
123
+        $this->userSession = $userSession;
124
+        $this->groupManager = $groupManager;
125
+        $this->userFolder = $userFolder;
126
+    }
127
+
128
+    /**
129
+     * This initializes the plugin.
130
+     *
131
+     * This function is called by \Sabre\DAV\Server, after
132
+     * addPlugin is called.
133
+     *
134
+     * This method should set up the required event subscriptions.
135
+     *
136
+     * @param \Sabre\DAV\Server $server
137
+     * @return void
138
+     */
139
+    public function initialize(\Sabre\DAV\Server $server) {
140
+
141
+        $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
142
+
143
+        $server->xml->elementMap[self::REPORT_NAME] = FilterRequest::class;
144
+
145
+        $this->server = $server;
146
+        $this->server->on('report', array($this, 'onReport'));
147
+    }
148
+
149
+    /**
150
+     * Returns a list of reports this plugin supports.
151
+     *
152
+     * This will be used in the {DAV:}supported-report-set property.
153
+     *
154
+     * @param string $uri
155
+     * @return array
156
+     */
157
+    public function getSupportedReportSet($uri) {
158
+        return [self::REPORT_NAME];
159
+    }
160
+
161
+    /**
162
+     * REPORT operations to look for files
163
+     *
164
+     * @param string $reportName
165
+     * @param mixed $report
166
+     * @param string $uri
167
+     * @return bool
168
+     * @throws BadRequest
169
+     * @throws PreconditionFailed
170
+     * @internal param $ [] $report
171
+     */
172
+    public function onReport($reportName, $report, $uri) {
173
+
174
+        $reportTargetNode = $this->server->tree->getNodeForPath($uri);
175
+        if (!$reportTargetNode instanceof Directory || $reportName !== self::REPORT_NAME) {
176
+            return;
177
+        }
178
+
179
+        $requestedProps = $report->properties;
180
+        $filterRules = $report->filters;
181
+
182
+        // "systemtag" is always an array of tags, favorite a string/int/null
183
+        if (empty($filterRules['systemtag']) && is_null($filterRules['favorite'])) {
184
+            // FIXME: search currently not possible because results are missing properties!
185
+            throw new BadRequest('No filter criteria specified');
186
+        } else {
187
+            if (isset($report->search['pattern'])) {
188
+                // TODO: implement this at some point...
189
+                throw new BadRequest('Search pattern cannot be combined with filter');
190
+            }
191
+
192
+            // gather all file ids matching filter
193
+            try {
194
+                $resultFileIds = $this->processFilterRules($filterRules);
195
+            } catch (TagNotFoundException $e) {
196
+                throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e);
197
+            }
198
+
199
+            // pre-slice the results if needed for pagination to not waste
200
+            // time resolving nodes that will not be returned anyway
201
+            $resultFileIds = $this->slice($resultFileIds, $report);
202
+
203
+            // find sabre nodes by file id, restricted to the root node path
204
+            $results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds);
205
+        }
206
+
207
+        $filesUri = $this->getFilesBaseUri($uri, $reportTargetNode->getPath());
208
+        $results = $this->prepareResponses($filesUri, $requestedProps, $results);
209
+
210
+        $xml = $this->server->generateMultiStatus($results);
211
+
212
+        $this->server->httpResponse->setStatus(207);
213
+        $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
214
+        $this->server->httpResponse->setBody($xml);
215
+
216
+        return false;
217
+    }
218
+
219
+    private function slice($results, $report) {
220
+        if (!is_null($report->search)) {
221
+            $length = $report->search['limit'];
222
+            $offset = $report->search['offset'];
223
+            $results = array_slice($results, $offset, $length);
224
+        }
225
+        return $results;
226
+    }
227
+
228
+    /**
229
+     * Returns the base uri of the files root by removing
230
+     * the subpath from the URI
231
+     *
232
+     * @param string $uri URI from this request
233
+     * @param string $subPath subpath to remove from the URI
234
+     *
235
+     * @return string files base uri
236
+     */
237
+    private function getFilesBaseUri($uri, $subPath) {
238
+        $uri = trim($uri, '/');
239
+        $subPath = trim($subPath, '/');
240
+        if (empty($subPath)) {
241
+            $filesUri = $uri;
242
+        } else {
243
+            $filesUri = substr($uri, 0, strlen($uri) - strlen($subPath));
244
+        }
245
+        $filesUri = trim($filesUri, '/');
246
+        if (empty($filesUri)) {
247
+            return '';
248
+        }
249
+        return '/' . $filesUri;
250
+    }
251
+
252
+    /**
253
+     * Find file ids matching the given filter rules
254
+     *
255
+     * @param array $filterRules
256
+     * @return array array of unique file id results
257
+     *
258
+     * @throws TagNotFoundException whenever a tag was not found
259
+     */
260
+    protected function processFilterRules($filterRules) {
261
+        $resultFileIds = null;
262
+        $systemTagIds = $filterRules['systemtag'];
263
+        $favoriteFilter = $filterRules['favorite'];
264
+
265
+        if ($favoriteFilter !== null) {
266
+            $resultFileIds = $this->fileTagger->load('files')->getFavorites();
267
+            if (empty($resultFileIds)) {
268
+                return [];
269
+            }
270
+        }
271
+
272
+        if (!empty($systemTagIds)) {
273
+            $fileIds = $this->getSystemTagFileIds($systemTagIds);
274
+            if (empty($resultFileIds)) {
275
+                $resultFileIds = $fileIds;
276
+            } else {
277
+                $resultFileIds = array_intersect($fileIds, $resultFileIds);
278
+            }
279
+        }
280
+
281
+        return $resultFileIds;
282
+    }
283
+
284
+    private function getSystemTagFileIds($systemTagIds) {
285
+        $resultFileIds = null;
286
+
287
+        // check user permissions, if applicable
288
+        if (!$this->isAdmin()) {
289
+            // check visibility/permission
290
+            $tags = $this->tagManager->getTagsByIds($systemTagIds);
291
+            $unknownTagIds = [];
292
+            foreach ($tags as $tag) {
293
+                if (!$tag->isUserVisible()) {
294
+                    $unknownTagIds[] = $tag->getId();
295
+                }
296
+            }
297
+
298
+            if (!empty($unknownTagIds)) {
299
+                throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found');
300
+            }
301
+        }
302
+
303
+        // fetch all file ids and intersect them
304
+        foreach ($systemTagIds as $systemTagId) {
305
+            $fileIds = $this->tagMapper->getObjectIdsForTags($systemTagId, 'files');
306
+
307
+            if (empty($fileIds)) {
308
+                // This tag has no files, nothing can ever show up
309
+                return [];
310
+            }
311
+
312
+            // first run ?
313
+            if ($resultFileIds === null) {
314
+                $resultFileIds = $fileIds;
315
+            } else {
316
+                $resultFileIds = array_intersect($resultFileIds, $fileIds);
317
+            }
318
+
319
+            if (empty($resultFileIds)) {
320
+                // Empty intersection, nothing can show up anymore
321
+                return [];
322
+            }
323
+        }
324
+        return $resultFileIds;
325
+    }
326
+
327
+    /**
328
+     * Prepare propfind response for the given nodes
329
+     *
330
+     * @param string $filesUri $filesUri URI leading to root of the files URI,
331
+     * with a leading slash but no trailing slash
332
+     * @param string[] $requestedProps requested properties
333
+     * @param Node[] nodes nodes for which to fetch and prepare responses
334
+     * @return Response[]
335
+     */
336
+    public function prepareResponses($filesUri, $requestedProps, $nodes) {
337
+        $results = [];
338
+        foreach ($nodes as $node) {
339
+            $propFind = new PropFind($filesUri . $node->getPath(), $requestedProps);
340
+
341
+            $this->server->getPropertiesByNode($propFind, $node);
342
+            // copied from Sabre Server's getPropertiesForPath
343
+            $result = $propFind->getResultForMultiStatus();
344
+            $result['href'] = $propFind->getPath();
345
+
346
+            $results[] = $result;
347
+        }
348
+        return $results;
349
+    }
350
+
351
+    /**
352
+     * Find Sabre nodes by file ids
353
+     *
354
+     * @param Node $rootNode root node for search
355
+     * @param array $fileIds file ids
356
+     * @return Node[] array of Sabre nodes
357
+     */
358
+    public function findNodesByFileIds($rootNode, $fileIds) {
359
+        $folder = $this->userFolder;
360
+        if (trim($rootNode->getPath(), '/') !== '') {
361
+            $folder = $folder->get($rootNode->getPath());
362
+        }
363
+
364
+        $results = [];
365
+        foreach ($fileIds as $fileId) {
366
+            $entry = $folder->getById($fileId);
367
+            if ($entry) {
368
+                $entry = current($entry);
369
+                $node = $this->makeSabreNode($entry);
370
+                if ($node) {
371
+                    $results[] = $node;
372
+                }
373
+            }
374
+        }
375
+
376
+        return $results;
377
+    }
378
+
379
+    private function makeSabreNode(\OCP\Files\Node $filesNode) {
380
+        if ($filesNode instanceof \OCP\Files\File) {
381
+            return new File($this->fileView, $filesNode);
382
+        } else if ($filesNode instanceof \OCP\Files\Folder) {
383
+            return new Directory($this->fileView, $filesNode);
384
+        }
385
+        throw new \Exception('Unrecognized Files API node returned, aborting');
386
+    }
387
+
388
+    /**
389
+     * Returns whether the currently logged in user is an administrator
390
+     */
391
+    private function isAdmin() {
392
+        $user = $this->userSession->getUser();
393
+        if ($user !== null) {
394
+            return $this->groupManager->isAdmin($user->getUID());
395
+        }
396
+        return false;
397
+    }
398 398
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -246,7 +246,7 @@  discard block
 block discarded – undo
246 246
 		if (empty($filesUri)) {
247 247
 			return '';
248 248
 		}
249
-		return '/' . $filesUri;
249
+		return '/'.$filesUri;
250 250
 	}
251 251
 
252 252
 	/**
@@ -296,7 +296,7 @@  discard block
 block discarded – undo
296 296
 			}
297 297
 
298 298
 			if (!empty($unknownTagIds)) {
299
-				throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found');
299
+				throw new TagNotFoundException('Tag with ids '.implode(', ', $unknownTagIds).' not found');
300 300
 			}
301 301
 		}
302 302
 
@@ -336,7 +336,7 @@  discard block
 block discarded – undo
336 336
 	public function prepareResponses($filesUri, $requestedProps, $nodes) {
337 337
 		$results = [];
338 338
 		foreach ($nodes as $node) {
339
-			$propFind = new PropFind($filesUri . $node->getPath(), $requestedProps);
339
+			$propFind = new PropFind($filesUri.$node->getPath(), $requestedProps);
340 340
 
341 341
 			$this->server->getPropertiesByNode($propFind, $node);
342 342
 			// copied from Sabre Server's getPropertiesForPath
Please login to merge, or discard this patch.