Skip to content

Commit

Permalink
Merge pull request #2713 from nextcloud/backport/2644/stable27
Browse files Browse the repository at this point in the history
[stable27] Add a listener to update trashed items when parent is renamed
  • Loading branch information
come-nc authored Jan 11, 2024
2 parents 752b9f9 + 0e36f90 commit 8dbac82
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 10 deletions.
4 changes: 4 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
use OCA\GroupFolders\Helper\LazyFolder;
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;
Expand All @@ -58,6 +59,8 @@
use OCP\AppFramework\IAppContainer;
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;
use OCP\ICacheFactory;
Expand Down Expand Up @@ -90,6 +93,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->registerServiceAlias('GroupAppFolder', LazyFolder::class);

Expand Down
67 changes: 67 additions & 0 deletions lib/Listeners/NodeRenamedListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

/**
* @copyright Copyright (c) 2023 Côme Chilliet <[email protected]>
*
* @author Côme Chilliet <[email protected]>
*
* @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\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<NodeRenamedEvent>
*/
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)) {
// Get internal path on parent to avoid NotFoundException
$sourcePath = $source->getParent()->getInternalPath();
if ($sourcePath !== '') {
$sourcePath .= '/';
}
$sourcePath .= $source->getName();
$targetPath = $target->getInternalPath();
$this->trashManager->updateTrashedChildren($sourceStorage->getFolderId(), $targetStorage->getFolderId(), $sourcePath, $targetPath);
}
}
}
32 changes: 29 additions & 3 deletions lib/Trash/TrashManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -80,4 +80,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 ending 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();
}
}
11 changes: 4 additions & 7 deletions tests/psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@
</UndefinedMethod>
</file>
<file src="lib/AppInfo/Application.php">
<UndefinedClass occurrences="2">
<InvalidArgument occurrences="1">
<code>NodeRenamedListener::class</code>
</InvalidArgument>
<UndefinedClass occurrences="1">
<code>BeforeTemplateRenderedEvent</code>
<code>CircleDestroyedEvent</code>
</UndefinedClass>
</file>
<file src="lib/Folder/FolderManager.php">
<MismatchingDocblockReturnType occurrences="1">
<code>array{id: mixed, mount_point: mixed, groups: array&lt;empty, empty&gt;|mixed, quota: int, size: int|mixed, acl: bool}|false</code>
</MismatchingDocblockReturnType>
</file>
<file src="lib/Helper/LazyFolder.php">
<InvalidReturnStatement occurrences="2">
<code>$this-&gt;__call(__FUNCTION__, func_get_args())</code>
Expand Down

0 comments on commit 8dbac82

Please sign in to comment.