diff --git a/apps/files_trashbin/appinfo/info.xml b/apps/files_trashbin/appinfo/info.xml
index eb8f46dced15d5cf1a0435e803e8c50443d91249..023cad1264f82e1cc16158388645ac6dcec1c764 100644
--- a/apps/files_trashbin/appinfo/info.xml
+++ b/apps/files_trashbin/appinfo/info.xml
@@ -44,4 +44,8 @@ To prevent a user from running out of disk space, the Deleted files app will not
 			<plugin>OCA\Files_Trashbin\Sabre\PropfindPlugin</plugin>
 		</plugins>
 	</sabre>
+
+	<trash>
+		<backend for="OCP\Files\Storage\IStorage">OCA\Files_Trashbin\Trash\LegacyTrashBackend</backend>
+	</trash>
 </info>
diff --git a/apps/files_trashbin/composer/composer/autoload_classmap.php b/apps/files_trashbin/composer/composer/autoload_classmap.php
index 164d64333cec22d3cd8f5cd4eb46331877b498a4..011e178a5bd10e0c15412f0637c3729216696de4 100644
--- a/apps/files_trashbin/composer/composer/autoload_classmap.php
+++ b/apps/files_trashbin/composer/composer/autoload_classmap.php
@@ -19,6 +19,8 @@ return array(
     'OCA\\Files_Trashbin\\Helper' => $baseDir . '/../lib/Helper.php',
     'OCA\\Files_Trashbin\\Hooks' => $baseDir . '/../lib/Hooks.php',
     'OCA\\Files_Trashbin\\Sabre\\AbstractTrash' => $baseDir . '/../lib/Sabre/AbstractTrash.php',
+    'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFile' => $baseDir . '/../lib/Sabre/AbstractTrashFile.php',
+    'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFolder' => $baseDir . '/../lib/Sabre/AbstractTrashFolder.php',
     'OCA\\Files_Trashbin\\Sabre\\ITrash' => $baseDir . '/../lib/Sabre/ITrash.php',
     'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => $baseDir . '/../lib/Sabre/PropfindPlugin.php',
     'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => $baseDir . '/../lib/Sabre/RestoreFolder.php',
@@ -30,5 +32,12 @@ return array(
     'OCA\\Files_Trashbin\\Sabre\\TrashHome' => $baseDir . '/../lib/Sabre/TrashHome.php',
     'OCA\\Files_Trashbin\\Sabre\\TrashRoot' => $baseDir . '/../lib/Sabre/TrashRoot.php',
     'OCA\\Files_Trashbin\\Storage' => $baseDir . '/../lib/Storage.php',
+    'OCA\\Files_Trashbin\\Trash\\BackendNotFoundException' => $baseDir . '/../lib/Trash/BackendNotFoundException.php',
+    'OCA\\Files_Trashbin\\Trash\\ITrashBackend' => $baseDir . '/../lib/Trash/ITrashBackend.php',
+    'OCA\\Files_Trashbin\\Trash\\ITrashItem' => $baseDir . '/../lib/Trash/ITrashItem.php',
+    'OCA\\Files_Trashbin\\Trash\\ITrashManager' => $baseDir . '/../lib/Trash/ITrashManager.php',
+    'OCA\\Files_Trashbin\\Trash\\LegacyTrashBackend' => $baseDir . '/../lib/Trash/LegacyTrashBackend.php',
+    'OCA\\Files_Trashbin\\Trash\\TrashItem' => $baseDir . '/../lib/Trash/TrashItem.php',
+    'OCA\\Files_Trashbin\\Trash\\TrashManager' => $baseDir . '/../lib/Trash/TrashManager.php',
     'OCA\\Files_Trashbin\\Trashbin' => $baseDir . '/../lib/Trashbin.php',
 );
diff --git a/apps/files_trashbin/composer/composer/autoload_static.php b/apps/files_trashbin/composer/composer/autoload_static.php
index 6ebb8c35f317e0e74c0b80660b98c3dc3bdddcee..37144ec428824c3dd325da70f6a81da99e6e8fdf 100644
--- a/apps/files_trashbin/composer/composer/autoload_static.php
+++ b/apps/files_trashbin/composer/composer/autoload_static.php
@@ -34,6 +34,8 @@ class ComposerStaticInitFiles_Trashbin
         'OCA\\Files_Trashbin\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
         'OCA\\Files_Trashbin\\Hooks' => __DIR__ . '/..' . '/../lib/Hooks.php',
         'OCA\\Files_Trashbin\\Sabre\\AbstractTrash' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrash.php',
+        'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFile' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrashFile.php',
+        'OCA\\Files_Trashbin\\Sabre\\AbstractTrashFolder' => __DIR__ . '/..' . '/../lib/Sabre/AbstractTrashFolder.php',
         'OCA\\Files_Trashbin\\Sabre\\ITrash' => __DIR__ . '/..' . '/../lib/Sabre/ITrash.php',
         'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => __DIR__ . '/..' . '/../lib/Sabre/PropfindPlugin.php',
         'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => __DIR__ . '/..' . '/../lib/Sabre/RestoreFolder.php',
@@ -45,6 +47,13 @@ class ComposerStaticInitFiles_Trashbin
         'OCA\\Files_Trashbin\\Sabre\\TrashHome' => __DIR__ . '/..' . '/../lib/Sabre/TrashHome.php',
         'OCA\\Files_Trashbin\\Sabre\\TrashRoot' => __DIR__ . '/..' . '/../lib/Sabre/TrashRoot.php',
         'OCA\\Files_Trashbin\\Storage' => __DIR__ . '/..' . '/../lib/Storage.php',
+        'OCA\\Files_Trashbin\\Trash\\BackendNotFoundException' => __DIR__ . '/..' . '/../lib/Trash/BackendNotFoundException.php',
+        'OCA\\Files_Trashbin\\Trash\\ITrashBackend' => __DIR__ . '/..' . '/../lib/Trash/ITrashBackend.php',
+        'OCA\\Files_Trashbin\\Trash\\ITrashItem' => __DIR__ . '/..' . '/../lib/Trash/ITrashItem.php',
+        'OCA\\Files_Trashbin\\Trash\\ITrashManager' => __DIR__ . '/..' . '/../lib/Trash/ITrashManager.php',
+        'OCA\\Files_Trashbin\\Trash\\LegacyTrashBackend' => __DIR__ . '/..' . '/../lib/Trash/LegacyTrashBackend.php',
+        'OCA\\Files_Trashbin\\Trash\\TrashItem' => __DIR__ . '/..' . '/../lib/Trash/TrashItem.php',
+        'OCA\\Files_Trashbin\\Trash\\TrashManager' => __DIR__ . '/..' . '/../lib/Trash/TrashManager.php',
         'OCA\\Files_Trashbin\\Trashbin' => __DIR__ . '/..' . '/../lib/Trashbin.php',
     );
 
diff --git a/apps/files_trashbin/lib/AppInfo/Application.php b/apps/files_trashbin/lib/AppInfo/Application.php
index 8e4ec255567faa1f6cefaa28d095a12f6434c64a..06a34e0df84f36407c0876675ca09174aa36cf33 100644
--- a/apps/files_trashbin/lib/AppInfo/Application.php
+++ b/apps/files_trashbin/lib/AppInfo/Application.php
@@ -24,8 +24,11 @@
 namespace OCA\Files_Trashbin\AppInfo;
 
 use OCA\DAV\Connector\Sabre\Principal;
+use OCA\Files_Trashbin\Trash\ITrashManager;
+use OCA\Files_Trashbin\Trash\TrashManager;
 use OCP\AppFramework\App;
 use OCA\Files_Trashbin\Expiration;
