Skip to content

Commit e3b7694

Browse files
authored
Merge pull request #1389 from nextcloud/backport/1384/stable9
[stable9] fix(FileListener): Listen to user mount events
2 parents 26f1946 + 24aa8c4 commit e3b7694

File tree

2 files changed

+82
-16
lines changed

2 files changed

+82
-16
lines changed

lib/AppInfo/Application.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
use OCP\Files\Events\Node\NodeDeletedEvent;
2222
use OCP\Files\Events\Node\NodeRenamedEvent;
2323
use OCP\Files\Events\NodeRemovedFromCache;
24-
use OCP\Share\Events\ShareAcceptedEvent;
2524
use OCP\Share\Events\ShareCreatedEvent;
2625
use OCP\Share\Events\ShareDeletedEvent;
2726

@@ -39,11 +38,18 @@ public function __construct() {
3938
$dispatcher->addServiceListener(NodeCreatedEvent::class, FileListener::class);
4039
$dispatcher->addServiceListener(NodeRenamedEvent::class, FileListener::class);
4140
$dispatcher->addServiceListener(BeforeNodeRenamedEvent::class, FileListener::class);
42-
$dispatcher->addServiceListener(ShareCreatedEvent::class, FileListener::class);
43-
$dispatcher->addServiceListener(ShareAcceptedEvent::class, FileListener::class);
44-
$dispatcher->addServiceListener(ShareDeletedEvent::class, FileListener::class);
4541
$dispatcher->addServiceListener(CacheEntryInsertedEvent::class, FileListener::class);
4642
$dispatcher->addServiceListener(NodeRemovedFromCache::class, FileListener::class);
43+
// These events were added mid-way through NC 30, 31
44+
if (class_exists('OCP\Files\Config\Event\UserMountAddedEvent')) {
45+
$dispatcher->addServiceListener('OCP\Files\Config\Event\UserMountAddedEvent', FileListener::class);
46+
$dispatcher->addServiceListener('OCP\Files\Config\Event\UserMountRemovedEvent', FileListener::class);
47+
// it is not fired as of now, Added and Removed events are fired instead in that order
48+
// $context->addServiceListener('OCP\Files\Config\Event\UserMountUpdatedEvent', FileListener::class);
49+
} else {
50+
$dispatcher->addServiceListener(ShareCreatedEvent::class, FileListener::class);
51+
$dispatcher->addServiceListener(ShareDeletedEvent::class, FileListener::class);
52+
}
4753
}
4854

