|
|
|
@ -29,9 +29,13 @@ use OCA\Files_Trashbin\Trash\ITrashItem;
|
|
|
|
|
use OCA\Files_Trashbin\Trash\ITrashManager;
|
|
|
|
|
use OCP\EventDispatcher\Event;
|
|
|
|
|
use OCP\EventDispatcher\IEventListener;
|
|
|
|
|
use OCP\Exceptions\AbortedEventException;
|
|
|
|
|
use OCP\Files\Cache\CacheEntryRemovedEvent;
|
|
|
|
|
use OCP\Files\Events\Node\AbstractNodesEvent;
|
|
|
|
|
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\Folder;
|
|
|
|
|
use OCP\Files\Node;
|
|
|
|
|
use OCP\Files\NotFoundException;
|
|
|
|
@ -44,7 +48,7 @@ use OCP\IUserSession;
|
|
|
|
|
* @template-implements IEventListener<Event>
|
|
|
|
|
*/
|
|
|
|
|
class SyncLivePhotosListener implements IEventListener {
|
|
|
|
|
/** @var Array<int, string> */
|
|
|
|
|
/** @var Array<int> */
|
|
|
|
|
private array $pendingRenames = [];
|
|
|
|
|
/** @var Array<int, bool> */
|
|
|
|
|
private array $pendingDeletion = [];
|
|
|
|
@ -65,7 +69,6 @@ class SyncLivePhotosListener implements IEventListener {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$peerFile = null;
|
|
|
|
|
|
|
|
|
|
if ($event instanceof BeforeNodeRenamedEvent) {
|
|
|
|
|
$peerFile = $this->getLivePhotoPeer($event->getSource()->getId());
|
|
|
|
|
} elseif ($event instanceof BeforeNodeRestoredEvent) {
|
|
|
|
@ -74,6 +77,8 @@ class SyncLivePhotosListener implements IEventListener {
|
|
|
|
|
$peerFile = $this->getLivePhotoPeer($event->getNode()->getId());
|
|
|
|
|
} elseif ($event instanceof CacheEntryRemovedEvent) {
|
|
|
|
|
$peerFile = $this->getLivePhotoPeer($event->getFileId());
|
|
|
|
|
} elseif ($event instanceof BeforeNodeCopiedEvent || $event instanceof NodeCopiedEvent) {
|
|
|
|
|
$peerFile = $this->getLivePhotoPeer($event->getSource()->getId());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($peerFile === null) {
|
|
|
|
@ -81,13 +86,17 @@ class SyncLivePhotosListener implements IEventListener {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($event instanceof BeforeNodeRenamedEvent) {
|
|
|
|
|
$this->handleMove($event, $peerFile);
|
|
|
|
|
$this->handleMove($event, $peerFile, false);
|
|
|
|
|
} elseif ($event instanceof BeforeNodeDeletedEvent) {
|
|
|
|
|
$this->handleDeletion($event, $peerFile);
|
|
|
|
|
} elseif ($event instanceof CacheEntryRemovedEvent) {
|
|
|
|
|
$peerFile->delete();
|
|
|
|
|
} elseif ($event instanceof BeforeNodeRestoredEvent) {
|
|
|
|
|
$this->handleRestore($event, $peerFile);
|
|
|
|
|
} elseif ($event instanceof BeforeNodeCopiedEvent) {
|
|
|
|
|
$this->handleMove($event, $peerFile, true);
|
|
|
|
|
} elseif ($event instanceof NodeCopiedEvent) {
|
|
|
|
|
$this->handleCopy($event, $peerFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -98,44 +107,79 @@ class SyncLivePhotosListener implements IEventListener {
|
|
|
|
|
* of pending renames inside the 'pendingRenames' property,
|
|
|
|
|
* to prevent infinite recursive.
|
|
|
|
|
*/
|
|
|
|
|
private function handleMove(BeforeNodeRenamedEvent $event, Node $peerFile): void {
|
|
|
|
|
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();
|
|
|
|
|
$targetParent = $targetFile->getParent();
|
|
|
|
|
$sourceExtension = $sourceFile->getExtension();
|
|
|
|
|
$peerFileExtension = $peerFile->getExtension();
|
|
|
|
|
$targetName = $targetFile->getName();
|
|
|
|
|
$targetPath = $targetFile->getPath();
|
|
|
|
|
|
|
|
|
|
if (!str_ends_with($targetName, ".".$sourceExtension)) {
|
|
|
|
|
$event->abortOperation(new NotPermittedException("Cannot change the extension of a Live Photo"));
|
|
|
|
|
if (!str_ends_with($targetName, "." . $sourceExtension)) {
|
|
|
|
|
throw new AbortedEventException('Cannot change the extension of a Live Photo');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$targetParent->get($targetName);
|
|
|
|
|
$event->abortOperation(new NotPermittedException("A file already exist at destination path of the Live Photo"));
|
|
|
|
|
} catch (NotFoundException $ex) {
|
|
|
|
|
throw new AbortedEventException('A file already exist at destination path of the Live Photo');
|
|
|
|
|
} catch (NotFoundException) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$peerTargetName = substr($targetName, 0, -strlen($sourceExtension)) . $peerFileExtension;
|
|
|
|
|
try {
|
|
|
|
|
$targetParent->get($peerTargetName);
|
|
|
|
|
$event->abortOperation(new NotPermittedException("A file already exist at destination path of the Live Photo"));
|
|
|
|
|
} catch (NotFoundException $ex) {
|
|
|
|
|
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
|
|
|
|
|
if (array_key_exists($peerFile->getId(), $this->pendingRenames)) {
|
|
|
|
|
if ($prepForCopyOnly || in_array($peerFile->getId(), $this->pendingRenames)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->pendingRenames[$sourceFile->getId()] = $targetPath;
|
|
|
|
|
$this->pendingRenames[] = $sourceFile->getId();
|
|
|
|
|
try {
|
|
|
|
|
$peerFile->move($targetParent->getPath() . '/' . $peerTargetName);
|
|
|
|
|
} catch (\Throwable $ex) {
|
|
|
|
|
$event->abortOperation($ex);
|
|
|
|
|
throw new AbortedEventException($ex->getMessage());
|
|
|
|
|
}
|
|
|
|
|
unset($this->pendingRenames[$sourceFile->getId()]);
|
|
|
|
|
|
|
|
|
|
array_diff($this->pendingRenames, [$sourceFile->getId()]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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();
|
|
|
|
|
$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);
|
|
|
|
|
$targetMetadata = $this->filesMetadataManager->getMetadata($targetFile->getId(), true);
|
|
|
|
|
$targetMetadata->setString('files-live-photo', (string)$newPeerFile->getId());
|
|
|
|
|
$this->filesMetadataManager->saveMetadata($targetMetadata);
|
|
|
|
|
$peerMetadata = $this->filesMetadataManager->getMetadata($newPeerFile->getId(), true);
|
|
|
|
|
$peerMetadata->setString('files-live-photo', (string)$targetFile->getId());
|
|
|
|
|
$this->filesMetadataManager->saveMetadata($peerMetadata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -152,14 +196,14 @@ class SyncLivePhotosListener implements IEventListener {
|
|
|
|
|
unset($this->pendingDeletion[$peerFile->getId()]);
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
$event->abortOperation(new NotPermittedException("Cannot delete the video part of a live photo"));
|
|
|
|
|
throw new AbortedEventException("Cannot delete the video part of a live photo");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$this->pendingDeletion[$deletedFile->getId()] = true;
|
|
|
|
|
try {
|
|
|
|
|
$peerFile->delete();
|
|
|
|
|
} catch (\Throwable $ex) {
|
|
|
|
|
$event->abortOperation($ex);
|
|
|
|
|
throw new AbortedEventException($ex->getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|