Completed
Push — master ( c7757b...dc5820 )
by Morris
35:05 queued 19:24
created
apps/files_trashbin/lib/Trashbin.php 2 patches
Indentation   +952 added lines, -952 removed lines patch added patch discarded remove patch
@@ -49,956 +49,956 @@
 block discarded – undo
49 49
 
50 50
 class Trashbin {
51 51
 
52
-	// unit: percentage; 50% of available disk space/quota
53
-	const DEFAULTMAXSIZE = 50;
54
-
55
-	/**
56
-	 * Whether versions have already be rescanned during this PHP request
57
-	 *
58
-	 * @var bool
59
-	 */
60
-	private static $scannedVersions = false;
61
-
62
-	/**
63
-	 * Ensure we don't need to scan the file during the move to trash
64
-	 * by triggering the scan in the pre-hook
65
-	 *
66
-	 * @param array $params
67
-	 */
68
-	public static function ensureFileScannedHook($params) {
69
-		try {
70
-			self::getUidAndFilename($params['path']);
71
-		} catch (NotFoundException $e) {
72
-			// nothing to scan for non existing files
73
-		}
74
-	}
75
-
76
-	/**
77
-	 * get the UID of the owner of the file and the path to the file relative to
78
-	 * owners files folder
79
-	 *
80
-	 * @param string $filename
81
-	 * @return array
82
-	 * @throws \OC\User\NoUserException
83
-	 */
84
-	public static function getUidAndFilename($filename) {
85
-		$uid = Filesystem::getOwner($filename);
86
-		$userManager = \OC::$server->getUserManager();
87
-		// if the user with the UID doesn't exists, e.g. because the UID points
88
-		// to a remote user with a federated cloud ID we use the current logged-in
89
-		// user. We need a valid local user to move the file to the right trash bin
90
-		if (!$userManager->userExists($uid)) {
91
-			$uid = User::getUser();
92
-		}
93
-		if (!$uid) {
94
-			// no owner, usually because of share link from ext storage
95
-			return [null, null];
96
-		}
97
-		Filesystem::initMountPoints($uid);
98
-		if ($uid != User::getUser()) {
99
-			$info = Filesystem::getFileInfo($filename);
100
-			$ownerView = new View('/' . $uid . '/files');
101
-			try {
102
-				$filename = $ownerView->getPath($info['fileid']);
103
-			} catch (NotFoundException $e) {
104
-				$filename = null;
105
-			}
106
-		}
107
-		return [$uid, $filename];
108
-	}
109
-
110
-	/**
111
-	 * get original location of files for user
112
-	 *
113
-	 * @param string $user
114
-	 * @return array (filename => array (timestamp => original location))
115
-	 */
116
-	public static function getLocations($user) {
117
-		$query = \OC_DB::prepare('SELECT `id`, `timestamp`, `location`'
118
-			. ' FROM `*PREFIX*files_trash` WHERE `user`=?');
119
-		$result = $query->execute(array($user));
120
-		$array = array();
121
-		while ($row = $result->fetchRow()) {
122
-			if (isset($array[$row['id']])) {
123
-				$array[$row['id']][$row['timestamp']] = $row['location'];
124
-			} else {
125
-				$array[$row['id']] = array($row['timestamp'] => $row['location']);
126
-			}
127
-		}
128
-		return $array;
129
-	}
130
-
131
-	/**
132
-	 * get original location of file
133
-	 *
134
-	 * @param string $user
135
-	 * @param string $filename
136
-	 * @param string $timestamp
137
-	 * @return string original location
138
-	 */
139
-	public static function getLocation($user, $filename, $timestamp) {
140
-		$query = \OC_DB::prepare('SELECT `location` FROM `*PREFIX*files_trash`'
141
-			. ' WHERE `user`=? AND `id`=? AND `timestamp`=?');
142
-		$result = $query->execute(array($user, $filename, $timestamp))->fetchAll();
143
-		if (isset($result[0]['location'])) {
144
-			return $result[0]['location'];
145
-		} else {
146
-			return false;
147
-		}
148
-	}
149
-
150
-	private static function setUpTrash($user) {
151
-		$view = new View('/' . $user);
152
-		if (!$view->is_dir('files_trashbin')) {
153
-			$view->mkdir('files_trashbin');
154
-		}
155
-		if (!$view->is_dir('files_trashbin/files')) {
156
-			$view->mkdir('files_trashbin/files');
157
-		}
158
-		if (!$view->is_dir('files_trashbin/versions')) {
159
-			$view->mkdir('files_trashbin/versions');
160
-		}
161
-		if (!$view->is_dir('files_trashbin/keys')) {
162
-			$view->mkdir('files_trashbin/keys');
163
-		}
164
-	}
165
-
166
-
167
-	/**
168
-	 * copy file to owners trash
169
-	 *
170
-	 * @param string $sourcePath
171
-	 * @param string $owner
172
-	 * @param string $targetPath
173
-	 * @param $user
174
-	 * @param integer $timestamp
175
-	 */
176
-	private static function copyFilesToUser($sourcePath, $owner, $targetPath, $user, $timestamp) {
177
-		self::setUpTrash($owner);
178
-
179
-		$targetFilename = basename($targetPath);
180
-		$targetLocation = dirname($targetPath);
181
-
182
-		$sourceFilename = basename($sourcePath);
183
-
184
-		$view = new View('/');
185
-
186
-		$target = $user . '/files_trashbin/files/' . $targetFilename . '.d' . $timestamp;
187
-		$source = $owner . '/files_trashbin/files/' . $sourceFilename . '.d' . $timestamp;
188
-		self::copy_recursive($source, $target, $view);
189
-
190
-
191
-		if ($view->file_exists($target)) {
192
-			$query = \OC_DB::prepare("INSERT INTO `*PREFIX*files_trash` (`id`,`timestamp`,`location`,`user`) VALUES (?,?,?,?)");
193
-			$result = $query->execute(array($targetFilename, $timestamp, $targetLocation, $user));
194
-			if (!$result) {
195
-				\OCP\Util::writeLog('files_trashbin', 'trash bin database couldn\'t be updated for the files owner', \OCP\Util::ERROR);
196
-			}
197
-		}
198
-	}
199
-
200
-
201
-	/**
202
-	 * move file to the trash bin
203
-	 *
204
-	 * @param string $file_path path to the deleted file/directory relative to the files root directory
205
-	 * @param bool $ownerOnly delete for owner only (if file gets moved out of a shared folder)
206
-	 *
207
-	 * @return bool
208
-	 */
209
-	public static function move2trash($file_path, $ownerOnly = false) {
210
-		// get the user for which the filesystem is setup
211
-		$root = Filesystem::getRoot();
212
-		list(, $user) = explode('/', $root);
213
-		list($owner, $ownerPath) = self::getUidAndFilename($file_path);
214
-
215
-		// if no owner found (ex: ext storage + share link), will use the current user's trashbin then
216
-		if (is_null($owner)) {
217
-			$owner = $user;
218
-			$ownerPath = $file_path;
219
-		}
220
-
221
-		$ownerView = new View('/' . $owner);
222
-		// file has been deleted in between
223
-		if (is_null($ownerPath) || $ownerPath === '' || !$ownerView->file_exists('/files/' . $ownerPath)) {
224
-			return true;
225
-		}
226
-
227
-		self::setUpTrash($user);
228
-		if ($owner !== $user) {
229
-			// also setup for owner
230
-			self::setUpTrash($owner);
231
-		}
232
-
233
-		$path_parts = pathinfo($ownerPath);
234
-
235
-		$filename = $path_parts['basename'];
236
-		$location = $path_parts['dirname'];
237
-		$timestamp = time();
238
-
239
-		// disable proxy to prevent recursive calls
240
-		$trashPath = '/files_trashbin/files/' . $filename . '.d' . $timestamp;
241
-
242
-		/** @var \OC\Files\Storage\Storage $trashStorage */
243
-		list($trashStorage, $trashInternalPath) = $ownerView->resolvePath($trashPath);
244
-		/** @var \OC\Files\Storage\Storage $sourceStorage */
245
-		list($sourceStorage, $sourceInternalPath) = $ownerView->resolvePath('/files/' . $ownerPath);
246
-		try {
247
-			$moveSuccessful = true;
248
-			if ($trashStorage->file_exists($trashInternalPath)) {
249
-				$trashStorage->unlink($trashInternalPath);
250
-			}
251
-			$trashStorage->moveFromStorage($sourceStorage, $sourceInternalPath, $trashInternalPath);
252
-		} catch (\OCA\Files_Trashbin\Exceptions\CopyRecursiveException $e) {
253
-			$moveSuccessful = false;
254
-			if ($trashStorage->file_exists($trashInternalPath)) {
255
-				$trashStorage->unlink($trashInternalPath);
256
-			}
257
-			\OCP\Util::writeLog('files_trashbin', 'Couldn\'t move ' . $file_path . ' to the trash bin', \OCP\Util::ERROR);
258
-		}
259
-
260
-		if ($sourceStorage->file_exists($sourceInternalPath)) { // failed to delete the original file, abort
261
-			if ($sourceStorage->is_dir($sourceInternalPath)) {
262
-				$sourceStorage->rmdir($sourceInternalPath);
263
-			} else {
264
-				$sourceStorage->unlink($sourceInternalPath);
265
-			}
266
-			return false;
267
-		}
268
-
269
-		$trashStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $trashInternalPath);
270
-
271
-		if ($moveSuccessful) {
272
-			$query = \OC_DB::prepare("INSERT INTO `*PREFIX*files_trash` (`id`,`timestamp`,`location`,`user`) VALUES (?,?,?,?)");
273
-			$result = $query->execute(array($filename, $timestamp, $location, $owner));
274
-			if (!$result) {
275
-				\OCP\Util::writeLog('files_trashbin', 'trash bin database couldn\'t be updated', \OCP\Util::ERROR);
276
-			}
277
-			\OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_moveToTrash', array('filePath' => Filesystem::normalizePath($file_path),
278
-				'trashPath' => Filesystem::normalizePath($filename . '.d' . $timestamp)));
279
-
280
-			self::retainVersions($filename, $owner, $ownerPath, $timestamp);
281
-
282
-			// if owner !== user we need to also add a copy to the users trash
283
-			if ($user !== $owner && $ownerOnly === false) {
284
-				self::copyFilesToUser($ownerPath, $owner, $file_path, $user, $timestamp);
285
-			}
286
-		}
287
-
288
-		self::scheduleExpire($user);
289
-
290
-		// if owner !== user we also need to update the owners trash size
291
-		if ($owner !== $user) {
292
-			self::scheduleExpire($owner);
293
-		}
294
-
295
-		return $moveSuccessful;
296
-	}
297
-
298
-	/**
299
-	 * Move file versions to trash so that they can be restored later
300
-	 *
301
-	 * @param string $filename of deleted file
302
-	 * @param string $owner owner user id
303
-	 * @param string $ownerPath path relative to the owner's home storage
304
-	 * @param integer $timestamp when the file was deleted
305
-	 */
306
-	private static function retainVersions($filename, $owner, $ownerPath, $timestamp) {
307
-		if (\OCP\App::isEnabled('files_versions') && !empty($ownerPath)) {
308
-
309
-			$user = User::getUser();
310
-			$rootView = new View('/');
311
-
312
-			if ($rootView->is_dir($owner . '/files_versions/' . $ownerPath)) {
313
-				if ($owner !== $user) {
314
-					self::copy_recursive($owner . '/files_versions/' . $ownerPath, $owner . '/files_trashbin/versions/' . basename($ownerPath) . '.d' . $timestamp, $rootView);
315
-				}
316
-				self::move($rootView, $owner . '/files_versions/' . $ownerPath, $user . '/files_trashbin/versions/' . $filename . '.d' . $timestamp);
317
-			} else if ($versions = \OCA\Files_Versions\Storage::getVersions($owner, $ownerPath)) {
318
-
319
-				foreach ($versions as $v) {
320
-					if ($owner !== $user) {
321
-						self::copy($rootView, $owner . '/files_versions' . $v['path'] . '.v' . $v['version'], $owner . '/files_trashbin/versions/' . $v['name'] . '.v' . $v['version'] . '.d' . $timestamp);
322
-					}
323
-					self::move($rootView, $owner . '/files_versions' . $v['path'] . '.v' . $v['version'], $user . '/files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp);
324
-				}
325
-			}
326
-		}
327
-	}
328
-
329
-	/**
330
-	 * Move a file or folder on storage level
331
-	 *
332
-	 * @param View $view
333
-	 * @param string $source
334
-	 * @param string $target
335
-	 * @return bool
336
-	 */
337
-	private static function move(View $view, $source, $target) {
338
-		/** @var \OC\Files\Storage\Storage $sourceStorage */
339
-		list($sourceStorage, $sourceInternalPath) = $view->resolvePath($source);
340
-		/** @var \OC\Files\Storage\Storage $targetStorage */
341
-		list($targetStorage, $targetInternalPath) = $view->resolvePath($target);
342
-		/** @var \OC\Files\Storage\Storage $ownerTrashStorage */
343
-
344
-		$result = $targetStorage->moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
345
-		if ($result) {
346
-			$targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
347
-		}
348
-		return $result;
349
-	}
350
-
351
-	/**
352
-	 * Copy a file or folder on storage level
353
-	 *
354
-	 * @param View $view
355
-	 * @param string $source
356
-	 * @param string $target
357
-	 * @return bool
358
-	 */
359
-	private static function copy(View $view, $source, $target) {
360
-		/** @var \OC\Files\Storage\Storage $sourceStorage */
361
-		list($sourceStorage, $sourceInternalPath) = $view->resolvePath($source);
362
-		/** @var \OC\Files\Storage\Storage $targetStorage */
363
-		list($targetStorage, $targetInternalPath) = $view->resolvePath($target);
364
-		/** @var \OC\Files\Storage\Storage $ownerTrashStorage */
365
-
366
-		$result = $targetStorage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
367
-		if ($result) {
368
-			$targetStorage->getUpdater()->update($targetInternalPath);
369
-		}
370
-		return $result;
371
-	}
372
-
373
-	/**
374
-	 * Restore a file or folder from trash bin
375
-	 *
376
-	 * @param string $file path to the deleted file/folder relative to "files_trashbin/files/",
377
-	 * including the timestamp suffix ".d12345678"
378
-	 * @param string $filename name of the file/folder
379
-	 * @param int $timestamp time when the file/folder was deleted
380
-	 *
381
-	 * @return bool true on success, false otherwise
382
-	 */
383
-	public static function restore($file, $filename, $timestamp) {
384
-		$user = User::getUser();
385
-		$view = new View('/' . $user);
386
-
387
-		$location = '';
388
-		if ($timestamp) {
389
-			$location = self::getLocation($user, $filename, $timestamp);
390
-			if ($location === false) {
391
-				\OCP\Util::writeLog('files_trashbin', 'trash bin database inconsistent! ($user: ' . $user . ' $filename: ' . $filename . ', $timestamp: ' . $timestamp . ')', \OCP\Util::ERROR);
392
-			} else {
393
-				// if location no longer exists, restore file in the root directory
394
-				if ($location !== '/' &&
395
-					(!$view->is_dir('files/' . $location) ||
396
-						!$view->isCreatable('files/' . $location))
397
-				) {
398
-					$location = '';
399
-				}
400
-			}
401
-		}
402
-
403
-		// we need a  extension in case a file/dir with the same name already exists
404
-		$uniqueFilename = self::getUniqueFilename($location, $filename, $view);
405
-
406
-		$source = Filesystem::normalizePath('files_trashbin/files/' . $file);
407
-		$target = Filesystem::normalizePath('files/' . $location . '/' . $uniqueFilename);
408
-		if (!$view->file_exists($source)) {
409
-			return false;
410
-		}
411
-		$mtime = $view->filemtime($source);
412
-
413
-		// restore file
414
-		$restoreResult = $view->rename($source, $target);
415
-
416
-		// handle the restore result
417
-		if ($restoreResult) {
418
-			$fakeRoot = $view->getRoot();
419
-			$view->chroot('/' . $user . '/files');
420
-			$view->touch('/' . $location . '/' . $uniqueFilename, $mtime);
421
-			$view->chroot($fakeRoot);
422
-			\OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', array('filePath' => Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename),
423
-				'trashPath' => Filesystem::normalizePath($file)));
424
-
425
-			self::restoreVersions($view, $file, $filename, $uniqueFilename, $location, $timestamp);
426
-
427
-			if ($timestamp) {
428
-				$query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=? AND `id`=? AND `timestamp`=?');
429
-				$query->execute(array($user, $filename, $timestamp));
430
-			}
431
-
432
-			return true;
433
-		}
434
-
435
-		return false;
436
-	}
437
-
438
-	/**
439
-	 * restore versions from trash bin
440
-	 *
441
-	 * @param View $view file view
442
-	 * @param string $file complete path to file
443
-	 * @param string $filename name of file once it was deleted
444
-	 * @param string $uniqueFilename new file name to restore the file without overwriting existing files
445
-	 * @param string $location location if file
446
-	 * @param int $timestamp deletion time
447
-	 * @return false|null
448
-	 */
449
-	private static function restoreVersions(View $view, $file, $filename, $uniqueFilename, $location, $timestamp) {
450
-
451
-		if (\OCP\App::isEnabled('files_versions')) {
452
-
453
-			$user = User::getUser();
454
-			$rootView = new View('/');
455
-
456
-			$target = Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename);
457
-
458
-			list($owner, $ownerPath) = self::getUidAndFilename($target);
459
-
460
-			// file has been deleted in between
461
-			if (empty($ownerPath)) {
462
-				return false;
463
-			}
464
-
465
-			if ($timestamp) {
466
-				$versionedFile = $filename;
467
-			} else {
468
-				$versionedFile = $file;
469
-			}
470
-
471
-			if ($view->is_dir('/files_trashbin/versions/' . $file)) {
472
-				$rootView->rename(Filesystem::normalizePath($user . '/files_trashbin/versions/' . $file), Filesystem::normalizePath($owner . '/files_versions/' . $ownerPath));
473
-			} else if ($versions = self::getVersionsFromTrash($versionedFile, $timestamp, $user)) {
474
-				foreach ($versions as $v) {
475
-					if ($timestamp) {
476
-						$rootView->rename($user . '/files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, $owner . '/files_versions/' . $ownerPath . '.v' . $v);
477
-					} else {
478
-						$rootView->rename($user . '/files_trashbin/versions/' . $versionedFile . '.v' . $v, $owner . '/files_versions/' . $ownerPath . '.v' . $v);
479
-					}
480
-				}
481
-			}
482
-		}
483
-	}
484
-
485
-	/**
486
-	 * delete all files from the trash
487
-	 */
488
-	public static function deleteAll() {
489
-		$user = User::getUser();
490
-		$userRoot = \OC::$server->getUserFolder($user)->getParent();
491
-		$view = new View('/' . $user);
492
-		$fileInfos = $view->getDirectoryContent('files_trashbin/files');
493
-
494
-		try {
495
-			$trash = $userRoot->get('files_trashbin');
496
-		} catch (NotFoundException $e) {
497
-			return false;
498
-		}
499
-
500
-		// Array to store the relative path in (after the file is deleted, the view won't be able to relativise the path anymore)
501
-		$filePaths = array();
502
-		foreach($fileInfos as $fileInfo){
503
-			$filePaths[] = $view->getRelativePath($fileInfo->getPath());
504
-		}
505
-		unset($fileInfos); // save memory
506
-
507
-		// Bulk PreDelete-Hook
508
-		\OC_Hook::emit('\OCP\Trashbin', 'preDeleteAll', array('paths' => $filePaths));
509
-
510
-		// Single-File Hooks
511
-		foreach($filePaths as $path){
512
-			self::emitTrashbinPreDelete($path);
513
-		}
514
-
515
-		// actual file deletion
516
-		$trash->delete();
517
-		$query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=?');
518
-		$query->execute(array($user));
519
-
520
-		// Bulk PostDelete-Hook
521
-		\OC_Hook::emit('\OCP\Trashbin', 'deleteAll', array('paths' => $filePaths));
522
-
523
-		// Single-File Hooks
524
-		foreach($filePaths as $path){
525
-			self::emitTrashbinPostDelete($path);
526
-		}
527
-
528
-		$trash = $userRoot->newFolder('files_trashbin');
529
-		$trash->newFolder('files');
530
-
531
-		return true;
532
-	}
533
-
534
-	/**
535
-	 * wrapper function to emit the 'preDelete' hook of \OCP\Trashbin before a file is deleted
536
-	 * @param string $path
537
-	 */
538
-	protected static function emitTrashbinPreDelete($path){
539
-		\OC_Hook::emit('\OCP\Trashbin', 'preDelete', array('path' => $path));
540
-	}
541
-
542
-	/**
543
-	 * wrapper function to emit the 'delete' hook of \OCP\Trashbin after a file has been deleted
544
-	 * @param string $path
545
-	 */
546
-	protected static function emitTrashbinPostDelete($path){
547
-		\OC_Hook::emit('\OCP\Trashbin', 'delete', array('path' => $path));
548
-	}
549
-
550
-	/**
551
-	 * delete file from trash bin permanently
552
-	 *
553
-	 * @param string $filename path to the file
554
-	 * @param string $user
555
-	 * @param int $timestamp of deletion time
556
-	 *
557
-	 * @return int size of deleted files
558
-	 */
559
-	public static function delete($filename, $user, $timestamp = null) {
560
-		$userRoot = \OC::$server->getUserFolder($user)->getParent();
561
-		$view = new View('/' . $user);
562
-		$size = 0;
563
-
564
-		if ($timestamp) {
565
-			$query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=? AND `id`=? AND `timestamp`=?');
566
-			$query->execute(array($user, $filename, $timestamp));
567
-			$file = $filename . '.d' . $timestamp;
568
-		} else {
569
-			$file = $filename;
570
-		}
571
-
572
-		$size += self::deleteVersions($view, $file, $filename, $timestamp, $user);
573
-
574
-		try {
575
-			$node = $userRoot->get('/files_trashbin/files/' . $file);
576
-		} catch (NotFoundException $e) {
577
-			return $size;
578
-		}
579
-
580
-		if ($node instanceof Folder) {
581
-			$size += self::calculateSize(new View('/' . $user . '/files_trashbin/files/' . $file));
582
-		} else if ($node instanceof File) {
583
-			$size += $view->filesize('/files_trashbin/files/' . $file);
584
-		}
585
-
586
-		self::emitTrashbinPreDelete('/files_trashbin/files/' . $file);
587
-		$node->delete();
588
-		self::emitTrashbinPostDelete('/files_trashbin/files/' . $file);
589
-
590
-		return $size;
591
-	}
592
-
593
-	/**
594
-	 * @param View $view
595
-	 * @param string $file
596
-	 * @param string $filename
597
-	 * @param integer|null $timestamp
598
-	 * @param string $user
599
-	 * @return int
600
-	 */
601
-	private static function deleteVersions(View $view, $file, $filename, $timestamp, $user) {
602
-		$size = 0;
603
-		if (\OCP\App::isEnabled('files_versions')) {
604
-			if ($view->is_dir('files_trashbin/versions/' . $file)) {
605
-				$size += self::calculateSize(new View('/' . $user . '/files_trashbin/versions/' . $file));
606
-				$view->unlink('files_trashbin/versions/' . $file);
607
-			} else if ($versions = self::getVersionsFromTrash($filename, $timestamp, $user)) {
608
-				foreach ($versions as $v) {
609
-					if ($timestamp) {
610
-						$size += $view->filesize('/files_trashbin/versions/' . $filename . '.v' . $v . '.d' . $timestamp);
611
-						$view->unlink('/files_trashbin/versions/' . $filename . '.v' . $v . '.d' . $timestamp);
612
-					} else {
613
-						$size += $view->filesize('/files_trashbin/versions/' . $filename . '.v' . $v);
614
-						$view->unlink('/files_trashbin/versions/' . $filename . '.v' . $v);
615
-					}
616
-				}
617
-			}
618
-		}
619
-		return $size;
620
-	}
621
-
622
-	/**
623
-	 * check to see whether a file exists in trashbin
624
-	 *
625
-	 * @param string $filename path to the file
626
-	 * @param int $timestamp of deletion time
627
-	 * @return bool true if file exists, otherwise false
628
-	 */
629
-	public static function file_exists($filename, $timestamp = null) {
630
-		$user = User::getUser();
631
-		$view = new View('/' . $user);
632
-
633
-		if ($timestamp) {
634
-			$filename = $filename . '.d' . $timestamp;
635
-		} else {
636
-			$filename = $filename;
637
-		}
638
-
639
-		$target = Filesystem::normalizePath('files_trashbin/files/' . $filename);
640
-		return $view->file_exists($target);
641
-	}
642
-
643
-	/**
644
-	 * deletes used space for trash bin in db if user was deleted
645
-	 *
646
-	 * @param string $uid id of deleted user
647
-	 * @return bool result of db delete operation
648
-	 */
649
-	public static function deleteUser($uid) {
650
-		$query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=?');
651
-		return $query->execute(array($uid));
652
-	}
653
-
654
-	/**
655
-	 * calculate remaining free space for trash bin
656
-	 *
657
-	 * @param integer $trashbinSize current size of the trash bin
658
-	 * @param string $user
659
-	 * @return int available free space for trash bin
660
-	 */
661
-	private static function calculateFreeSpace($trashbinSize, $user) {
662
-		$softQuota = true;
663
-		$userObject = \OC::$server->getUserManager()->get($user);
664
-		if(is_null($userObject)) {
665
-			return 0;
666
-		}
667
-		$quota = $userObject->getQuota();
668
-		if ($quota === null || $quota === 'none') {
669
-			$quota = Filesystem::free_space('/');
670
-			$softQuota = false;
671
-			// inf or unknown free space
672
-			if ($quota < 0) {
673
-				$quota = PHP_INT_MAX;
674
-			}
675
-		} else {
676
-			$quota = \OCP\Util::computerFileSize($quota);
677
-		}
678
-
679
-		// calculate available space for trash bin
680
-		// subtract size of files and current trash bin size from quota
681
-		if ($softQuota) {
682
-			$userFolder = \OC::$server->getUserFolder($user);
683
-			if(is_null($userFolder)) {
684
-				return 0;
685
-			}
686
-			$free = $quota - $userFolder->getSize(); // remaining free space for user
687
-			if ($free > 0) {
688
-				$availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $trashbinSize; // how much space can be used for versions
689
-			} else {
690
-				$availableSpace = $free - $trashbinSize;
691
-			}
692
-		} else {
693
-			$availableSpace = $quota;
694
-		}
695
-
696
-		return $availableSpace;
697
-	}
698
-
699
-	/**
700
-	 * resize trash bin if necessary after a new file was added to Nextcloud
701
-	 *
702
-	 * @param string $user user id
703
-	 */
704
-	public static function resizeTrash($user) {
705
-
706
-		$size = self::getTrashbinSize($user);
707
-
708
-		$freeSpace = self::calculateFreeSpace($size, $user);
709
-
710
-		if ($freeSpace < 0) {
711
-			self::scheduleExpire($user);
712
-		}
713
-	}
714
-
715
-	/**
716
-	 * clean up the trash bin
717
-	 *
718
-	 * @param string $user
719
-	 */
720
-	public static function expire($user) {
721
-		$trashBinSize = self::getTrashbinSize($user);
722
-		$availableSpace = self::calculateFreeSpace($trashBinSize, $user);
723
-
724
-		$dirContent = Helper::getTrashFiles('/', $user, 'mtime');
725
-
726
-		// delete all files older then $retention_obligation
727
-		list($delSize, $count) = self::deleteExpiredFiles($dirContent, $user);
728
-
729
-		$availableSpace += $delSize;
730
-
731
-		// delete files from trash until we meet the trash bin size limit again
732
-		self::deleteFiles(array_slice($dirContent, $count), $user, $availableSpace);
733
-	}
734
-
735
-	/**
736
-	 * @param string $user
737
-	 */
738
-	private static function scheduleExpire($user) {
739
-		// let the admin disable auto expire
740
-		$application = new Application();
741
-		$expiration = $application->getContainer()->query('Expiration');
742
-		if ($expiration->isEnabled()) {
743
-			\OC::$server->getCommandBus()->push(new Expire($user));
744
-		}
745
-	}
746
-
747
-	/**
748
-	 * if the size limit for the trash bin is reached, we delete the oldest
749
-	 * files in the trash bin until we meet the limit again
750
-	 *
751
-	 * @param array $files
752
-	 * @param string $user
753
-	 * @param int $availableSpace available disc space
754
-	 * @return int size of deleted files
755
-	 */
756
-	protected static function deleteFiles($files, $user, $availableSpace) {
757
-		$application = new Application();
758
-		$expiration = $application->getContainer()->query('Expiration');
759
-		$size = 0;
760
-
761
-		if ($availableSpace < 0) {
762
-			foreach ($files as $file) {
763
-				if ($availableSpace < 0 && $expiration->isExpired($file['mtime'], true)) {
764
-					$tmp = self::delete($file['name'], $user, $file['mtime']);
765
-					\OCP\Util::writeLog('files_trashbin', 'remove "' . $file['name'] . '" (' . $tmp . 'B) to meet the limit of trash bin size (50% of available quota)', \OCP\Util::INFO);
766
-					$availableSpace += $tmp;
767
-					$size += $tmp;
768
-				} else {
769
-					break;
770
-				}
771
-			}
772
-		}
773
-		return $size;
774
-	}
775
-
776
-	/**
777
-	 * delete files older then max storage time
778
-	 *
779
-	 * @param array $files list of files sorted by mtime
780
-	 * @param string $user
781
-	 * @return integer[] size of deleted files and number of deleted files
782
-	 */
783
-	public static function deleteExpiredFiles($files, $user) {
784
-		$application = new Application();
785
-		$expiration = $application->getContainer()->query('Expiration');
786
-		$size = 0;
787
-		$count = 0;
788
-		foreach ($files as $file) {
789
-			$timestamp = $file['mtime'];
790
-			$filename = $file['name'];
791
-			if ($expiration->isExpired($timestamp)) {
792
-				$count++;
793
-				$size += self::delete($filename, $user, $timestamp);
794
-				\OC::$server->getLogger()->info(
795
-					'Remove "' . $filename . '" from trashbin because it exceeds max retention obligation term.',
796
-					['app' => 'files_trashbin']
797
-				);
798
-			} else {
799
-				break;
800
-			}
801
-		}
802
-
803
-		return array($size, $count);
804
-	}
805
-
806
-	/**
807
-	 * recursive copy to copy a whole directory
808
-	 *
809
-	 * @param string $source source path, relative to the users files directory
810
-	 * @param string $destination destination path relative to the users root directoy
811
-	 * @param View $view file view for the users root directory
812
-	 * @return int
813
-	 * @throws Exceptions\CopyRecursiveException
814
-	 */
815
-	private static function copy_recursive($source, $destination, View $view) {
816
-		$size = 0;
817
-		if ($view->is_dir($source)) {
818
-			$view->mkdir($destination);
819
-			$view->touch($destination, $view->filemtime($source));
820
-			foreach ($view->getDirectoryContent($source) as $i) {
821
-				$pathDir = $source . '/' . $i['name'];
822
-				if ($view->is_dir($pathDir)) {
823
-					$size += self::copy_recursive($pathDir, $destination . '/' . $i['name'], $view);
824
-				} else {
825
-					$size += $view->filesize($pathDir);
826
-					$result = $view->copy($pathDir, $destination . '/' . $i['name']);
827
-					if (!$result) {
828
-						throw new \OCA\Files_Trashbin\Exceptions\CopyRecursiveException();
829
-					}
830
-					$view->touch($destination . '/' . $i['name'], $view->filemtime($pathDir));
831
-				}
832
-			}
833
-		} else {
834
-			$size += $view->filesize($source);
835
-			$result = $view->copy($source, $destination);
836
-			if (!$result) {
837
-				throw new \OCA\Files_Trashbin\Exceptions\CopyRecursiveException();
838
-			}
839
-			$view->touch($destination, $view->filemtime($source));
840
-		}
841
-		return $size;
842
-	}
843
-
844
-	/**
845
-	 * find all versions which belong to the file we want to restore
846
-	 *
847
-	 * @param string $filename name of the file which should be restored
848
-	 * @param int $timestamp timestamp when the file was deleted
849
-	 * @return array
850
-	 */
851
-	private static function getVersionsFromTrash($filename, $timestamp, $user) {
852
-		$view = new View('/' . $user . '/files_trashbin/versions');
853
-		$versions = array();
854
-
855
-		//force rescan of versions, local storage may not have updated the cache
856
-		if (!self::$scannedVersions) {
857
-			/** @var \OC\Files\Storage\Storage $storage */
858
-			list($storage,) = $view->resolvePath('/');
859
-			$storage->getScanner()->scan('files_trashbin/versions');
860
-			self::$scannedVersions = true;
861
-		}
862
-
863
-		if ($timestamp) {
864
-			// fetch for old versions
865
-			$matches = $view->searchRaw($filename . '.v%.d' . $timestamp);
866
-			$offset = -strlen($timestamp) - 2;
867
-		} else {
868
-			$matches = $view->searchRaw($filename . '.v%');
869
-		}
870
-
871
-		if (is_array($matches)) {
872
-			foreach ($matches as $ma) {
873
-				if ($timestamp) {
874
-					$parts = explode('.v', substr($ma['path'], 0, $offset));
875
-					$versions[] = (end($parts));
876
-				} else {
877
-					$parts = explode('.v', $ma);
878
-					$versions[] = (end($parts));
879
-				}
880
-			}
881
-		}
882
-		return $versions;
883
-	}
884
-
885
-	/**
886
-	 * find unique extension for restored file if a file with the same name already exists
887
-	 *
888
-	 * @param string $location where the file should be restored
889
-	 * @param string $filename name of the file
890
-	 * @param View $view filesystem view relative to users root directory
891
-	 * @return string with unique extension
892
-	 */
893
-	private static function getUniqueFilename($location, $filename, View $view) {
894
-		$ext = pathinfo($filename, PATHINFO_EXTENSION);
895
-		$name = pathinfo($filename, PATHINFO_FILENAME);
896
-		$l = \OC::$server->getL10N('files_trashbin');
897
-
898
-		$location = '/' . trim($location, '/');
899
-
900
-		// if extension is not empty we set a dot in front of it
901
-		if ($ext !== '') {
902
-			$ext = '.' . $ext;
903
-		}
904
-
905
-		if ($view->file_exists('files' . $location . '/' . $filename)) {
906
-			$i = 2;
907
-			$uniqueName = $name . " (" . $l->t("restored") . ")" . $ext;
908
-			while ($view->file_exists('files' . $location . '/' . $uniqueName)) {
909
-				$uniqueName = $name . " (" . $l->t("restored") . " " . $i . ")" . $ext;
910
-				$i++;
911
-			}
912
-
913
-			return $uniqueName;
914
-		}
915
-
916
-		return $filename;
917
-	}
918
-
919
-	/**
920
-	 * get the size from a given root folder
921
-	 *
922
-	 * @param View $view file view on the root folder
923
-	 * @return integer size of the folder
924
-	 */
925
-	private static function calculateSize($view) {
926
-		$root = \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . $view->getAbsolutePath('');
927
-		if (!file_exists($root)) {
928
-			return 0;
929
-		}
930
-		$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($root), \RecursiveIteratorIterator::CHILD_FIRST);
931
-		$size = 0;
932
-
933
-		/**
934
-		 * RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
935
-		 * This bug is fixed in PHP 5.5.9 or before
936
-		 * See #8376
937
-		 */
938
-		$iterator->rewind();
939
-		while ($iterator->valid()) {
940
-			$path = $iterator->current();
941
-			$relpath = substr($path, strlen($root) - 1);
942
-			if (!$view->is_dir($relpath)) {
943
-				$size += $view->filesize($relpath);
944
-			}
945
-			$iterator->next();
946
-		}
947
-		return $size;
948
-	}
949
-
950
-	/**
951
-	 * get current size of trash bin from a given user
952
-	 *
953
-	 * @param string $user user who owns the trash bin
954
-	 * @return integer trash bin size
955
-	 */
956
-	private static function getTrashbinSize($user) {
957
-		$view = new View('/' . $user);
958
-		$fileInfo = $view->getFileInfo('/files_trashbin');
959
-		return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
960
-	}
961
-
962
-	/**
963
-	 * register hooks
964
-	 */
965
-	public static function registerHooks() {
966
-		// create storage wrapper on setup
967
-		\OCP\Util::connectHook('OC_Filesystem', 'preSetup', 'OCA\Files_Trashbin\Storage', 'setupStorage');
968
-		//Listen to delete user signal
969
-		\OCP\Util::connectHook('OC_User', 'pre_deleteUser', 'OCA\Files_Trashbin\Hooks', 'deleteUser_hook');
970
-		//Listen to post write hook
971
-		\OCP\Util::connectHook('OC_Filesystem', 'post_write', 'OCA\Files_Trashbin\Hooks', 'post_write_hook');
972
-		// pre and post-rename, disable trash logic for the copy+unlink case
973
-		\OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Files_Trashbin\Trashbin', 'ensureFileScannedHook');
974
-		\OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Files_Trashbin\Storage', 'preRenameHook');
975
-		\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Files_Trashbin\Storage', 'postRenameHook');
976
-	}
977
-
978
-	/**
979
-	 * check if trash bin is empty for a given user
980
-	 *
981
-	 * @param string $user
982
-	 * @return bool
983
-	 */
984
-	public static function isEmpty($user) {
985
-
986
-		$view = new View('/' . $user . '/files_trashbin');
987
-		if ($view->is_dir('/files') && $dh = $view->opendir('/files')) {
988
-			while ($file = readdir($dh)) {
989
-				if (!Filesystem::isIgnoredDir($file)) {
990
-					return false;
991
-				}
992
-			}
993
-		}
994
-		return true;
995
-	}
996
-
997
-	/**
998
-	 * @param $path
999
-	 * @return string
1000
-	 */
1001
-	public static function preview_icon($path) {
1002
-		return \OCP\Util::linkToRoute('core_ajax_trashbin_preview', array('x' => 32, 'y' => 32, 'file' => $path));
1003
-	}
52
+    // unit: percentage; 50% of available disk space/quota
53
+    const DEFAULTMAXSIZE = 50;
54
+
55
+    /**
56
+     * Whether versions have already be rescanned during this PHP request
57
+     *
58
+     * @var bool
59
+     */
60
+    private static $scannedVersions = false;
61
+
62
+    /**
63
+     * Ensure we don't need to scan the file during the move to trash
64
+     * by triggering the scan in the pre-hook
65
+     *
66
+     * @param array $params
67
+     */
68
+    public static function ensureFileScannedHook($params) {
69
+        try {
70
+            self::getUidAndFilename($params['path']);
71
+        } catch (NotFoundException $e) {
72
+            // nothing to scan for non existing files
73
+        }
74
+    }
75
+
76
+    /**
77
+     * get the UID of the owner of the file and the path to the file relative to
78
+     * owners files folder
79
+     *
80
+     * @param string $filename
81
+     * @return array
82
+     * @throws \OC\User\NoUserException
83
+     */
84
+    public static function getUidAndFilename($filename) {
85
+        $uid = Filesystem::getOwner($filename);
86
+        $userManager = \OC::$server->getUserManager();
87
+        // if the user with the UID doesn't exists, e.g. because the UID points
88
+        // to a remote user with a federated cloud ID we use the current logged-in
89
+        // user. We need a valid local user to move the file to the right trash bin
90
+        if (!$userManager->userExists($uid)) {
91
+            $uid = User::getUser();
92
+        }
93
+        if (!$uid) {
94
+            // no owner, usually because of share link from ext storage
95
+            return [null, null];
96
+        }
97
+        Filesystem::initMountPoints($uid);
98
+        if ($uid != User::getUser()) {
99
+            $info = Filesystem::getFileInfo($filename);
100
+            $ownerView = new View('/' . $uid . '/files');
101
+            try {
102
+                $filename = $ownerView->getPath($info['fileid']);
103
+            } catch (NotFoundException $e) {
104
+                $filename = null;
105
+            }
106
+        }
107
+        return [$uid, $filename];
108
+    }
109
+
110
+    /**
111
+     * get original location of files for user
112
+     *
113
+     * @param string $user
114
+     * @return array (filename => array (timestamp => original location))
115
+     */
116
+    public static function getLocations($user) {
117
+        $query = \OC_DB::prepare('SELECT `id`, `timestamp`, `location`'
118
+            . ' FROM `*PREFIX*files_trash` WHERE `user`=?');
119
+        $result = $query->execute(array($user));
120
+        $array = array();
121
+        while ($row = $result->fetchRow()) {
122
+            if (isset($array[$row['id']])) {
123
+                $array[$row['id']][$row['timestamp']] = $row['location'];
124
+            } else {
125
+                $array[$row['id']] = array($row['timestamp'] => $row['location']);
126
+            }
127
+        }
128
+        return $array;
129
+    }
130
+
131
+    /**
132
+     * get original location of file
133
+     *
134
+     * @param string $user
135
+     * @param string $filename
136
+     * @param string $timestamp
137
+     * @return string original location
138
+     */
139
+    public static function getLocation($user, $filename, $timestamp) {
140
+        $query = \OC_DB::prepare('SELECT `location` FROM `*PREFIX*files_trash`'
141
+            . ' WHERE `user`=? AND `id`=? AND `timestamp`=?');
142
+        $result = $query->execute(array($user, $filename, $timestamp))->fetchAll();
143
+        if (isset($result[0]['location'])) {
144
+            return $result[0]['location'];
145
+        } else {
146
+            return false;
147
+        }
148
+    }
149
+
150
+    private static function setUpTrash($user) {
151
+        $view = new View('/' . $user);
152
+        if (!$view->is_dir('files_trashbin')) {
153
+            $view->mkdir('files_trashbin');
154
+        }
155
+        if (!$view->is_dir('files_trashbin/files')) {
156
+            $view->mkdir('files_trashbin/files');
157
+        }
158
+        if (!$view->is_dir('files_trashbin/versions')) {
159
+            $view->mkdir('files_trashbin/versions');
160
+        }
161
+        if (!$view->is_dir('files_trashbin/keys')) {
162
+            $view->mkdir('files_trashbin/keys');
163
+        }
164
+    }
165
+
166
+
167
+    /**
168
+     * copy file to owners trash
169
+     *
170
+     * @param string $sourcePath
171
+     * @param string $owner
172
+     * @param string $targetPath
173
+     * @param $user
174
+     * @param integer $timestamp
175
+     */
176
+    private static function copyFilesToUser($sourcePath, $owner, $targetPath, $user, $timestamp) {
177
+        self::setUpTrash($owner);
178
+
179
+        $targetFilename = basename($targetPath);
180
+        $targetLocation = dirname($targetPath);
181
+
182
+        $sourceFilename = basename($sourcePath);
183
+
184
+        $view = new View('/');
185
+
186
+        $target = $user . '/files_trashbin/files/' . $targetFilename . '.d' . $timestamp;
187
+        $source = $owner . '/files_trashbin/files/' . $sourceFilename . '.d' . $timestamp;
188
+        self::copy_recursive($source, $target, $view);
189
+
190
+
191
+        if ($view->file_exists($target)) {
192
+            $query = \OC_DB::prepare("INSERT INTO `*PREFIX*files_trash` (`id`,`timestamp`,`location`,`user`) VALUES (?,?,?,?)");
193
+            $result = $query->execute(array($targetFilename, $timestamp, $targetLocation, $user));
194
+            if (!$result) {
195
+                \OCP\Util::writeLog('files_trashbin', 'trash bin database couldn\'t be updated for the files owner', \OCP\Util::ERROR);
196
+            }
197
+        }
198
+    }
199
+
200
+
201
+    /**
202
+     * move file to the trash bin
203
+     *
204
+     * @param string $file_path path to the deleted file/directory relative to the files root directory
205
+     * @param bool $ownerOnly delete for owner only (if file gets moved out of a shared folder)
206
+     *
207
+     * @return bool
208
+     */
209
+    public static function move2trash($file_path, $ownerOnly = false) {
210
+        // get the user for which the filesystem is setup
211
+        $root = Filesystem::getRoot();
212
+        list(, $user) = explode('/', $root);
213
+        list($owner, $ownerPath) = self::getUidAndFilename($file_path);
214
+
215
+        // if no owner found (ex: ext storage + share link), will use the current user's trashbin then
216
+        if (is_null($owner)) {
217
+            $owner = $user;
218
+            $ownerPath = $file_path;
219
+        }
220
+
221
+        $ownerView = new View('/' . $owner);
222
+        // file has been deleted in between
223
+        if (is_null($ownerPath) || $ownerPath === '' || !$ownerView->file_exists('/files/' . $ownerPath)) {
224
+            return true;
225
+        }
226
+
227
+        self::setUpTrash($user);
228
+        if ($owner !== $user) {
229
+            // also setup for owner
230
+            self::setUpTrash($owner);
231
+        }
232
+
233
+        $path_parts = pathinfo($ownerPath);
234
+
235
+        $filename = $path_parts['basename'];
236
+        $location = $path_parts['dirname'];
237
+        $timestamp = time();
238
+
239
+        // disable proxy to prevent recursive calls
240
+        $trashPath = '/files_trashbin/files/' . $filename . '.d' . $timestamp;
241
+
242
+        /** @var \OC\Files\Storage\Storage $trashStorage */
243
+        list($trashStorage, $trashInternalPath) = $ownerView->resolvePath($trashPath);
244
+        /** @var \OC\Files\Storage\Storage $sourceStorage */
245
+        list($sourceStorage, $sourceInternalPath) = $ownerView->resolvePath('/files/' . $ownerPath);
246
+        try {
247
+            $moveSuccessful = true;
248
+            if ($trashStorage->file_exists($trashInternalPath)) {
249
+                $trashStorage->unlink($trashInternalPath);
250
+            }
251
+            $trashStorage->moveFromStorage($sourceStorage, $sourceInternalPath, $trashInternalPath);
252
+        } catch (\OCA\Files_Trashbin\Exceptions\CopyRecursiveException $e) {
253
+            $moveSuccessful = false;
254
+            if ($trashStorage->file_exists($trashInternalPath)) {
255
+                $trashStorage->unlink($trashInternalPath);
256
+            }
257
+            \OCP\Util::writeLog('files_trashbin', 'Couldn\'t move ' . $file_path . ' to the trash bin', \OCP\Util::ERROR);
258
+        }
259
+
260
+        if ($sourceStorage->file_exists($sourceInternalPath)) { // failed to delete the original file, abort
261
+            if ($sourceStorage->is_dir($sourceInternalPath)) {
262
+                $sourceStorage->rmdir($sourceInternalPath);
263
+            } else {
264
+                $sourceStorage->unlink($sourceInternalPath);
265
+            }
266
+            return false;
267
+        }
268
+
269
+        $trashStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $trashInternalPath);
270
+
271
+        if ($moveSuccessful) {
272
+            $query = \OC_DB::prepare("INSERT INTO `*PREFIX*files_trash` (`id`,`timestamp`,`location`,`user`) VALUES (?,?,?,?)");
273
+            $result = $query->execute(array($filename, $timestamp, $location, $owner));
274
+            if (!$result) {
275
+                \OCP\Util::writeLog('files_trashbin', 'trash bin database couldn\'t be updated', \OCP\Util::ERROR);
276
+            }
277
+            \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_moveToTrash', array('filePath' => Filesystem::normalizePath($file_path),
278
+                'trashPath' => Filesystem::normalizePath($filename . '.d' . $timestamp)));
279
+
280
+            self::retainVersions($filename, $owner, $ownerPath, $timestamp);
281
+
282
+            // if owner !== user we need to also add a copy to the users trash
283
+            if ($user !== $owner && $ownerOnly === false) {
284
+                self::copyFilesToUser($ownerPath, $owner, $file_path, $user, $timestamp);
285
+            }
286
+        }
287
+
288
+        self::scheduleExpire($user);
289
+
290
+        // if owner !== user we also need to update the owners trash size
291
+        if ($owner !== $user) {
292
+            self::scheduleExpire($owner);
293
+        }
294
+
295
+        return $moveSuccessful;
296
+    }
297
+
298
+    /**
299
+     * Move file versions to trash so that they can be restored later
300
+     *
301
+     * @param string $filename of deleted file
302
+     * @param string $owner owner user id
303
+     * @param string $ownerPath path relative to the owner's home storage
304
+     * @param integer $timestamp when the file was deleted
305
+     */
306
+    private static function retainVersions($filename, $owner, $ownerPath, $timestamp) {
307
+        if (\OCP\App::isEnabled('files_versions') && !empty($ownerPath)) {
308
+
309
+            $user = User::getUser();
310
+            $rootView = new View('/');
311
+
312
+            if ($rootView->is_dir($owner . '/files_versions/' . $ownerPath)) {
313
+                if ($owner !== $user) {
314
+                    self::copy_recursive($owner . '/files_versions/' . $ownerPath, $owner . '/files_trashbin/versions/' . basename($ownerPath) . '.d' . $timestamp, $rootView);
315
+                }
316
+                self::move($rootView, $owner . '/files_versions/' . $ownerPath, $user . '/files_trashbin/versions/' . $filename . '.d' . $timestamp);
317
+            } else if ($versions = \OCA\Files_Versions\Storage::getVersions($owner, $ownerPath)) {
318
+
319
+                foreach ($versions as $v) {
320
+                    if ($owner !== $user) {
321
+                        self::copy($rootView, $owner . '/files_versions' . $v['path'] . '.v' . $v['version'], $owner . '/files_trashbin/versions/' . $v['name'] . '.v' . $v['version'] . '.d' . $timestamp);
322
+                    }
323
+                    self::move($rootView, $owner . '/files_versions' . $v['path'] . '.v' . $v['version'], $user . '/files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp);
324
+                }
325
+            }
326
+        }
327
+    }
328
+
329
+    /**
330
+     * Move a file or folder on storage level
331
+     *
332
+     * @param View $view
333
+     * @param string $source
334
+     * @param string $target
335
+     * @return bool
336
+     */
337
+    private static function move(View $view, $source, $target) {
338
+        /** @var \OC\Files\Storage\Storage $sourceStorage */
339
+        list($sourceStorage, $sourceInternalPath) = $view->resolvePath($source);
340
+        /** @var \OC\Files\Storage\Storage $targetStorage */
341
+        list($targetStorage, $targetInternalPath) = $view->resolvePath($target);
342
+        /** @var \OC\Files\Storage\Storage $ownerTrashStorage */
343
+
344
+        $result = $targetStorage->moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
345
+        if ($result) {
346
+            $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
347
+        }
348
+        return $result;
349
+    }
350
+
351
+    /**
352
+     * Copy a file or folder on storage level
353
+     *
354
+     * @param View $view
355
+     * @param string $source
356
+     * @param string $target
357
+     * @return bool
358
+     */
359
+    private static function copy(View $view, $source, $target) {
360
+        /** @var \OC\Files\Storage\Storage $sourceStorage */
361
+        list($sourceStorage, $sourceInternalPath) = $view->resolvePath($source);
362
+        /** @var \OC\Files\Storage\Storage $targetStorage */
363
+        list($targetStorage, $targetInternalPath) = $view->resolvePath($target);
364
+        /** @var \OC\Files\Storage\Storage $ownerTrashStorage */
365
+
366
+        $result = $targetStorage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath);
367
+        if ($result) {
368
+            $targetStorage->getUpdater()->update($targetInternalPath);
369
+        }
370
+        return $result;
371
+    }
372
+
373
+    /**
374
+     * Restore a file or folder from trash bin
375
+     *
376
+     * @param string $file path to the deleted file/folder relative to "files_trashbin/files/",
377
+     * including the timestamp suffix ".d12345678"
378
+     * @param string $filename name of the file/folder
379
+     * @param int $timestamp time when the file/folder was deleted
380
+     *
381
+     * @return bool true on success, false otherwise
382
+     */
383
+    public static function restore($file, $filename, $timestamp) {
384
+        $user = User::getUser();
385
+        $view = new View('/' . $user);
386
+
387
+        $location = '';
388
+        if ($timestamp) {
389
+            $location = self::getLocation($user, $filename, $timestamp);
390
+            if ($location === false) {
391
+                \OCP\Util::writeLog('files_trashbin', 'trash bin database inconsistent! ($user: ' . $user . ' $filename: ' . $filename . ', $timestamp: ' . $timestamp . ')', \OCP\Util::ERROR);
392
+            } else {
393
+                // if location no longer exists, restore file in the root directory
394
+                if ($location !== '/' &&
395
+                    (!$view->is_dir('files/' . $location) ||
396
+                        !$view->isCreatable('files/' . $location))
397
+                ) {
398
+                    $location = '';
399
+                }
400
+            }
401
+        }
402
+
403
+        // we need a  extension in case a file/dir with the same name already exists
404
+        $uniqueFilename = self::getUniqueFilename($location, $filename, $view);
405
+
406
+        $source = Filesystem::normalizePath('files_trashbin/files/' . $file);
407
+        $target = Filesystem::normalizePath('files/' . $location . '/' . $uniqueFilename);
408
+        if (!$view->file_exists($source)) {
409
+            return false;
410
+        }
411
+        $mtime = $view->filemtime($source);
412
+
413
+        // restore file
414
+        $restoreResult = $view->rename($source, $target);
415
+
416
+        // handle the restore result
417
+        if ($restoreResult) {
418
+            $fakeRoot = $view->getRoot();
419
+            $view->chroot('/' . $user . '/files');
420
+            $view->touch('/' . $location . '/' . $uniqueFilename, $mtime);
421
+            $view->chroot($fakeRoot);
422
+            \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', array('filePath' => Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename),
423
+                'trashPath' => Filesystem::normalizePath($file)));
424
+
425
+            self::restoreVersions($view, $file, $filename, $uniqueFilename, $location, $timestamp);
426
+
427
+            if ($timestamp) {
428
+                $query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=? AND `id`=? AND `timestamp`=?');
429
+                $query->execute(array($user, $filename, $timestamp));
430
+            }
431
+
432
+            return true;
433
+        }
434
+
435
+        return false;
436
+    }
437
+
438
+    /**
439
+     * restore versions from trash bin
440
+     *
441
+     * @param View $view file view
442
+     * @param string $file complete path to file
443
+     * @param string $filename name of file once it was deleted
444
+     * @param string $uniqueFilename new file name to restore the file without overwriting existing files
445
+     * @param string $location location if file
446
+     * @param int $timestamp deletion time
447
+     * @return false|null
448
+     */
449
+    private static function restoreVersions(View $view, $file, $filename, $uniqueFilename, $location, $timestamp) {
450
+
451
+        if (\OCP\App::isEnabled('files_versions')) {
452
+
453
+            $user = User::getUser();
454
+            $rootView = new View('/');
455
+
456
+            $target = Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename);
457
+
458
+            list($owner, $ownerPath) = self::getUidAndFilename($target);
459
+
460
+            // file has been deleted in between
461
+            if (empty($ownerPath)) {
462
+                return false;
463
+            }
464
+
465
+            if ($timestamp) {
466
+                $versionedFile = $filename;
467
+            } else {
468
+                $versionedFile = $file;
469
+            }
470
+
471
+            if ($view->is_dir('/files_trashbin/versions/' . $file)) {
472
+                $rootView->rename(Filesystem::normalizePath($user . '/files_trashbin/versions/' . $file), Filesystem::normalizePath($owner . '/files_versions/' . $ownerPath));
473
+            } else if ($versions = self::getVersionsFromTrash($versionedFile, $timestamp, $user)) {
474
+                foreach ($versions as $v) {
475
+                    if ($timestamp) {
476
+                        $rootView->rename($user . '/files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, $owner . '/files_versions/' . $ownerPath . '.v' . $v);
477
+                    } else {
478
+                        $rootView->rename($user . '/files_trashbin/versions/' . $versionedFile . '.v' . $v, $owner . '/files_versions/' . $ownerPath . '.v' . $v);
479
+                    }
480
+                }
481
+            }
482
+        }
483
+    }
484
+
485
+    /**
486
+     * delete all files from the trash
487
+     */
488
+    public static function deleteAll() {
489
+        $user = User::getUser();
490
+        $userRoot = \OC::$server->getUserFolder($user)->getParent();
491
+        $view = new View('/' . $user);
492
+        $fileInfos = $view->getDirectoryContent('files_trashbin/files');
493
+
494
+        try {
495
+            $trash = $userRoot->get('files_trashbin');
496
+        } catch (NotFoundException $e) {
497
+            return false;
498
+        }
499
+
500
+        // Array to store the relative path in (after the file is deleted, the view won't be able to relativise the path anymore)
501
+        $filePaths = array();
502
+        foreach($fileInfos as $fileInfo){
503
+            $filePaths[] = $view->getRelativePath($fileInfo->getPath());
504
+        }
505
+        unset($fileInfos); // save memory
506
+
507
+        // Bulk PreDelete-Hook
508
+        \OC_Hook::emit('\OCP\Trashbin', 'preDeleteAll', array('paths' => $filePaths));
509
+
510
+        // Single-File Hooks
511
+        foreach($filePaths as $path){
512
+            self::emitTrashbinPreDelete($path);
513
+        }
514
+
515
+        // actual file deletion
516
+        $trash->delete();
517
+        $query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=?');
518
+        $query->execute(array($user));
519
+
520
+        // Bulk PostDelete-Hook
521
+        \OC_Hook::emit('\OCP\Trashbin', 'deleteAll', array('paths' => $filePaths));
522
+
523
+        // Single-File Hooks
524
+        foreach($filePaths as $path){
525
+            self::emitTrashbinPostDelete($path);
526
+        }
527
+
528
+        $trash = $userRoot->newFolder('files_trashbin');
529
+        $trash->newFolder('files');
530
+
531
+        return true;
532
+    }
533
+
534
+    /**
535
+     * wrapper function to emit the 'preDelete' hook of \OCP\Trashbin before a file is deleted
536
+     * @param string $path
537
+     */
538
+    protected static function emitTrashbinPreDelete($path){
539
+        \OC_Hook::emit('\OCP\Trashbin', 'preDelete', array('path' => $path));
540
+    }
541
+
542
+    /**
543
+     * wrapper function to emit the 'delete' hook of \OCP\Trashbin after a file has been deleted
544
+     * @param string $path
545
+     */
546
+    protected static function emitTrashbinPostDelete($path){
547
+        \OC_Hook::emit('\OCP\Trashbin', 'delete', array('path' => $path));
548
+    }
549
+
550
+    /**
551
+     * delete file from trash bin permanently
552
+     *
553
+     * @param string $filename path to the file
554
+     * @param string $user
555
+     * @param int $timestamp of deletion time
556
+     *
557
+     * @return int size of deleted files
558
+     */
559
+    public static function delete($filename, $user, $timestamp = null) {
560
+        $userRoot = \OC::$server->getUserFolder($user)->getParent();
561
+        $view = new View('/' . $user);
562
+        $size = 0;
563
+
564
+        if ($timestamp) {
565
+            $query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=? AND `id`=? AND `timestamp`=?');
566
+            $query->execute(array($user, $filename, $timestamp));
567
+            $file = $filename . '.d' . $timestamp;
568
+        } else {
569
+            $file = $filename;
570
+        }
571
+
572
+        $size += self::deleteVersions($view, $file, $filename, $timestamp, $user);
573
+
574
+        try {
575
+            $node = $userRoot->get('/files_trashbin/files/' . $file);
576
+        } catch (NotFoundException $e) {
577
+            return $size;
578
+        }
579
+
580
+        if ($node instanceof Folder) {
581
+            $size += self::calculateSize(new View('/' . $user . '/files_trashbin/files/' . $file));
582
+        } else if ($node instanceof File) {
583
+            $size += $view->filesize('/files_trashbin/files/' . $file);
584
+        }
585
+
586
+        self::emitTrashbinPreDelete('/files_trashbin/files/' . $file);
587
+        $node->delete();
588
+        self::emitTrashbinPostDelete('/files_trashbin/files/' . $file);
589
+
590
+        return $size;
591
+    }
592
+
593
+    /**
594
+     * @param View $view
595
+     * @param string $file
596
+     * @param string $filename
597
+     * @param integer|null $timestamp
598
+     * @param string $user
599
+     * @return int
600
+     */
601
+    private static function deleteVersions(View $view, $file, $filename, $timestamp, $user) {
602
+        $size = 0;
603
+        if (\OCP\App::isEnabled('files_versions')) {
604
+            if ($view->is_dir('files_trashbin/versions/' . $file)) {
605
+                $size += self::calculateSize(new View('/' . $user . '/files_trashbin/versions/' . $file));
606
+                $view->unlink('files_trashbin/versions/' . $file);
607
+            } else if ($versions = self::getVersionsFromTrash($filename, $timestamp, $user)) {
608
+                foreach ($versions as $v) {
609
+                    if ($timestamp) {
610
+                        $size += $view->filesize('/files_trashbin/versions/' . $filename . '.v' . $v . '.d' . $timestamp);
611
+                        $view->unlink('/files_trashbin/versions/' . $filename . '.v' . $v . '.d' . $timestamp);
612
+                    } else {
613
+                        $size += $view->filesize('/files_trashbin/versions/' . $filename . '.v' . $v);
614
+                        $view->unlink('/files_trashbin/versions/' . $filename . '.v' . $v);
615
+                    }
616
+                }
617
+            }
618
+        }
619
+        return $size;
620
+    }
621
+
622
+    /**
623
+     * check to see whether a file exists in trashbin
624
+     *
625
+     * @param string $filename path to the file
626
+     * @param int $timestamp of deletion time
627
+     * @return bool true if file exists, otherwise false
628
+     */
629
+    public static function file_exists($filename, $timestamp = null) {
630
+        $user = User::getUser();
631
+        $view = new View('/' . $user);
632
+
633
+        if ($timestamp) {
634
+            $filename = $filename . '.d' . $timestamp;
635
+        } else {
636
+            $filename = $filename;
637
+        }
638
+
639
+        $target = Filesystem::normalizePath('files_trashbin/files/' . $filename);
640
+        return $view->file_exists($target);
641
+    }
642
+
643
+    /**
644
+     * deletes used space for trash bin in db if user was deleted
645
+     *
646
+     * @param string $uid id of deleted user
647
+     * @return bool result of db delete operation
648
+     */
649
+    public static function deleteUser($uid) {
650
+        $query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=?');
651
+        return $query->execute(array($uid));
652
+    }
653
+
654
+    /**
655
+     * calculate remaining free space for trash bin
656
+     *
657
+     * @param integer $trashbinSize current size of the trash bin
658
+     * @param string $user
659
+     * @return int available free space for trash bin
660
+     */
661
+    private static function calculateFreeSpace($trashbinSize, $user) {
662
+        $softQuota = true;
663
+        $userObject = \OC::$server->getUserManager()->get($user);
664
+        if(is_null($userObject)) {
665
+            return 0;
666
+        }
667
+        $quota = $userObject->getQuota();
668
+        if ($quota === null || $quota === 'none') {
669
+            $quota = Filesystem::free_space('/');
670
+            $softQuota = false;
671
+            // inf or unknown free space
672
+            if ($quota < 0) {
673
+                $quota = PHP_INT_MAX;
674
+            }
675
+        } else {
676
+            $quota = \OCP\Util::computerFileSize($quota);
677
+        }
678
+
679
+        // calculate available space for trash bin
680
+        // subtract size of files and current trash bin size from quota
681
+        if ($softQuota) {
682
+            $userFolder = \OC::$server->getUserFolder($user);
683
+            if(is_null($userFolder)) {
684
+                return 0;
685
+            }
686
+            $free = $quota - $userFolder->getSize(); // remaining free space for user
687
+            if ($free > 0) {
688
+                $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $trashbinSize; // how much space can be used for versions
689
+            } else {
690
+                $availableSpace = $free - $trashbinSize;
691
+            }
692
+        } else {
693
+            $availableSpace = $quota;
694
+        }
695
+
696
+        return $availableSpace;
697
+    }
698
+
699
+    /**
700
+     * resize trash bin if necessary after a new file was added to Nextcloud
701
+     *
702
+     * @param string $user user id
703
+     */
704
+    public static function resizeTrash($user) {
705
+
706
+        $size = self::getTrashbinSize($user);
707
+
708
+        $freeSpace = self::calculateFreeSpace($size, $user);
709
+
710
+        if ($freeSpace < 0) {
711
+            self::scheduleExpire($user);
712
+        }
713
+    }
714
+
715
+    /**
716
+     * clean up the trash bin
717
+     *
718
+     * @param string $user
719
+     */
720
+    public static function expire($user) {
721
+        $trashBinSize = self::getTrashbinSize($user);
722
+        $availableSpace = self::calculateFreeSpace($trashBinSize, $user);
723
+
724
+        $dirContent = Helper::getTrashFiles('/', $user, 'mtime');
725
+
726
+        // delete all files older then $retention_obligation
727
+        list($delSize, $count) = self::deleteExpiredFiles($dirContent, $user);
728
+
729
+        $availableSpace += $delSize;
730
+
731
+        // delete files from trash until we meet the trash bin size limit again
732
+        self::deleteFiles(array_slice($dirContent, $count), $user, $availableSpace);
733
+    }
734
+
735
+    /**
736
+     * @param string $user
737
+     */
738
+    private static function scheduleExpire($user) {
739
+        // let the admin disable auto expire
740
+        $application = new Application();
741
+        $expiration = $application->getContainer()->query('Expiration');
742
+        if ($expiration->isEnabled()) {
743
+            \OC::$server->getCommandBus()->push(new Expire($user));
744
+        }
745
+    }
746
+
747
+    /**
748
+     * if the size limit for the trash bin is reached, we delete the oldest
749
+     * files in the trash bin until we meet the limit again
750
+     *
751
+     * @param array $files
752
+     * @param string $user
753
+     * @param int $availableSpace available disc space
754
+     * @return int size of deleted files
755
+     */
756
+    protected static function deleteFiles($files, $user, $availableSpace) {
757
+        $application = new Application();
758
+        $expiration = $application->getContainer()->query('Expiration');
759
+        $size = 0;
760
+
761
+        if ($availableSpace < 0) {
762
+            foreach ($files as $file) {
763
+                if ($availableSpace < 0 && $expiration->isExpired($file['mtime'], true)) {
764
+                    $tmp = self::delete($file['name'], $user, $file['mtime']);
765
+                    \OCP\Util::writeLog('files_trashbin', 'remove "' . $file['name'] . '" (' . $tmp . 'B) to meet the limit of trash bin size (50% of available quota)', \OCP\Util::INFO);
766
+                    $availableSpace += $tmp;
767
+                    $size += $tmp;
768
+                } else {
769
+                    break;
770
+                }
771
+            }
772
+        }
773
+        return $size;
774
+    }
775
+
776
+    /**
777
+     * delete files older then max storage time
778
+     *
779
+     * @param array $files list of files sorted by mtime
780
+     * @param string $user
781
+     * @return integer[] size of deleted files and number of deleted files
782
+     */
783
+    public static function deleteExpiredFiles($files, $user) {
784
+        $application = new Application();
785
+        $expiration = $application->getContainer()->query('Expiration');
786
+        $size = 0;
787
+        $count = 0;
788
+        foreach ($files as $file) {
789
+            $timestamp = $file['mtime'];
790
+            $filename = $file['name'];
791
+            if ($expiration->isExpired($timestamp)) {
792
+                $count++;
793
+                $size += self::delete($filename, $user, $timestamp);
794
+                \OC::$server->getLogger()->info(
795
+                    'Remove "' . $filename . '" from trashbin because it exceeds max retention obligation term.',
796
+                    ['app' => 'files_trashbin']
797
+                );
798
+            } else {
799
+                break;
800
+            }
801
+        }
802
+
803
+        return array($size, $count);
804
+    }
805
+
806
+    /**
807
+     * recursive copy to copy a whole directory
808
+     *
809
+     * @param string $source source path, relative to the users files directory
810
+     * @param string $destination destination path relative to the users root directoy
811
+     * @param View $view file view for the users root directory
812
+     * @return int
813
+     * @throws Exceptions\CopyRecursiveException
814
+     */
815
+    private static function copy_recursive($source, $destination, View $view) {
816
+        $size = 0;
817
+        if ($view->is_dir($source)) {
818
+            $view->mkdir($destination);
819
+            $view->touch($destination, $view->filemtime($source));
820
+            foreach ($view->getDirectoryContent($source) as $i) {
821
+                $pathDir = $source . '/' . $i['name'];
822
+                if ($view->is_dir($pathDir)) {
823
+                    $size += self::copy_recursive($pathDir, $destination . '/' . $i['name'], $view);
824
+                } else {
825
+                    $size += $view->filesize($pathDir);
826
+                    $result = $view->copy($pathDir, $destination . '/' . $i['name']);
827
+                    if (!$result) {
828
+                        throw new \OCA\Files_Trashbin\Exceptions\CopyRecursiveException();
829
+                    }
830
+                    $view->touch($destination . '/' . $i['name'], $view->filemtime($pathDir));
831
+                }
832
+            }
833
+        } else {
834
+            $size += $view->filesize($source);
835
+            $result = $view->copy($source, $destination);
836
+            if (!$result) {
837
+                throw new \OCA\Files_Trashbin\Exceptions\CopyRecursiveException();
838
+            }
839
+            $view->touch($destination, $view->filemtime($source));
840
+        }
841
+        return $size;
842
+    }
843
+
844
+    /**
845
+     * find all versions which belong to the file we want to restore
846
+     *
847
+     * @param string $filename name of the file which should be restored
848
+     * @param int $timestamp timestamp when the file was deleted
849
+     * @return array
850
+     */
851
+    private static function getVersionsFromTrash($filename, $timestamp, $user) {
852
+        $view = new View('/' . $user . '/files_trashbin/versions');
853
+        $versions = array();
854
+
855
+        //force rescan of versions, local storage may not have updated the cache
856
+        if (!self::$scannedVersions) {
857
+            /** @var \OC\Files\Storage\Storage $storage */
858
+            list($storage,) = $view->resolvePath('/');
859
+            $storage->getScanner()->scan('files_trashbin/versions');
860
+            self::$scannedVersions = true;
861
+        }
862
+
863
+        if ($timestamp) {
864
+            // fetch for old versions
865
+            $matches = $view->searchRaw($filename . '.v%.d' . $timestamp);
866
+            $offset = -strlen($timestamp) - 2;
867
+        } else {
868
+            $matches = $view->searchRaw($filename . '.v%');
869
+        }
870
+
871
+        if (is_array($matches)) {
872
+            foreach ($matches as $ma) {
873
+                if ($timestamp) {
874
+                    $parts = explode('.v', substr($ma['path'], 0, $offset));
875
+                    $versions[] = (end($parts));
876
+                } else {
877
+                    $parts = explode('.v', $ma);
878
+                    $versions[] = (end($parts));
879
+                }
880
+            }
881
+        }
882
+        return $versions;
883
+    }
884
+
885
+    /**
886
+     * find unique extension for restored file if a file with the same name already exists
887
+     *
888
+     * @param string $location where the file should be restored
889
+     * @param string $filename name of the file
890
+     * @param View $view filesystem view relative to users root directory
891
+     * @return string with unique extension
892
+     */
893
+    private static function getUniqueFilename($location, $filename, View $view) {
894
+        $ext = pathinfo($filename, PATHINFO_EXTENSION);
895
+        $name = pathinfo($filename, PATHINFO_FILENAME);
896
+        $l = \OC::$server->getL10N('files_trashbin');
897
+
898
+        $location = '/' . trim($location, '/');
899
+
900
+        // if extension is not empty we set a dot in front of it
901
+        if ($ext !== '') {
902
+            $ext = '.' . $ext;
903
+        }
904
+
905
+        if ($view->file_exists('files' . $location . '/' . $filename)) {
906
+            $i = 2;
907
+            $uniqueName = $name . " (" . $l->t("restored") . ")" . $ext;
908
+            while ($view->file_exists('files' . $location . '/' . $uniqueName)) {
909
+                $uniqueName = $name . " (" . $l->t("restored") . " " . $i . ")" . $ext;
910
+                $i++;
911
+            }
912
+
913
+            return $uniqueName;
914
+        }
915
+
916
+        return $filename;
917
+    }
918
+
919
+    /**
920
+     * get the size from a given root folder
921
+     *
922
+     * @param View $view file view on the root folder
923
+     * @return integer size of the folder
924
+     */
925
+    private static function calculateSize($view) {
926
+        $root = \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . $view->getAbsolutePath('');
927
+        if (!file_exists($root)) {
928
+            return 0;
929
+        }
930
+        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($root), \RecursiveIteratorIterator::CHILD_FIRST);
931
+        $size = 0;
932
+
933
+        /**
934
+         * RecursiveDirectoryIterator on an NFS path isn't iterable with foreach
935
+         * This bug is fixed in PHP 5.5.9 or before
936
+         * See #8376
937
+         */
938
+        $iterator->rewind();
939
+        while ($iterator->valid()) {
940
+            $path = $iterator->current();
941
+            $relpath = substr($path, strlen($root) - 1);
942
+            if (!$view->is_dir($relpath)) {
943
+                $size += $view->filesize($relpath);
944
+            }
945
+            $iterator->next();
946
+        }
947
+        return $size;
948
+    }
949
+
950
+    /**
951
+     * get current size of trash bin from a given user
952
+     *
953
+     * @param string $user user who owns the trash bin
954
+     * @return integer trash bin size
955
+     */
956
+    private static function getTrashbinSize($user) {
957
+        $view = new View('/' . $user);
958
+        $fileInfo = $view->getFileInfo('/files_trashbin');
959
+        return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
960
+    }
961
+
962
+    /**
963
+     * register hooks
964
+     */
965
+    public static function registerHooks() {
966
+        // create storage wrapper on setup
967
+        \OCP\Util::connectHook('OC_Filesystem', 'preSetup', 'OCA\Files_Trashbin\Storage', 'setupStorage');
968
+        //Listen to delete user signal
969
+        \OCP\Util::connectHook('OC_User', 'pre_deleteUser', 'OCA\Files_Trashbin\Hooks', 'deleteUser_hook');
970
+        //Listen to post write hook
971
+        \OCP\Util::connectHook('OC_Filesystem', 'post_write', 'OCA\Files_Trashbin\Hooks', 'post_write_hook');
972
+        // pre and post-rename, disable trash logic for the copy+unlink case
973
+        \OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Files_Trashbin\Trashbin', 'ensureFileScannedHook');
974
+        \OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Files_Trashbin\Storage', 'preRenameHook');
975
+        \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Files_Trashbin\Storage', 'postRenameHook');
976
+    }
977
+
978
+    /**
979
+     * check if trash bin is empty for a given user
980
+     *
981
+     * @param string $user
982
+     * @return bool
983
+     */
984
+    public static function isEmpty($user) {
985
+
986
+        $view = new View('/' . $user . '/files_trashbin');
987
+        if ($view->is_dir('/files') && $dh = $view->opendir('/files')) {
988
+            while ($file = readdir($dh)) {
989
+                if (!Filesystem::isIgnoredDir($file)) {
990
+                    return false;
991
+                }
992
+            }
993
+        }
994
+        return true;
995
+    }
996
+
997
+    /**
998
+     * @param $path
999
+     * @return string
1000
+     */
1001
+    public static function preview_icon($path) {
1002
+        return \OCP\Util::linkToRoute('core_ajax_trashbin_preview', array('x' => 32, 'y' => 32, 'file' => $path));
1003
+    }
1004 1004
 }
