Passed
Push — master ( 24a853...a0d9dc )
by Julius
33:51 queued 17:54
created
lib/private/Files/Node/Folder.php 2 patches
Indentation   +453 added lines, -453 removed lines patch added patch discarded remove patch
@@ -50,457 +50,457 @@
 block discarded – undo
50 50
 use OCP\IUserManager;
51 51
 
52 52
 class Folder extends Node implements \OCP\Files\Folder {
53
-	/**
54
-	 * Creates a Folder that represents a non-existing path
55
-	 *
56
-	 * @param string $path path
57
-	 * @return string non-existing node class
58
-	 */
59
-	protected function createNonExistingNode($path) {
60
-		return new NonExistingFolder($this->root, $this->view, $path);
61
-	}
62
-
63
-	/**
64
-	 * @param string $path path relative to the folder
65
-	 * @return string
66
-	 * @throws \OCP\Files\NotPermittedException
67
-	 */
68
-	public function getFullPath($path) {
69
-		if (!$this->isValidPath($path)) {
70
-			throw new NotPermittedException('Invalid path');
71
-		}
72
-		return $this->path . $this->normalizePath($path);
73
-	}
74
-
75
-	/**
76
-	 * @param string $path
77
-	 * @return string|null
78
-	 */
79
-	public function getRelativePath($path) {
80
-		if ($this->path === '' or $this->path === '/') {
81
-			return $this->normalizePath($path);
82
-		}
83
-		if ($path === $this->path) {
84
-			return '/';
85
-		} elseif (strpos($path, $this->path . '/') !== 0) {
86
-			return null;
87
-		} else {
88
-			$path = substr($path, strlen($this->path));
89
-			return $this->normalizePath($path);
90
-		}
91
-	}
92
-
93
-	/**
94
-	 * check if a node is a (grand-)child of the folder
95
-	 *
96
-	 * @param \OC\Files\Node\Node $node
97
-	 * @return bool
98
-	 */
99
-	public function isSubNode($node) {
100
-		return strpos($node->getPath(), $this->path . '/') === 0;
101
-	}
102
-
103
-	/**
104
-	 * get the content of this directory
105
-	 *
106
-	 * @return Node[]
107
-	 * @throws \OCP\Files\NotFoundException
108
-	 */
109
-	public function getDirectoryListing() {
110
-		$folderContent = $this->view->getDirectoryContent($this->path);
111
-
112
-		return array_map(function (FileInfo $info) {
113
-			if ($info->getMimetype() === 'httpd/unix-directory') {
114
-				return new Folder($this->root, $this->view, $info->getPath(), $info);
115
-			} else {
116
-				return new File($this->root, $this->view, $info->getPath(), $info);
117
-			}
118
-		}, $folderContent);
119
-	}
120
-
121
-	/**
122
-	 * @param string $path
123
-	 * @param FileInfo $info
124
-	 * @return File|Folder
125
-	 */
126
-	protected function createNode($path, FileInfo $info = null) {
127
-		if (is_null($info)) {
128
-			$isDir = $this->view->is_dir($path);
129
-		} else {
130
-			$isDir = $info->getType() === FileInfo::TYPE_FOLDER;
131
-		}
132
-		if ($isDir) {
133
-			return new Folder($this->root, $this->view, $path, $info);
134
-		} else {
135
-			return new File($this->root, $this->view, $path, $info);
136
-		}
137
-	}
138
-
139
-	/**
140
-	 * Get the node at $path
141
-	 *
142
-	 * @param string $path
143
-	 * @return \OC\Files\Node\Node
144
-	 * @throws \OCP\Files\NotFoundException
145
-	 */
146
-	public function get($path) {
147
-		return $this->root->get($this->getFullPath($path));
148
-	}
149
-
150
-	/**
151
-	 * @param string $path
152
-	 * @return bool
153
-	 */
154
-	public function nodeExists($path) {
155
-		try {
156
-			$this->get($path);
157
-			return true;
158
-		} catch (NotFoundException $e) {
159
-			return false;
160
-		}
161
-	}
162
-
163
-	/**
164
-	 * @param string $path
165
-	 * @return \OC\Files\Node\Folder
166
-	 * @throws \OCP\Files\NotPermittedException
167
-	 */
168
-	public function newFolder($path) {
169
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
170
-			$fullPath = $this->getFullPath($path);
171
-			$nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
172
-			$this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
173
-			if (!$this->view->mkdir($fullPath)) {
174
-				throw new NotPermittedException('Could not create folder');
175
-			}
176
-			$node = new Folder($this->root, $this->view, $fullPath);
177
-			$this->sendHooks(['postWrite', 'postCreate'], [$node]);
178
-			return $node;
179
-		} else {
180
-			throw new NotPermittedException('No create permission for folder');
181
-		}
182
-	}
183
-
184
-	/**
185
-	 * @param string $path
186
-	 * @param string | resource | null $content
187
-	 * @return \OC\Files\Node\File
188
-	 * @throws \OCP\Files\NotPermittedException
189
-	 */
190
-	public function newFile($path, $content = null) {
191
-		if (empty($path)) {
192
-			throw new NotPermittedException('Could not create as provided path is empty');
193
-		}
194
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
195
-			$fullPath = $this->getFullPath($path);
196
-			$nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
197
-			$this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
198
-			if ($content !== null) {
199
-				$result = $this->view->file_put_contents($fullPath, $content);
200
-			} else {
201
-				$result = $this->view->touch($fullPath);
202
-			}
203
-			if ($result === false) {
204
-				throw new NotPermittedException('Could not create path');
205
-			}
206
-			$node = new File($this->root, $this->view, $fullPath);
207
-			$this->sendHooks(['postWrite', 'postCreate'], [$node]);
208
-			return $node;
209
-		}
210
-		throw new NotPermittedException('No create permission for path');
211
-	}
212
-
213
-	private function queryFromOperator(ISearchOperator $operator, string $uid = null): ISearchQuery {
214
-		if ($uid === null) {
215
-			$user = null;
216
-		} else {
217
-			/** @var IUserManager $userManager */
218
-			$userManager = \OC::$server->query(IUserManager::class);
219
-			$user = $userManager->get($uid);
220
-		}
221
-		return new SearchQuery($operator, 0, 0, [], $user);
222
-	}
223
-
224
-	/**
225
-	 * search for files with the name matching $query
226
-	 *
227
-	 * @param string|ISearchQuery $query
228
-	 * @return \OC\Files\Node\Node[]
229
-	 */
230
-	public function search($query) {
231
-		if (is_string($query)) {
232
-			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%' . $query . '%'));
233
-		}
234
-
235
-		// search is handled by a single query covering all caches that this folder contains
236
-		// this is done by collect
237
-
238
-		$limitToHome = $query->limitToHome();
239
-		if ($limitToHome && count(explode('/', $this->path)) !== 3) {
240
-			throw new \InvalidArgumentException('searching by owner is only allows on the users home folder');
241
-		}
242
-
243
-		$rootLength = strlen($this->path);
244
-		$mount = $this->root->getMount($this->path);
245
-		$storage = $mount->getStorage();
246
-		$internalPath = $mount->getInternalPath($this->path);
247
-
248
-		// collect all caches for this folder, indexed by their mountpoint relative to this folder
249
-		// and save the mount which is needed later to construct the FileInfo objects
250
-
251
-		if ($internalPath !== '') {
252
-			// a temporary CacheJail is used to handle filtering down the results to within this folder
253
-			$caches = ['' => new CacheJail($storage->getCache(''), $internalPath)];
254
-		} else {
255
-			$caches = ['' => $storage->getCache('')];
256
-		}
257
-		$mountByMountPoint = ['' => $mount];
258
-
259
-		if (!$limitToHome) {
260
-			$mounts = $this->root->getMountsIn($this->path);
261
-			foreach ($mounts as $mount) {
262
-				$storage = $mount->getStorage();
263
-				if ($storage) {
264
-					$relativeMountPoint = ltrim(substr($mount->getMountPoint(), $rootLength), '/');
265
-					$caches[$relativeMountPoint] = $storage->getCache('');
266
-					$mountByMountPoint[$relativeMountPoint] = $mount;
267
-				}
268
-			}
269
-		}
270
-
271
-		/** @var QuerySearchHelper $searchHelper */
272
-		$searchHelper = \OC::$server->get(QuerySearchHelper::class);
273
-		$resultsPerCache = $searchHelper->searchInCaches($query, $caches);
274
-
275
-		// loop trough all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
276
-		$files = array_merge(...array_map(function (array $results, $relativeMountPoint) use ($mountByMountPoint) {
277
-			$mount = $mountByMountPoint[$relativeMountPoint];
278
-			return array_map(function (ICacheEntry $result) use ($relativeMountPoint, $mount) {
279
-				return $this->cacheEntryToFileInfo($mount, $relativeMountPoint, $result);
280
-			}, $results);
281
-		}, array_values($resultsPerCache), array_keys($resultsPerCache)));
282
-
283
-		// don't include this folder in the results
284
-		$files = array_filter($files, function (FileInfo $file) {
285
-			return $file->getPath() !== $this->getPath();
286
-		});
287
-
288
-		// since results were returned per-cache, they are no longer fully sorted
289
-		$order = $query->getOrder();
290
-		if ($order) {
291
-			usort($files, function (FileInfo $a, FileInfo $b) use ($order) {
292
-				foreach ($order as $orderField) {
293
-					$cmp = $orderField->sortFileInfo($a, $b);
294
-					if ($cmp !== 0) {
295
-						return $cmp;
296
-					}
297
-				}
298
-				return 0;
299
-			});
300
-		}
301
-
302
-		return array_map(function (FileInfo $file) {
303
-			return $this->createNode($file->getPath(), $file);
304
-		}, $files);
305
-	}
306
-
307
-	private function cacheEntryToFileInfo(IMountPoint $mount, string $appendRoot, ICacheEntry $cacheEntry): FileInfo {
308
-		$cacheEntry['internalPath'] = $cacheEntry['path'];
309
-		$cacheEntry['path'] = $appendRoot . $cacheEntry->getPath();
310
-		$subPath = $cacheEntry['path'] !== '' ? '/' . $cacheEntry['path'] : '';
311
-		return new \OC\Files\FileInfo($this->path . $subPath, $mount->getStorage(), $cacheEntry['internalPath'], $cacheEntry, $mount);
312
-	}
313
-
314
-	/**
315
-	 * search for files by mimetype
316
-	 *
317
-	 * @param string $mimetype
318
-	 * @return Node[]
319
-	 */
320
-	public function searchByMime($mimetype) {
321
-		if (strpos($mimetype, '/') === false) {
322
-			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%'));
323
-		} else {
324
-			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $mimetype));
325
-		}
326
-		return $this->search($query);
327
-	}
328
-
329
-	/**
330
-	 * search for files by tag
331
-	 *
332
-	 * @param string|int $tag name or tag id
333
-	 * @param string $userId owner of the tags
334
-	 * @return Node[]
335
-	 */
336
-	public function searchByTag($tag, $userId) {
337
-		$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'tagname', $tag), $userId);
338
-		return $this->search($query);
339
-	}
340
-
341
-	/**
342
-	 * @param int $id
343
-	 * @return \OC\Files\Node\Node[]
344
-	 */
345
-	public function getById($id) {
346
-		$mountCache = $this->root->getUserMountCache();
347
-		if (strpos($this->getPath(), '/', 1) > 0) {
348
-			[, $user] = explode('/', $this->getPath());
349
-		} else {
350
-			$user = null;
351
-		}
352
-		$mountsContainingFile = $mountCache->getMountsForFileId((int)$id, $user);
353
-		$mounts = $this->root->getMountsIn($this->path);
354
-		$mounts[] = $this->root->getMount($this->path);
355
-		/** @var IMountPoint[] $folderMounts */
356
-		$folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
357
-			return $mountPoint->getMountPoint();
358
-		}, $mounts), $mounts);
359
-
360
-		/** @var ICachedMountInfo[] $mountsContainingFile */
361
-		$mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
362
-			return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
363
-		}));
364
-
365
-		if (count($mountsContainingFile) === 0) {
366
-			if ($user === $this->getAppDataDirectoryName()) {
367
-				return $this->getByIdInRootMount((int)$id);
368
-			}
369
-			return [];
370
-		}
371
-
372
-		$nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($folderMounts, $id) {
373
-			$mount = $folderMounts[$cachedMountInfo->getMountPoint()];
374
-			$cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
375
-			if (!$cacheEntry) {
376
-				return null;
377
-			}
378
-
379
-			// cache jails will hide the "true" internal path
380
-			$internalPath = ltrim($cachedMountInfo->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
381
-			$pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
382
-			$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
383
-			$absolutePath = rtrim($cachedMountInfo->getMountPoint() . $pathRelativeToMount, '/');
384
-			return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
385
-				$absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
386
-				\OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
387
-			));
388
-		}, $mountsContainingFile);
389
-
390
-		$nodes = array_filter($nodes);
391
-
392
-		return array_filter($nodes, function (Node $node) {
393
-			return $this->getRelativePath($node->getPath());
394
-		});
395
-	}
396
-
397
-	protected function getAppDataDirectoryName(): string {
398
-		$instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid');
399
-		return 'appdata_' . $instanceId;
400
-	}
401
-
402
-	/**
403
-	 * In case the path we are currently in is inside the appdata_* folder,
404
-	 * the original getById method does not work, because it can only look inside
405
-	 * the user's mount points. But the user has no mount point for the root storage.
406
-	 *
407
-	 * So in that case we directly check the mount of the root if it contains
408
-	 * the id. If it does we check if the path is inside the path we are working
409
-	 * in.
410
-	 *
411
-	 * @param int $id
412
-	 * @return array
413
-	 */
414
-	protected function getByIdInRootMount(int $id): array {
415
-		$mount = $this->root->getMount('');
416
-		$cacheEntry = $mount->getStorage()->getCache($this->path)->get($id);
417
-		if (!$cacheEntry) {
418
-			return [];
419
-		}
420
-
421
-		$absolutePath = '/' . ltrim($cacheEntry->getPath(), '/');
422
-		$currentPath = rtrim($this->path, '/') . '/';
423
-
424
-		if (strpos($absolutePath, $currentPath) !== 0) {
425
-			return [];
426
-		}
427
-
428
-		return [$this->root->createNode(
429
-			$absolutePath, new \OC\Files\FileInfo(
430
-			$absolutePath,
431
-			$mount->getStorage(),
432
-			$cacheEntry->getPath(),
433
-			$cacheEntry,
434
-			$mount
435
-		))];
436
-	}
437
-
438
-	public function getFreeSpace() {
439
-		return $this->view->free_space($this->path);
440
-	}
441
-
442
-	public function delete() {
443
-		if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
444
-			$this->sendHooks(['preDelete']);
445
-			$fileInfo = $this->getFileInfo();
446
-			$this->view->rmdir($this->path);
447
-			$nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
448
-			$this->sendHooks(['postDelete'], [$nonExisting]);
449
-			$this->exists = false;
450
-		} else {
451
-			throw new NotPermittedException('No delete permission for path');
452
-		}
453
-	}
454
-
455
-	/**
456
-	 * Add a suffix to the name in case the file exists
457
-	 *
458
-	 * @param string $name
459
-	 * @return string
460
-	 * @throws NotPermittedException
461
-	 */
462
-	public function getNonExistingName($name) {
463
-		$uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view);
464
-		return trim($this->getRelativePath($uniqueName), '/');
465
-	}
466
-
467
-	/**
468
-	 * @param int $limit
469
-	 * @param int $offset
470
-	 * @return \OCP\Files\Node[]
471
-	 */
472
-	public function getRecent($limit, $offset = 0) {
473
-		$query = new SearchQuery(
474
-			new SearchBinaryOperator(
475
-				// filter out non empty folders
476
-				ISearchBinaryOperator::OPERATOR_OR,
477
-				[
478
-					new SearchBinaryOperator(
479
-						ISearchBinaryOperator::OPERATOR_NOT,
480
-						[
481
-							new SearchComparison(
482
-								ISearchComparison::COMPARE_EQUAL,
483
-								'mimetype',
484
-								FileInfo::MIMETYPE_FOLDER
485
-							),
486
-						]
487
-					),
488
-					new SearchComparison(
489
-						ISearchComparison::COMPARE_EQUAL,
490
-						'size',
491
-						0
492
-					),
493
-				]
494
-			),
495
-			$limit,
496
-			$offset,
497
-			[
498
-				new SearchOrder(
499
-					ISearchOrder::DIRECTION_DESCENDING,
500
-					'mtime'
501
-				),
502
-			]
503
-		);
504
-		return $this->search($query);
505
-	}
53
+    /**
54
+     * Creates a Folder that represents a non-existing path
55
+     *
56
+     * @param string $path path
57
+     * @return string non-existing node class
58
+     */
59
+    protected function createNonExistingNode($path) {
60
+        return new NonExistingFolder($this->root, $this->view, $path);
61
+    }
62
+
63
+    /**
64
+     * @param string $path path relative to the folder
65
+     * @return string
66
+     * @throws \OCP\Files\NotPermittedException
67
+     */
68
+    public function getFullPath($path) {
69
+        if (!$this->isValidPath($path)) {
70
+            throw new NotPermittedException('Invalid path');
71
+        }
72
+        return $this->path . $this->normalizePath($path);
73
+    }
74
+
75
+    /**
76
+     * @param string $path
77
+     * @return string|null
78
+     */
79
+    public function getRelativePath($path) {
80
+        if ($this->path === '' or $this->path === '/') {
81
+            return $this->normalizePath($path);
82
+        }
83
+        if ($path === $this->path) {
84
+            return '/';
85
+        } elseif (strpos($path, $this->path . '/') !== 0) {
86
+            return null;
87
+        } else {
88
+            $path = substr($path, strlen($this->path));
89
+            return $this->normalizePath($path);
90
+        }
91
+    }
92
+
93
+    /**
94
+     * check if a node is a (grand-)child of the folder
95
+     *
96
+     * @param \OC\Files\Node\Node $node
97
+     * @return bool
98
+     */
99
+    public function isSubNode($node) {
100
+        return strpos($node->getPath(), $this->path . '/') === 0;
101
+    }
102
+
103
+    /**
104
+     * get the content of this directory
105
+     *
106
+     * @return Node[]
107
+     * @throws \OCP\Files\NotFoundException
108
+     */
109
+    public function getDirectoryListing() {
110
+        $folderContent = $this->view->getDirectoryContent($this->path);
111
+
112
+        return array_map(function (FileInfo $info) {
113
+            if ($info->getMimetype() === 'httpd/unix-directory') {
114
+                return new Folder($this->root, $this->view, $info->getPath(), $info);
115
+            } else {
116
+                return new File($this->root, $this->view, $info->getPath(), $info);
117
+            }
118
+        }, $folderContent);
119
+    }
120
+
121
+    /**
122
+     * @param string $path
123
+     * @param FileInfo $info
124
+     * @return File|Folder
125
+     */
126
+    protected function createNode($path, FileInfo $info = null) {
127
+        if (is_null($info)) {
128
+            $isDir = $this->view->is_dir($path);
129
+        } else {
130
+            $isDir = $info->getType() === FileInfo::TYPE_FOLDER;
131
+        }
132
+        if ($isDir) {
133
+            return new Folder($this->root, $this->view, $path, $info);
134
+        } else {
135
+            return new File($this->root, $this->view, $path, $info);
136
+        }
137
+    }
138
+
139
+    /**
140
+     * Get the node at $path
141
+     *
142
+     * @param string $path
143
+     * @return \OC\Files\Node\Node
144
+     * @throws \OCP\Files\NotFoundException
145
+     */
146
+    public function get($path) {
147
+        return $this->root->get($this->getFullPath($path));
148
+    }
149
+
150
+    /**
151
+     * @param string $path
152
+     * @return bool
153
+     */
154
+    public function nodeExists($path) {
155
+        try {
156
+            $this->get($path);
157
+            return true;
158
+        } catch (NotFoundException $e) {
159
+            return false;
160
+        }
161
+    }
162
+
163
+    /**
164
+     * @param string $path
165
+     * @return \OC\Files\Node\Folder
166
+     * @throws \OCP\Files\NotPermittedException
167
+     */
168
+    public function newFolder($path) {
169
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
170
+            $fullPath = $this->getFullPath($path);
171
+            $nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
172
+            $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
173
+            if (!$this->view->mkdir($fullPath)) {
174
+                throw new NotPermittedException('Could not create folder');
175
+            }
176
+            $node = new Folder($this->root, $this->view, $fullPath);
177
+            $this->sendHooks(['postWrite', 'postCreate'], [$node]);
178
+            return $node;
179
+        } else {
180
+            throw new NotPermittedException('No create permission for folder');
181
+        }
182
+    }
183
+
184
+    /**
185
+     * @param string $path
186
+     * @param string | resource | null $content
187
+     * @return \OC\Files\Node\File
188
+     * @throws \OCP\Files\NotPermittedException
189
+     */
190
+    public function newFile($path, $content = null) {
191
+        if (empty($path)) {
192
+            throw new NotPermittedException('Could not create as provided path is empty');
193
+        }
194
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
195
+            $fullPath = $this->getFullPath($path);
196
+            $nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
197
+            $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
198
+            if ($content !== null) {
199
+                $result = $this->view->file_put_contents($fullPath, $content);
200
+            } else {
201
+                $result = $this->view->touch($fullPath);
202
+            }
203
+            if ($result === false) {
204
+                throw new NotPermittedException('Could not create path');
205
+            }
206
+            $node = new File($this->root, $this->view, $fullPath);
207
+            $this->sendHooks(['postWrite', 'postCreate'], [$node]);
208
+            return $node;
209
+        }
210
+        throw new NotPermittedException('No create permission for path');
211
+    }
212
+
213
+    private function queryFromOperator(ISearchOperator $operator, string $uid = null): ISearchQuery {
214
+        if ($uid === null) {
215
+            $user = null;
216
+        } else {
217
+            /** @var IUserManager $userManager */
218
+            $userManager = \OC::$server->query(IUserManager::class);
219
+            $user = $userManager->get($uid);
220
+        }
221
+        return new SearchQuery($operator, 0, 0, [], $user);
222
+    }
223
+
224
+    /**
225
+     * search for files with the name matching $query
226
+     *
227
+     * @param string|ISearchQuery $query
228
+     * @return \OC\Files\Node\Node[]
229
+     */
230
+    public function search($query) {
231
+        if (is_string($query)) {
232
+            $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%' . $query . '%'));
233
+        }
234
+
235
+        // search is handled by a single query covering all caches that this folder contains
236
+        // this is done by collect
237
+
238
+        $limitToHome = $query->limitToHome();
239
+        if ($limitToHome && count(explode('/', $this->path)) !== 3) {
240
+            throw new \InvalidArgumentException('searching by owner is only allows on the users home folder');
241
+        }
242
+
243
+        $rootLength = strlen($this->path);
244
+        $mount = $this->root->getMount($this->path);
245
+        $storage = $mount->getStorage();
246
+        $internalPath = $mount->getInternalPath($this->path);
247
+
248
+        // collect all caches for this folder, indexed by their mountpoint relative to this folder
249
+        // and save the mount which is needed later to construct the FileInfo objects
250
+
251
+        if ($internalPath !== '') {
252
+            // a temporary CacheJail is used to handle filtering down the results to within this folder
253
+            $caches = ['' => new CacheJail($storage->getCache(''), $internalPath)];
254
+        } else {
255
+            $caches = ['' => $storage->getCache('')];
256
+        }
257
+        $mountByMountPoint = ['' => $mount];
258
+
259
+        if (!$limitToHome) {
260
+            $mounts = $this->root->getMountsIn($this->path);
261
+            foreach ($mounts as $mount) {
262
+                $storage = $mount->getStorage();
263
+                if ($storage) {
264
+                    $relativeMountPoint = ltrim(substr($mount->getMountPoint(), $rootLength), '/');
265
+                    $caches[$relativeMountPoint] = $storage->getCache('');
266
+                    $mountByMountPoint[$relativeMountPoint] = $mount;
267
+                }
268
+            }
269
+        }
270
+
271
+        /** @var QuerySearchHelper $searchHelper */
272
+        $searchHelper = \OC::$server->get(QuerySearchHelper::class);
273
+        $resultsPerCache = $searchHelper->searchInCaches($query, $caches);
274
+
275
+        // loop trough all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
276
+        $files = array_merge(...array_map(function (array $results, $relativeMountPoint) use ($mountByMountPoint) {
277
+            $mount = $mountByMountPoint[$relativeMountPoint];
278
+            return array_map(function (ICacheEntry $result) use ($relativeMountPoint, $mount) {
279
+                return $this->cacheEntryToFileInfo($mount, $relativeMountPoint, $result);
280
+            }, $results);
281
+        }, array_values($resultsPerCache), array_keys($resultsPerCache)));
282
+
283
+        // don't include this folder in the results
284
+        $files = array_filter($files, function (FileInfo $file) {
285
+            return $file->getPath() !== $this->getPath();
286
+        });
287
+
288
+        // since results were returned per-cache, they are no longer fully sorted
289
+        $order = $query->getOrder();
290
+        if ($order) {
291
+            usort($files, function (FileInfo $a, FileInfo $b) use ($order) {
292
+                foreach ($order as $orderField) {
293
+                    $cmp = $orderField->sortFileInfo($a, $b);
294
+                    if ($cmp !== 0) {
295
+                        return $cmp;
296
+                    }
297
+                }
298
+                return 0;
299
+            });
300
+        }
301
+
302
+        return array_map(function (FileInfo $file) {
303
+            return $this->createNode($file->getPath(), $file);
304
+        }, $files);
305
+    }
306
+
307
+    private function cacheEntryToFileInfo(IMountPoint $mount, string $appendRoot, ICacheEntry $cacheEntry): FileInfo {
308
+        $cacheEntry['internalPath'] = $cacheEntry['path'];
309
+        $cacheEntry['path'] = $appendRoot . $cacheEntry->getPath();
310
+        $subPath = $cacheEntry['path'] !== '' ? '/' . $cacheEntry['path'] : '';
311
+        return new \OC\Files\FileInfo($this->path . $subPath, $mount->getStorage(), $cacheEntry['internalPath'], $cacheEntry, $mount);
312
+    }
313
+
314
+    /**
315
+     * search for files by mimetype
316
+     *
317
+     * @param string $mimetype
318
+     * @return Node[]
319
+     */
320
+    public function searchByMime($mimetype) {
321
+        if (strpos($mimetype, '/') === false) {
322
+            $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%'));
323
+        } else {
324
+            $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $mimetype));
325
+        }
326
+        return $this->search($query);
327
+    }
328
+
329
+    /**
330
+     * search for files by tag
331
+     *
332
+     * @param string|int $tag name or tag id
333
+     * @param string $userId owner of the tags
334
+     * @return Node[]
335
+     */
336
+    public function searchByTag($tag, $userId) {
337
+        $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'tagname', $tag), $userId);
338
+        return $this->search($query);
339
+    }
340
+
341
+    /**
342
+     * @param int $id
343
+     * @return \OC\Files\Node\Node[]
344
+     */
345
+    public function getById($id) {
346
+        $mountCache = $this->root->getUserMountCache();
347
+        if (strpos($this->getPath(), '/', 1) > 0) {
348
+            [, $user] = explode('/', $this->getPath());
349
+        } else {
350
+            $user = null;
351
+        }
352
+        $mountsContainingFile = $mountCache->getMountsForFileId((int)$id, $user);
353
+        $mounts = $this->root->getMountsIn($this->path);
354
+        $mounts[] = $this->root->getMount($this->path);
355
+        /** @var IMountPoint[] $folderMounts */
356
+        $folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
357
+            return $mountPoint->getMountPoint();
358
+        }, $mounts), $mounts);
359
+
360
+        /** @var ICachedMountInfo[] $mountsContainingFile */
361
+        $mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
362
+            return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
363
+        }));
364
+
365
+        if (count($mountsContainingFile) === 0) {
366
+            if ($user === $this->getAppDataDirectoryName()) {
367
+                return $this->getByIdInRootMount((int)$id);
368
+            }
369
+            return [];
370
+        }
371
+
372
+        $nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($folderMounts, $id) {
373
+            $mount = $folderMounts[$cachedMountInfo->getMountPoint()];
374
+            $cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
375
+            if (!$cacheEntry) {
376
+                return null;
377
+            }
378
+
379
+            // cache jails will hide the "true" internal path
380
+            $internalPath = ltrim($cachedMountInfo->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
381
+            $pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
382
+            $pathRelativeToMount = ltrim($pathRelativeToMount, '/');
383
+            $absolutePath = rtrim($cachedMountInfo->getMountPoint() . $pathRelativeToMount, '/');
384
+            return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
385
+                $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
386
+                \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
387
+            ));
388
+        }, $mountsContainingFile);
389
+
390
+        $nodes = array_filter($nodes);
391
+
392
+        return array_filter($nodes, function (Node $node) {
393
+            return $this->getRelativePath($node->getPath());
394
+        });
395
+    }
396
+
397
+    protected function getAppDataDirectoryName(): string {
398
+        $instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid');
399
+        return 'appdata_' . $instanceId;
400
+    }
401
+
402
+    /**
403
+     * In case the path we are currently in is inside the appdata_* folder,
404
+     * the original getById method does not work, because it can only look inside
405
+     * the user's mount points. But the user has no mount point for the root storage.
406
+     *
407
+     * So in that case we directly check the mount of the root if it contains
408
+     * the id. If it does we check if the path is inside the path we are working
409
+     * in.
410
+     *
411
+     * @param int $id
412
+     * @return array
413
+     */
414
+    protected function getByIdInRootMount(int $id): array {
415
+        $mount = $this->root->getMount('');
416
+        $cacheEntry = $mount->getStorage()->getCache($this->path)->get($id);
417
+        if (!$cacheEntry) {
418
+            return [];
419
+        }
420
+
421
+        $absolutePath = '/' . ltrim($cacheEntry->getPath(), '/');
422
+        $currentPath = rtrim($this->path, '/') . '/';
423
+
424
+        if (strpos($absolutePath, $currentPath) !== 0) {
425
+            return [];
426
+        }
427
+
428
+        return [$this->root->createNode(
429
+            $absolutePath, new \OC\Files\FileInfo(
430
+            $absolutePath,
431
+            $mount->getStorage(),
432
+            $cacheEntry->getPath(),
433
+            $cacheEntry,
434
+            $mount
435
+        ))];
436
+    }
437
+
438
+    public function getFreeSpace() {
439
+        return $this->view->free_space($this->path);
440
+    }
441
+
442
+    public function delete() {
443
+        if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
444
+            $this->sendHooks(['preDelete']);
445
+            $fileInfo = $this->getFileInfo();
446
+            $this->view->rmdir($this->path);
447
+            $nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
448
+            $this->sendHooks(['postDelete'], [$nonExisting]);
449
+            $this->exists = false;
450
+        } else {
451
+            throw new NotPermittedException('No delete permission for path');
452
+        }
453
+    }
454
+
455
+    /**
456
+     * Add a suffix to the name in case the file exists
457
+     *
458
+     * @param string $name
459
+     * @return string
460
+     * @throws NotPermittedException
461
+     */
462
+    public function getNonExistingName($name) {
463
+        $uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view);
464
+        return trim($this->getRelativePath($uniqueName), '/');
465
+    }
466
+
467
+    /**
468
+     * @param int $limit
469
+     * @param int $offset
470
+     * @return \OCP\Files\Node[]
471
+     */
472
+    public function getRecent($limit, $offset = 0) {
473
+        $query = new SearchQuery(
474
+            new SearchBinaryOperator(
475
+                // filter out non empty folders
476
+                ISearchBinaryOperator::OPERATOR_OR,
477
+                [
478
+                    new SearchBinaryOperator(
479
+                        ISearchBinaryOperator::OPERATOR_NOT,
480
+                        [
481
+                            new SearchComparison(
482
+                                ISearchComparison::COMPARE_EQUAL,
483
+                                'mimetype',
484
+                                FileInfo::MIMETYPE_FOLDER
485
+                            ),
486
+                        ]
487
+                    ),
488
+                    new SearchComparison(
489
+                        ISearchComparison::COMPARE_EQUAL,
490
+                        'size',
491
+                        0
492
+                    ),
493
+                ]
494
+            ),
495
+            $limit,
496
+            $offset,
497
+            [
498
+                new SearchOrder(
499
+                    ISearchOrder::DIRECTION_DESCENDING,
500
+                    'mtime'
501
+                ),
502
+            ]
503
+        );
504
+        return $this->search($query);
505
+    }
506 506
 }