+use OCP\AppFramework\IAppContainer;
 use OCP\AppFramework\Utility\ITimeFactory;
 use OCA\Files_Trashbin\Capabilities;
 
@@ -61,5 +64,36 @@ class Application extends App {
 				\OC::$server->getConfig()
 			);
 		});
+
+		$container->registerService(ITrashManager::class, function(IAppContainer $c) {
+			return new TrashManager();
+		});
+
+		$this->registerTrashBackends();
+	}
+
+	public function registerTrashBackends() {
+		$server = $this->getContainer()->getServer();
+		$logger = $server->getLogger();
+		$appManager = $server->getAppManager();
+		/** @var ITrashManager $trashManager */
+		$trashManager = $this->getContainer()->getServer()->query(ITrashManager::class);
+		foreach($appManager->getInstalledApps() as $app) {
+			$appInfo = $appManager->getAppInfo($app);
+			if (isset($appInfo['trash'])) {
+				$backends = $appInfo['trash'];
+				foreach($backends as $backend) {
+					$class = $backend['@value'];
+					$for = $backend['@attributes']['for'];
+
+					try {
+						$backendObject = $server->query($class);
+						$trashManager->registerBackend($for, $backendObject);
+					} catch (\Exception $e) {
+						$logger->logException($e);
+					}
+				}
+			}
+		}
 	}
 }
diff --git a/apps/files_trashbin/lib/Sabre/AbstractTrash.php b/apps/files_trashbin/lib/Sabre/AbstractTrash.php
index 43f9cc02749cb23121ec560bd58285b565d60056..5d4b19513a46f920cde69f780e144ffcf83e6c16 100644
--- a/apps/files_trashbin/lib/Sabre/AbstractTrash.php
+++ b/apps/files_trashbin/lib/Sabre/AbstractTrash.php
@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 /**
  * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
  *
@@ -21,13 +22,24 @@
 
 namespace OCA\Files_Trashbin\Sabre;
 
+use OCA\Files_Trashbin\Trash\ITrashItem;
+use OCA\Files_Trashbin\Trash\ITrashManager;
 use OCP\Files\FileInfo;
+use OCP\IUser;
 
 abstract class AbstractTrash implements ITrash {
-	/** @var FileInfo */
+	/** @var ITrashItem */
 	protected $data;
 
