From c952570e6700906cbed71a7036e68211b38936f5 Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 20 May 2025 17:14:05 +0200 Subject: [PATCH 1/2] fix(node): emit hooks on `Node::copy()` When calling `Files\Node\Node::copy()`, `Files\View::copy()` gets called, but `Files\View::fakeRoot` is empty so the hooks are not emitted if no path is given to `Files\View::shouldEmitHooks()`. This results in node-related events like `NodeCopiedEvent` not being fired when copying files via `Files\Node\Node::copy()`. `Files\View::shouldEmitHooks()` is given a path as parameter in almost all places except when called from the `copy()` function. This commit changes it and passes the copy target path. Fixes: nextcloud/collectives#1756 Signed-off-by: Jonas --- lib/private/Files/View.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index 6832b4e1551..63eecf5e1d6 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -938,7 +938,7 @@ class View { try { $exists = $this->file_exists($target); - if ($this->shouldEmitHooks()) { + if ($this->shouldEmitHooks($target)) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_copy, @@ -978,7 +978,7 @@ class View { $this->changeLock($target, ILockingProvider::LOCK_SHARED); $lockTypePath2 = ILockingProvider::LOCK_SHARED; - if ($this->shouldEmitHooks() && $result !== false) { + if ($this->shouldEmitHooks($target) && $result !== false) { \OC_Hook::emit( Filesystem::CLASSNAME, Filesystem::signal_post_copy, From e5b4ae4ebefbc9ca33599589394cae8d3bb571a5 Mon Sep 17 00:00:00 2001 From: Jonas Date: Tue, 27 May 2025 10:05:02 +0200 Subject: [PATCH 2/2] fix(SyncLivePhotosListener): Don't handle copy event emitted from us Running $peerFile->copy() causes a second BeforeNodeCopiedEvent now, which we don't want to handle. Signed-off-by: Jonas --- apps/files/lib/Listener/SyncLivePhotosListener.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/files/lib/Listener/SyncLivePhotosListener.php b/apps/files/lib/Listener/SyncLivePhotosListener.php index 6334e5d16a6..b6773e8c452 100644 --- a/apps/files/lib/Listener/SyncLivePhotosListener.php +++ b/apps/files/lib/Listener/SyncLivePhotosListener.php @@ -37,6 +37,8 @@ class SyncLivePhotosListener implements IEventListener { private array $pendingRenames = []; /** @var Array */ private array $pendingDeletion = []; + /** @var Array */ + private array $pendingCopies = []; public function __construct( private ?Folder $userFolder, @@ -153,7 +155,6 @@ class SyncLivePhotosListener implements IEventListener { $targetName = $targetFile->getName(); $peerTargetName = substr($targetName, 0, -strlen($sourceExtension)) . $peerFileExtension; - if ($targetParent->nodeExists($peerTargetName)) { // If the copy was a folder copy, then the peer file already exists. $targetPeerFile = $targetParent->get($peerTargetName); @@ -225,6 +226,11 @@ class SyncLivePhotosListener implements IEventListener { $this->handleCopyRecursive($event, $sourceChild, $targetChild); } } elseif ($sourceNode instanceof File && $targetNode instanceof File) { + // in case the copy was initiated from this listener, we stop right now + if (in_array($sourceNode->getId(), $this->pendingCopies)) { + return; + } + $peerFileId = $this->livePhotosService->getLivePhotoPeerId($sourceNode->getId()); if ($peerFileId === null) { return; @@ -234,11 +240,13 @@ class SyncLivePhotosListener implements IEventListener { return; } + $this->pendingCopies[] = $peerFileId; if ($event instanceof BeforeNodeCopiedEvent) { $this->runMoveOrCopyChecks($sourceNode, $targetNode, $peerFile); } elseif ($event instanceof NodeCopiedEvent) { $this->handleCopy($sourceNode, $targetNode, $peerFile); } + $this->pendingCopies = array_diff($this->pendingCopies, [$peerFileId]); } else { throw new Exception('Source and target type are not matching'); }