Please login to merge, or discard this patch.
Spacing   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -69,7 +69,7 @@  discard block
 block discarded – undo
69 69
 		if (!$this->isValidPath($path)) {
70 70
 			throw new NotPermittedException('Invalid path');
71 71
 		}
72
-		return $this->path . $this->normalizePath($path);
72
+		return $this->path.$this->normalizePath($path);
73 73
 	}
74 74
 
75 75
 	/**
@@ -82,7 +82,7 @@  discard block
 block discarded – undo
82 82
 		}
83 83
 		if ($path === $this->path) {
84 84
 			return '/';
85
-		} elseif (strpos($path, $this->path . '/') !== 0) {
85
+		} elseif (strpos($path, $this->path.'/') !== 0) {
86 86
 			return null;
87 87
 		} else {
88 88
 			$path = substr($path, strlen($this->path));
@@ -97,7 +97,7 @@  discard block
 block discarded – undo
97 97
 	 * @return bool
98 98
 	 */
99 99
 	public function isSubNode($node) {
100
-		return strpos($node->getPath(), $this->path . '/') === 0;
100
+		return strpos($node->getPath(), $this->path.'/') === 0;
101 101
 	}
102 102
 
103 103
 	/**
@@ -109,7 +109,7 @@  discard block
 block discarded – undo
109 109
 	public function getDirectoryListing() {
110 110
 		$folderContent = $this->view->getDirectoryContent($this->path);
111 111
 
112
-		return array_map(function (FileInfo $info) {
112
+		return array_map(function(FileInfo $info) {
113 113
 			if ($info->getMimetype() === 'httpd/unix-directory') {
114 114
 				return new Folder($this->root, $this->view, $info->getPath(), $info);
115 115
 			} else {
@@ -229,7 +229,7 @@  discard block
 block discarded – undo
229 229
 	 */
