diff --git a/apps/files/lib/Listener/SyncLivePhotosListener.php b/apps/files/lib/Listener/SyncLivePhotosListener.php index 34789187644ac..1a4a7444fd634 100644 --- a/apps/files/lib/Listener/SyncLivePhotosListener.php +++ b/apps/files/lib/Listener/SyncLivePhotosListener.php @@ -8,6 +8,10 @@ namespace OCA\Files\Listener; +use Exception; +use OC\Files\Node\NonExistingFile; +use OC\Files\Node\NonExistingFolder; +use OC\Files\View; use OC\FilesMetadata\Model\FilesMetadata; use OCA\Files\Service\LivePhotosService; use OCP\EventDispatcher\Event; @@ -19,7 +23,9 @@ use OCP\Files\Events\Node\BeforeNodeDeletedEvent; use OCP\Files\Events\Node\BeforeNodeRenamedEvent; use OCP\Files\Events\Node\NodeCopiedEvent; +use OCP\Files\File; use OCP\Files\Folder; +use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\FilesMetadata\IFilesMetadataManager; @@ -37,6 +43,8 @@ public function __construct( private ?Folder $userFolder, private IFilesMetadataManager $filesMetadataManager, private LivePhotosService $livePhotosService, + private IRootFolder $rootFolder, + private View $view, ) { } @@ -45,39 +53,37 @@ public function handle(Event $event): void { return; } - $peerFileId = null; - - if ($event instanceof BeforeNodeRenamedEvent) { - $peerFileId = $this->livePhotosService->getLivePhotoPeerId($event->getSource()->getId()); - } elseif ($event instanceof BeforeNodeDeletedEvent) { - $peerFileId = $this->livePhotosService->getLivePhotoPeerId($event->getNode()->getId()); - } elseif ($event instanceof CacheEntryRemovedEvent) { - $peerFileId = $this->livePhotosService->getLivePhotoPeerId($event->getFileId()); - } elseif ($event instanceof BeforeNodeCopiedEvent || $event instanceof NodeCopiedEvent) { - $peerFileId = $this->livePhotosService->getLivePhotoPeerId($event->getSource()->getId()); - } + if ($event instanceof BeforeNodeCopiedEvent || $event instanceof NodeCopiedEvent) { + $this->handleCopyRecursive($event, $event->getSource(), $event->getTarget()); + } else { + $peerFileId = null; + + if ($event instanceof BeforeNodeRenamedEvent) { + $peerFileId = $this->livePhotosService->getLivePhotoPeerId($event->getSource()->getId()); + } elseif ($event instanceof BeforeNodeDeletedEvent) { + $peerFileId = $this->livePhotosService->getLivePhotoPeerId($event->getNode()->getId()); + } elseif ($event instanceof CacheEntryRemovedEvent) { + $peerFileId = $this->livePhotosService->getLivePhotoPeerId($event->getFileId()); + } - if ($peerFileId === null) { - return; // Not a live photo. - } + if ($peerFileId === null) { + return; // Not a live photo. + } - // Check the user's folder. - $peerFile = $this->userFolder->getFirstNodeById($peerFileId); + // Check the user's folder. + $peerFile = $this->userFolder->getFirstNodeById($peerFileId); - if ($peerFile === null) { - return; // Peer file not found. - } + if ($peerFile === null) { + return; // Peer file not found. + } - if ($event instanceof BeforeNodeRenamedEvent) { - $this->handleMove($event, $peerFile, false); - } elseif ($event instanceof BeforeNodeDeletedEvent) { - $this->handleDeletion($event, $peerFile); - } elseif ($event instanceof CacheEntryRemovedEvent) { - $peerFile->delete(); - } elseif ($event instanceof BeforeNodeCopiedEvent) { - $this->handleMove($event, $peerFile, true); - } elseif ($event instanceof NodeCopiedEvent) { - $this->handleCopy($event, $peerFile); + if ($event instanceof BeforeNodeRenamedEvent) { + $this->handleMove($event->getSource(), $event->getTarget(), $peerFile, false); + } elseif ($event instanceof BeforeNodeDeletedEvent) { + $this->handleDeletion($event, $peerFile); + } elseif ($event instanceof CacheEntryRemovedEvent) { + $peerFile->delete(); + } } } @@ -88,14 +94,7 @@ public function handle(Event $event): void { * of pending renames inside the 'pendingRenames' property, * to prevent infinite recursive. */ - private function handleMove(AbstractNodesEvent $event, Node $peerFile, bool $prepForCopyOnly = false): void { - if (!($event instanceof BeforeNodeCopiedEvent) && - !($event instanceof BeforeNodeRenamedEvent)) { - return; - } - - $sourceFile = $event->getSource(); - $targetFile = $event->getTarget(); + private function handleMove(Node $sourceFile, Node $targetFile, Node $peerFile, bool $prepForCopyOnly = false): void { $targetParent = $targetFile->getParent(); $sourceExtension = $sourceFile->getExtension(); $peerFileExtension = $peerFile->getExtension(); @@ -112,10 +111,12 @@ private function handleMove(AbstractNodesEvent $event, Node $peerFile, bool $pre } $peerTargetName = substr($targetName, 0, -strlen($sourceExtension)) . $peerFileExtension; - try { - $targetParent->get($peerTargetName); - throw new AbortedEventException('A file already exist at destination path of the Live Photo'); - } catch (NotFoundException) { + if (!($targetParent instanceof NonExistingFolder)) { + try { + $targetParent->get($peerTargetName); + throw new AbortedEventException('A file already exist at destination path of the Live Photo'); + } catch (NotFoundException) { + } } // in case the rename was initiated from this listener, we stop right now @@ -136,33 +137,31 @@ private function handleMove(AbstractNodesEvent $event, Node $peerFile, bool $pre /** * handle copy, we already know if it is doable from BeforeNodeCopiedEvent, so we just copy the linked file - * - * @param NodeCopiedEvent $event - * @param Node $peerFile */ - private function handleCopy(NodeCopiedEvent $event, Node $peerFile): void { - $sourceFile = $event->getSource(); + private function handleCopy(File $sourceFile, File $targetFile, File $peerFile): void { $sourceExtension = $sourceFile->getExtension(); $peerFileExtension = $peerFile->getExtension(); - $targetFile = $event->getTarget(); $targetParent = $targetFile->getParent(); $targetName = $targetFile->getName(); $peerTargetName = substr($targetName, 0, -strlen($sourceExtension)) . $peerFileExtension; - /** - * let's use freshly set variable. - * we copy the file and get its id. We already have the id of the current copy - * We have everything to update metadata and keep the link between the 2 copies. - */ - $newPeerFile = $peerFile->copy($targetParent->getPath() . '/' . $peerTargetName); + + if ($targetParent->nodeExists($peerTargetName)) { + // If the copy was a folder copy, then the peer file already exists. + $targetPeerFile = $targetParent->get($peerTargetName); + } else { + // If the copy was a file copy, then we need to create the peer file. + $targetPeerFile = $peerFile->copy($targetParent->getPath() . '/' . $peerTargetName); + } + /** @var FilesMetadata $targetMetadata */ $targetMetadata = $this->filesMetadataManager->getMetadata($targetFile->getId(), true); $targetMetadata->setStorageId($targetFile->getStorage()->getCache()->getNumericStorageId()); - $targetMetadata->setString('files-live-photo', (string)$newPeerFile->getId()); + $targetMetadata->setString('files-live-photo', (string)$targetPeerFile->getId()); $this->filesMetadataManager->saveMetadata($targetMetadata); /** @var FilesMetadata $peerMetadata */ - $peerMetadata = $this->filesMetadataManager->getMetadata($newPeerFile->getId(), true); - $peerMetadata->setStorageId($newPeerFile->getStorage()->getCache()->getNumericStorageId()); + $peerMetadata = $this->filesMetadataManager->getMetadata($targetPeerFile->getId(), true); + $peerMetadata->setStorageId($targetPeerFile->getStorage()->getCache()->getNumericStorageId()); $peerMetadata->setString('files-live-photo', (string)$targetFile->getId()); $this->filesMetadataManager->saveMetadata($peerMetadata); } @@ -193,4 +192,47 @@ private function handleDeletion(BeforeNodeDeletedEvent $event, Node $peerFile): } return; } + + /* + * Recursively get all the peer ids of a live photo. + * Needed when coping a folder. + * + * @param BeforeNodeCopiedEvent|NodeCopiedEvent $event + */ + private function handleCopyRecursive(Event $event, Node $sourceNode, Node $targetNode): void { + if ($sourceNode instanceof Folder && $targetNode instanceof Folder) { + foreach ($sourceNode->getDirectoryListing() as $sourceChild) { + if ($event instanceof BeforeNodeCopiedEvent) { + if ($sourceChild instanceof Folder) { + $targetChild = new NonExistingFolder($this->rootFolder, $this->view, $targetNode->getPath().'/'.$sourceChild->getName(), null, $targetNode); + } else { + $targetChild = new NonExistingFile($this->rootFolder, $this->view, $targetNode->getPath().'/'.$sourceChild->getName(), null, $targetNode); + } + } elseif ($event instanceof NodeCopiedEvent) { + $targetChild = $targetNode->get($sourceChild->getName()); + } else { + throw new Exception('Event is type is not supported'); + } + + $this->handleCopyRecursive($event, $sourceChild, $targetChild); + } + } else if ($sourceNode instanceof File && $targetNode instanceof File) { + $peerFileId = $this->livePhotosService->getLivePhotoPeerId($sourceNode->getId()); + if ($peerFileId === null) { + return; + } + $peerFile = $this->userFolder->getFirstNodeById($peerFileId); + if ($peerFile === null) { + return; + } + + if ($event instanceof BeforeNodeCopiedEvent) { + $this->handleMove($sourceNode, $targetNode, $peerFile, true); + } elseif ($event instanceof NodeCopiedEvent) { + $this->handleCopy($sourceNode, $targetNode, $peerFile); + } + } else { + throw new Exception('Source and target type are not matching'); + } + } }