Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/files_trashbin/lib/BackgroundJob/ExpireTrash.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private function getNextOffset(): int {

}

private function resetOffset() {
private function resetOffset(): void {
$this->runMutexOperation(function () {
$this->appConfig->setValueInt(Application::APP_ID, self::OFFSET_CONFIG_KEY_NAME, 0);
});
Expand Down
62 changes: 36 additions & 26 deletions apps/files_trashbin/lib/Command/CleanUp.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,36 @@
*/
namespace OCA\Files_Trashbin\Command;

use OC\Core\Command\Base;
use OC\Files\SetupManager;
use OC\User\LazyUser;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IDBConnection;
use OCP\IUser;
use OCP\IUserBackend;
use OCP\IUserManager;
use OCP\Util;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class CleanUp extends Command {
class CleanUp extends Base {

public function __construct(
protected IRootFolder $rootFolder,
protected IUserManager $userManager,
protected IDBConnection $dbConnection,
protected SetupManager $setupManager,
) {
parent::__construct();
}

protected function configure() {
parent::configure();
$this
->setName('trashbin:cleanup')
->setDescription('Remove deleted files')
Expand All @@ -53,9 +60,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
throw new InvalidOptionException('Either specify a user_id or --all-users');
} elseif (!empty($users)) {
foreach ($users as $user) {
if ($this->userManager->userExists($user)) {
$userObject = $this->userManager->get($user);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather rename $users and $user to $userIds and $userId as it would be even less confusing. But that's a nitpick.

if ($userObject) {
$output->writeln("Remove deleted files of <info>$user</info>");
$this->removeDeletedFiles($user, $output, $verbose);
$this->removeDeletedFiles($userObject, $output, $verbose);
} else {
$output->writeln("<error>Unknown user $user</error>");
return 1;
Expand All @@ -75,7 +83,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$users = $backend->getUsers('', $limit, $offset);
foreach ($users as $user) {
$output->writeln(" <info>$user</info>");
$this->removeDeletedFiles($user, $output, $verbose);
$userObject = new LazyUser($user, $this->userManager, null, $backend);
$this->removeDeletedFiles($userObject, $output, $verbose);
}
$offset += $limit;
} while (count($users) >= $limit);
Expand All @@ -89,30 +98,31 @@ protected function execute(InputInterface $input, OutputInterface $output): int
/**
* remove deleted files for the given user
*/
protected function removeDeletedFiles(string $uid, OutputInterface $output, bool $verbose): void {
\OC_Util::tearDownFS();
\OC_Util::setupFS($uid);
$path = '/' . $uid . '/files_trashbin';
if ($this->rootFolder->nodeExists($path)) {
protected function removeDeletedFiles(IUser $user, OutputInterface $output, bool $verbose): void {
$this->setupManager->tearDown();
$this->setupManager->setupForUser($user);
$path = '/' . $user->getUID() . '/files_trashbin';
try {
$node = $this->rootFolder->get($path);

if ($verbose) {
$output->writeln('Deleting <info>' . Util::humanFileSize($node->getSize()) . "</info> in trash for <info>$uid</info>.");
}
$node->delete();
if ($this->rootFolder->nodeExists($path)) {
$output->writeln('<error>Trash folder sill exists after attempting to delete it</error>');
return;
}
$query = $this->dbConnection->getQueryBuilder();
$query->delete('files_trash')
->where($query->expr()->eq('user', $query->createParameter('uid')))
->setParameter('uid', $uid);
$query->executeStatement();
} else {
} catch (NotFoundException|NotPermittedException) {
if ($verbose) {
$output->writeln("No trash found for <info>$uid</info>");
$output->writeln("No trash found for <info>{$user->getUID()}</info>");
}
return;
}

if ($verbose) {
$output->writeln('Deleting <info>' . Util::humanFileSize($node->getSize()) . "</info> in trash for <info>{$user->getUID()}</info>.");
}
$node->delete();
if ($this->rootFolder->nodeExists($path)) {
$output->writeln('<error>Trash folder sill exists after attempting to delete it</error>');
return;
}
$query = $this->dbConnection->getQueryBuilder();
$query->delete('files_trash')
->where($query->expr()->eq('user', $query->createParameter('uid')))
->setParameter('uid', $user->getUID());
$query->executeStatement();
}
}
37 changes: 32 additions & 5 deletions apps/files_trashbin/lib/Command/Expire.php
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use strong type in the constructor?

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@
namespace OCA\Files_Trashbin\Command;

use OC\Command\FileAccess;
use OC\Files\SetupManager;
use OCA\Files_Trashbin\Trashbin;
use OCP\Command\ICommand;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IUser;
use OCP\IUserManager;
use OCP\Server;

Expand All @@ -26,14 +32,35 @@ public function __construct(

public function handle() {
$userManager = Server::get(IUserManager::class);
if (!$userManager->userExists($this->user)) {
$userObject = $userManager->get($this->user);
if (!$userObject) {
// User has been deleted already
return;
}

\OC_Util::tearDownFS();
\OC_Util::setupFS($this->user);
Trashbin::expire($this->user);
\OC_Util::tearDownFS();
$rootFolder = $this->getTrashRoot($userObject);
if (!$rootFolder) {
return;
}

Trashbin::expire($rootFolder, $userObject);
$setupManager = Server::get(SetupManager::class);
$setupManager->tearDown();
}

protected function getTrashRoot(IUser $user): ?Folder {
$setupManager = Server::get(SetupManager::class);
$rootFolder = Server::get(IRootFolder::class);
Comment on lines +52 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we use DI?

$setupManager->tearDown();
$setupManager->setupForUser($user);

try {
/** @var Folder $folder */
$folder = $rootFolder->getUserFolder($user->getUID())->getParent()->get('files_trashbin');
return $folder;
} catch (NotFoundException|NotPermittedException) {
$setupManager->tearDown();
return null;
}
}
}
52 changes: 25 additions & 27 deletions apps/files_trashbin/lib/Command/ExpireTrash.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,36 @@
*/
namespace OCA\Files_Trashbin\Command;

use OC\Files\View;
use OC\Core\Command\Base;
use OC\Files\SetupManager;
use OCA\Files_Trashbin\Expiration;
use OCA\Files_Trashbin\Trashbin;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IUser;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class ExpireTrash extends Command {
class ExpireTrash extends Base {

/**
* @param IUserManager|null $userManager
* @param Expiration|null $expiration
*/
public function __construct(
private LoggerInterface $logger,
private ?IUserManager $userManager = null,
private ?Expiration $expiration = null,
readonly private LoggerInterface $logger,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
readonly private LoggerInterface $logger,
private readonly LoggerInterface $logger,

Same for the others

readonly private ?IUserManager $userManager,
readonly private ?Expiration $expiration,
readonly private SetupManager $setupManager,
readonly private IRootFolder $rootFolder,
) {
parent::__construct();
}

protected function configure() {
parent::configure();
$this
->setName('trashbin:expire')
->setDescription('Expires the users trashbin')
Expand Down Expand Up @@ -81,31 +84,26 @@ protected function execute(InputInterface $input, OutputInterface $output): int

public function expireTrashForUser(IUser $user) {
try {
$uid = $user->getUID();
if (!$this->setupFS($uid)) {
$trashRoot = $this->getTrashRoot($user);
if (!$trashRoot) {
return;
}
Trashbin::expire($uid);
Trashbin::expire($trashRoot, $user);
} catch (\Throwable $e) {
$this->logger->error('Error while expiring trashbin for user ' . $user->getUID(), ['exception' => $e]);
}
}

/**
* Act on behalf on trash item owner
* @param string $user
* @return boolean
*/
protected function setupFS($user) {
\OC_Util::tearDownFS();
\OC_Util::setupFS($user);
protected function getTrashRoot(IUser $user): ?Folder {
$this->setupManager->tearDown();
$this->setupManager->setupForUser($user);

// Check if this user has a trashbin directory
$view = new View('/' . $user);
if (!$view->is_dir('/files_trashbin/files')) {
return false;
try {
/** @var Folder $folder */
$folder = $this->rootFolder->getUserFolder($user->getUID())->getParent()->get('files_trashbin');
return $folder;
} catch (NotFoundException|NotPermittedException) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be caught? If you let it bubble up it will get logged. But I’m not sure whether it should get logged. NotFound would just mean the user trashbin was not used yes.
NotPermitted maybe we should log.

return null;
}

return true;
}
}
15 changes: 9 additions & 6 deletions apps/files_trashbin/lib/Command/RestoreAllFiles.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
namespace OCA\Files_Trashbin\Command;

use OC\Core\Command\Base;
use OC\Files\SetupManager;
use OCA\Files_Trashbin\Trash\ITrashManager;
use OCA\Files_Trashbin\Trash\TrashItem;
use OCP\Files\IRootFolder;
use OCP\IDBConnection;
use OCP\IL10N;
use OCP\IUserBackend;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\L10N\IFactory;
use Symfony\Component\Console\Exception\InvalidOptionException;
use Symfony\Component\Console\Input\InputArgument;
Expand Down Expand Up @@ -48,6 +50,8 @@ public function __construct(
protected IUserManager $userManager,
protected IDBConnection $dbConnection,
protected ITrashManager $trashManager,
protected SetupManager $setupManager,
protected IUserSession $userSession,
IFactory $l10nFactory,
) {
parent::__construct();
Expand Down Expand Up @@ -140,17 +144,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int
* Restore deleted files for the given user according to the given filters
*/
protected function restoreDeletedFiles(string $uid, int $scope, ?int $since, ?int $until, bool $dryRun, OutputInterface $output): void {
\OC_Util::tearDownFS();
\OC_Util::setupFS($uid);
\OC_User::setUserId($uid);

$user = $this->userManager->get($uid);

if ($user === null) {
if (!$user) {
$output->writeln("<error>Unknown user $uid</error>");
return;
}

$this->setupManager->tearDown();
$this->setupManager->setupForUser($user);
$this->userSession->setUser($user);

$userTrashItems = $this->filterTrashItems(
$this->trashManager->listTrashRoot($user),
$scope,
Expand Down
6 changes: 4 additions & 2 deletions apps/files_trashbin/lib/Command/Size.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use OC\Core\Command\Base;
use OCP\Command\IBus;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IUser;
use OCP\IUserManager;
Expand All @@ -21,6 +22,7 @@

class Size extends Base {
public function __construct(
private IAppConfig $appConfig,
private IConfig $config,
private IUserManager $userManager,
private IBus $commandBus,
Expand Down Expand Up @@ -55,7 +57,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->config->setUserValue($user, 'files_trashbin', 'trashbin_size', (string)$parsedSize);
$this->commandBus->push(new Expire($user));
} else {
$this->config->setAppValue('files_trashbin', 'trashbin_size', (string)$parsedSize);
$this->appConfig->setValueInt('files_trashbin', 'trashbin_size', $parsedSize);
$output->writeln('<info>Warning: changing the default trashbin size will automatically trigger cleanup of existing trashbins,</info>');
$output->writeln('<info>a users trashbin can exceed the configured size until they move a new file to the trashbin.</info>');
}
Expand All @@ -67,7 +69,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

private function printTrashbinSize(InputInterface $input, OutputInterface $output, ?string $user) {
$globalSize = (int)$this->config->getAppValue('files_trashbin', 'trashbin_size', '-1');
$globalSize = $this->appConfig->getValueInt('files_trashbin', 'trashbin_size', -1);
if ($globalSize < 0) {
$globalHumanSize = 'default (50% of available space)';
} else {
Expand Down
22 changes: 20 additions & 2 deletions apps/files_trashbin/lib/Listener/EventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,38 @@
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Events\BeforeFileSystemSetupEvent;
use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IUserManager;
use OCP\User\Events\BeforeUserDeletedEvent;

/** @template-implements IEventListener<NodeWrittenEvent|BeforeUserDeletedEvent|BeforeFileSystemSetupEvent> */
class EventListener implements IEventListener {
public function __construct(
private IUserManager $userManager,
private IRootFolder $rootFolder,
private ?string $userId = null,
) {
}

public function handle(Event $event): void {
if ($event instanceof NodeWrittenEvent) {
// Resize trash
if (!empty($this->userId)) {
Trashbin::resizeTrash($this->userId);
if (empty($this->userId)) {
return;
}
try {
/** @var Folder $trashRoot */
$trashRoot = $this->rootFolder->get('/' . $this->userId . '/files_trashbin');
} catch (NotFoundException|NotPermittedException) {
return;
}

$user = $this->userManager->get($this->userId);
if ($user) {
Trashbin::resizeTrash($trashRoot, $user);
}
}

Expand Down
Loading
Loading