Merge pull request #11439 from nextcloud/trash-modular-api
Modular trashbin api
This commit is contained in:
commit
4ad27260a9
|
@ -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>
|
||||
|
|
|
@ -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',
|
||||
);
|
||||
|
|
|
@ -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',
|
||||
);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,27 +22,31 @@ declare(strict_types=1);
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Trashbin\Controller;
|
||||
|
||||
use OCA\Files_Trashbin\Trash\ITrashManager;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Files\File;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\IMimeTypeDetector;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\IPreview;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserSession;
|
||||
|
||||
class PreviewController extends Controller {
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
/** @var string */
|
||||
private $userId;
|
||||
/** @var ITrashManager */
|
||||
private $trashManager;
|
||||
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
|
||||
/** @var IMimeTypeDetector */
|
||||
private $mimeTypeDetector;
|
||||
|
@ -53,17 +57,21 @@ class PreviewController extends Controller {
|
|||
/** @var ITimeFactory */
|
||||
private $time;
|
||||
|
||||
public function __construct(string $appName,
|
||||
IRequest $request,
|
||||
IRootFolder $rootFolder,
|
||||
string $userId,
|
||||
IMimeTypeDetector $mimeTypeDetector,
|
||||
IPreview $previewManager,
|
||||
ITimeFactory $time) {
|
||||
public function __construct(
|
||||
string $appName,
|
||||
IRequest $request,
|
||||
IRootFolder $rootFolder,
|
||||
ITrashManager $trashManager,
|
||||
IUserSession $userSession,
|
||||
IMimeTypeDetector $mimeTypeDetector,
|
||||
IPreview $previewManager,
|
||||
ITimeFactory $time
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
|
||||
$this->trashManager = $trashManager;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->userId = $userId;
|
||||
$this->userSession = $userSession;
|
||||
$this->mimeTypeDetector = $mimeTypeDetector;
|
||||
$this->previewManager = $previewManager;
|
||||
$this->time = $time;
|
||||
|
@ -86,39 +94,28 @@ class PreviewController extends Controller {
|
|||
}
|
||||
|
||||
try {
|
||||
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
||||
/** @var Folder $trash */
|
||||
$trash = $userFolder->getParent()->get('files_trashbin/files');
|
||||
$trashFiles = $trash->getById($fileId);
|
||||
|
||||
if (empty($trashFiles)) {
|
||||
throw new NotFoundException();
|
||||
$file = $this->trashManager->getTrashNodeById($this->userSession->getUser(), $fileId);
|
||||
if ($file === null) {
|
||||
return new DataResponse([], Http::STATUS_NOT_FOUND);
|
||||
}
|
||||
|
||||
$trashFile = array_pop($trashFiles);
|
||||
|
||||
if ($trashFile instanceof Folder) {
|
||||
if ($file instanceof Folder) {
|
||||
return new DataResponse([], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$pathParts = pathinfo($file->getName());
|
||||
$extension = $pathParts['extension'];
|
||||
$fileName = $pathParts['filename'];
|
||||
/*
|
||||
* Files in the root of the trashbin are timetamped.
|
||||
* So we have to strip that in order to properly detect the mimetype of the file.
|
||||
*/
|
||||
if ($trashFile->getParent()->getPath() === $trash->getPath()) {
|
||||
/** @var File $trashFile */
|
||||
$fileName = $trashFile->getName();
|
||||
$i = strrpos($fileName, '.');
|
||||
if ($i !== false) {
|
||||
$fileName = substr($fileName, 0, $i);
|
||||
}
|
||||
|
||||
if (preg_match('/d\d+/', $extension)) {
|
||||
$mimeType = $this->mimeTypeDetector->detectPath($fileName);
|
||||
} else {
|
||||
$mimeType = $this->mimeTypeDetector->detectPath($trashFile->getName());
|
||||
$mimeType = $this->mimeTypeDetector->detectPath($file->getName());
|
||||
}
|
||||
|
||||
$f = $this->previewManager->getPreview($trashFile, $x, $y, true, IPreview::MODE_FILL, $mimeType);
|
||||
$f = $this->previewManager->getPreview($file, $x, $y, true, IPreview::MODE_FILL, $mimeType);
|
||||
$response = new Http\FileDisplayResponse($f, Http::STATUS_OK, ['Content-Type' => $f->getMimeType()]);
|
||||
|
||||
// Cache previews for 24H
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
|
@ -21,13 +22,20 @@
|
|||
|
||||
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;
|
||||
|
||||
public function __construct(ITrashManager $trashManager, ITrashItem $data) {
|
||||
$this->trashManager = $trashManager;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
|
@ -36,7 +44,7 @@ abstract class AbstractTrash implements ITrash {
|
|||
}
|
||||
|
||||
public function getDeletionTime(): int {
|
||||
return $this->data->getMtime();
|
||||
return $this->data->getDeletedTime();
|
||||
}
|
||||
|
||||
public function getFileId(): int {
|
||||
|
@ -66,4 +74,17 @@ abstract class AbstractTrash implements ITrash {
|
|||
public function getName(): string {
|
||||
return $this->data->getName();
|
||||
}
|
||||
|
||||
public function getOriginalLocation(): string {
|
||||
return $this->data->getOriginalLocation();
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
$this->trashManager->removeItem($this->data);
|
||||
}
|
||||
|
||||
public function restore(): bool {
|
||||
$this->trashManager->restoreItem($this->data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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->data);
|
||||
|
||||
$children = array_map(function (ITrashItem $entry) {
|
||||
if ($entry->getType() === FileInfo::TYPE_FOLDER) {
|
||||
return new TrashFolderFolder($this->trashManager, $entry);
|
||||
}
|
||||
return new TrashFolderFile($this->trashManager, $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();
|
||||
}
|
||||
}
|
|
@ -31,14 +31,6 @@ use Sabre\DAV\INode;
|
|||
|
||||
|
||||
class RestoreFolder implements ICollection, IMoveTarget {
|
||||
|
||||
/** @var string */
|
||||
protected $userId;
|
||||
|
||||
public function __construct(string $userId) {
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
public function createFile($name, $data = null) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
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'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
public function getOriginalLocation(): string {
|
||||
return $this->location . '/' . $this->getFilename();
|
||||
}
|
||||
class TrashFolderFolder extends AbstractTrashFolder {
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
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(),
|
||||
new TrashRoot($this->user, $this->trashManager)
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -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 getChildren(): array {
|
||||
$entries = $this->trashManager->listTrashRoot($this->user);
|
||||
|
||||
$children = array_map(function (ITrashItem $entry) {
|
||||
if ($entry->getType() === FileInfo::TYPE_FOLDER) {
|
||||
return new TrashFolder($this->trashManager, $entry);
|
||||
}
|
||||
return new TrashFile($this->trashManager, $entry);
|
||||
}, $entries);
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
public function getChild($name): ITrash {
|
||||
$entries = \OCA\Files_Trashbin\Helper::getTrashFiles('/', $this->userId);
|
||||
$entries = $this->getChildren();
|
||||
|
||||
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);
|
||||
if ($entry->getName() === $name) {
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
public function getChildren(): array {
|
||||
$entries = \OCA\Files_Trashbin\Helper::getTrashFiles('/', $this->userId);
|
||||
|
||||
$children = array_map(function (FileInfo $entry) {
|
||||
if ($entry->getType() === FileInfo::TYPE_FOLDER) {
|
||||
return new TrashFolder('/', $this->userId, $entry);
|
||||
}
|
||||
return new TrashFile($this->userId, $entry);
|
||||
}, $entries);
|
||||
|
||||
return $children;
|
||||
}
|
||||
|
||||
public function childExists($name): bool {
|
||||
$entries = \OCA\Files_Trashbin\Helper::getTrashFiles('/', $this->userId);
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
if ($entry->getName() . '.d'.$entry->getMtime() === $name) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
$this->getChild($name);
|
||||
return true;
|
||||
} catch (NotFound $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getLastModified(): int {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?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\Node;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
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 ITrashItem $folder
|
||||
* @return ITrashItem[]
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function listTrashFolder(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 ITrashItem $item
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function removeItem(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): bool;
|
||||
|
||||
/**
|
||||
* @param IUser $user
|
||||
* @param int $fileId
|
||||
* @return Node|null
|
||||
*/
|
||||
public function getTrashNodeById(IUser $user, int $fileId);
|
||||
}
|
|
@ -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\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
|
||||
*
|
||||
* @return string
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getOriginalLocation(): 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;
|
||||
|
||||
/**
|
||||
* Get the user for which this trash item applies
|
||||
*
|
||||
* @return IUser
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getUser(): IUser;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
<?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\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\Storage\IStorage;
|
||||
use OCP\IUser;
|
||||
|
||||
class LegacyTrashBackend implements ITrashBackend {
|
||||
/** @var array */
|
||||
private $deletedFiles = [];
|
||||
|
||||
/** @var IRootFolder */
|
||||
private $rootFolder;
|
||||
|
||||
public function __construct(IRootFolder $rootFolder) {
|
||||
$this->rootFolder = $rootFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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() . '/' . $file->getName(),
|
||||
$file->getMTime(),
|
||||
$parentTrashPath . '/' . $file->getName() . ($isRoot ? '.d' . $file->getMtime() : ''),
|
||||
$file,
|
||||
$user
|
||||
);
|
||||
}, $items);
|
||||
}
|
||||
|
||||
public function listTrashRoot(IUser $user): array {
|
||||
$entries = Helper::getTrashFiles('/', $user->getUID());
|
||||
return $this->mapTrashItems($entries, $user);
|
||||
}
|
||||
|
||||
public function listTrashFolder(ITrashItem $folder): array {
|
||||
$user = $folder->getUser();
|
||||
$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(ITrashItem $item) {
|
||||
$user = $item->getUser();
|
||||
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): bool {
|
||||
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;
|
||||
}
|
||||
|
||||
public function getTrashNodeById(IUser $user, int $fileId) {
|
||||
try {
|
||||
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
|
||||
$trash = $userFolder->getParent()->get('files_trashbin/files');
|
||||
$trashFiles = $trash->getById($fileId);
|
||||
if (!$trashFiles) {
|
||||
return null;
|
||||
}
|
||||
return $trashFiles ? array_pop($trashFiles) : null;
|
||||
} catch (NotFoundException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
<?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;
|
||||
/** @var IUser */
|
||||
private $user;
|
||||
|
||||
public function __construct(
|
||||
ITrashBackend $backend,
|
||||
string $originalLocation,
|
||||
int $deletedTime,
|
||||
string $trashPath,
|
||||
FileInfo $fileInfo,
|
||||
IUser $user
|
||||
) {
|
||||
$this->backend = $backend;
|
||||
$this->orignalLocation = $originalLocation;
|
||||
$this->deletedTime = $deletedTime;
|
||||
$this->trashPath = $trashPath;
|
||||
$this->fileInfo = $fileInfo;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
public function getTrashBackend(): ITrashBackend {
|
||||
return $this->backend;
|
||||
}
|
||||
|
||||
public function getOriginalLocation(): 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 getUser(): IUser {
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
<?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(ITrashItem $folder): array {
|
||||
return $this->getBackendForItem($folder)->listTrashFolder($folder);
|
||||
}
|
||||
|
||||
public function restoreItem(ITrashItem $item) {
|
||||
return $this->getBackendForItem($item)->restoreItem($item);
|
||||
}
|
||||
|
||||
public function removeItem(ITrashItem $item) {
|
||||
$this->getBackendForItem($item)->removeItem($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 ($storage) {
|
||||
if (
|
||||
$storage->instanceOfStorage($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): bool {
|
||||
if ($this->trashPaused) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$backend = $this->getBackendForStorage($storage);
|
||||
return $backend->moveToTrash($storage, $internalPath);
|
||||
} catch (BackendNotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getTrashNodeById(IUser $user, int $fileId) {
|
||||
foreach ($this->backends as $backend) {
|
||||
$item = $backend->getTrashNodeById($user, $fileId);
|
||||
if ($item !== null) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function pauseTrash() {
|
||||
$this->trashPaused = true;
|
||||
}
|
||||
|
||||
public function resumeTrash() {
|
||||
$this->trashPaused = false;
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
namespace OCA\Files_Trashbin\Tests\Controller;
|
||||
|
||||
use OCA\Files_Trashbin\Controller\PreviewController;
|
||||
use OCA\Files_Trashbin\Trash\ITrashManager;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||
|
@ -31,14 +32,14 @@ use OCP\Files\File;
|
|||
use OCP\Files\Folder;
|
||||
use OCP\Files\IMimeTypeDetector;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\SimpleFS\ISimpleFile;
|
||||
use OCP\IPreview;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserSession;
|
||||
use Test\TestCase;
|
||||
|
||||
class PreviewControllerTest extends TestCase {
|
||||
|
||||
/** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $rootFolder;
|
||||
|
||||
|
@ -57,6 +58,12 @@ class PreviewControllerTest extends TestCase {
|
|||
/** @var PreviewController */
|
||||
private $controller;
|
||||
|
||||
/** @var ITrashManager|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $trashManager;
|
||||
|
||||
/** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $userSession;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
|
@ -65,12 +72,23 @@ class PreviewControllerTest extends TestCase {
|
|||
$this->mimeTypeDetector = $this->createMock(IMimeTypeDetector::class);
|
||||
$this->previewManager = $this->createMock(IPreview::class);
|
||||
$this->time = $this->createMock(ITimeFactory::class);
|
||||
$this->trashManager = $this->createMock(ITrashManager::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->willReturn($this->userId);
|
||||
|
||||
$this->userSession->expects($this->any())
|
||||
->method('getUser')
|
||||
->willReturn($user);
|
||||
|
||||
$this->controller = new PreviewController(
|
||||
'files_versions',
|
||||
$this->createMock(IRequest::class),
|
||||
$this->rootFolder,
|
||||
$this->userId,
|
||||
$this->trashManager,
|
||||
$this->userSession,
|
||||
$this->mimeTypeDetector,
|
||||
$this->previewManager,
|
||||
$this->time
|
||||
|
@ -114,11 +132,15 @@ class PreviewControllerTest extends TestCase {
|
|||
->with($this->equalTo(42))
|
||||
->willReturn([$file]);
|
||||
$file->method('getName')
|
||||
->willReturn('file.1234');
|
||||
->willReturn('file.d1234');
|
||||
|
||||
$file->method('getParent')
|
||||
->willReturn($trash);
|
||||
|
||||
$this->trashManager->expects($this->any())
|
||||
->method('getTrashNodeById')
|
||||
->willReturn($file);
|
||||
|
||||
$preview = $this->createMock(ISimpleFile::class);
|
||||
$this->previewManager->method('getPreview')
|
||||
->with($this->equalTo($file), 10, 10, true, IPreview::MODE_FILL, 'myMime')
|
||||
|
@ -177,6 +199,9 @@ class PreviewControllerTest extends TestCase {
|
|||
->willReturn($trash);
|
||||
|
||||
$folder = $this->createMock(Folder::class);
|
||||
$this->trashManager->expects($this->any())
|
||||
->method('getTrashNodeById')
|
||||
->willReturn($folder);
|
||||
$trash->method('getById')
|
||||
->with($this->equalTo(43))
|
||||
->willReturn([$folder]);
|
||||
|
|
|
@ -34,6 +34,7 @@ use OC\Files\Storage\Temporary;
|
|||
use OC\Files\Filesystem;
|
||||
use OCA\Files_Trashbin\Events\MoveToTrashEvent;
|
||||
use OCA\Files_Trashbin\Storage;
|
||||
use OCA\Files_Trashbin\Trash\ITrashManager;
|
||||
use OCP\Files\Cache\ICache;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\Node;
|
||||
|
@ -546,6 +547,7 @@ class StorageTest extends \Test\TestCase {
|
|||
->disableOriginalConstructor()->getMock();
|
||||
$rootFolder = $this->createMock(IRootFolder::class);
|
||||
$node = $this->getMockBuilder(Node::class)->disableOriginalConstructor()->getMock();
|
||||
$trashManager = $this->createMock(ITrashManager::class);
|
||||
$event = $this->getMockBuilder(MoveToTrashEvent::class)->disableOriginalConstructor()->getMock();
|
||||
$event->expects($this->any())->method('shouldMoveToTrashBin')->willReturn(!$appDisablesTrash);
|
||||
|
||||
|
@ -555,6 +557,7 @@ class StorageTest extends \Test\TestCase {
|
|||
->setConstructorArgs(
|
||||
[
|
||||
['mountPoint' => $mountPoint, 'storage' => $tmpStorage],
|
||||
$trashManager,
|
||||
$userManager,
|
||||
$logger,
|
||||
$eventDispatcher,
|
||||
|
|
|
@ -187,6 +187,12 @@ class CacheWrapper extends Cache {
|
|||
$this->getCache()->move($source, $target);
|
||||
}
|
||||
|
||||
protected function getMoveInfo($path) {
|
||||
/** @var Cache $cache */
|
||||
$cache = $this->getCache();
|
||||
return $cache->getMoveInfo($path);
|
||||
}
|
||||
|
||||
public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
|
||||
$this->getCache()->moveFromCache($sourceCache, $sourcePath, $targetPath);
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
|
|||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
return basename($this->getPath());
|
||||
return isset($this->data['name']) ? $this->data['name'] : basename($this->getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -65,6 +65,7 @@ use OC\Files\Storage\StorageFactory;
|
|||
use OC\Lockdown\Filesystem\NullStorage;
|
||||
use OCP\Files\Config\IMountProvider;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\Storage\IStorageFactory;
|
||||
use OCP\ILogger;
|
||||
use OCP\IUserManager;
|
||||
|
||||
|
@ -246,11 +247,11 @@ class Filesystem {
|
|||
/**
|
||||
* Returns the storage factory
|
||||
*
|
||||
* @return \OCP\Files\Storage\IStorageFactory
|
||||
* @return IStorageFactory
|
||||
*/
|
||||
public static function getLoader() {
|
||||
if (!self::$loader) {
|
||||
self::$loader = new StorageFactory();
|
||||
self::$loader = \OC::$server->query(IStorageFactory::class);
|
||||
}
|
||||
return self::$loader;
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ use OC\Files\Mount\ObjectHomeMountProvider;
|
|||
use OC\Files\Node\HookConnector;
|
||||
use OC\Files\Node\LazyRoot;
|
||||
use OC\Files\Node\Root;
|
||||
use OC\Files\Storage\StorageFactory;
|
||||
use OC\Files\View;
|
||||
use OC\Http\Client\ClientService;
|
||||
use OC\IntegrityCheck\Checker;
|
||||
|
@ -135,6 +136,7 @@ use OCP\Federation\ICloudFederationProviderManager;
|
|||
use OCP\Federation\ICloudIdManager;
|
||||
use OCP\Authentication\LoginCredentials\IStore;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\Storage\IStorageFactory;
|
||||
use OCP\GlobalScale\IConfig;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IDBConnection;
|
||||
|
@ -1174,6 +1176,10 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
});
|
||||
$this->registerAlias(IContactsStore::class, ContactsStore::class);
|
||||
|
||||
$this->registerService(IStorageFactory::class, function() {
|
||||
return new StorageFactory();
|
||||
});
|
||||
|
||||
$this->registerAlias(IDashboardManager::class, Dashboard\DashboardManager::class);
|
||||
|
||||
$this->connectDispatcher();
|
||||
|
@ -2024,4 +2030,11 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
public function getRemoteInstanceFactory() {
|
||||
return $this->query(IInstanceFactory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return IStorageFactory
|
||||
*/
|
||||
public function getStorageFactory() {
|
||||
return $this->query(IStorageFactory::class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -584,4 +584,10 @@ interface IServerContainer extends IContainer {
|
|||
* @since 13.0.0
|
||||
*/
|
||||
public function getRemoteInstanceFactory();
|
||||
|
||||
/**
|
||||
* @return \OCP\Files\Storage\IStorageFactory
|
||||
* @since 15.0.0
|
||||
*/
|
||||
public function getStorageFactory();
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue