From 3cae83a9af299bc435dd3cbaae973a16411ad616 Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Wed, 9 Sep 2026 17:00:47 +0200 Subject: [PATCH] enh(performance): get deck share permissions in one query Signed-off-by: grnd-alt --- lib/Db/AclMapper.php | 27 ++++++++++++++++++++++++ lib/Service/PermissionService.php | 25 +++++++++++++++++++++++ lib/Sharing/DeckShareProvider.php | 34 +++++++++++++++---------------- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/lib/Db/AclMapper.php b/lib/Db/AclMapper.php index defba19d12..e853bb4765 100644 --- a/lib/Db/AclMapper.php +++ b/lib/Db/AclMapper.php @@ -44,6 +44,33 @@ public function findAll(int $boardId, ?int $limit = null, ?int $offset = null) { return $this->findEntities($qb); } + /** + * @return array Acl entries grouped by card id + * @throws \OCP\DB\Exception + */ + public function findInCards(array $cardIds, ?int $limit = null, ?int $offset = null): array { + $qb = $this->db->getQueryBuilder(); + $qb->select('acl.id', 'acl.board_id', 'acl.type', 'acl.participant', 'acl.permission_edit', 'acl.permission_share', 'acl.permission_manage', 'acl.created_at', 'acl.last_modified_at', 'c.id AS card_id') + ->from('deck_board_acl', 'acl') + ->innerJoin('acl', 'deck_boards', 'b', 'acl.board_id = b.id') + ->innerJoin('b', 'deck_stacks', 's', 's.board_id = b.id') + ->innerJoin('s', 'deck_cards', 'c', 'c.stack_id = s.id') + ->where($qb->expr()->in('c.id', $qb->createParameter('cardIds'))) + ->setMaxResults($limit) + ->setFirstResult($offset); + + $aclsByCardId = []; + foreach ($this->chunkQuery($cardIds, function (array $ids) use ($qb) { + $qb->setParameter('cardIds', $ids, IQueryBuilder::PARAM_INT_ARRAY); + return $qb->executeQuery()->fetchAll(); + }) as $row) { + $cardId = (int)$row['card_id']; + unset($row['card_id']); + $aclsByCardId[$cardId][] = Acl::fromRow($row); + } + return $aclsByCardId; + } + public function findIn(array $boardIds, ?int $limit = null, ?int $offset = null): array { $qb = $this->db->getQueryBuilder(); $qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage', 'created_at', 'last_modified_at') diff --git a/lib/Service/PermissionService.php b/lib/Service/PermissionService.php index 2f370fa457..a22c6e12b4 100644 --- a/lib/Service/PermissionService.php +++ b/lib/Service/PermissionService.php @@ -63,6 +63,31 @@ public function setAccessToken(string $token) { $this->accessToken = $token; } + /** + * Get permissions for all supplied card ids + * @param int[] $cardIds + * @param ?string $userId + * @param bool $allowDeleted + * @return array> + */ + public function getPermissionsForCards(array $cardIds, ?string $userId = null, bool $allowDeleted = false): array { + if ($userId === null) { + $userId = $this->userId; + } + $permissions = $this->aclMapper->findInCards($cardIds); + return array_map( + function (array $acls) use ($userId) { + return [ + Acl::PERMISSION_READ => $this->userCan($acls, Acl::PERMISSION_READ, $userId), + Acl::PERMISSION_EDIT => $this->userCan($acls, Acl::PERMISSION_EDIT, $userId), + Acl::PERMISSION_MANAGE => $this->userCan($acls, Acl::PERMISSION_MANAGE, $userId), + Acl::PERMISSION_SHARE => $this->userCan($acls, Acl::PERMISSION_SHARE, $userId), + ]; + }, + $permissions + ); + } + /** * Get current user permissions for a board by id * diff --git a/lib/Sharing/DeckShareProvider.php b/lib/Sharing/DeckShareProvider.php index 408f39d4da..ef41e4301e 100644 --- a/lib/Sharing/DeckShareProvider.php +++ b/lib/Sharing/DeckShareProvider.php @@ -269,22 +269,6 @@ private function createShareObject(array $data): IShare { return $share; } - private function applyBoardPermission($share, $permissions, $userId) { - try { - $this->permissionService->checkPermission($this->cardMapper, $share->getSharedWith(), Acl::PERMISSION_EDIT, $userId, true); - } catch (NoPermissionException $e) { - $permissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE; - $permissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE; - $permissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE; - } - - try { - $this->permissionService->checkPermission($this->cardMapper, $share->getSharedWith(), Acl::PERMISSION_SHARE, $userId, true); - } catch (NoPermissionException $e) { - $permissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE; - } - $share->setPermissions($permissions); - } /** * @inheritDoc */ @@ -652,11 +636,13 @@ private function resolveSharesForRecipient(array $shares, string $userId): array /** @var int[] $ids */ $ids = []; + $cardIds = []; /** @var IShare[] $shareMap */ $shareMap = []; foreach ($shareSlice as $share) { $ids[] = (int)$share->getId(); + $cardIds[] = (int)$share->getSharedWith(); $shareMap[$share->getId()] = $share; } @@ -673,8 +659,22 @@ private function resolveSharesForRecipient(array $shares, string $userId): array $stmt = $query->executeQuery(); + $permissions = $this->permissionService->getPermissionsForCards($cardIds, $userId); + while ($data = $stmt->fetch()) { - $this->applyBoardPermission($shareMap[$data['parent']], (int)$data['permissions'], $userId); + $parent = $shareMap[$data['parent']]; + $sharePermissions = (int)$data['permissions']; + if (isset($permissions[(int)$parent->getSharedWith()])) { + if ($permissions[(int)$parent->getSharedWith()][Acl::PERMISSION_EDIT] === false) { + $sharePermissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE; + $sharePermissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE; + $sharePermissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE; + } + if ($permissions[(int)$parent->getSharedWith()][Acl::PERMISSION_SHARE] === false) { + $sharePermissions &= Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE; + } + } + $shareMap[$data['parent']]->setPermissions($sharePermissions); $shareMap[$data['parent']]->setTarget($data['file_target']); }