fix: Handle copy of folders containing live photos

We need to recursively look for live photos in the folder,
and then handle them as usual.

Signed-off-by: Louis Chemineau <louis@chmn.me>
pull/49293/head
Louis Chemineau 1 year ago
parent f6c48a45ae
commit e37664bd08
No known key found for this signature in database
  1. 154
      apps/files/lib/Listener/SyncLivePhotosListener.php

@ -8,6 +8,10 @@ declare(strict_types=1);
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\BeforeNodeCopiedEvent;
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 @@ class SyncLivePhotosListener implements IEventListener {
private ?Folder $userFolder,
private IFilesMetadataManager $filesMetadataManager,
private LivePhotosService $livePhotosService,
private IRootFolder $rootFolder,
private View $view,
) {
}
@ -45,39 +53,37 @@ class SyncLivePhotosListener implements IEventListener {
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 @@ class SyncLivePhotosListener implements IEventListener {
* 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 @@ class SyncLivePhotosListener implements IEventListener {
}
$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 @@ class SyncLivePhotosListener implements IEventListener {
/**
* 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 @@ class SyncLivePhotosListener implements IEventListener {
}
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);
}
} elseif ($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');
}
}
}

Loading…
Cancel
Save