summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorLouis <louis@chmn.me>2023-12-02 00:21:27 +0100
committerGitHub <noreply@github.com>2023-12-02 00:21:27 +0100
commit917e271730a59126d9dba45490c0bfd1ac7e2a7d (patch)
tree1c894b151cc5720081fa3f95c5aca6bb02ec9884 /apps
parented809fd0f644ef9102958b9c2f5a3fa6c585cbc0 (diff)
parent263f9e1e77a833cd1c1b4f37ee53fa59c6e5ea42 (diff)
downloadnextcloud-server-917e271730a59126d9dba45490c0bfd1ac7e2a7d.tar.gz
nextcloud-server-917e271730a59126d9dba45490c0bfd1ac7e2a7d.zip
Merge pull request #41941 from nextcloud/backport/41924/stable28
[stable28] Add comment in SyncLivePhotosListener
Diffstat (limited to 'apps')
-rw-r--r--apps/files/lib/Listener/SyncLivePhotosListener.php224
1 files changed, 131 insertions, 93 deletions
diff --git a/apps/files/lib/Listener/SyncLivePhotosListener.php b/apps/files/lib/Listener/SyncLivePhotosListener.php
index 558b9e8eed6..32dcdc81084 100644
--- a/apps/files/lib/Listener/SyncLivePhotosListener.php
+++ b/apps/files/lib/Listener/SyncLivePhotosListener.php
@@ -32,6 +32,7 @@ use OCP\EventDispatcher\IEventListener;
use OCP\Files\Cache\CacheEntryRemovedEvent;
use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
+use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
@@ -74,8 +75,6 @@ class SyncLivePhotosListener implements IEventListener {
$peerFile = $this->getLivePhotoPeer($event->getNode()->getId());
} elseif ($event instanceof CacheEntryRemovedEvent) {
$peerFile = $this->getLivePhotoPeer($event->getFileId());
- } else {
- return;
}
if ($peerFile === null) {
@@ -83,118 +82,152 @@ class SyncLivePhotosListener implements IEventListener {
}
if ($event instanceof BeforeNodeRenamedEvent) {
- $sourceFile = $event->getSource();
- $targetFile = $event->getTarget();
- $targetParent = $targetFile->getParent();
- $sourceExtension = $sourceFile->getExtension();
- $peerFileExtension = $peerFile->getExtension();
- $targetName = $targetFile->getName();
- $targetPath = $targetFile->getPath();
-
- // Prevent rename of the .mov file if the peer file do not have the same path.
- if ($sourceFile->getMimetype() === 'video/quicktime') {
- $peerFilePath = $this->pendingRenames[$peerFile->getId()] ?? $peerFile->getPath();
- $targetPathWithoutExtension = preg_replace("/\.$sourceExtension$/", '', $targetPath);
- $peerFilePathWithoutExtension = preg_replace("/\.$peerFileExtension$/", '', $peerFilePath);
-
- if ($targetPathWithoutExtension !== $peerFilePathWithoutExtension) {
- $event->abortOperation(new NotPermittedException("The video part of a live photo need to have the same name as the image"));
- }
-
- unset($this->pendingRenames[$peerFile->getId()]);
- return;
- }
-
- if (!str_ends_with($targetName, ".".$sourceExtension)) {
- $event->abortOperation(new NotPermittedException("Cannot change the extension of a live photo"));
- }
+ $this->handleMove($event, $peerFile);
+ } elseif ($event instanceof BeforeNodeDeletedEvent) {
+ $this->handleDeletion($event, $peerFile);
+ } elseif ($event instanceof CacheEntryRemovedEvent) {
+ $peerFile->delete();
+ } elseif ($event instanceof BeforeNodeRestoredEvent) {
+ $this->handleRestore($event, $peerFile);
+ }
+ }
- try {
- $targetParent->get($targetName);
- $event->abortOperation(new NotPermittedException("A file already exist at destination path"));
- } catch (NotFoundException $ex) {
- }
- try {
- $peerTargetName = preg_replace("/\.$sourceExtension$/", '.mov', $targetName);
- $targetParent->get($peerTargetName);
- $event->abortOperation(new NotPermittedException("A file already exist at destination path"));
- } catch (NotFoundException $ex) {
+ /**
+ * During rename events, which also include move operations,
+ * we rename the peer file using the same name.
+ * This means that a move operation on the .jpg will trigger
+ * another recursive one for the .mov.
+ * Move operations on the .mov file directly are currently blocked.
+ * The event listener being singleton, we can store the current state
+ * of pending renames inside the 'pendingRenames' property,
+ * to prevent infinite recursivity.
+ */
+ private function handleMove(BeforeNodeRenamedEvent $event, Node $peerFile): void {
+ $sourceFile = $event->getSource();
+ $targetFile = $event->getTarget();
+ $targetParent = $targetFile->getParent();
+ $sourceExtension = $sourceFile->getExtension();
+ $peerFileExtension = $peerFile->getExtension();
+ $targetName = $targetFile->getName();
+ $targetPath = $targetFile->getPath();
+
+ // Prevent rename of the .mov file if the peer file do not have the same path.
+ if ($sourceFile->getMimetype() === 'video/quicktime') {
+ $peerFilePath = $this->pendingRenames[$peerFile->getId()] ?? $peerFile->getPath();
+ $targetPathWithoutExtension = preg_replace("/\.$sourceExtension$/", '', $targetPath);
+ $peerFilePathWithoutExtension = preg_replace("/\.$peerFileExtension$/", '', $peerFilePath);
+
+ if ($targetPathWithoutExtension !== $peerFilePathWithoutExtension) {
+ $event->abortOperation(new NotPermittedException("The video part of a live photo need to have the same name as the image"));
}
- $peerTargetPath = preg_replace("/\.$sourceExtension$/", '.mov', $targetPath);
- $this->pendingRenames[$sourceFile->getId()] = $targetPath;
- try {
- $peerFile->move($peerTargetPath);
- } catch (\Throwable $ex) {
- $event->abortOperation($ex);
- }
+ unset($this->pendingRenames[$peerFile->getId()]);
return;
}
- if ($event instanceof BeforeNodeDeletedEvent) {
- $deletedFile = $event->getNode();
- if ($deletedFile->getMimetype() === 'video/quicktime') {
- if (isset($this->pendingDeletion[$peerFile->getId()])) {
- unset($this->pendingDeletion[$peerFile->getId()]);
- return;
- } else {
- $event->abortOperation(new NotPermittedException("Cannot delete the video part of a live photo"));
- }
- } else {
- $this->pendingDeletion[$deletedFile->getId()] = true;
- try {
- $peerFile->delete();
- } catch (\Throwable $ex) {
- $event->abortOperation($ex);
- }
- }
- return;
+ if (!str_ends_with($targetName, ".".$sourceExtension)) {
+ $event->abortOperation(new NotPermittedException("Cannot change the extension of a live photo"));
}
- if ($event instanceof CacheEntryRemovedEvent) {
- $peerFile->delete();
+ try {
+ $targetParent->get($targetName);
+ $event->abortOperation(new NotPermittedException("A file already exist at destination path"));
+ } catch (NotFoundException $ex) {
+ }
+ try {
+ $peerTargetName = preg_replace("/\.$sourceExtension$/", '.mov', $targetName);
+ $targetParent->get($peerTargetName);
+ $event->abortOperation(new NotPermittedException("A file already exist at destination path"));
+ } catch (NotFoundException $ex) {
}
- if ($event instanceof BeforeNodeRestoredEvent) {
- $sourceFile = $event->getSource();
+ $peerTargetPath = preg_replace("/\.$sourceExtension$/", '.mov', $targetPath);
+ $this->pendingRenames[$sourceFile->getId()] = $targetPath;
+ try {
+ $peerFile->move($peerTargetPath);
+ } catch (\Throwable $ex) {
+ $event->abortOperation($ex);
+ }
+ return;
+ }
- if ($sourceFile->getMimetype() === 'video/quicktime') {
- if (isset($this->pendingRestores[$peerFile->getId()])) {
- unset($this->pendingRestores[$peerFile->getId()]);
- return;
- } else {
- $event->abortOperation(new NotPermittedException("Cannot restore the video part of a live photo"));
- }
+ /**
+ * During deletion event, we trigger another recursive delete on the peer file.
+ * Delete operations on the .mov file directly are currently blocked.
+ * The event listener being singleton, we can store the current state
+ * of pending deletions inside the 'pendingDeletions' property,
+ * to prevent infinite recursivity.
+ */
+ private function handleDeletion(BeforeNodeDeletedEvent $event, Node $peerFile): void {
+ $deletedFile = $event->getNode();
+ if ($deletedFile->getMimetype() === 'video/quicktime') {
+ if (isset($this->pendingDeletion[$peerFile->getId()])) {
+ unset($this->pendingDeletion[$peerFile->getId()]);
+ return;
} else {
- $user = $this->userSession->getUser();
- if ($user === null) {
- return;
- }
+ $event->abortOperation(new NotPermittedException("Cannot delete the video part of a live photo"));
+ }
+ } else {
+ $this->pendingDeletion[$deletedFile->getId()] = true;
+ try {
+ $peerFile->delete();
+ } catch (\Throwable $ex) {
+ $event->abortOperation($ex);
+ }
+ }
+ return;
+ }
- $peerTrashItem = $this->trashManager->getTrashNodeById($user, $peerFile->getId());
+ /**
+ * During restore event, we trigger another recursive restore on the peer file.
+ * Restore operations on the .mov file directly are currently blocked.
+ * The event listener being singleton, we can store the current state
+ * of pending restores inside the 'pendingRestores' property,
+ * to prevent infinite recursivity.
+ */
+ private function handleRestore(BeforeNodeRestoredEvent $event, Node $peerFile): void {
+ $sourceFile = $event->getSource();
+
+ if ($sourceFile->getMimetype() === 'video/quicktime') {
+ if (isset($this->pendingRestores[$peerFile->getId()])) {
+ unset($this->pendingRestores[$peerFile->getId()]);
+ return;
+ } else {
+ $event->abortOperation(new NotPermittedException("Cannot restore the video part of a live photo"));
+ }
+ } else {
+ $user = $this->userSession->getUser();
+ if ($user === null) {
+ return;
+ }
- // Peer file in not in the bin, no need to restore it.
- if ($peerTrashItem === null) {
- return;
- }
+ $peerTrashItem = $this->trashManager->getTrashNodeById($user, $peerFile->getId());
+ // Peer file is not in the bin, no need to restore it.
+ if ($peerTrashItem === null) {
+ return;
+ }
- $trashRoot = $this->trashManager->listTrashRoot($user);
- $trashItem = $this->getTrashItem($trashRoot, $peerFile->getInternalPath());
+ $trashRoot = $this->trashManager->listTrashRoot($user);
+ $trashItem = $this->getTrashItem($trashRoot, $peerFile->getInternalPath());
- if ($trashItem === null) {
- $event->abortOperation(new NotFoundException("Couldn't find peer file in trashbin"));
- }
+ if ($trashItem === null) {
+ $event->abortOperation(new NotFoundException("Couldn't find peer file in trashbin"));
+ }
- $this->pendingRestores[$sourceFile->getId()] = true;
- try {
- $this->trashManager->restoreItem($trashItem);
- } catch (\Throwable $ex) {
- $event->abortOperation($ex);
- }
+ $this->pendingRestores[$sourceFile->getId()] = true;
+ try {
+ $this->trashManager->restoreItem($trashItem);
+ } catch (\Throwable $ex) {
+ $event->abortOperation($ex);
}
}
}
+ /**
+ * Helper method to get the associated live photo file.
+ * We first look for it in the user folder, and if we
+ * cannot find it here, we look for it in the user's trashbin.
+ */
private function getLivePhotoPeer(int $nodeId): ?Node {
if ($this->userFolder === null || $this->userSession === null) {
return null;
@@ -231,6 +264,11 @@ class SyncLivePhotosListener implements IEventListener {
return null;
}
+ /**
+ * There is currently no method to restore a file based on its fileId or path.
+ * So we have to manually find a ITrashItem from the trash item list.
+ * TODO: This should be replaced by a proper method in the TrashManager.
+ */
private function getTrashItem(array $trashFolder, string $path): ?ITrashItem {
foreach($trashFolder as $trashItem) {
if (str_starts_with($path, "files_trashbin/files".$trashItem->getTrashPath())) {