From 8f60465e8a56a3f9ea29c51dc2596e1c4f55c4ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 28 Nov 2023 14:28:16 +0100 Subject: [PATCH] Add a listener to update trashed items when parent is renamed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/AppInfo/Application.php | 3 ++ lib/Listeners/NodeRenamedListener.php | 62 +++++++++++++++++++++++++++ lib/Trash/TrashManager.php | 32 ++++++++++++-- 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 lib/Listeners/NodeRenamedListener.php diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 3b1d22f3a..a06bde607 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -43,6 +43,7 @@ use OCA\GroupFolders\Folder\FolderManager; use OCA\GroupFolders\Listeners\CircleDestroyedEventListener; use OCA\GroupFolders\Listeners\LoadAdditionalScriptsListener; +use OCA\GroupFolders\Listeners\NodeRenamedListener; use OCA\GroupFolders\Mount\MountProvider; use OCA\GroupFolders\Trash\TrashBackend; use OCA\GroupFolders\Trash\TrashManager; @@ -55,6 +56,7 @@ use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\Config\IMountProviderCollection; +use OCP\Files\Events\Node\NodeRenamedEvent; use OCP\Files\Folder; use OCP\Files\IMimeTypeLoader; use OCP\Files\IRootFolder; @@ -90,6 +92,7 @@ public function register(IRegistrationContext $context): void { $context->registerEventListener(LoadAdditionalScriptsEvent::class, LoadAdditionalScriptsListener::class); $context->registerEventListener(BeforeTemplateRenderedEvent::class, LoadAdditionalScriptsListener::class); $context->registerEventListener(CircleDestroyedEvent::class, CircleDestroyedEventListener::class); + $context->registerEventListener(NodeRenamedEvent::class, NodeRenamedListener::class); $context->registerService('GroupAppFolder', function (ContainerInterface $c): Folder { /** @var IRootFolder $rootFolder */ diff --git a/lib/Listeners/NodeRenamedListener.php b/lib/Listeners/NodeRenamedListener.php new file mode 100644 index 000000000..df2f4d7ef --- /dev/null +++ b/lib/Listeners/NodeRenamedListener.php @@ -0,0 +1,62 @@ + + * + * @author Côme Chilliet + * + * @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 . + * + */ + +namespace OCA\GroupFolders\Listeners; + +use OCA\GroupFolders\Mount\GroupFolderStorage; +use OCA\GroupFolders\Trash\TrashManager; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\Events\Node\NodeRenamedEvent; +use OCP\Files\Folder; +use Psr\Log\LoggerInterface; + +/** + * @template-implements IEventListener + */ +class NodeRenamedListener implements IEventListener { + public function __construct( + private TrashManager $trashManager, + private LoggerInterface $logger, + ) { + } + + public function handle(Event $event): void { + $source = $event->getSource(); + $target = $event->getTarget(); + // Look at the parent because the node itself is not existing anymore + $sourceStorage = $source->getParent()->getStorage(); + $targetStorage = $target->getStorage(); + + if (($target instanceof Folder) && + $sourceStorage->instanceOfStorage(GroupFolderStorage::class) && + $targetStorage->instanceOfStorage(GroupFolderStorage::class)) { + $sourcePath = preg_replace('/^'.preg_quote($source->getParent()->getMountPoint()->getMountPoint(), '/').'/', '', $source->getPath()); + $targetPath = preg_replace('/^'.preg_quote($target->getMountPoint()->getMountPoint(), '/').'/', '', $target->getPath()); + $this->trashManager->updateTrashedChildren($sourceStorage->getFolderId(), $targetStorage->getFolderId(), $sourcePath, $targetPath); + } + } +} diff --git a/lib/Trash/TrashManager.php b/lib/Trash/TrashManager.php index a769da298..f1e1e7670 100644 --- a/lib/Trash/TrashManager.php +++ b/lib/Trash/TrashManager.php @@ -25,9 +25,9 @@ use OCP\IDBConnection; class TrashManager { - private IDBConnection $connection; - - public function __construct(IDBConnection $connection) { + public function __construct( + private IDBConnection $connection, + ) { $this->connection = $connection; } @@ -90,4 +90,30 @@ public function emptyTrashbin(int $folderId): void { ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT))); $query->executeStatement(); } + + public function updateTrashedChildren(int $fromFolderId, int $toFolderId, string $fromLocation, string $toLocation): void { + // Update deep children + $query = $this->connection->getQueryBuilder(); + $fun = $query->func(); + $sourceLength = mb_strlen($fromLocation); + $newPathFunction = $fun->concat( + $query->createNamedParameter($toLocation), + $fun->substring('original_location', $query->createNamedParameter($sourceLength + 1, IQueryBuilder::PARAM_INT))// +1 for the leading slash + ); + $query->update('group_folders_trash') + ->set('folder_id', $query->createNamedParameter($toFolderId, IQueryBuilder::PARAM_INT)) + ->set('original_location', $newPathFunction) + ->where($query->expr()->eq('folder_id', $query->createNamedParameter($fromFolderId, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->like('original_location', $query->createNamedParameter($this->connection->escapeLikeParameter($fromLocation) . '/%'))); + $query->executeStatement(); + + // Update direct children + $query = $this->connection->getQueryBuilder(); + $query->update('group_folders_trash') + ->set('folder_id', $query->createNamedParameter($toFolderId, IQueryBuilder::PARAM_INT)) + ->set('original_location', $query->createNamedParameter($toLocation)) + ->where($query->expr()->eq('folder_id', $query->createNamedParameter($fromFolderId, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->eq('original_location', $query->createNamedParameter($fromLocation, IQueryBuilder::PARAM_STR))); + $query->executeStatement(); + } }