Please login to merge, or discard this patch.
Spacing   +72 added lines, -72 removed lines patch added patch discarded remove patch
@@ -97,7 +97,7 @@  discard block
 block discarded – undo
97 97
 		Filesystem::initMountPoints($uid);
98 98
 		if ($uid != User::getUser()) {
99 99
 			$info = Filesystem::getFileInfo($filename);
100
-			$ownerView = new View('/' . $uid . '/files');
100
+			$ownerView = new View('/'.$uid.'/files');
101 101
 			try {
102 102
 				$filename = $ownerView->getPath($info['fileid']);
103 103
 			} catch (NotFoundException $e) {
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
 	}
149 149
 
150 150
 	private static function setUpTrash($user) {
151
-		$view = new View('/' . $user);
151
+		$view = new View('/'.$user);
152 152
 		if (!$view->is_dir('files_trashbin')) {
153 153
 			$view->mkdir('files_trashbin');
154 154
 		}
@@ -183,8 +183,8 @@  discard block
 block discarded – undo
183 183
 
184 184
 		$view = new View('/');
185 185
 
186
-		$target = $user . '/files_trashbin/files/' . $targetFilename . '.d' . $timestamp;
187
-		$source = $owner . '/files_trashbin/files/' . $sourceFilename . '.d' . $timestamp;
186
+		$target = $user.'/files_trashbin/files/'.$targetFilename.'.d'.$timestamp;
187
+		$source = $owner.'/files_trashbin/files/'.$sourceFilename.'.d'.$timestamp;
188 188
 		self::copy_recursive($source, $target, $view);
189 189
 
190 190
 
@@ -218,9 +218,9 @@  discard block
 block discarded – undo
218 218
 			$ownerPath = $file_path;
219 219
 		}
220 220
 
221
-		$ownerView = new View('/' . $owner);
221
+		$ownerView = new View('/'.$owner);
222 222
 		// file has been deleted in between
223
-		if (is_null($ownerPath) || $ownerPath === '' || !$ownerView->file_exists('/files/' . $ownerPath)) {
223
+		if (is_null($ownerPath) || $ownerPath === '' || !$ownerView->file_exists('/files/'.$ownerPath)) {
224 224
 			return true;
225 225
 		}
226 226
 
@@ -237,12 +237,12 @@  discard block
 block discarded – undo
237 237
 		$timestamp = time();
238 238
 
239 239
 		// disable proxy to prevent recursive calls
240
-		$trashPath = '/files_trashbin/files/' . $filename . '.d' . $timestamp;
240
+		$trashPath = '/files_trashbin/files/'.$filename.'.d'.$timestamp;
241 241
 
242 242
 		/** @var \OC\Files\Storage\Storage $trashStorage */
243 243
 		list($trashStorage, $trashInternalPath) = $ownerView->resolvePath($trashPath);
244 244
 		/** @var \OC\Files\Storage\Storage $sourceStorage */
245
-		list($sourceStorage, $sourceInternalPath) = $ownerView->resolvePath('/files/' . $ownerPath);
245
+		list($sourceStorage, $sourceInternalPath) = $ownerView->resolvePath('/files/'.$ownerPath);
246 246
 		try {
247 247
 			$moveSuccessful = true;
248 248
 			if ($trashStorage->file_exists($trashInternalPath)) {
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
 			if ($trashStorage->file_exists($trashInternalPath)) {
255 255
 				$trashStorage->unlink($trashInternalPath);
256 256
 			}
257
-			\OCP\Util::writeLog('files_trashbin', 'Couldn\'t move ' . $file_path . ' to the trash bin', \OCP\Util::ERROR);
257
+			\OCP\Util::writeLog('files_trashbin', 'Couldn\'t move '.$file_path.' to the trash bin', \OCP\Util::ERROR);
258 258
 		}
259 259
 
260 260
 		if ($sourceStorage->file_exists($sourceInternalPath)) { // failed to delete the original file, abort
@@ -275,7 +275,7 @@  discard block
 block discarded – undo
275 275
 				\OCP\Util::writeLog('files_trashbin', 'trash bin database couldn\'t be updated', \OCP\Util::ERROR);
276 276
 			}
277 277
 			\OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_moveToTrash', array('filePath' => Filesystem::normalizePath($file_path),
278
-				'trashPath' => Filesystem::normalizePath($filename . '.d' . $timestamp)));
278
+				'trashPath' => Filesystem::normalizePath($filename.'.d'.$timestamp)));
279 279
 
