diff --git a/lib/Db/FsActionMapper.php b/lib/Db/FsActionMapper.php index 56d46585c..f5ac0753e 100644 --- a/lib/Db/FsActionMapper.php +++ b/lib/Db/FsActionMapper.php @@ -227,21 +227,24 @@ public function insertDeletion(int $storageId, int $nodeId): FsCreation|FsDeleti /** * @param int $nodeId - * @param string $owner - * @param list $addedUsers - * @param list $targetUsers - * @return FsCreation|FsDeletion|FsMove|FsAccessUpdate - * @throws Exception|MultipleObjectsReturnedException + * @return FsMove + * @throws Exception */ - public function insertMove(int $nodeId, string $owner, array $addedUsers, array $targetUsers): Entity { + public function insertMove(int $nodeId): FsMove { + // A move for this node may still be pending. Replace it with a fresh row rather than + // keeping it: the job deletes the rows it has processed by ID, so a row the job was + // already working on would be deleted without this move having been processed. A + // fresh row has a new ID, which the running job doesn't know about, so it survives + // until the next run. + $this->db->beginTransaction(); try { - $move = $this->findByNodeId(FsMove::class, $nodeId); - } catch (DoesNotExistException $e) { + $qb = $this->db->getQueryBuilder(); + $qb->delete(FsMove::$tableName) + ->where($qb->expr()->eq('node_id', $qb->createPositionalParameter($nodeId, IQueryBuilder::PARAM_INT))); + $qb->executeStatement(); + $move = new FsMove(); $move->setNodeId($nodeId); - $move->setOwner($owner); - $move->setAddedUsers($addedUsers); - $move->setTargetUsers($targetUsers); $this->insert($move); $arguments = [ 'type' => FsDeletion::class ]; if (!$this->jobList->has(ProcessFsActionsJob::class, $arguments)) { diff --git a/lib/Db/FsMove.php b/lib/Db/FsMove.php index 198994b24..4abc004db 100644 --- a/lib/Db/FsMove.php +++ b/lib/Db/FsMove.php @@ -15,24 +15,19 @@ * @package OCA\Recognize\Db * @method int getNodeId() * @method setNodeId(int $nodeId) - * @method string getOwner() - * @method setOwner(string $owner) */ final class FsMove extends Entity { protected ?int $nodeId = null; - protected ?string $owner = null; - protected ?string $addedUsers = null; - protected ?string $targetUsers = null; /** * @var string[] */ - public static array $columns = ['id', 'node_id', 'owner', 'added_users', 'target_users']; + public static array $columns = ['id', 'node_id']; /** * @var string[] */ - public static array $fields = ['id', 'nodeId', 'owner', 'addedUsers', 'targetUsers']; + public static array $fields = ['id', 'nodeId']; public static string $tableName = 'recognize_fs_moves'; diff --git a/lib/Hooks/FileListener.php b/lib/Hooks/FileListener.php index 1426b6384..3cb3f0bd4 100644 --- a/lib/Hooks/FileListener.php +++ b/lib/Hooks/FileListener.php @@ -15,8 +15,6 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Files\Cache\CacheEntryInsertedEvent; -use OCP\Files\Config\ICachedMountInfo; -use OCP\Files\Config\IUserMountCache; use OCP\Files\Events\Node\BeforeNodeDeletedEvent; use OCP\Files\Events\Node\BeforeNodeRenamedEvent; use OCP\Files\Events\Node\NodeCreatedEvent; @@ -37,9 +35,6 @@ final class FileListener implements IEventListener { private ?bool $movingFromIgnoredTerritory; private ?array $movingDirFromIgnoredTerritory; - /** @var list */ - private array $sourceUserIds; - private ?Node $source = null; /** @var array */ private array $addedMounts = []; @@ -48,28 +43,10 @@ public function __construct( private LoggerInterface $logger, private IgnoreService $ignoreService, private IRootFolder $rootFolder, - private IUserMountCache $userMountCache, private FsActionMapper $fsActionMapper, ) { $this->movingFromIgnoredTerritory = null; $this->movingDirFromIgnoredTerritory = null; - $this->sourceUserIds = []; - } - - /** - * @param int $nodeId - * @return list - * @throws InvalidPathException - * @throws NotFoundException - */ - private function getUsersWithFileAccess(int $nodeId): array { - $this->userMountCache->clear(); - $mountInfos = $this->userMountCache->getMountsForFileId($nodeId); - $userIds = array_map(static function (ICachedMountInfo $mountInfo) { - return $mountInfo->getUser()->getUID(); - }, $mountInfos); - - return array_values(array_unique($userIds)); } public function handle(Event $event): void { @@ -112,8 +89,6 @@ public function handle(Event $event): void { } else { $this->movingDirFromIgnoredTerritory = $this->getDirIgnores($event->getSource()); } - $this->sourceUserIds = $this->getUsersWithFileAccess($event->getSource()->getId()); - $this->source = $event->getSource(); return; } if ($event instanceof NodeRenamedEvent) { @@ -171,7 +146,7 @@ public function handle(Event $event): void { return; } } - $this->postRename($this->source ?? $event->getSource(), $event->getTarget()); + $this->postRename($event->getTarget()); return; } if ($event instanceof BeforeNodeDeletedEvent) { diff --git a/lib/Migration/Version013002000Date20260924120000.php b/lib/Migration/Version013002000Date20260924120000.php new file mode 100644 index 000000000..0f0bab26c --- /dev/null +++ b/lib/Migration/Version013002000Date20260924120000.php @@ -0,0 +1,47 @@ +hasTable('recognize_fs_moves')) { + return null; + } + + $changed = false; + $table = $schema->getTable('recognize_fs_moves'); + foreach (['owner', 'added_users', 'target_users'] as $column) { + if ($table->hasColumn($column)) { + $table->dropColumn($column); + $changed = true; + } + } + return $changed ? $schema : null; + } +} diff --git a/lib/Service/FsActionService.php b/lib/Service/FsActionService.php index 23bd2b643..2c0b4465a 100644 --- a/lib/Service/FsActionService.php +++ b/lib/Service/FsActionService.php @@ -32,6 +32,7 @@ final class FsActionService { public const BATCH_SIZE = 1000; + public function __construct( private FsActionMapper $fsActionMapper, private LoggerInterface $logger, @@ -75,6 +76,9 @@ public function processActionsByClass(string $className): void { * @param array $actions */ public function processActions(array $actions): void { + // The mount tables are read repeatedly while processing a batch, so refresh them + // once here rather than on every lookup. + $this->userMountCache->clear(); $lastUserId = null; foreach ($actions as $action) { switch ($action::class) { @@ -149,7 +153,6 @@ public function processActions(array $actions): void { * @return list */ private function getUsersWithFileAccess(int $nodeId): array { - $this->userMountCache->clear(); $mountInfos = $this->userMountCache->getMountsForFileId($nodeId); $userIds = array_map(static function (ICachedMountInfo $mountInfo) { return $mountInfo->getUser()->getUID(); @@ -164,7 +167,6 @@ private function getUsersWithFileAccess(int $nodeId): array { * @throws Exception */ private function onAccessUpdate(int $storageId, int $rootId): void { - $userIds = $this->getUsersWithFileAccess($rootId); $files = $this->storageService->getFilesInMount($storageId, $rootId, [ClusteringFaceClassifier::MODEL_NAME], 0, 0); $userIdsToScheduleClustering = []; foreach ($files as $fileInfo) { @@ -336,13 +338,13 @@ private function onMove(string $ownerId, array $usersToAdd, array $targetUserIds if ($node instanceof Folder) { try { foreach ($node->getDirectoryListing() as $n) { - if (!in_array($n->getMimetype(), Constants::IMAGE_FORMATS)) { + // Recurse into subfolders: we only get a rename event for the top node, + // so the whole subtree has to be walked here. + if ($n->getType() !== FileInfo::TYPE_FOLDER && !in_array($n->getMimetype(), Constants::IMAGE_FORMATS)) { continue; } $this->onMove($ownerId, $usersToAdd, $targetUserIds, $n); } - } catch (NotFoundException|Exception|InvalidPathException $e) { - $this->logger->warning('Error in recognize file listener', ['exception' => $e]); } return; } diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 3cbe274bb..e6f2bc3c3 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -965,7 +965,12 @@ + + + + getUserId()]]> +