diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index ad8a22c..68333f7 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -26,6 +26,7 @@ use OCP\Files\Cache\CacheEntryInsertedEvent; use OCP\Files\Cache\CacheEntryRemovedEvent; use OCP\Files\Cache\CacheEntryUpdatedEvent; +use OCP\Files\Events\Node\NodeDeletedEvent; use OCP\Group\Events\UserAddedEvent; use OCP\Group\Events\UserRemovedEvent; use OCP\Security\CSP\AddContentSecurityPolicyEvent; @@ -71,6 +72,7 @@ public function attachHooks( $eventDispatcher->addListener(CacheEntryInsertedEvent::class, [$listener, 'cacheListener']); $eventDispatcher->addListener(CacheEntryUpdatedEvent::class, [$listener, 'cacheListener']); $eventDispatcher->addListener(CacheEntryRemovedEvent::class, [$listener, 'cacheListener']); + $eventDispatcher->addListener(NodeDeletedEvent::class, [$listener, 'nodeDeletedListener']); $eventDispatcher->addListener(UserAddedEvent::class, [$listener, 'groupListener']); $eventDispatcher->addListener(UserRemovedEvent::class, [$listener, 'groupListener']); diff --git a/lib/Listener.php b/lib/Listener.php index 97b13ee..64f421e 100644 --- a/lib/Listener.php +++ b/lib/Listener.php @@ -14,7 +14,9 @@ use OCP\Activity\IEvent; use OCP\EventDispatcher\Event; use OCP\Files\Cache\ICacheEvent; +use OCP\Files\Events\Node\NodeDeletedEvent; use OCP\Files\IHomeStorage; +use OCP\Files\NotFoundException; use OCP\Files\Storage\IStorage; use OCP\Group\Events\UserAddedEvent; use OCP\Group\Events\UserRemovedEvent; @@ -35,22 +37,36 @@ public function __construct(IQueue $queue) { public function cacheListener(Event $event): void { if ($event instanceof ICacheEvent) { - $path = $event->getPath(); - - $storage = $event->getStorage(); - while ($storage->instanceOfStorage(Jail::class)) { - /** @var Jail $storage */ - $path = $storage->getUnjailedPath($path); - $storage = $storage->getUnjailedStorage(); - } - - if ($this->shouldNotifyPath($event->getStorage(), $path)) { - $this->queue->push('notify_storage_update', [ - 'storage' => $event->getStorageId(), - 'path' => $path, - 'file_id' => $event->getFileId(), - ]); - } + $this->pushStorageUpdate($event->getStorage(), $event->getStorageId(), $event->getPath(), $event->getFileId()); + } + } + + /** + * Moving a file to the trash bin only updates its cache entry to the path in the trash bin, + * which users don't see. Notify the path it was deleted from as well. + */ + public function nodeDeletedListener(NodeDeletedEvent $event): void { + $node = $event->getNode(); + try { + $storage = $node->getStorage(); + $this->pushStorageUpdate($storage, $storage->getCache()->getNumericStorageId(), $node->getInternalPath(), $node->getId()); + } catch (NotFoundException) { + } + } + + private function pushStorageUpdate(IStorage $storage, int $storageId, string $path, int $fileId): void { + while ($storage->instanceOfStorage(Jail::class)) { + /** @var Jail $storage */ + $path = $storage->getUnjailedPath($path); + $storage = $storage->getUnjailedStorage(); + } + + if ($this->shouldNotifyPath($storage, $path)) { + $this->queue->push('notify_storage_update', [ + 'storage' => $storageId, + 'path' => $path, + 'file_id' => $fileId, + ]); } } diff --git a/tests/lib/ListenerTest.php b/tests/lib/ListenerTest.php index 7fdf3ad..9676b5b 100644 --- a/tests/lib/ListenerTest.php +++ b/tests/lib/ListenerTest.php @@ -11,6 +11,12 @@ use OCA\NotifyPush\Listener; use OCA\NotifyPush\Queue\IQueue; use OCP\Files\Cache\CacheEntryInsertedEvent; +use OCP\Files\Cache\CacheEntryUpdatedEvent; +use OCP\Files\Cache\ICache; +use OCP\Files\Events\Node\NodeDeletedEvent; +use OCP\Files\File; +use OCP\Files\IHomeStorage; +use OCP\Files\NotFoundException; use OCP\Files\Storage\IStorage; use OCP\Group\Events\UserAddedEvent; use OCP\Group\Events\UserRemovedEvent; @@ -56,6 +62,50 @@ public function testCacheEvents() { ], $events); } + private function getHomeStorage(): IStorage { + $cache = $this->createMock(ICache::class); + $cache->method('getNumericStorageId')->willReturn(1); + $storage = $this->createMock(IStorage::class); + $storage->method('getCache')->willReturn($cache); + $storage->method('instanceOfStorage')->willReturnCallback(static fn (string $class): bool => $class === IHomeStorage::class); + return $storage; + } + + public function testFileMovedToTheTrashBin() { + $events = []; + $listener = new Listener($this->getQueue($events)); + $storage = $this->getHomeStorage(); + + // The cache entry moves to the trash bin, users don't see that path + $listener->cacheListener(new CacheEntryUpdatedEvent($storage, 'files_trashbin/files/report.md.d1760000000', 12, 1)); + $this->assertEquals([], $events); + + $node = $this->createMock(File::class); + $node->method('getStorage')->willReturn($storage); + $node->method('getInternalPath')->willReturn('files/Documents/report.md'); + $node->method('getId')->willReturn(12); + $listener->nodeDeletedListener(new NodeDeletedEvent($node)); + + $this->assertEquals([ + 'notify_storage_update' => [ + ['storage' => 1, 'path' => 'files/Documents/report.md', 'file_id' => 12], + ], + ], $events); + } + + public function testDeletedNodeWithoutInfo() { + $events = []; + $listener = new Listener($this->getQueue($events)); + + $node = $this->createMock(File::class); + $node->method('getStorage')->willReturn($this->getHomeStorage()); + $node->method('getInternalPath')->willReturn('files/report.md'); + $node->method('getId')->willThrowException(new NotFoundException()); + $listener->nodeDeletedListener(new NodeDeletedEvent($node)); + + $this->assertEquals([], $events); + } + public function testGroupEvents() { $events = []; $queue = $this->getQueue($events);