280 280
 			self::retainVersions($filename, $owner, $ownerPath, $timestamp);
281 281
 
@@ -309,18 +309,18 @@  discard block
 block discarded – undo
309 309
 			$user = User::getUser();
310 310
 			$rootView = new View('/');
311 311
 
312
-			if ($rootView->is_dir($owner . '/files_versions/' . $ownerPath)) {
312
+			if ($rootView->is_dir($owner.'/files_versions/'.$ownerPath)) {
313 313
 				if ($owner !== $user) {
314
-					self::copy_recursive($owner . '/files_versions/' . $ownerPath, $owner . '/files_trashbin/versions/' . basename($ownerPath) . '.d' . $timestamp, $rootView);
314
+					self::copy_recursive($owner.'/files_versions/'.$ownerPath, $owner.'/files_trashbin/versions/'.basename($ownerPath).'.d'.$timestamp, $rootView);
315 315
 				}
316
-				self::move($rootView, $owner . '/files_versions/' . $ownerPath, $user . '/files_trashbin/versions/' . $filename . '.d' . $timestamp);
316
+				self::move($rootView, $owner.'/files_versions/'.$ownerPath, $user.'/files_trashbin/versions/'.$filename.'.d'.$timestamp);
317 317
 			} else if ($versions = \OCA\Files_Versions\Storage::getVersions($owner, $ownerPath)) {
318 318
 
319 319
 				foreach ($versions as $v) {
320 320
 					if ($owner !== $user) {
321
-						self::copy($rootView, $owner . '/files_versions' . $v['path'] . '.v' . $v['version'], $owner . '/files_trashbin/versions/' . $v['name'] . '.v' . $v['version'] . '.d' . $timestamp);
321
+						self::copy($rootView, $owner.'/files_versions'.$v['path'].'.v'.$v['version'], $owner.'/files_trashbin/versions/'.$v['name'].'.v'.$v['version'].'.d'.$timestamp);
322 322
 					}
323
-					self::move($rootView, $owner . '/files_versions' . $v['path'] . '.v' . $v['version'], $user . '/files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp);
323
+					self::move($rootView, $owner.'/files_versions'.$v['path'].'.v'.$v['version'], $user.'/files_trashbin/versions/'.$filename.'.v'.$v['version'].'.d'.$timestamp);
324 324
 				}
325 325
 			}
326 326
 		}
@@ -382,18 +382,18 @@  discard block
 block discarded – undo
382 382
 	 */
383 383
 	public static function restore($file, $filename, $timestamp) {
384 384
 		$user = User::getUser();
385
-		$view = new View('/' . $user);
385
+		$view = new View('/'.$user);
386 386
 
387 387
 		$location = '';
388 388
 		if ($timestamp) {
389 389
 			$location = self::getLocation($user, $filename, $timestamp);
390 390
 			if ($location === false) {
391
-				\OCP\Util::writeLog('files_trashbin', 'trash bin database inconsistent! ($user: ' . $user . ' $filename: ' . $filename . ', $timestamp: ' . $timestamp . ')', \OCP\Util::ERROR);
391
+				\OCP\Util::writeLog('files_trashbin', 'trash bin database inconsistent! ($user: '.$user.' $filename: '.$filename.', $timestamp: '.$timestamp.')', \OCP\Util::ERROR);
392 392
 			} else {
393 393
 				// if location no longer exists, restore file in the root directory
394 394
 				if ($location !== '/' &&
395
-					(!$view->is_dir('files/' . $location) ||
396
-						!$view->isCreatable('files/' . $location))
395
+					(!$view->is_dir('files/'.$location) ||
396
+						!$view->isCreatable('files/'.$location))
397 397
 				) {
398 398
 					$location = '';
399 399
 				}
@@ -403,8 +403,8 @@  discard block
 block discarded – undo
403 403
 		// we need a  extension in case a file/dir with the same name already exists
404 404
 		$uniqueFilename = self::getUniqueFilename($location, $filename, $view);
405 405
 
406
-		$source = Filesystem::normalizePath('files_trashbin/files/' . $file);
407
-		$target = Filesystem::normalizePath('files/' . $location . '/' . $uniqueFilename);
406
+		$source = Filesystem::normalizePath('files_trashbin/files/'.$file);
407
+		$target = Filesystem::normalizePath('files/'.$location.'/'.$uniqueFilename);
408 408
 		if (!$view->file_exists($source)) {
409 409
 			return false;
410 410
 		}
@@ -416,10 +416,10 @@  discard block
 block discarded – undo
416 416
 		// handle the restore result
417 417
 		if ($restoreResult) {
418 418
 			$fakeRoot = $view->getRoot();
419
-			$view->chroot('/' . $user . '/files');
420
-			$view->touch('/' . $location . '/' . $uniqueFilename, $mtime);
419
+			$view->chroot('/'.$user.'/files');
420
+			$view->touch('/'.$location.'/'.$uniqueFilename, $mtime);
421 421
 			$view->chroot($fakeRoot);
422
-			\OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', array('filePath' => Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename),
422
+			\OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', array('filePath' => Filesystem::normalizePath('/'.$location.'/'.$uniqueFilename),
423 423
 				'trashPath' => Filesystem::normalizePath($file)));
424 424
 
425 425
 			self::restoreVersions($view, $file, $filename, $uniqueFilename, $location, $timestamp);
@@ -453,7 +453,7 @@  discard block
 block discarded – undo
453 453
 			$user = User::getUser();
454 454
 			$rootView = new View('/');
455 455
 
456
-			$target = Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename);
456
+			$target = Filesystem::normalizePath('/'.$location.'/'.$uniqueFilename);
457 457
 
458 458
 			list($owner, $ownerPath) = self::getUidAndFilename($target);
459 459
 
@@ -468,14 +468,14 @@  discard block
 block discarded – undo
468 468
 				$versionedFile = $file;
469 469
 			}
470 470
 
471
-			if ($view->is_dir('/files_trashbin/versions/' . $file)) {
472
-				$rootView->rename(Filesystem::normalizePath($user . '/files_trashbin/versions/' . $file), Filesystem::normalizePath($owner . '/files_versions/' . $ownerPath));
471
+			if ($view->is_dir('/files_trashbin/versions/'.$file)) {
472
+				$rootView->rename(Filesystem::normalizePath($user.'/files_trashbin/versions/'.$file), Filesystem::normalizePath($owner.'/files_versions/'.$ownerPath));
473 473
 			} else if ($versions = self::getVersionsFromTrash($versionedFile, $timestamp, $user)) {
474 474
 				foreach ($versions as $v) {
475 475
 					if ($timestamp) {
476
-						$rootView->rename($user . '/files_trashbin/versions/' . $versionedFile . '.v' . $v . '.d' . $timestamp, $owner . '/files_versions/' . $ownerPath . '.v' . $v);
476
+						$rootView->rename($user.'/files_trashbin/versions/'.$versionedFile.'.v'.$v.'.d'.$timestamp, $owner.'/files_versions/'.$ownerPath.'.v'.$v);
477 477
 					} else {
478
-						$rootView->rename($user . '/files_trashbin/versions/' . $versionedFile . '.v' . $v, $owner . '/files_versions/' . $ownerPath . '.v' . $v);
478
+						$rootView->rename($user.'/files_trashbin/versions/'.$versionedFile.'.v'.$v, $owner.'/files_versions/'.$ownerPath.'.v'.$v);
479 479
 					}
480 480
 				}
481 481
 			}
@@ -488,7 +488,7 @@  discard block
 block discarded – undo
488 488
 	public static function deleteAll() {
489 489
 		$user = User::getUser();
490 490
 		$userRoot = \OC::$server->getUserFolder($user)->getParent();
491
-		$view = new View('/' . $user);
491
+		$view = new View('/'.$user);
492 492
 		$fileInfos = $view->getDirectoryContent('files_trashbin/files');
493 493
 
494 494
 		try {
@@ -499,7 +499,7 @@  discard block
 block discarded – undo
499 499
 
500 500
 		// Array to store the relative path in (after the file is deleted, the view won't be able to relativise the path anymore)
501 501
 		$filePaths = array();
502
-		foreach($fileInfos as $fileInfo){
502
+		foreach ($fileInfos as $fileInfo) {
503 503
 			$filePaths[] = $view->getRelativePath($fileInfo->getPath());
504 504
 		}
505 505
 		unset($fileInfos); // save memory
@@ -508,7 +508,7 @@  discard block
 block discarded – undo
508 508
 		\OC_Hook::emit('\OCP\Trashbin', 'preDeleteAll', array('paths' => $filePaths));
509 509
 
510 510
 		// Single-File Hooks
511
-		foreach($filePaths as $path){
511
+		foreach ($filePaths as $path) {
512 512
 			self::emitTrashbinPreDelete($path);
513 513
 		}
514 514
 
@@ -521,7 +521,7 @@  discard block
 block discarded – undo
521 521
 		\OC_Hook::emit('\OCP\Trashbin', 'deleteAll', array('paths' => $filePaths));
522 522
 
523 523
 		// Single-File Hooks
524
-		foreach($filePaths as $path){
524
+		foreach ($filePaths as $path) {
525 525
 			self::emitTrashbinPostDelete($path);
526 526
 		}
527 527
 
@@ -535,7 +535,7 @@  discard block
 block discarded – undo
535 535
 	 * wrapper function to emit the 'preDelete' hook of \OCP\Trashbin before a file is deleted
536 536
 	 * @param string $path
537 537
 	 */
538
-	protected static function emitTrashbinPreDelete($path){
538
+	protected static function emitTrashbinPreDelete($path) {
539 539
 		\OC_Hook::emit('\OCP\Trashbin', 'preDelete', array('path' => $path));
540 540
 	}
541 541
 
@@ -543,7 +543,7 @@  discard block
 block discarded – undo
543 543
 	 * wrapper function to emit the 'delete' hook of \OCP\Trashbin after a file has been deleted
544 544
 	 * @param string $path
545 545
 	 */
546
-	protected static function emitTrashbinPostDelete($path){
546
+	protected static function emitTrashbinPostDelete($path) {
547 547
 		\OC_Hook::emit('\OCP\Trashbin', 'delete', array('path' => $path));
548 548
 	}
549 549
 
@@ -558,13 +558,13 @@  discard block
 block discarded – undo
558 558
 	 */
559 559
 	public static function delete($filename, $user, $timestamp = null) {
560 560
 		$userRoot = \OC::$server->getUserFolder($user)->getParent();
561
-		$view = new View('/' . $user);
561
+		$view = new View('/'.$user);
562 562
 		$size = 0;
563 563
 
564 564
 		if ($timestamp) {
565 565
 			$query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=? AND `id`=? AND `timestamp`=?');
566 566
 			$query->execute(array($user, $filename, $timestamp));
567
-			$file = $filename . '.d' . $timestamp;
567
+			$file = $filename.'.d'.$timestamp;
568 568
 		} else {
569 569
 			$file = $filename;
570 570
 		}
@@ -572,20 +572,20 @@  discard block
 block discarded – undo
572 572
 		$size += self::deleteVersions($view, $file, $filename, $timestamp, $user);
573 573
 
574 574
 		try {
575
-			$node = $userRoot->get('/files_trashbin/files/' . $file);
575
+			$node = $userRoot->get('/files_trashbin/files/'.$file);
576 576
 		} catch (NotFoundException $e) {
577 577
 			return $size;
578 578
 		}
579 579
 
580 580
 		if ($node instanceof Folder) {
581
-			$size += self::calculateSize(new View('/' . $user . '/files_trashbin/files/' . $file));
581
+			$size += self::calculateSize(new View('/'.$user.'/files_trashbin/files/'.$file));
582 582
 		} else if ($node instanceof File) {
583
-			$size += $view->filesize('/files_trashbin/files/' . $file);
583
+			$size += $view->filesize('/files_trashbin/files/'.$file);
584 584
 		}
585 585
 
586
-		self::emitTrashbinPreDelete('/files_trashbin/files/' . $file);
586
+		self::emitTrashbinPreDelete('/files_trashbin/files/'.$file);
587 587
 		$node->delete();
588
-		self::emitTrashbinPostDelete('/files_trashbin/files/' . $file);
588
+		self::emitTrashbinPostDelete('/files_trashbin/files/'.$file);
589 589
 
590 590
 		return $size;
591 591
 	}
@@ -601,17 +601,17 @@  discard block
 block discarded – undo
601 601
 	private static function deleteVersions(View $view, $file, $filename, $timestamp, $user) {
602 602
 		$size = 0;
603 603
 		if (\OCP\App::isEnabled('files_versions')) {
604
-			if ($view->is_dir('files_trashbin/versions/' . $file)) {
605
-				$size += self::calculateSize(new View('/' . $user . '/files_trashbin/versions/' . $file));
606
-				$view->unlink('files_trashbin/versions/' . $file);
604
+			if ($view->is_dir('files_trashbin/versions/'.$file)) {
605
+				$size += self::calculateSize(new View('/'.$user.'/files_trashbin/versions/'.$file));
606
+				$view->unlink('files_trashbin/versions/'.$file);
607 607
 			} else if ($versions = self::getVersionsFromTrash($filename, $timestamp, $user)) {
608 608
 				foreach ($versions as $v) {
609 609
 					if ($timestamp) {
610
-						$size += $view->filesize('/files_trashbin/versions/' . $filename . '.v' . $v . '.d' . $timestamp);
611
-						$view->unlink('/files_trashbin/versions/' . $filename . '.v' . $v . '.d' . $timestamp);
610
+						$size += $view->filesize('/files_trashbin/versions/'.$filename.'.v'.$v.'.d'.$timestamp);
611
+						$view->unlink('/files_trashbin/versions/'.$filename.'.v'.$v.'.d'.$timestamp);
612 612
 					} else {
613
-						$size += $view->filesize('/files_trashbin/versions/' . $filename . '.v' . $v);
614
-						$view->unlink('/files_trashbin/versions/' . $filename . '.v' . $v);
613
+						$size += $view->filesize('/files_trashbin/versions/'.$filename.'.v'.$v);
614
+						$view->unlink('/files_trashbin/versions/'.$filename.'.v'.$v);
615 615
 					}
616 616
 				}
617 617
 			}
@@ -628,15 +628,15 @@  discard block
 block discarded – undo
628 628
 	 */
629 629
 	public static function file_exists($filename, $timestamp = null) {
630 630
 		$user = User::getUser();
631
-		$view = new View('/' . $user);
631
+		$view = new View('/'.$user);
632 632
 
633 633
 		if ($timestamp) {
634
-			$filename = $filename . '.d' . $timestamp;
634
+			$filename = $filename.'.d'.$timestamp;
635 635
 		} else {
636 636
 			$filename = $filename;
637 637
 		}
638 638
 
639
-		$target = Filesystem::normalizePath('files_trashbin/files/' . $filename);
639
+		$target = Filesystem::normalizePath('files_trashbin/files/'.$filename);
640 640
 		return $view->file_exists($target);
641 641
 	}
642 642
 
@@ -661,7 +661,7 @@  discard block
 block discarded – undo
661 661
 	private static function calculateFreeSpace($trashbinSize, $user) {
662 662
 		$softQuota = true;
663 663
 		$userObject = \OC::$server->getUserManager()->get($user);
664
-		if(is_null($userObject)) {
664
+		if (is_null($userObject)) {
665 665
 			return 0;
666 666
 		}
667 667
 		$quota = $userObject->getQuota();
@@ -680,7 +680,7 @@  discard block
 block discarded – undo
680 680
 		// subtract size of files and current trash bin size from quota
681 681
 		if ($softQuota) {
682 682
 			$userFolder = \OC::$server->getUserFolder($user);
683
-			if(is_null($userFolder)) {
683
+			if (is_null($userFolder)) {
684 684
 				return 0;
685 685
 			}
686 686
 			$free = $quota - $userFolder->getSize(); // remaining free space for user
@@ -762,7 +762,7 @@  discard block
 block discarded – undo
762 762
 			foreach ($files as $file) {
763 763
 				if ($availableSpace < 0 && $expiration->isExpired($file['mtime'], true)) {
764 764
 					$tmp = self::delete($file['name'], $user, $file['mtime']);
765
-					\OCP\Util::writeLog('files_trashbin', 'remove "' . $file['name'] . '" (' . $tmp . 'B) to meet the limit of trash bin size (50% of available quota)', \OCP\Util::INFO);
765
+					\OCP\Util::writeLog('files_trashbin', 'remove "'.$file['name'].'" ('.$tmp.'B) to meet the limit of trash bin size (50% of available quota)', \OCP\Util::INFO);
766 766
 					$availableSpace += $tmp;
767 767
 					$size += $tmp;
768 768
 				} else {
@@ -792,7 +792,7 @@  discard block
 block discarded – undo
792 792
 				$count++;
793 793
 				$size += self::delete($filename, $user, $timestamp);
794 794
 				\OC::$server->getLogger()->info(
795
-					'Remove "' . $filename . '" from trashbin because it exceeds max retention obligation term.',
795
+					'Remove "'.$filename.'" from trashbin because it exceeds max retention obligation term.',
796 796
 					['app' => 'files_trashbin']
797 797
 				);
798 798
 			} else {
@@ -818,16 +818,16 @@  discard block
 block discarded – undo
818 818
 			$view->mkdir($destination);
819 819
 			$view->touch($destination, $view->filemtime($source));
820 820
 			foreach ($view->getDirectoryContent($source) as $i) {
821
-				$pathDir = $source . '/' . $i['name'];
821
+				$pathDir = $source.'/'.$i['name'];
822 822
 				if ($view->is_dir($pathDir)) {
823
-					$size += self::copy_recursive($pathDir, $destination . '/' . $i['name'], $view);
823
+					$size += self::copy_recursive($pathDir, $destination.'/'.$i['name'], $view);
824 824
 				} else {
825 825
 					$size += $view->filesize($pathDir);
826
-					$result = $view->copy($pathDir, $destination . '/' . $i['name']);
826
+					$result = $view->copy($pathDir, $destination.'/'.$i['name']);
827 827
 					if (!$result) {
828 828
 						throw new \OCA\Files_Trashbin\Exceptions\CopyRecursiveException();
829 829
 					}
830
-					$view->touch($destination . '/' . $i['name'], $view->filemtime($pathDir));
830
+					$view->touch($destination.'/'.$i['name'], $view->filemtime($pathDir));
831 831
 				}
832 832
 			}
833 833
 		} else {
@@ -849,7 +849,7 @@  discard block
 block discarded – undo
849 849
 	 * @return array
850 850
 	 */
851 851
 	private static function getVersionsFromTrash($filename, $timestamp, $user) {
852
-		$view = new View('/' . $user . '/files_trashbin/versions');
852
+		$view = new View('/'.$user.'/files_trashbin/versions');
853 853
 		$versions = array();
854 854
 
855 855
 		//force rescan of versions, local storage may not have updated the cache
@@ -862,10 +862,10 @@  discard block
 block discarded – undo
862 862
 
863 863
 		if ($timestamp) {
864 864
 			// fetch for old versions
865
-			$matches = $view->searchRaw($filename . '.v%.d' . $timestamp);
865
+			$matches = $view->searchRaw($filename.'.v%.d'.$timestamp);
866 866
 			$offset = -strlen($timestamp) - 2;
867 867
 		} else {
868
-			$matches = $view->searchRaw($filename . '.v%');
868
+			$matches = $view->searchRaw($filename.'.v%');
869 869
 		}
870 870
 
871 871
 		if (is_array($matches)) {
@@ -895,18 +895,18 @@  discard block
 block discarded – undo
895 895
 		$name = pathinfo($filename, PATHINFO_FILENAME);
896 896
 		$l = \OC::$server->getL10N('files_trashbin');
897 897
 
898
-		$location = '/' . trim($location, '/');
898
+		$location = '/'.trim($location, '/');
899 899
 
900 900
 		// if extension is not empty we set a dot in front of it
901 901
 		if ($ext !== '') {
902
-			$ext = '.' . $ext;
902
+			$ext = '.'.$ext;
903 903
 		}
904 904
 
905
-		if ($view->file_exists('files' . $location . '/' . $filename)) {
905
+		if ($view->file_exists('files'.$location.'/'.$filename)) {
906 906
 			$i = 2;
907
-			$uniqueName = $name . " (" . $l->t("restored") . ")" . $ext;
908
-			while ($view->file_exists('files' . $location . '/' . $uniqueName)) {
909
-				$uniqueName = $name . " (" . $l->t("restored") . " " . $i . ")" . $ext;
907
+			$uniqueName = $name." (".$l->t("restored").")".$ext;
908
+			while ($view->file_exists('files'.$location.'/'.$uniqueName)) {
909
+				$uniqueName = $name." (".$l->t("restored")." ".$i.")".$ext;
910 910
 				$i++;
911 911
 			}
912 912
 
@@ -923,7 +923,7 @@  discard block
 block discarded – undo
923 923
 	 * @return integer size of the folder
924 924
 	 */
925 925
 	private static function calculateSize($view) {
926
-		$root = \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . $view->getAbsolutePath('');
926
+		$root = \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data').$view->getAbsolutePath('');
927 927
 		if (!file_exists($root)) {
928 928
 			return 0;
929 929
 		}
@@ -954,7 +954,7 @@  discard block
 block discarded – undo
954 954
 	 * @return integer trash bin size
955 955
 	 */
956 956
 	private static function getTrashbinSize($user) {
957
-		$view = new View('/' . $user);
957
+		$view = new View('/'.$user);
958 958
 		$fileInfo = $view->getFileInfo('/files_trashbin');
959 959
 		return isset($fileInfo['size']) ? $fileInfo['size'] : 0;
960 960
 	}
@@ -983,7 +983,7 @@  discard block
 block discarded – undo
983 983
 	 */
984 984
 	public static function isEmpty($user) {
985 985
 
986
-		$view = new View('/' . $user . '/files_trashbin');
986
+		$view = new View('/'.$user.'/files_trashbin');
987 987
 		if ($view->is_dir('/files') && $dh = $view->opendir('/files')) {
988 988
 			while ($file = readdir($dh)) {
989 989
 				if (!Filesystem::isIgnoredDir($file)) {
Please login to merge, or discard this patch.