230 230
 	public function search($query) {
231 231
 		if (is_string($query)) {
232
-			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%' . $query . '%'));
232
+			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%'.$query.'%'));
233 233
 		}
234 234
 
235 235
 		// search is handled by a single query covering all caches that this folder contains
@@ -273,22 +273,22 @@  discard block
 block discarded – undo
273 273
 		$resultsPerCache = $searchHelper->searchInCaches($query, $caches);
274 274
 
275 275
 		// loop trough all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
276
-		$files = array_merge(...array_map(function (array $results, $relativeMountPoint) use ($mountByMountPoint) {
276
+		$files = array_merge(...array_map(function(array $results, $relativeMountPoint) use ($mountByMountPoint) {
277 277
 			$mount = $mountByMountPoint[$relativeMountPoint];
278
-			return array_map(function (ICacheEntry $result) use ($relativeMountPoint, $mount) {
278
+			return array_map(function(ICacheEntry $result) use ($relativeMountPoint, $mount) {
279 279
 				return $this->cacheEntryToFileInfo($mount, $relativeMountPoint, $result);
280 280
 			}, $results);
281 281
 		}, array_values($resultsPerCache), array_keys($resultsPerCache)));
282 282
 
283 283
 		// don't include this folder in the results
284
-		$files = array_filter($files, function (FileInfo $file) {
284
+		$files = array_filter($files, function(FileInfo $file) {
285 285
 			return $file->getPath() !== $this->getPath();
286 286
 		});
287 287
 
288 288
 		// since results were returned per-cache, they are no longer fully sorted
289 289
 		$order = $query->getOrder();
290 290
 		if ($order) {
291
-			usort($files, function (FileInfo $a, FileInfo $b) use ($order) {
291
+			usort($files, function(FileInfo $a, FileInfo $b) use ($order) {
292 292
 				foreach ($order as $orderField) {
293 293
 					$cmp = $orderField->sortFileInfo($a, $b);
294 294
 					if ($cmp !== 0) {
@@ -299,16 +299,16 @@  discard block
 block discarded – undo
299 299
 			});
300 300
 		}
301 301
 
302
-		return array_map(function (FileInfo $file) {
302
+		return array_map(function(FileInfo $file) {
303 303
 			return $this->createNode($file->getPath(), $file);
304 304
 		}, $files);
305 305
 	}
306 306
 
307 307
 	private function cacheEntryToFileInfo(IMountPoint $mount, string $appendRoot, ICacheEntry $cacheEntry): FileInfo {
308 308
 		$cacheEntry['internalPath'] = $cacheEntry['path'];
309
-		$cacheEntry['path'] = $appendRoot . $cacheEntry->getPath();
310
-		$subPath = $cacheEntry['path'] !== '' ? '/' . $cacheEntry['path'] : '';
311
-		return new \OC\Files\FileInfo($this->path . $subPath, $mount->getStorage(), $cacheEntry['internalPath'], $cacheEntry, $mount);
309
+		$cacheEntry['path'] = $appendRoot.$cacheEntry->getPath();
310
+		$subPath = $cacheEntry['path'] !== '' ? '/'.$cacheEntry['path'] : '';
311
+		return new \OC\Files\FileInfo($this->path.$subPath, $mount->getStorage(), $cacheEntry['internalPath'], $cacheEntry, $mount);
312 312
 	}
313 313
 
314 314
 	/**
@@ -319,7 +319,7 @@  discard block
 block discarded – undo
319 319
 	 */
320 320
 	public function searchByMime($mimetype) {
321 321
 		if (strpos($mimetype, '/') === false) {
322
-			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%'));
322
+			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype.'/%'));
323 323
 		} else {
324 324
 			$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', $mimetype));
325 325
 		}
@@ -349,38 +349,38 @@  discard block
 block discarded – undo
349 349
 		} else {
350 350
 			$user = null;
351 351
 		}
352
-		$mountsContainingFile = $mountCache->getMountsForFileId((int)$id, $user);
352
+		$mountsContainingFile = $mountCache->getMountsForFileId((int) $id, $user);
353 353
 		$mounts = $this->root->getMountsIn($this->path);
354 354
 		$mounts[] = $this->root->getMount($this->path);
355 355
 		/** @var IMountPoint[] $folderMounts */
356
-		$folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) {
356
+		$folderMounts = array_combine(array_map(function(IMountPoint $mountPoint) {
357 357
 			return $mountPoint->getMountPoint();
358 358
 		}, $mounts), $mounts);
359 359
 
360 360
 		/** @var ICachedMountInfo[] $mountsContainingFile */
361
-		$mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
361
+		$mountsContainingFile = array_values(array_filter($mountsContainingFile, function(ICachedMountInfo $cachedMountInfo) use ($folderMounts) {
362 362
 			return isset($folderMounts[$cachedMountInfo->getMountPoint()]);
363 363
 		}));
364 364
 
365 365
 		if (count($mountsContainingFile) === 0) {
366 366
 			if ($user === $this->getAppDataDirectoryName()) {
367
-				return $this->getByIdInRootMount((int)$id);
367
+				return $this->getByIdInRootMount((int) $id);
368 368
 			}
369 369
 			return [];
370 370
 		}
371 371
 
372
-		$nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($folderMounts, $id) {
372
+		$nodes = array_map(function(ICachedMountInfo $cachedMountInfo) use ($folderMounts, $id) {
373 373
 			$mount = $folderMounts[$cachedMountInfo->getMountPoint()];
374
-			$cacheEntry = $mount->getStorage()->getCache()->get((int)$id);
374
+			$cacheEntry = $mount->getStorage()->getCache()->get((int) $id);
375 375
 			if (!$cacheEntry) {
376 376
 				return null;
377 377
 			}
378 378
 
379 379
 			// cache jails will hide the "true" internal path
380
-			$internalPath = ltrim($cachedMountInfo->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/');
380
+			$internalPath = ltrim($cachedMountInfo->getRootInternalPath().'/'.$cacheEntry->getPath(), '/');
381 381
 			$pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath()));
382 382
 			$pathRelativeToMount = ltrim($pathRelativeToMount, '/');
383
-			$absolutePath = rtrim($cachedMountInfo->getMountPoint() . $pathRelativeToMount, '/');
383
+			$absolutePath = rtrim($cachedMountInfo->getMountPoint().$pathRelativeToMount, '/');
384 384
 			return $this->root->createNode($absolutePath, new \OC\Files\FileInfo(
385 385
 				$absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount,
386 386
 				\OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount))
@@ -389,14 +389,14 @@  discard block
 block discarded – undo
389 389
 
390 390
 		$nodes = array_filter($nodes);
391 391
 
392
-		return array_filter($nodes, function (Node $node) {
392
+		return array_filter($nodes, function(Node $node) {
393 393
 			return $this->getRelativePath($node->getPath());
394 394
 		});
395 395
 	}
396 396
 
397 397
 	protected function getAppDataDirectoryName(): string {
398 398
 		$instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid');
399
-		return 'appdata_' . $instanceId;
399
+		return 'appdata_'.$instanceId;
400 400
 	}
401 401
 
402 402
 	/**
@@ -418,8 +418,8 @@  discard block
 block discarded – undo
418 418
 			return [];
419 419
 		}
420 420
 
421
-		$absolutePath = '/' . ltrim($cacheEntry->getPath(), '/');
422
-		$currentPath = rtrim($this->path, '/') . '/';
421
+		$absolutePath = '/'.ltrim($cacheEntry->getPath(), '/');
422
+		$currentPath = rtrim($this->path, '/').'/';
423 423
 
424 424
 		if (strpos($absolutePath, $currentPath) !== 0) {
425 425
 			return [];
Please login to merge, or discard this patch.