-	public function __construct(FileInfo $data) {
+	/** @var ITrashManager */
+	protected $trashManager;
+
+	/** @var IUser */
+	protected $user;
+
+	public function __construct(ITrashManager $trashManager, IUser $user, ITrashItem $data) {
+		$this->trashManager = $trashManager;
+		$this->user = $user;
 		$this->data = $data;
 	}
 
@@ -36,7 +48,7 @@ abstract class AbstractTrash implements ITrash {
 	}
 
 	public function getDeletionTime(): int {
-		return $this->data->getMtime();
+		return $this->data->getDeletedTime();
 	}
 
 	public function getFileId(): int {
@@ -66,4 +78,17 @@ abstract class AbstractTrash implements ITrash {
 	public function getName(): string {
 		return $this->data->getName();
 	}
+
+	public function getOriginalLocation(): string {
+		return $this->data->getOriginalLocation($this->user);
+	}
+
+	public function delete() {
+		$this->trashManager->removeItem($this->user, $this->data);
+	}
+
+	public function restore(): bool {
+		$this->trashManager->restoreItem($this->data);
+		return true;
+	}
 }
diff --git a/apps/files_trashbin/lib/Sabre/AbstractTrashFile.php b/apps/files_trashbin/lib/Sabre/AbstractTrashFile.php
new file mode 100644
index 0000000000000000000000000000000000000000..da7c94eb35d9987a48bb0e7012c924ad2b4440ec
--- /dev/null
+++ b/apps/files_trashbin/lib/Sabre/AbstractTrashFile.php
@@ -0,0 +1,36 @@
+<?php
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Trashbin\Sabre;
+
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\IFile;
+
+abstract class AbstractTrashFile extends AbstractTrash implements IFile , ITrash{
+	public function put($data) {
+		throw new Forbidden();
+	}
+
+	public function setName($name) {
+		throw new Forbidden();
+	}
+}
diff --git a/apps/files_trashbin/lib/Sabre/AbstractTrashFolder.php b/apps/files_trashbin/lib/Sabre/AbstractTrashFolder.php
new file mode 100644
index 0000000000000000000000000000000000000000..19a6ba2d3fbedb68dc074776dd7ba407002a6d8e
--- /dev/null
+++ b/apps/files_trashbin/lib/Sabre/AbstractTrashFolder.php
@@ -0,0 +1,77 @@
+<?php
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Trashbin\Sabre;
+
+use OCA\Files_Trashbin\Trash\ITrashItem;
+use OCP\Files\FileInfo;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\ICollection;
+
+abstract class AbstractTrashFolder extends AbstractTrash implements ICollection, ITrash {
+	public function getChildren(): array {
+		$entries = $this->trashManager->listTrashFolder($this->user, $this->data);
+
+		$children = array_map(function (ITrashItem $entry) {
+			if ($entry->getType() === FileInfo::TYPE_FOLDER) {
+				return new TrashFolderFolder($this->trashManager, $this->user, $entry);
+			}
+			return new TrashFolderFile($this->trashManager, $this->user, $entry);
+		}, $entries);
+
+		return $children;
+	}
+
+	public function getChild($name): ITrash {
+		$entries = $this->getChildren();
+
+		foreach ($entries as $entry) {
+			if ($entry->getName() === $name) {
+				return $entry;
+			}
+		}
+
+		throw new NotFound();
+	}
+
+	public function childExists($name): bool {
+		try {
+			$this->getChild($name);
+			return true;
+		} catch (NotFound $e) {
+			return false;
+		}
+	}
+
+	public function setName($name) {
+		throw new Forbidden();
+	}
+
+	public function createFile($name, $data = null) {
+		throw new Forbidden();
+	}
+
+	public function createDirectory($name) {
+		throw new Forbidden();
+	}
+}
diff --git a/apps/files_trashbin/lib/Sabre/RootCollection.php b/apps/files_trashbin/lib/Sabre/RootCollection.php
index be31d200f71045b1714647b67757947215fda7ec..0b55953aa3fb57a5e6951300d218005343c933f7 100644
--- a/apps/files_trashbin/lib/Sabre/RootCollection.php
+++ b/apps/files_trashbin/lib/Sabre/RootCollection.php
@@ -21,18 +21,27 @@ declare(strict_types=1);
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
+
 namespace OCA\Files_Trashbin\Sabre;
 
+use OCA\Files_Trashbin\Trash\ITrashManager;
 use OCP\IConfig;
 use Sabre\DAV\INode;
 use Sabre\DAVACL\AbstractPrincipalCollection;
 use Sabre\DAVACL\PrincipalBackend;
 
 class RootCollection extends AbstractPrincipalCollection {
+	/** @var ITrashManager */
+	private $trashManager;
 
-	public function __construct(PrincipalBackend\BackendInterface $principalBackend, IConfig $config) {
+	public function __construct(
+		ITrashManager $trashManager,
+		PrincipalBackend\BackendInterface $principalBackend,
+		IConfig $config
+	) {
 		parent::__construct($principalBackend, 'principals/users');
 
+		$this->trashManager = $trashManager;
 		$this->disableListing = !$config->getSystemValue('debug', false);
 	}
 
@@ -47,12 +56,12 @@ class RootCollection extends AbstractPrincipalCollection {
 	 * @return INode
 	 */
 	public function getChildForPrincipal(array $principalInfo): TrashHome {
-		list(,$name) = \Sabre\Uri\split($principalInfo['uri']);
+		list(, $name) = \Sabre\Uri\split($principalInfo['uri']);
 		$user = \OC::$server->getUserSession()->getUser();
 		if (is_null($user) || $name !== $user->getUID()) {
 			throw new \Sabre\DAV\Exception\Forbidden();
 		}
-		return new TrashHome($principalInfo);
+		return new TrashHome($principalInfo, $this->trashManager, $user);
 	}
 
 	public function getName(): string {
diff --git a/apps/files_trashbin/lib/Sabre/TrashFile.php b/apps/files_trashbin/lib/Sabre/TrashFile.php
index 840ca6a19388b63c84ae4e1992177b333fc7482e..dd6500e5b8168beffbf99edfa9e6e2caba216197 100644
--- a/apps/files_trashbin/lib/Sabre/TrashFile.php
+++ b/apps/files_trashbin/lib/Sabre/TrashFile.php
@@ -21,46 +21,15 @@ declare(strict_types=1);
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-namespace OCA\Files_Trashbin\Sabre;
-
-use OCP\Files\FileInfo;
-use Sabre\DAV\Exception\Forbidden;
-use Sabre\DAV\IFile;
-
-class TrashFile extends AbstractTrash implements IFile, ITrash {
-	/** @var string */
-	private $userId;
-
-	public function __construct(string $userId, FileInfo $data) {
-		$this->userId = $userId;
-		parent::__construct($data);
-	}
 
-	public function put($data) {
-		throw new Forbidden();
-	}
+namespace OCA\Files_Trashbin\Sabre;
 
+class TrashFile extends AbstractTrashFile {
 	public function get() {
-		return $this->data->getStorage()->fopen($this->data->getInternalPath().'.d'.$this->getLastModified(), 'rb');
-	}
-
-	public function delete() {
-		\OCA\Files_Trashbin\Trashbin::delete($this->data->getName(), $this->userId, $this->getLastModified());
+		return $this->data->getStorage()->fopen($this->data->getInternalPath() . '.d' . $this->getLastModified(), 'rb');
 	}
 
 	public function getName(): string {
 		return $this->data->getName() . '.d' . $this->getLastModified();
 	}
-
-	public function setName($name) {
-		throw new Forbidden();
-	}
-
-	public function restore(): bool {
-		return \OCA\Files_Trashbin\Trashbin::restore($this->getName(), $this->data->getName(), $this->getLastModified());
-	}
-
-	public function getOriginalLocation(): string {
-		return $this->data['extraData'];
-	}
 }
diff --git a/apps/files_trashbin/lib/Sabre/TrashFolder.php b/apps/files_trashbin/lib/Sabre/TrashFolder.php
index d884eefcc9f94a316ea6332a2f74dd13c9557e4f..108aaf4f31296d53b4152a5b19aaf6277b4c74a9 100644
--- a/apps/files_trashbin/lib/Sabre/TrashFolder.php
+++ b/apps/files_trashbin/lib/Sabre/TrashFolder.php
@@ -23,85 +23,9 @@ declare(strict_types=1);
  */
 namespace OCA\Files_Trashbin\Sabre;
 
-use OCP\Files\FileInfo;
-use Sabre\DAV\Exception\Forbidden;
-use Sabre\DAV\Exception\NotFound;
-use Sabre\DAV\ICollection;
-
-class TrashFolder extends AbstractTrash implements ICollection, ITrash {
-	/** @var string */
-	private $userId;
-
-	public function __construct(string $root, string $userId, FileInfo $data) {
-		$this->userId = $userId;
-		parent::__construct($data);
-	}
-
-	public function createFile($name, $data = null) {
-		throw new Forbidden();
-	}
-
-	public function createDirectory($name) {
-		throw new Forbidden();
-	}
-
-	public function getChild($name): ITrash {
-		$entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->getName(), $this->userId);
-
-		foreach ($entries as $entry) {
-			if ($entry->getName() === $name) {
-				if ($entry->getType() === FileInfo::TYPE_FOLDER) {
-					return new TrashFolderFolder($this->getName(), $this->userId, $entry, $this->getOriginalLocation());
-				}
-				return new TrashFolderFile($this->getName(), $this->userId, $entry, $this->getOriginalLocation());
-			}
-		}
-
-		throw new NotFound();
-	}
-
-	public function getChildren(): array {
-		$entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->getName(), $this->userId);
-
-		$children = array_map(function (FileInfo $entry) {
-			if ($entry->getType() === FileInfo::TYPE_FOLDER) {
-				return new TrashFolderFolder($this->getName(), $this->userId, $entry, $this->getOriginalLocation());
-			}
-			return new TrashFolderFile($this->getName(), $this->userId, $entry, $this->getOriginalLocation());
-		}, $entries);
-
-		return $children;
-	}
-
-	public function childExists($name): bool {
-		$entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->getName(), $this->userId);
-
-		foreach ($entries as $entry) {
-			if ($entry->getName() === $name) {
-				return true;
-			}
-		}
-
-		return false;
-	}
-
-	public function delete() {
-		\OCA\Files_Trashbin\Trashbin::delete($this->data->getName(), $this->userId, $this->getLastModified());
-	}
 
+class TrashFolder extends AbstractTrashFolder {
 	public function getName(): string {
 		return $this->data->getName() . '.d' . $this->getLastModified();
 	}
-
-	public function setName($name) {
-		throw new Forbidden();
-	}
-
-	public function restore(): bool {
-		return \OCA\Files_Trashbin\Trashbin::restore($this->getName(), $this->data->getName(), $this->getLastModified());
-	}
-
-	public function getOriginalLocation(): string {
-		return $this->data['extraData'];
-	}
 }
diff --git a/apps/files_trashbin/lib/Sabre/TrashFolderFile.php b/apps/files_trashbin/lib/Sabre/TrashFolderFile.php
index 3e28d048b42b75aaac90fdd599dba3a262fdea5e..31ee9535b72a5b971f2fccbd396f4e85703738a3 100644
--- a/apps/files_trashbin/lib/Sabre/TrashFolderFile.php
+++ b/apps/files_trashbin/lib/Sabre/TrashFolderFile.php
@@ -23,51 +23,9 @@ declare(strict_types=1);
  */
 namespace OCA\Files_Trashbin\Sabre;
 
-use OCP\Files\FileInfo;
-use Sabre\DAV\Exception\Forbidden;
-use Sabre\DAV\IFile;
-
-class TrashFolderFile extends AbstractTrash implements IFile, ITrash {
-	/** @var string */
-	private $root;
-
-	/** @var string */
-	private $userId;
-
-	/** @var string */
-	private $location;
-
-	public function __construct(string $root,
-								string $userId,
-								FileInfo $data,
-								string $location) {
-		$this->root = $root;
-		$this->userId = $userId;
-		$this->location = $location;
-		parent::__construct($data);
-	}
-
-	public function put($data) {
-		throw new Forbidden();
-	}
 
+class TrashFolderFile extends AbstractTrashFile {
 	public function get() {
 		return $this->data->getStorage()->fopen($this->data->getInternalPath(), 'rb');
 	}
-
-	public function delete() {
-		\OCA\Files_Trashbin\Trashbin::delete($this->root . '/' . $this->getName(), $this->userId, null);
-	}
-
-	public function setName($name) {
-		throw new Forbidden();
-	}
-
-	public function restore(): bool {
-		return \OCA\Files_Trashbin\Trashbin::restore($this->root . '/' . $this->getName(), $this->data->getName(), null);
-	}
-
-	public function getOriginalLocation(): string {
-		return $this->location . '/' . $this->getFilename();
-	}
 }
diff --git a/apps/files_trashbin/lib/Sabre/TrashFolderFolder.php b/apps/files_trashbin/lib/Sabre/TrashFolderFolder.php
index 4ee9a0e2db0b1c7165cd0b069ef55d2fa3d1e293..5332b7dd4c05b58003c0292cf1a5a86f1191d5fc 100644
--- a/apps/files_trashbin/lib/Sabre/TrashFolderFolder.php
+++ b/apps/files_trashbin/lib/Sabre/TrashFolderFolder.php
@@ -21,95 +21,8 @@ declare(strict_types=1);
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-namespace OCA\Files_Trashbin\Sabre;
-
-use OCP\Files\FileInfo;
-use Sabre\DAV\Exception\Forbidden;
-use Sabre\DAV\Exception\NotFound;
-use Sabre\DAV\ICollection;
-
-class TrashFolderFolder extends AbstractTrash implements ICollection, ITrash {
-
-	/** @var string */
-	private $root;
-
-	/** @var string */
-	private $userId;
-
-	/** @var string */
-	private $location;
-
-	public function __construct(string $root,
-								string $userId,
-								FileInfo $data,
-								string $location) {
-		$this->root = $root;
-		$this->userId = $userId;
-		$this->location = $location;
-		parent::__construct($data);
-	}
-
-	public function createFile($name, $data = null) {
-		throw new Forbidden();
-	}
-
-	public function createDirectory($name) {
-		throw new Forbidden();
-	}
-
-	public function getChild($name): ITrash {
-		$entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->root . '/' . $this->getName(), $this->userId);
-
-		foreach ($entries as $entry) {
-			if ($entry->getName() === $name) {
-				if ($entry->getType() === FileInfo::TYPE_FOLDER) {
-					return new TrashFolderFolder($this->root . '/' . $this->getName(), $this->userId, $entry, $this->getOriginalLocation());
-				}
-				return new TrashFolderFile($this->root . '/' . $this->getName(), $this->userId, $entry, $this->getOriginalLocation());
-			}
-		}
 
-		throw new NotFound();
-	}
-
-	public function getChildren(): array {
-		$entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->root . '/' . $this->getName(), $this->userId);
-
-		$children = array_map(function (FileInfo $entry) {
-			if ($entry->getType() === FileInfo::TYPE_FOLDER) {
-				return new TrashFolderFolder($this->root.'/'.$this->getName(), $this->userId, $entry, $this->getOriginalLocation());
-			}
-			return new TrashFolderFile($this->root.'/'.$this->getName(), $this->userId, $entry, $this->getOriginalLocation());
-		}, $entries);
-
-		return $children;
-	}
-
-	public function childExists($name): bool {
-		$entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->root . '/' . $this->getName(), $this->userId);
-
-		foreach ($entries as $entry) {
-			if ($entry->getName() === $name) {
-				return true;
-			}
-		}
-
-		return false;
-	}
-
-	public function delete() {
-		\OCA\Files_Trashbin\Trashbin::delete($this->root . '/' . $this->getName(), $this->userId, null);
-	}
-
-	public function setName($name) {
-		throw new Forbidden();
-	}
-
-	public function restore(): bool {
-		return \OCA\Files_Trashbin\Trashbin::restore($this->root . '/' . $this->getName(), $this->data->getName(), null);
-	}
+namespace OCA\Files_Trashbin\Sabre;
 
-	public function getOriginalLocation(): string {
-		return $this->location . '/' . $this->getFilename();
-	}
+class TrashFolderFolder extends AbstractTrashFolder {
 }
diff --git a/apps/files_trashbin/lib/Sabre/TrashHome.php b/apps/files_trashbin/lib/Sabre/TrashHome.php
index d1c50c9c6a3ebf400a5f56312003f287e36fb38b..416b88635a425dafeecdf3a3ad3f547f10c266c9 100644
--- a/apps/files_trashbin/lib/Sabre/TrashHome.php
+++ b/apps/files_trashbin/lib/Sabre/TrashHome.php
@@ -21,19 +21,33 @@ declare(strict_types=1);
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
+
 namespace OCA\Files_Trashbin\Sabre;
 
+use OCA\Files_Trashbin\Trash\ITrashManager;
+use OCP\IUser;
 use Sabre\DAV\Exception\Forbidden;
 use Sabre\DAV\Exception\NotFound;
 use Sabre\DAV\ICollection;
 
 class TrashHome implements ICollection {
+	/** @var ITrashManager */
+	private $trashManager;
 
 	/** @var array */
 	private $principalInfo;
 
-	public function __construct(array $principalInfo) {
+	/** @var IUser */
+	private $user;
+
+	public function __construct(
+		array $principalInfo,
+		ITrashManager $trashManager,
+		IUser $user
+	) {
 		$this->principalInfo = $principalInfo;
+		$this->trashManager = $trashManager;
+		$this->user = $user;
 	}
 
 	public function delete() {
@@ -41,7 +55,7 @@ class TrashHome implements ICollection {
 	}
 
 	public function getName(): string {
-		list(,$name) = \Sabre\Uri\split($this->principalInfo['uri']);
+		list(, $name) = \Sabre\Uri\split($this->principalInfo['uri']);
 		return $name;
 	}
 
@@ -58,24 +72,20 @@ class TrashHome implements ICollection {
 	}
 
 	public function getChild($name) {
-		list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']);
-
 		if ($name === 'restore') {
-			return new RestoreFolder($userId);
+			return new RestoreFolder($this->user->getUID());
 		}
 		if ($name === 'trash') {
-			return new TrashRoot($userId);
+			return new TrashRoot($this->user, $this->trashManager);
 		}
 
 		throw new NotFound();
 	}
 
 	public function getChildren(): array {
-		list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']);
-
 		return [
-			new RestoreFolder($userId),
-			new TrashRoot($userId),
+			new RestoreFolder($this->user->getUID()),
+			new TrashRoot($this->user, $this->trashManager)
 		];
 	}
 
diff --git a/apps/files_trashbin/lib/Sabre/TrashRoot.php b/apps/files_trashbin/lib/Sabre/TrashRoot.php
index 73b9d44d7e13f1494b4a6dc13d999daca788aedd..57cfe2b2dff8c2ce9e415e3d7d5162c2b5257559 100644
--- a/apps/files_trashbin/lib/Sabre/TrashRoot.php
+++ b/apps/files_trashbin/lib/Sabre/TrashRoot.php
@@ -23,18 +23,25 @@ declare(strict_types=1);
  */
 namespace OCA\Files_Trashbin\Sabre;
 
+use OCA\Files_Trashbin\Trash\ITrashItem;
+use OCA\Files_Trashbin\Trash\ITrashManager;
 use OCP\Files\FileInfo;
+use OCP\IUser;
 use Sabre\DAV\Exception\Forbidden;
 use Sabre\DAV\Exception\NotFound;
 use Sabre\DAV\ICollection;
 
 class TrashRoot implements ICollection {
 
-	/** @var string */
-	private $userId;
+	/** @var IUser */
+	private $user;
 
-	public function __construct(string $userId) {
-		$this->userId = $userId;
+	/** @var ITrashManager  */
+	private $trashManager;
+
+	public function __construct(IUser $user, ITrashManager $trashManager) {
+		$this->user = $user;
+		$this->trashManager = $trashManager;
 	}
 
 	public function delete() {
@@ -57,44 +64,38 @@ class TrashRoot implements ICollection {
 		throw new Forbidden('Not allowed to create folders in the trashbin');
 	}
 
-	public function getChild($name): ITrash {
-		$entries = \OCA\Files_Trashbin\Helper::getTrashFiles('/', $this->userId);
-
-		foreach ($entries as $entry) {
-			if ($entry->getName() . '.d'.$entry->getMtime() === $name) {
-				if ($entry->getType() === FileInfo::TYPE_FOLDER) {
-					return new TrashFolder('/', $this->userId, $entry);
-				}
-				return new TrashFile($this->userId, $entry);
-			}
-		}
-
-		throw new NotFound();
-	}
-
 	public function getChildren(): array {
-			$entries = \OCA\Files_Trashbin\Helper::getTrashFiles('/', $this->userId);
+		$entries = $this->trashManager->listTrashRoot($this->user);
 
-		$children = array_map(function (FileInfo $entry) {
+		$children = array_map(function (ITrashItem $entry) {
 			if ($entry->getType() === FileInfo::TYPE_FOLDER) {
-				return new TrashFolder('/', $this->userId, $entry);
+				return new TrashFolder($this->trashManager, $this->user, $entry);
 			}
-			return new TrashFile($this->userId, $entry);
+			return new TrashFile($this->trashManager, $this->user, $entry);
 		}, $entries);
 
 		return $children;
 	}
 
-	public function childExists($name): bool {
-		$entries = \OCA\Files_Trashbin\Helper::getTrashFiles('/', $this->userId);
+	public function getChild($name): ITrash {
+		$entries = $this->getChildren();
 
 		foreach ($entries as $entry) {
-			if ($entry->getName() . '.d'.$entry->getMtime() === $name) {
-				return true;
+			if ($entry->getName() === $name) {
+				return $entry;
 			}
 		}
 
-		return false;
+		throw new NotFound();
+	}
+
+	public function childExists($name): bool {
+		try {
+			$this->getChild($name);
+			return true;
+		} catch (NotFound $e) {
+			return false;
+		}
 	}
 
 	public function getLastModified(): int {
diff --git a/apps/files_trashbin/lib/Storage.php b/apps/files_trashbin/lib/Storage.php
index 54b47a6a19e4bf0afa3f0e2767018a9ac67bf29f..0db634eeb9ea7556983313292b32c0100f7415b1 100644
--- a/apps/files_trashbin/lib/Storage.php
+++ b/apps/files_trashbin/lib/Storage.php
@@ -31,34 +31,18 @@ use OC\Files\Filesystem;
 use OC\Files\Storage\Wrapper\Wrapper;
 use OC\Files\View;
 use OCA\Files_Trashbin\Events\MoveToTrashEvent;
+use OCA\Files_Trashbin\Trash\ITrashManager;
 use OCP\Encryption\Exceptions\GenericEncryptionException;
 use OCP\Files\IRootFolder;
+use OCP\Files\Mount\IMountPoint;
 use OCP\Files\Node;
 use OCP\ILogger;
 use OCP\IUserManager;
 use Symfony\Component\EventDispatcher\EventDispatcher;
 
 class Storage extends Wrapper {
-
+	/** @var IMountPoint */
 	private $mountPoint;
-	// remember already deleted files to avoid infinite loops if the trash bin
-	// move files across storages
-	private $deletedFiles = array();
-
-	/**
-	 * Disable trash logic
-	 *
-	 * @var bool
-	 */
-	private static $disableTrash = false;
-
-	/**
-	 * remember which file/folder was moved out of s shared folder
-	 * in this case we want to add a copy to the owners trash bin
-	 *
-	 * @var array
-	 */
-	private static $moveOutOfSharedFolder = [];
 
 	/** @var  IUserManager */
 	private $userManager;
@@ -72,21 +56,29 @@ class Storage extends Wrapper {
 	/** @var IRootFolder */
 	private $rootFolder;
 
+	/** @var ITrashManager */
+	private $trashManager;
+
 	/**
 	 * Storage constructor.
 	 *
 	 * @param array $parameters
+	 * @param ITrashManager $trashManager
 	 * @param IUserManager|null $userManager
 	 * @param ILogger|null $logger
 	 * @param EventDispatcher|null $eventDispatcher
 	 * @param IRootFolder|null $rootFolder
 	 */
-	public function __construct($parameters,
-								IUserManager $userManager = null,
-								ILogger $logger = null,
-								EventDispatcher $eventDispatcher = null,
-								IRootFolder $rootFolder = null) {
+	public function __construct(
+		$parameters,
+		ITrashManager $trashManager = null,
+		IUserManager $userManager = null,
+		ILogger $logger = null,
+		EventDispatcher $eventDispatcher = null,
+		IRootFolder $rootFolder = null
+	) {
 		$this->mountPoint = $parameters['mountPoint'];
+		$this->trashManager = $trashManager;
 		$this->userManager = $userManager;
 		$this->logger = $logger;
 		$this->eventDispatcher = $eventDispatcher;
@@ -94,81 +86,6 @@ class Storage extends Wrapper {
 		parent::__construct($parameters);
 	}
 
-	/**
-	 * @internal
-	 */
-	public static function preRenameHook($params) {
-		// in cross-storage cases, a rename is a copy + unlink,
-		// that last unlink must not go to trash, only exception:
-		// if the file was moved from a shared storage to a local folder,
-		// in this case the owner should get a copy in his trash bin so that
-		// they can restore the files again
-
-		$oldPath = $params['oldpath'];
-		$newPath = dirname($params['newpath']);
-		$currentUser = \OC::$server->getUserSession()->getUser();
-
-		$fileMovedOutOfSharedFolder = false;
-
-		try {
-			if ($currentUser) {
-				$currentUserId = $currentUser->getUID();
-
-				$view = new View($currentUserId . '/files');
-				$fileInfo = $view->getFileInfo($oldPath);
-				if ($fileInfo) {
-					$sourceStorage = $fileInfo->getStorage();
-					$sourceOwner = $view->getOwner($oldPath);
-					$targetOwner = $view->getOwner($newPath);
-
-					if ($sourceOwner !== $targetOwner
-						&& $sourceStorage->instanceOfStorage('OCA\Files_Sharing\SharedStorage')
-					) {
-						$fileMovedOutOfSharedFolder = true;
-					}
-				}
-			}
-		} catch (\Exception $e) {
-			// do nothing, in this case we just disable the trashbin and continue
-			\OC::$server->getLogger()->logException($e, [
-				'message' => 'Trashbin storage could not check if a file was moved out of a shared folder.',
-				'level' => ILogger::DEBUG,
-				'app' => 'files_trashbin',
-			]);
-		}
-
-		if($fileMovedOutOfSharedFolder) {
-			self::$moveOutOfSharedFolder['/' . $currentUserId . '/files' . $oldPath] = true;
-		} else {
-			self::$disableTrash = true;
-		}
-
-	}
-
-	/**
-	 * @internal
-	 */
-	public static function postRenameHook($params) {
-		self::$disableTrash = false;
-	}
-
-	/**
-	 * Rename path1 to path2 by calling the wrapped storage.
-	 *
-	 * @param string $path1 first path
-	 * @param string $path2 second path
-	 * @return bool
-	 */
-	public function rename($path1, $path2) {
-		$result = $this->storage->rename($path1, $path2);
-		if ($result === false) {
-			// when rename failed, the post_rename hook isn't triggered,
-			// but we still want to reenable the trash logic
-			self::$disableTrash = false;
-		}
-		return $result;
-	}
-
 	/**
 	 * Deletes the given file by moving it into the trashbin.
 	 *
@@ -178,22 +95,15 @@ class Storage extends Wrapper {
 	 */
 	public function unlink($path) {
 		try {
-			if (isset(self::$moveOutOfSharedFolder[$this->mountPoint . $path])) {
-				$result = $this->doDelete($path, 'unlink', true);
-				unset(self::$moveOutOfSharedFolder[$this->mountPoint . $path]);
-			} else {
-				$result = $this->doDelete($path, 'unlink');
-			}
+			return $this->doDelete($path, 'unlink');
 		} catch (GenericEncryptionException $e) {
 			// in case of a encryption exception we delete the file right away
 			$this->logger->info(
-				"Can't move file" .  $path .
+				"Can't move file" . $path .
 				"to the trash bin, therefore it was deleted right away");
 
-			$result = $this->storage->unlink($path);
+			return $this->storage->unlink($path);
 		}
-
-		return $result;
 	}
 
 	/**
@@ -204,14 +114,7 @@ class Storage extends Wrapper {
 	 * @return bool true if the operation succeeded, false otherwise
 	 */
 	public function rmdir($path) {
-		if (isset(self::$moveOutOfSharedFolder[$this->mountPoint . $path])) {
-			$result = $this->doDelete($path, 'rmdir', true);
-			unset(self::$moveOutOfSharedFolder[$this->mountPoint . $path]);
-		} else {
-			$result = $this->doDelete($path, 'rmdir');
-		}
-
-		return $result;
+		return $this->doDelete($path, 'rmdir');
 	}
 
 	/**
@@ -221,7 +124,7 @@ class Storage extends Wrapper {
 	 * @param $path
 	 * @return bool
 	 */
-	protected function shouldMoveToTrash($path){
+	protected function shouldMoveToTrash($path) {
 
 		// check if there is a app which want to disable the trash bin for this file
 		$fileId = $this->storage->getCache()->getId($path);
@@ -262,17 +165,16 @@ class Storage extends Wrapper {
 	 *
 	 * @param string $path path of file or folder to delete
 	 * @param string $method either "unlink" or "rmdir"
-	 * @param bool $ownerOnly delete for owner only (if file gets moved out of a shared folder)
 	 *
 	 * @return bool true if the operation succeeded, false otherwise
 	 */
-	private function doDelete($path, $method, $ownerOnly = false) {
-		if (self::$disableTrash
-			|| !\OC::$server->getAppManager()->isEnabledForUser('files_trashbin')
+	private function doDelete($path, $method) {
+		if (
+			!\OC::$server->getAppManager()->isEnabledForUser('files_trashbin')
 			|| (pathinfo($path, PATHINFO_EXTENSION) === 'part')
 			|| $this->shouldMoveToTrash($path) === false
 		) {
-			return call_user_func_array([$this->storage, $method], [$path]);
+			return call_user_func([$this->storage, $method], $path);
 		}
 
 		// check permissions before we continue, this is especially important for
@@ -281,28 +183,12 @@ class Storage extends Wrapper {
 			return false;
 		}
 
-		$normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path, true, false, true);
-		$result = true;
-		$view = Filesystem::getView();
-		if (!isset($this->deletedFiles[$normalized]) && $view instanceof View) {
-			$this->deletedFiles[$normalized] = $normalized;
-			if ($filesPath = $view->getRelativePath($normalized)) {
-				$filesPath = trim($filesPath, '/');
-				$result = \OCA\Files_Trashbin\Trashbin::move2trash($filesPath, $ownerOnly);
-				// in cross-storage cases the file will be copied
-				// but not deleted, so we delete it here
-				if ($result) {
-					call_user_func_array([$this->storage, $method], [$path]);
-				}
-			} else {
-				$result = call_user_func_array([$this->storage, $method], [$path]);
-			}
-			unset($this->deletedFiles[$normalized]);
-		} else if ($this->storage->file_exists($path)) {
-			$result = call_user_func_array([$this->storage, $method], [$path]);
+		$isMovedToTrash = $this->trashManager->moveToTrash($this, $path);
+		if (!$isMovedToTrash) {
+			return call_user_func([$this->storage, $method], $path);
+		} else {
+			return true;
 		}
-
-		return $result;
 	}
 
 	/**
@@ -311,7 +197,8 @@ class Storage extends Wrapper {
 	public static function setupStorage() {
 		\OC\Files\Filesystem::addStorageWrapper('oc_trashbin', function ($mountPoint, $storage) {
 			return new \OCA\Files_Trashbin\Storage(
-				array('storage' => $storage, 'mountPoint' => $mountPoint),
+				['storage' => $storage, 'mountPoint' => $mountPoint],
+				\OC::$server->query(ITrashManager::class),
 				\OC::$server->getUserManager(),
 				\OC::$server->getLogger(),
 				\OC::$server->getEventDispatcher(),
@@ -320,4 +207,7 @@ class Storage extends Wrapper {
 		}, 1);
 	}
 
+	public function getMountPoint() {
+		return $this->mountPoint;
+	}
 }
diff --git a/apps/files_trashbin/lib/Trash/BackendNotFoundException.php b/apps/files_trashbin/lib/Trash/BackendNotFoundException.php
new file mode 100644
index 0000000000000000000000000000000000000000..13289db7ab0e3536d0f0ba9612fffd05eeafdeda
--- /dev/null
+++ b/apps/files_trashbin/lib/Trash/BackendNotFoundException.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Trashbin\Trash;
+
+class BackendNotFoundException extends \Exception {
+
+}
diff --git a/apps/files_trashbin/lib/Trash/ITrashBackend.php b/apps/files_trashbin/lib/Trash/ITrashBackend.php
new file mode 100644
index 0000000000000000000000000000000000000000..35b16a00c22fbb60f3ab69d6d8548fa3544e5ef8
--- /dev/null
+++ b/apps/files_trashbin/lib/Trash/ITrashBackend.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Trashbin\Trash;
+
+use OCP\Files\FileInfo;
+use OCP\Files\Storage\IStorage;
+use OCP\IUser;
+
+/**
+ * @since 15.0.0
+ */
+interface ITrashBackend {
+	/**
+	 * List all trash items in the root of the trashbin
+	 *
+	 * @param IUser $user
+	 * @return ITrashItem[]
+	 * @since 15.0.0
+	 */
+	public function listTrashRoot(IUser $user): array;
+
+	/**
+	 * List all trash items in a subfolder in the trashbin
+	 *
+	 * @param IUser $user
+	 * @param ITrashItem $folder
+	 * @return ITrashItem[]
+	 * @since 15.0.0
+	 */
+	public function listTrashFolder(IUser $user, ITrashItem $folder): array;
+
+	/**
+	 * Restore a trashbin item
+	 *
+	 * @param ITrashItem $item
+	 * @since 15.0.0
+	 */
+	public function restoreItem(ITrashItem $item);
+
+	/**
+	 * Permanently remove an item from trash
+	 *
+	 * @param IUser $user
+	 * @param ITrashItem $item
+	 * @return
+	 * @since 15.0.0
+	 */
+	public function removeItem(IUser $user, ITrashItem $item);
+
+	/**
+	 * Move a file or folder to trash
+	 *
+	 * @param IStorage $storage
+	 * @param string $internalPath
+	 * @return boolean whether or not the file was moved to trash, if false then the file should be deleted normally
+	 * @since 15.0.0
+	 */
+	public function moveToTrash(IStorage $storage, string $internalPath);
+}
diff --git a/apps/files_trashbin/lib/Trash/ITrashItem.php b/apps/files_trashbin/lib/Trash/ITrashItem.php
new file mode 100644
index 0000000000000000000000000000000000000000..ed01894beef97b5376b76a522e9a1d9239fb5cd5
--- /dev/null
+++ b/apps/files_trashbin/lib/Trash/ITrashItem.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Trashbin\Trash;
+
+use OCP\Files\FileInfo;
+use OCP\IUser;
+
+/**
+ * @since 15.0.0
+ */
+interface ITrashItem extends FileInfo {
+	/**
+	 * Get the trash backend for this item
+	 *
+	 * @return ITrashBackend
+	 * @since 15.0.0
+	 */
+	public function getTrashBackend(): ITrashBackend;
+
+	/**
+	 * Get the original location for the trash item for the user
+	 *
+	 * @param IUser $user
+	 * @return string
+	 * @since 15.0.0
+	 */
+	public function getOriginalLocation(IUser $user): string;
+
+	/**
+	 * Get the timestamp that the file was moved to trash
+	 *
+	 * @return int
+	 * @since 15.0.0
+	 */
+	public function getDeletedTime(): int;
+
+	/**
+	 * Get the path of the item relative to the users trashbin
+	 *
+	 * @return string
+	 * @since 15.0.0
+	 */
+	public function getTrashPath(): string;
+
+	/**
+	 * Whether the item is a deleted item in the root of the trash, or a file in a subfolder
+	 *
+	 * @return bool
+	 * @since 15.0.0
+	 */
+	public function isRootItem(): bool;
+}
diff --git a/apps/files_trashbin/lib/Trash/ITrashManager.php b/apps/files_trashbin/lib/Trash/ITrashManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..fcd51f2d3716aa945200ec591817244fd061629c
--- /dev/null
+++ b/apps/files_trashbin/lib/Trash/ITrashManager.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Trashbin\Trash;
+
+use OCP\IUser;
+
+interface ITrashManager extends ITrashBackend {
+	/**
+	 * Add a backend for the trashbin
+	 *
+	 * @param string $storageType
+	 * @param ITrashBackend $backend
+	 * @since 15.0.0
+	 */
+	public function registerBackend(string $storageType, ITrashBackend $backend);
+
+	/**
+	 * List all trash items in the root of the trashbin
+	 *
+	 * @param IUser $user
+	 * @return ITrashItem[]
+	 * @since 15.0.0
+	 */
+	public function listTrashRoot(IUser $user): array;
+
+	/**
+	 * Temporally prevent files from being moved to the trash
+	 *
+	 * @since 15.0.0
+	 */
+	public function pauseTrash();
+
+	/**
+	 * @since 15.0.0
+	 */
+	public function resumeTrash();
+}
diff --git a/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php b/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php
new file mode 100644
index 0000000000000000000000000000000000000000..210c84228f7e8b0e3fac80d143a5b0e7e907803f
--- /dev/null
+++ b/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Trashbin\Trash;
+
+use OC\Files\Filesystem;
+use OC\Files\View;
+use OCA\Files_Trashbin\Helper;
+use OCA\Files_Trashbin\Storage;
+use OCA\Files_Trashbin\Trashbin;
+use OCP\Files\FileInfo;
+use OCP\Files\Storage\IStorage;
+use OCP\IUser;
+
+class LegacyTrashBackend implements ITrashBackend {
+	/** @var array */
+	private $deletedFiles = [];
+
+	/**
+	 * @param array $items
+	 * @param IUser $user
+	 * @param ITrashItem $parent
+	 * @return ITrashItem[]
+	 */
+	private function mapTrashItems(array $items, IUser $user, ITrashItem $parent = null): array {
+		$parentTrashPath = ($parent instanceof ITrashItem) ? $parent->getTrashPath() : '';
+		$isRoot = $parent === null;
+		return array_map(function (FileInfo $file) use ($parent, $parentTrashPath, $isRoot, $user) {
+			return new TrashItem(
+				$this,
+				$isRoot ? $file['extraData'] : $parent->getOriginalLocation($user) . '/' . $file->getName(),
+				$file->getMTime(),
+				$parentTrashPath . '/' . $file->getName() . ($isRoot ? '.d' . $file->getMtime() : ''),
+				$file
+			);
+		}, $items);
+	}
+
+	public function listTrashRoot(IUser $user): array {
+		$entries = Helper::getTrashFiles('/', $user->getUID());
+		return $this->mapTrashItems($entries, $user, null);
+	}
+
+	public function listTrashFolder(IUser $user, ITrashItem $folder): array {
+		$entries = Helper::getTrashFiles($folder->getTrashPath(), $user->getUID());
+		return $this->mapTrashItems($entries, $user, $folder);
+	}
+
+	public function restoreItem(ITrashItem $item) {
+		Trashbin::restore($item->getTrashPath(), $item->getName(), $item->isRootItem() ? $item->getDeletedTime() : null);
+	}
+
+	public function removeItem(IUser $user, ITrashItem $item) {
+		if ($item->isRootItem()) {
+			$path = substr($item->getTrashPath(), 0, -strlen('.d' . $item->getDeletedTime()));
+			Trashbin::delete($path, $user->getUID(), $item->getDeletedTime());
+		} else {
+			Trashbin::delete($item->getTrashPath(), $user->getUID(), null);
+		}
+
+	}
+
+	public function moveToTrash(IStorage $storage, string $internalPath) {
+		if (!$storage instanceof Storage) {
+			return false;
+		}
+		$normalized = Filesystem::normalizePath($storage->getMountPoint() . '/' . $internalPath, true, false, true);
+		$view = Filesystem::getView();
+		if (!isset($this->deletedFiles[$normalized]) && $view instanceof View) {
+			$this->deletedFiles[$normalized] = $normalized;
+			if ($filesPath = $view->getRelativePath($normalized)) {
+				$filesPath = trim($filesPath, '/');
+				$result = \OCA\Files_Trashbin\Trashbin::move2trash($filesPath);
+			} else {
+				$result = false;
+			}
+			unset($this->deletedFiles[$normalized]);
+		} else {
+			$result = false;
+		}
+
+		return $result;
+	}
+}
diff --git a/apps/files_trashbin/lib/Trash/TrashItem.php b/apps/files_trashbin/lib/Trash/TrashItem.php
new file mode 100644
index 0000000000000000000000000000000000000000..6d1c6dc328de71b6c937b1765ac9518e2ce79107
--- /dev/null
+++ b/apps/files_trashbin/lib/Trash/TrashItem.php
@@ -0,0 +1,187 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Trashbin\Trash;
+
+use OCP\Files\FileInfo;
+use OCP\IUser;
+
+class TrashItem implements ITrashItem {
+	/** @var ITrashBackend */
+	private $backend;
+	/** @var string */
+	private $orignalLocation;
+	/** @var int */
+	private $deletedTime;
+	/** @var string */
+	private $trashPath;
+	/** @var FileInfo */
+	private $fileInfo;
+
+	/**
+	 * TrashItem constructor.
+	 *
+	 * @param ITrashBackend $backend
+	 * @param string $originalLocation
+	 * @param int $deletedTime
+	 * @param string $trashPath
+	 * @param FileInfo $fileInfo
+	 */
+	public function __construct(ITrashBackend $backend, string $originalLocation, int $deletedTime, string $trashPath, FileInfo $fileInfo) {
+		$this->backend = $backend;
+		$this->orignalLocation = $originalLocation;
+		$this->deletedTime = $deletedTime;
+		$this->trashPath = $trashPath;
+		$this->fileInfo = $fileInfo;
+	}
+
+	public function getTrashBackend(): ITrashBackend {
+		return $this->backend;
+	}
+
+	public function getOriginalLocation(IUser $user): string {
+		return $this->orignalLocation;
+	}
+
+	public function getDeletedTime(): int {
+		return $this->deletedTime;
+	}
+
+	public function getTrashPath(): string {
+		return $this->trashPath;
+	}
+
+	public function isRootItem(): bool {
+		return substr_count($this->getTrashPath(), '/') === 1;
+	}
+
+	public function getEtag() {
+		return $this->fileInfo->getEtag();
+	}
+
+	public function getSize() {
+		return $this->fileInfo->getSize();
+	}
+
+	public function getMtime() {
+		return $this->fileInfo->getMtime();
+	}
+
+	public function getName() {
+		return $this->fileInfo->getName();
+	}
+
+	public function getInternalPath() {
+		return $this->fileInfo->getInternalPath();
+	}
+
+	
+	public function getPath() {
+		return $this->fileInfo->getPath();
+	}
+
+	
+	public function getMimetype() {
+		return $this->fileInfo->getMimetype();
+	}
+
+	
+	public function getMimePart() {
+		return $this->fileInfo->getMimePart();
+	}
+
+	
+	public function getStorage() {
+		return $this->fileInfo->getStorage();
+	}
+
+	
+	public function getId() {
+		return $this->fileInfo->getId();
+	}
+
+	
+	public function isEncrypted() {
+		return $this->fileInfo->isEncrypted();
+	}
+
+	
+	public function getPermissions() {
+		return $this->fileInfo->getPermissions();
+	}
+
+	
+	public function getType() {
+		return $this->fileInfo->getType();
+	}
+
+	
+	public function isReadable() {
+		return $this->fileInfo->isReadable();
+	}
+
+	
+	public function isUpdateable() {
+		return $this->fileInfo->isUpdateable();
+	}
+
+	
+	public function isCreatable() {
+		return $this->fileInfo->isCreatable();
+	}
+
+	
+	public function isDeletable() {
+		return $this->fileInfo->isDeletable();
+	}
+
+	
+	public function isShareable() {
+		return $this->fileInfo->isShareable();
+	}
+
+	
+	public function isShared() {
+		return $this->fileInfo->isShared();
+	}
+
+	
+	public function isMounted() {
+		return $this->fileInfo->isMounted();
+	}
+
+	
+	public function getMountPoint() {
+		return $this->fileInfo->getMountPoint();
+	}
+
+	
+	public function getOwner() {
+		return $this->fileInfo->getOwner();
+	}
+
+	
+	public function getChecksum() {
+		return $this->fileInfo->getChecksum();
+	}
+
+
+}
diff --git a/apps/files_trashbin/lib/Trash/TrashManager.php b/apps/files_trashbin/lib/Trash/TrashManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..3870271cc7ed59220abd5a0e20dd43976b269372
--- /dev/null
+++ b/apps/files_trashbin/lib/Trash/TrashManager.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_Trashbin\Trash;
+
+use OCP\Files\FileInfo;
+use OCP\Files\Mount\IMountPoint;
+use OCP\Files\Storage\IStorage;
+use OCP\IUser;
+
+class TrashManager implements ITrashManager {
+	/** @var ITrashBackend[] */
+	private $backends = [];
+
+	private $trashPaused = false;
+
+	public function registerBackend(string $storageType, ITrashBackend $backend) {
+		$this->backends[$storageType] = $backend;
+	}
+
+	/**
+	 * @return ITrashBackend[]
+	 */
+	private function getBackends(): array {
+		return $this->backends;
+	}
+
+	public function listTrashRoot(IUser $user): array {
+		$items = array_reduce($this->getBackends(), function (array $items, ITrashBackend $backend) use ($user) {
+			return array_merge($items, $backend->listTrashRoot($user));
+		}, []);
+		usort($items, function (ITrashItem $a, ITrashItem $b) {
+			return $a->getDeletedTime() - $b->getDeletedTime();
+		});
+		return $items;
+	}
+
+	private function getBackendForItem(ITrashItem $item) {
+		return $item->getTrashBackend();
+	}
+
+	public function listTrashFolder(IUser $user, ITrashItem $folder): array {
+		return $this->getBackendForItem($folder)->listTrashFolder($user, $folder);
+	}
+
+	public function restoreItem(ITrashItem $item) {
+		return $this->getBackendForItem($item)->restoreItem($item);
+	}
+
+	public function removeItem(IUser $user, ITrashItem $item) {
+		$this->getBackendForItem($item)->removeItem($user, $item);
+	}
+
+	/**
+	 * @param IStorage $storage
+	 * @return ITrashBackend
+	 * @throws BackendNotFoundException
+	 */
+	public function getBackendForStorage(IStorage $storage): ITrashBackend {
+		$fullType = get_class($storage);
+		$foundType = array_reduce(array_keys($this->backends), function ($type, $registeredType) use ($fullType) {
+			if (
+				is_subclass_of($fullType, $registeredType) &&
+				($type === '' || is_subclass_of($registeredType, $type))
+			) {
+				return $registeredType;
+			} else {
+				return $type;
+			}
+		}, '');
+		if ($foundType === '') {
+			throw new BackendNotFoundException("Trash backend for $fullType not found");
+		} else {
+			return $this->backends[$foundType];
+		}
+	}
+
+	public function moveToTrash(IStorage $storage, string $internalPath) {
+		if ($this->trashPaused) {
+			return false;
+		}
+		try {
+			$backend = $this->getBackendForStorage($storage);
+			return $backend->moveToTrash($storage, $internalPath);
+		} catch (BackendNotFoundException $e) {
+			return false;
+		}
+	}
+
+	public function pauseTrash() {
+		$this->trashPaused = true;
+	}
+
+	public function resumeTrash() {
+		$this->trashPaused = false;
+	}
+}
diff --git a/apps/files_trashbin/lib/Trashbin.php b/apps/files_trashbin/lib/Trashbin.php
index b9a8a42a21c8aae85c806197d07e95cea23b75c0..96c6a4c0af791d01fce3f4f18131d03b9d9a3956 100644
--- a/apps/files_trashbin/lib/Trashbin.php
+++ b/apps/files_trashbin/lib/Trashbin.php
@@ -976,8 +976,6 @@ class Trashbin {
 		\OCP\Util::connectHook('OC_Filesystem', 'post_write', 'OCA\Files_Trashbin\Hooks', 'post_write_hook');
 		// pre and post-rename, disable trash logic for the copy+unlink case
 		\OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Files_Trashbin\Trashbin', 'ensureFileScannedHook');
-		\OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Files_Trashbin\Storage', 'preRenameHook');
-		\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Files_Trashbin\Storage', 'postRenameHook');
 	}
 
 	/**
diff --git a/resources/app-info.xsd b/resources/app-info.xsd
index 5c2ab89e213f7a1e4b8134e1ce3114d71f50f35e..fa06752c01d8b7c952837d57b0c1324e933b8bd7 100644
--- a/resources/app-info.xsd
+++ b/resources/app-info.xsd
@@ -61,6 +61,8 @@
                             maxOccurs="1" />
                 <xs:element name="sabre" type="sabre" minOccurs="0"
                             maxOccurs="1" />
+                <xs:element name="trash" type="trash" minOccurs="0"
+                            maxOccurs="1" />
             </xs:sequence>
         </xs:complexType>
         <xs:unique name="uniqueNameL10n">
@@ -653,11 +655,25 @@
         </xs:sequence>
     </xs:complexType>
 
+    <xs:complexType name="trash">
+        <xs:sequence>
+            <xs:element name="backend" type="trash-backend" minOccurs="1"
+                        maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+
+    <xs:complexType name="trash-backend">
+        <xs:simpleContent>
+            <xs:extension base="php-class">
+                <xs:attribute name="for" type="php-class" use="required"/>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+
     <xs:simpleType name="php-class">
         <xs:restriction base="xs:string">
             <xs:pattern
                     value="[a-zA-Z_][0-9a-zA-Z_]*(\\[a-zA-Z_][0-9a-zA-Z_]*)*"/>
         </xs:restriction>
     </xs:simpleType>
-
 </xs:schema>