4955
public function register(IRegistrationContext $context): void {

lib/Hooks/FileListener.php

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
use OCP\Files\Node;
3939
use OCP\Files\NotFoundException;
4040
use OCP\IGroupManager;
41-
use OCP\Share\Events\ShareAcceptedEvent;
41+
use OCP\Share\Events\ShareCreatedEvent;
4242
use OCP\Share\Events\ShareDeletedEvent;
4343
use OCP\Share\IManager;
4444
use Psr\Log\LoggerInterface;
@@ -53,6 +53,9 @@ final class FileListener implements IEventListener {
5353
private array $sourceUserIds;
5454
private ?Node $source = null;
5555

56+
/** @var array<string, bool> */
57+
private array $addedMounts = [];
58+
5659
public function __construct(
5760
private FaceDetectionMapper $faceDetectionMapper,
5861
private LoggerInterface $logger,
@@ -71,14 +74,14 @@ public function __construct(
7174
}
7275

7376
/**
74-
* @param Node $node
77+
* @param int $nodeId
7578
* @return list<string>
7679
* @throws InvalidPathException
7780
* @throws NotFoundException
7881
*/
79-
private function getUsersWithFileAccess(Node $node): array {
82+
private function getUsersWithFileAccess(int $nodeId): array {
8083
$this->userMountCache->clear();
81-
$mountInfos = $this->userMountCache->getMountsForFileId($node->getId());
84+
$mountInfos = $this->userMountCache->getMountsForFileId($nodeId);
8285
$userIds = array_map(static function (ICachedMountInfo $mountInfo) {
8386
return $mountInfo->getUser()->getUID();
8487
}, $mountInfos);
@@ -88,11 +91,31 @@ private function getUsersWithFileAccess(Node $node): array {
8891

8992
public function handle(Event $event): void {
9093
try {
91-
if ($event instanceof ShareAcceptedEvent) {
94+
if ($event instanceof \OCP\Files\Config\Event\UserMountAddedEvent) {
95+
$rootId = $event->mountPoint->getRootId();
96+
// Asynchronous, because we potentially recurse and this event needs to be handled fast
97+
$this->onAccessUpdate($event->mountPoint->getStorageId(), $rootId);
98+
// Remember that this mount was added in the current process (see UserMountRemovedEvent below)
99+
$this->addedMounts[$event->mountPoint->getUser()->getUID() . '-' . $rootId] = true;
100+
}
101+
102+
if ($event instanceof \OCP\Files\Config\Event\UserMountRemovedEvent) {
103+
// If we just added this mount, ignore the removal, as the 'removal' event is always fired after
104+
// the 'added' event in server
105+
$rootId = $event->mountPoint->getRootId();
106+
$mountKey = $event->mountPoint->getUser()->getUID() . '-' . $rootId;
107+
if (array_key_exists($mountKey, $this->addedMounts) && $this->addedMounts[$mountKey] === true) {
108+
return;
109+
}
110+
// Asynchronous, because we potentially recurse and this event needs to be handled fast
111+
$this->onAccessUpdate($event->mountPoint->getStorageId(), $rootId);
112+
}
113+
114+
if ($event instanceof ShareCreatedEvent) {
92115
$share = $event->getShare();
93116
$ownerId = $share->getShareOwner();
94117
$node = $share->getNode();
95-
$userIds = $this->getUsersWithFileAccess($node);
118+
$userIds = $this->getUsersWithFileAccess($node->getId());
96119

97120
if ($node->getType() === FileInfo::TYPE_FOLDER) {
98121
$mount = $node->getMountPoint();
@@ -126,7 +149,7 @@ public function handle(Event $event): void {
126149
if ($event instanceof ShareDeletedEvent) {
127150
$share = $event->getShare();
128151
$node = $share->getNode();
129-
$userIds = $this->getUsersWithFileAccess($node);
152+
$userIds = $this->getUsersWithFileAccess($node->getId());
130153

131154
if ($node->getType() === FileInfo::TYPE_FOLDER) {
132155
$mount = $node->getMountPoint();
@@ -159,7 +182,7 @@ public function handle(Event $event): void {
159182
} else {
160183
$this->movingDirFromIgnoredTerritory = $this->getDirIgnores($event->getSource());
161184
}
162-
$this->sourceUserIds = $this->getUsersWithFileAccess($event->getSource());
185+
$this->sourceUserIds = $this->getUsersWithFileAccess($event->getSource()->getId());
163186
$this->source = $event->getSource();
164187
return;
165188
}
@@ -390,7 +413,7 @@ public function postInsert(Node $node, bool $recurse = true, ?array $mimeTypes =
390413
* @throws Exception
391414
*/
392415
public function postRename(Node $source, Node $target): void {
393-
$targetUserIds = $this->getUsersWithFileAccess($target);
416+
$targetUserIds = $this->getUsersWithFileAccess($target->getId());
394417

395418
$usersToAdd = array_values(array_diff($targetUserIds, $this->sourceUserIds));
396419
$existingUsers = array_diff($targetUserIds, $usersToAdd);
@@ -412,11 +435,11 @@ public function postRename(Node $source, Node $target): void {
412435
private function copyFaceDetectionsForNode(string $ownerId, array $usersToAdd, array $targetUserIds, Node $node): void {
413436
if ($node instanceof Folder) {
414437
try {
415-
foreach ($node->getDirectoryListing() as $node) {
416-
if (!in_array($node->getMimetype(), Constants::IMAGE_FORMATS)) {
438+
foreach ($node->getDirectoryListing() as $n) {
439+
if (!in_array($n->getMimetype(), Constants::IMAGE_FORMATS)) {
417440
continue;
418441
}
419-
$this->copyFaceDetectionsForNode($ownerId, $usersToAdd, $targetUserIds, $node);
442+
$this->copyFaceDetectionsForNode($ownerId, $usersToAdd, $targetUserIds, $n);
420443
}
421444
} catch (NotFoundException|Exception|InvalidPathException $e) {
422445
$this->logger->warning('Error in recognize file listener', ['exception' => $e]);
@@ -512,4 +535,41 @@ private function resetIgnoreCache(Node $node) : void {
512535
}
513536
$this->ignoreService->clearCacheForStorage($storageId);
514537
}
538+
539+
/**
540+
* @throws NotFoundException
541+
* @throws InvalidPathException
542+
* @throws Exception
543+
*/
544+
private function onAccessUpdate(int $storageId, int $rootId): void {
545+
$userIds = $this->getUsersWithFileAccess($rootId);
546+
$files = $this->storageService->getFilesInMount($storageId, $rootId, [ClusteringFaceClassifier::MODEL_NAME], 0, 0);
547+
$userIdsToScheduleClustering = [];
548+
foreach ($files as $fileInfo) {
549+
$node = current($this->rootFolder->getById($fileInfo['fileid'])) ?: null;
550+
$ownerId = $node?->getOwner()?->getUID();
551+
if ($ownerId === null) {
552+
continue;
553+
}
554+
$detectionsForFile = $this->faceDetectionMapper->findByFileId($fileInfo['fileid']);
555+
$userHasDetectionForFile = [];
556+
foreach ($detectionsForFile as $detection) {
557+
$userHasDetectionForFile[$detection->getUserId()] = true;
558+
}
559+
foreach ($userIds as $userId) {
560+
if ($userId === $ownerId) {
561+
continue;
562+
}
563+
if ($userHasDetectionForFile[$userId] ?? false) {
564+
continue;
565+
}
566+
$this->faceDetectionMapper->copyDetectionsForFileFromUserToUser($fileInfo['fileid'], $ownerId, $userId);
567+
$userIdsToScheduleClustering[$userId] = true;
568+
}
569+
$this->faceDetectionMapper->removeDetectionsForFileFromUsersNotInList($fileInfo['fileid'], $userIds);
570+
}
571+
foreach (array_keys($userIdsToScheduleClustering) as $userId) {
572+
$this->jobList->add(ClusterFacesJob::class, ['userId' => $userId]);
573+
}
574+
}
515575
}

0 commit comments

Comments
 (0)