From d266a9477d9c2f6be6e4de2fce0939263634cbae Mon Sep 17 00:00:00 2001 From: Benjamin Frueh Date: Wed, 14 Oct 2026 22:17:15 +0200 Subject: [PATCH 1/4] fix(federation): show team names to remote team members Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Benjamin Frueh --- lib/Db/RowCellUsergroupMapper.php | 7 ++++++- lib/Helper/CircleHelper.php | 7 ++++--- lib/Middleware/ShareControlMiddleware.php | 6 ++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/Db/RowCellUsergroupMapper.php b/lib/Db/RowCellUsergroupMapper.php index af975a8c37..f4ac9c65ec 100644 --- a/lib/Db/RowCellUsergroupMapper.php +++ b/lib/Db/RowCellUsergroupMapper.php @@ -19,6 +19,7 @@ class RowCellUsergroupMapper extends RowCellMapperSuper { use RowCellBulkFetchTrait; protected string $table = 'tables_row_cells_usergroup'; + private ?string $userId = null; public function __construct( IDBConnection $db, @@ -30,6 +31,10 @@ public function __construct( parent::__construct($db, $this->table, RowCellUsergroup::class); } + public function setUserId(string $userId): void { + $this->userId = $userId; + } + public function filterValueToQueryParam(Column $column, mixed $value): mixed { return $value; } @@ -49,7 +54,7 @@ public function formatRowData(Column $column, array $row) { if ($valueType === UsergroupType::USER) { $displayName = $this->userManager->getDisplayName($value) ?? $value; } elseif ($valueType === UsergroupType::CIRCLE) { - $displayName = $this->circleHelper->getCircleDisplayName($value, ($this->userSession->getUser()?->getUID() ?: '')) ?: $value; + $displayName = $this->circleHelper->getCircleDisplayName($value, $this->userId ?? ($this->userSession->getUser()?->getUID() ?: '')) ?: $value; } elseif ($valueType === UsergroupType::GROUP) { $displayName = $this->groupHelper->getGroupDisplayName($value) ?: $value; } diff --git a/lib/Helper/CircleHelper.php b/lib/Helper/CircleHelper.php index 34b2477e95..831c851c10 100644 --- a/lib/Helper/CircleHelper.php +++ b/lib/Helper/CircleHelper.php @@ -23,6 +23,7 @@ class CircleHelper { private bool $circlesEnabled; private ?CirclesManager $circlesManager; + private array $federatedUsers = []; /** * @psalm-suppress UndefinedClass @@ -55,14 +56,14 @@ public function getCircleDisplayName(string $circleId, string $userId): string { } try { - $federatedUser = $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER); - $this->circlesManager->startSession($federatedUser); + $this->federatedUsers[$userId] ??= $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER); + $this->circlesManager->startSession($this->federatedUsers[$userId]); $circle = $this->circlesManager->getCircle($circleId); return $circle ? ($circle->getDisplayName() ?: $circleId) : $circleId; } catch (Throwable $e) { if ($e->getCode() === 404) { - return $this->l10n->t('Deleted team %s.', [$circleId]); + return $this->l10n->t('Team not found'); } $this->logger->warning('Failed to get circle display name: ' . $e->getMessage(), [ diff --git a/lib/Middleware/ShareControlMiddleware.php b/lib/Middleware/ShareControlMiddleware.php index 76bfa0bcac..f584700b66 100644 --- a/lib/Middleware/ShareControlMiddleware.php +++ b/lib/Middleware/ShareControlMiddleware.php @@ -10,6 +10,7 @@ use InvalidArgumentException; use OCA\Tables\Constants\ShareReceiverType; +use OCA\Tables\Db\RowCellUsergroupMapper; use OCA\Tables\Db\Share; use OCA\Tables\Errors\NotFoundError; use OCA\Tables\Errors\PermissionError; @@ -41,6 +42,7 @@ public function __construct( private readonly IOCMDiscoveryService $ocmDiscoveryService, private readonly ICloudIdManager $cloudIdManager, private readonly ConfigService $configService, + private readonly RowCellUsergroupMapper $usergroupMapper, ) { } @@ -56,6 +58,10 @@ public function beforeController(Controller $controller, string $methodName): vo $tokenInput = $this->request->getParam('token', ''); $this->assertShareTokenIsValidAndExisting($tokenInput); $this->assertIsAccessible($tokenInput); + + if ($this->share->getReceiverType() === ShareReceiverType::REMOTE) { + $this->usergroupMapper->setUserId($this->share->getReceiver()); + } } } From 719b0bb46e1bfce57ffe7c8cd3ee8ba81ec51c69 Mon Sep 17 00:00:00 2001 From: Benjamin Frueh Date: Fri, 16 Oct 2026 14:55:43 +0200 Subject: [PATCH 2/4] fix(federation): pass the remote user through a share context Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Benjamin Frueh --- lib/Db/RowCellUsergroupMapper.php | 18 ++++++++++----- lib/Middleware/ShareControlMiddleware.php | 9 +++----- lib/Share/ShareContext.php | 28 +++++++++++++++++++++++ 3 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 lib/Share/ShareContext.php diff --git a/lib/Db/RowCellUsergroupMapper.php b/lib/Db/RowCellUsergroupMapper.php index f4ac9c65ec..35f63f4514 100644 --- a/lib/Db/RowCellUsergroupMapper.php +++ b/lib/Db/RowCellUsergroupMapper.php @@ -10,6 +10,7 @@ use OCA\Tables\Constants\UsergroupType; use OCA\Tables\Helper\CircleHelper; use OCA\Tables\Helper\GroupHelper; +use OCA\Tables\Share\ShareContext; use OCP\IDBConnection; use OCP\IUserManager; use OCP\IUserSession; @@ -19,7 +20,6 @@ class RowCellUsergroupMapper extends RowCellMapperSuper { use RowCellBulkFetchTrait; protected string $table = 'tables_row_cells_usergroup'; - private ?string $userId = null; public function __construct( IDBConnection $db, @@ -27,14 +27,11 @@ public function __construct( private CircleHelper $circleHelper, private GroupHelper $groupHelper, protected IUserSession $userSession, + private ShareContext $shareContext, ) { parent::__construct($db, $this->table, RowCellUsergroup::class); } - public function setUserId(string $userId): void { - $this->userId = $userId; - } - public function filterValueToQueryParam(Column $column, mixed $value): mixed { return $value; } @@ -54,7 +51,7 @@ public function formatRowData(Column $column, array $row) { if ($valueType === UsergroupType::USER) { $displayName = $this->userManager->getDisplayName($value) ?? $value; } elseif ($valueType === UsergroupType::CIRCLE) { - $displayName = $this->circleHelper->getCircleDisplayName($value, $this->userId ?? ($this->userSession->getUser()?->getUID() ?: '')) ?: $value; + $displayName = $this->circleHelper->getCircleDisplayName($value, $this->getCurrentUserId()) ?: $value; } elseif ($valueType === UsergroupType::GROUP) { $displayName = $this->groupHelper->getGroupDisplayName($value) ?: $value; } @@ -68,4 +65,13 @@ public function formatRowData(Column $column, array $row) { public function hasMultipleValues(): bool { return true; } + + private function getCurrentUserId(): string { + $remoteUserId = $this->shareContext->getRemoteUserId(); + if ($remoteUserId !== null) { + return $remoteUserId; + } + + return $this->userSession->getUser()?->getUID() ?: ''; + } } diff --git a/lib/Middleware/ShareControlMiddleware.php b/lib/Middleware/ShareControlMiddleware.php index f584700b66..206b8442f0 100644 --- a/lib/Middleware/ShareControlMiddleware.php +++ b/lib/Middleware/ShareControlMiddleware.php @@ -10,7 +10,6 @@ use InvalidArgumentException; use OCA\Tables\Constants\ShareReceiverType; -use OCA\Tables\Db\RowCellUsergroupMapper; use OCA\Tables\Db\Share; use OCA\Tables\Errors\NotFoundError; use OCA\Tables\Errors\PermissionError; @@ -18,6 +17,7 @@ use OCA\Tables\Service\ConfigService; use OCA\Tables\Service\ShareService; use OCA\Tables\Service\ValueObject\ShareToken; +use OCA\Tables\Share\ShareContext; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; @@ -42,7 +42,7 @@ public function __construct( private readonly IOCMDiscoveryService $ocmDiscoveryService, private readonly ICloudIdManager $cloudIdManager, private readonly ConfigService $configService, - private readonly RowCellUsergroupMapper $usergroupMapper, + private readonly ShareContext $shareContext, ) { } @@ -58,10 +58,7 @@ public function beforeController(Controller $controller, string $methodName): vo $tokenInput = $this->request->getParam('token', ''); $this->assertShareTokenIsValidAndExisting($tokenInput); $this->assertIsAccessible($tokenInput); - - if ($this->share->getReceiverType() === ShareReceiverType::REMOTE) { - $this->usergroupMapper->setUserId($this->share->getReceiver()); - } + $this->shareContext->setShare($this->share); } } diff --git a/lib/Share/ShareContext.php b/lib/Share/ShareContext.php new file mode 100644 index 0000000000..081fced2f1 --- /dev/null +++ b/lib/Share/ShareContext.php @@ -0,0 +1,28 @@ +share = $share; + } + + public function getRemoteUserId(): ?string { + if ($this->share?->getReceiverType() !== ShareReceiverType::REMOTE) { + return null; + } + + return $this->share->getReceiver(); + } +} From 574406d16d27aa29e317985273da4cd54f95bbdf Mon Sep 17 00:00:00 2001 From: Benjamin Frueh Date: Fri, 16 Oct 2026 15:15:06 +0200 Subject: [PATCH 3/4] fix(federation): resolve each team display name once per request Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Benjamin Frueh --- lib/Helper/CircleHelper.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/Helper/CircleHelper.php b/lib/Helper/CircleHelper.php index 831c851c10..7e79138fde 100644 --- a/lib/Helper/CircleHelper.php +++ b/lib/Helper/CircleHelper.php @@ -23,7 +23,7 @@ class CircleHelper { private bool $circlesEnabled; private ?CirclesManager $circlesManager; - private array $federatedUsers = []; + private array $circleDisplayNames = []; /** * @psalm-suppress UndefinedClass @@ -51,13 +51,17 @@ public function isCirclesEnabled(): bool { } public function getCircleDisplayName(string $circleId, string $userId): string { + return $this->circleDisplayNames[$userId][$circleId] ??= $this->resolveCircleDisplayName($circleId, $userId); + } + + private function resolveCircleDisplayName(string $circleId, string $userId): string { if (!$this->circlesEnabled) { return $circleId; } try { - $this->federatedUsers[$userId] ??= $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER); - $this->circlesManager->startSession($this->federatedUsers[$userId]); + $federatedUser = $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER); + $this->circlesManager->startSession($federatedUser); $circle = $this->circlesManager->getCircle($circleId); return $circle ? ($circle->getDisplayName() ?: $circleId) : $circleId; From 58c789527da2f17245ed34536d95113ec13d3ea6 Mon Sep 17 00:00:00 2001 From: Benjamin Frueh Date: Fri, 16 Oct 2026 19:47:57 +0200 Subject: [PATCH 4/4] fix(federation): resolve each user only once per request Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: Benjamin Frueh --- lib/Helper/CircleHelper.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Helper/CircleHelper.php b/lib/Helper/CircleHelper.php index 7e79138fde..d362b2f0ea 100644 --- a/lib/Helper/CircleHelper.php +++ b/lib/Helper/CircleHelper.php @@ -24,6 +24,8 @@ class CircleHelper { private bool $circlesEnabled; private ?CirclesManager $circlesManager; private array $circleDisplayNames = []; + private array $federatedUsers = []; + private array $unreachableUserIds = []; /** * @psalm-suppress UndefinedClass @@ -55,12 +57,12 @@ public function getCircleDisplayName(string $circleId, string $userId): string { } private function resolveCircleDisplayName(string $circleId, string $userId): string { - if (!$this->circlesEnabled) { + if (!$this->circlesEnabled || isset($this->unreachableUserIds[$userId])) { return $circleId; } try { - $federatedUser = $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER); + $federatedUser = $this->federatedUsers[$userId] ??= $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER); $this->circlesManager->startSession($federatedUser); $circle = $this->circlesManager->getCircle($circleId); @@ -70,6 +72,7 @@ private function resolveCircleDisplayName(string $circleId, string $userId): str return $this->l10n->t('Team not found'); } + $this->unreachableUserIds[$userId] = true; $this->logger->warning('Failed to get circle display name: ' . $e->getMessage(), [ 'circleId' => $circleId, 'userId' => $userId