From 78058e5572e5581bc4cab5c5a9f7a8b3ae7cf70e Mon Sep 17 00:00:00 2001 From: Marcel Klehr Date: Wed, 17 Apr 2024 14:19:25 +0200 Subject: [PATCH] fix(FileListener): Listen to file movements across share boundaries Signed-off-by: Marcel Klehr --- lib/AppInfo/Application.php | 6 +++ lib/Hooks/FileListener.php | 84 ++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 97cefa71..92145937 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -21,6 +21,7 @@ use OCP\Files\Events\Node\NodeDeletedEvent; use OCP\Files\Events\Node\NodeRenamedEvent; use OCP\Files\Events\NodeRemovedFromCache; +use OCP\Files\IRootFolder; use OCP\Share\Events\ShareCreatedEvent; use OCP\Share\Events\ShareDeletedEvent; @@ -42,6 +43,11 @@ public function __construct() { $dispatcher->addServiceListener(ShareDeletedEvent::class, FileListener::class); $dispatcher->addServiceListener(CacheEntryInsertedEvent::class, FileListener::class); $dispatcher->addServiceListener(NodeRemovedFromCache::class, FileListener::class); + $rootFolder = $this->getContainer()->get(IRootFolder::class); + $rootFolder->listen('\OC\Files', 'postRename', function ($source, $target) { + $fileListener = $this->getContainer()->get(FileListener::class); + $fileListener->postRename($source, $target); + }); } public function register(IRegistrationContext $context): void { diff --git a/lib/Hooks/FileListener.php b/lib/Hooks/FileListener.php index a7a663be..a2682e8f 100644 --- a/lib/Hooks/FileListener.php +++ b/lib/Hooks/FileListener.php @@ -39,7 +39,7 @@ use Psr\Log\LoggerInterface; /** - * @psalm-implements IEventListener + * @template-implements IEventListener */ class FileListener implements IEventListener { private FaceDetectionMapper $faceDetectionMapper; @@ -73,7 +73,7 @@ public function handle(Event $event): void { $node = $share->getNode(); $accessList = $this->shareManager->getAccessList($node, true, true); - $userIds = array_map(fn (int|string $id) => (string)$id, array_keys($accessList['users'])); + $userIds = array_map(fn ($id) => strval($id), array_keys($accessList['users'])); if ($node->getType() === FileInfo::TYPE_FOLDER) { $mount = $node->getMountPoint(); @@ -315,6 +315,86 @@ public function postInsert(Node $node, bool $recurse = true): void { } } + public function preRename(Node $source, Node $target): void { + $sourceAccessList = $this->shareManager->getAccessList($source, true, true); + $sourceUserIds = array_map(fn ($id) => strval($id), array_keys($sourceAccessList['users'])); + $targetAccessList = $this->shareManager->getAccessList($target, true, true); + $targetUserIds = array_map(fn ($id) => strval($id), array_keys($targetAccessList['users'])); + + $usersToRemove = array_diff($sourceUserIds, $targetUserIds); + + if ($target->getType() === FileInfo::TYPE_FOLDER) { + $mount = $target->getMountPoint(); + if ($mount->getNumericStorageId() === null) { + return; + } + $files = $this->storageService->getFilesInMount($mount->getNumericStorageId(), $source->getId(), [ClusteringFaceClassifier::MODEL_NAME], 0, 0); + foreach ($files as $fileInfo) { + $this->faceDetectionMapper->removeDetectionsForFileFromUsersNotInList($fileInfo['fileid'], $usersToRemove); + } + } else { + $this->faceDetectionMapper->removeDetectionsForFileFromUsersNotInList($source->getId(), $usersToRemove); + } + } + + public function postRename(Node $source, Node $target): void { + if (in_array($source->getName(), [...Constants::IGNORE_MARKERS_ALL, ...Constants::IGNORE_MARKERS_IMAGE, ...Constants::IGNORE_MARKERS_AUDIO, ...Constants::IGNORE_MARKERS_VIDEO], true) && + !in_array($target->getName(), [...Constants::IGNORE_MARKERS_ALL, ...Constants::IGNORE_MARKERS_IMAGE, ...Constants::IGNORE_MARKERS_AUDIO, ...Constants::IGNORE_MARKERS_VIDEO], true)) { + $this->resetIgnoreCache($source); + $this->postInsert($target->getParent()); + return; + } + + if (!in_array($source->getName(), [...Constants::IGNORE_MARKERS_ALL, ...Constants::IGNORE_MARKERS_IMAGE, ...Constants::IGNORE_MARKERS_AUDIO, ...Constants::IGNORE_MARKERS_VIDEO], true) && + in_array($target->getName(), [...Constants::IGNORE_MARKERS_ALL, ...Constants::IGNORE_MARKERS_IMAGE, ...Constants::IGNORE_MARKERS_AUDIO, ...Constants::IGNORE_MARKERS_VIDEO], true)) { + $this->resetIgnoreCache($target); + $this->postDelete($target->getParent()); + return; + } + + if ($this->isIgnored($target)) { + $this->postDelete($target); + return; + } + if ($this->isIgnored($source) && !$this->isIgnored($target)) { + $this->postInsert($target); + return; + } + + $sourceAccessList = $this->shareManager->getAccessList($source, true, true); + $sourceUserIds = array_map(fn ($id) => strval($id), array_keys($sourceAccessList['users'])); + $targetAccessList = $this->shareManager->getAccessList($target, true, true); + $targetUserIds = array_map(fn ($id) => strval($id), array_keys($targetAccessList['users'])); + + $usersToAdd = array_diff($targetUserIds, $sourceUserIds); + $existingUsers = array_diff($targetUserIds, $usersToAdd); + // *handwaving* I know this is a stretch but it's good enough + $ownerId = $existingUsers[0]; + + if ($target->getType() === FileInfo::TYPE_FOLDER) { + $mount = $target->getMountPoint(); + if ($mount->getNumericStorageId() === null) { + return; + } + $files = $this->storageService->getFilesInMount($mount->getNumericStorageId(), $target->getId(), [ClusteringFaceClassifier::MODEL_NAME], 0, 0); + foreach ($files as $fileInfo) { + foreach ($usersToAdd as $userId) { + if (count($this->faceDetectionMapper->findByFileIdAndUser($target->getId(), $userId)) > 0) { + continue; + } + $this->faceDetectionMapper->copyDetectionsForFileFromUserToUser($fileInfo['fileid'], $ownerId, $userId); + } + } + } else { + foreach ($usersToAdd as $userId) { + if (count($this->faceDetectionMapper->findByFileIdAndUser($target->getId(), $userId)) > 0) { + continue; + } + $this->faceDetectionMapper->copyDetectionsForFileFromUserToUser($target->getId(), $ownerId, $userId); + } + } + } + /** * @param \OCP\Files\Node $node * @return bool