Skip to content

Commit 5dacb82

Browse files
committed
share with teams
Signed-off-by: Hoang Pham <hoangmaths96@gmail.com>
1 parent 98aacbc commit 5dacb82

22 files changed

+375
-68
lines changed

lib/Capabilities.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace OCA\Tables;
88

9+
use OCA\Tables\Helper\CircleHelper;
910
use OCP\App\IAppManager;
1011
use OCP\Capabilities\ICapability;
1112
use OCP\IConfig;
@@ -18,18 +19,23 @@
1819
*/
1920
class Capabilities implements ICapability {
2021
private IAppManager $appManager;
22+
2123
private LoggerInterface $logger;
24+
2225
private IConfig $config;
2326

24-
public function __construct(IAppManager $appManager, LoggerInterface $logger, IConfig $config) {
27+
private CircleHelper $circleHelper;
28+
29+
public function __construct(IAppManager $appManager, LoggerInterface $logger, IConfig $config, CircleHelper $circleHelper) {
2530
$this->appManager = $appManager;
2631
$this->logger = $logger;
2732
$this->config = $config;
33+
$this->circleHelper = $circleHelper;
2834
}
2935

3036
/**
3137
*
32-
* @return array{tables: array{enabled: bool, version: string, apiVersions: string[], features: string[], column_types: string[]}}
38+
* @return array{tables: array{enabled: bool, version: string, apiVersions: string[], features: string[], isCirclesEnabled: bool, column_types: string[]}}
3339
*
3440
* @inheritDoc
3541
*/
@@ -52,6 +58,7 @@ public function getCapabilities(): array {
5258
'favorite',
5359
'archive',
5460
],
61+
'isCirclesEnabled' => $this->circleHelper->isCirclesEnabled(),
5562
'column_types' => [
5663
'text-line',
5764
$textColumnVariant,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
namespace OCA\Tables\Constants;
9+
10+
/**
11+
* Constants for share receiver types
12+
*/
13+
class ShareReceiverType {
14+
public const USER = 'user';
15+
public const GROUP = 'group';
16+
public const CIRCLE = 'circle';
17+
}

lib/Db/LegacyRowMapper.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ private function buildFilterByColumnType($qb, array $filter, string $filterId):
113113
return null;
114114
}
115115

116-
private function getInnerFilterExpressions($qb, $filterGroup, int $groupIndex): array {
116+
/**
117+
* @param (float|int|string)[][] $filterGroup
118+
*
119+
* @psalm-param list<array{columnId: int, operator: 'begins-with'|'contains'|'ends-with'|'is-empty'|'is-equal'|'is-greater-than'|'is-greater-than-or-equal'|'is-lower-than'|'is-lower-than-or-equal', value: float|int|string}> $filterGroup
120+
*/
121+
private function getInnerFilterExpressions(IQueryBuilder $qb, array $filterGroup, int $groupIndex): array {
117122
$innerFilterExpressions = [];
118123
foreach ($filterGroup as $index => $filter) {
119124
$innerFilterExpressions[] = $this->buildFilterByColumnType($qb, $filter, $groupIndex.$index);

lib/Db/RowCellMapperSuper.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ public function formatEntity(Column $column, RowCellSuper $cell) {
4141
* Transform value from a filter rule to the actual query parameter used
4242
* for constructing the view filter query
4343
*/
44-
public function filterValueToQueryParam(Column $column, $value) {
44+
/**
45+
* @param float|string $value
46+
*/
47+
public function filterValueToQueryParam(Column $column, string|float $value) {
4548
return $value;
4649
}
4750

lib/Db/Share.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class Share extends Entity implements JsonSerializable {
4949

5050
protected ?string $receiver = null;
5151
protected ?string $receiverDisplayName = null;
52-
protected ?string $receiverType = null; // user, group
52+
protected ?string $receiverType = null; // user, group, circle
5353
protected ?int $nodeId = null;
5454
protected ?string $nodeType = null;
5555
protected ?bool $permissionRead = null;

lib/Db/ShareMapper.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,21 @@ public function findAllSharesFor(string $nodeType, string $receiver, string $use
102102
* @param string $nodeType
103103
* @param int $nodeId
104104
* @param string $sender
105+
* @param array<string> $excluded receiver types to exclude from results
105106
* @return array
106107
* @throws Exception
107108
*/
108-
public function findAllSharesForNode(string $nodeType, int $nodeId, string $sender): array {
109-
// TODO filter for sender...
109+
public function findAllSharesForNode(string $nodeType, int $nodeId, string $sender, array $excluded = []): array {
110110
$qb = $this->db->getQueryBuilder();
111111
$qb->select('*')
112112
->from($this->table)
113113
->andWhere($qb->expr()->eq('node_type', $qb->createNamedParameter($nodeType, IQueryBuilder::PARAM_STR)))
114114
->andWhere($qb->expr()->eq('node_id', $qb->createNamedParameter($nodeId, IQueryBuilder::PARAM_INT)));
115+
116+
if (!empty($excluded)) {
117+
$qb->andWhere($qb->expr()->notIn('receiver_type', $qb->createNamedParameter($excluded, IQueryBuilder::PARAM_STR_ARRAY)));
118+
}
119+
115120
return $this->findEntities($qb);
116121
}
117122

lib/Helper/CircleHelper.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
namespace OCA\Tables\Helper;
9+
10+
use OCA\Circles\CirclesManager;
11+
use OCA\Circles\Model\Member;
12+
use OCA\Circles\Model\Probes\CircleProbe;
13+
use OCP\App\IAppManager;
14+
use OCP\Server;
15+
use Psr\Log\LoggerInterface;
16+
use Throwable;
17+
18+
/**
19+
* @psalm-suppress UndefinedClass
20+
*/
21+
class CircleHelper {
22+
private bool $circlesEnabled;
23+
private ?CirclesManager $circlesManager;
24+
25+
/**
26+
* @psalm-suppress UndefinedClass
27+
*/
28+
public function __construct(
29+
private LoggerInterface $logger,
30+
IAppManager $appManager
31+
) {
32+
$this->circlesEnabled = $appManager->isEnabledForUser('circles');
33+
$this->circlesManager = null;
34+
35+
if ($this->circlesEnabled) {
36+
try {
37+
$this->circlesManager = Server::get(CirclesManager::class);
38+
} catch (Throwable $e) {
39+
$this->logger->warning('Failed to get CirclesManager: ' . $e->getMessage());
40+
$this->circlesEnabled = false;
41+
}
42+
}
43+
}
44+
45+
public function isCirclesEnabled(): bool {
46+
return $this->circlesEnabled;
47+
}
48+
49+
public function getCircleDisplayName(string $circleId, string $userId): string {
50+
if (!$this->circlesEnabled) {
51+
return $circleId;
52+
}
53+
54+
try {
55+
$federatedUser = $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER);
56+
$this->circlesManager->startSession($federatedUser);
57+
58+
$circle = $this->circlesManager->getCircle($circleId);
59+
return $circle ? ($circle->getDisplayName() ?: $circleId) : $circleId;
60+
} catch (Throwable $e) {
61+
$this->logger->warning('Failed to get circle display name: ' . $e->getMessage(), [
62+
'circleId' => $circleId,
63+
'userId' => $userId
64+
]);
65+
return $circleId;
66+
}
67+
}
68+
69+
public function getUserCircles(string $userId): array {
70+
if (!$this->circlesEnabled) {
71+
return [];
72+
}
73+
74+
try {
75+
$federatedUser = $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER);
76+
$this->circlesManager->startSession($federatedUser);
77+
$probe = new CircleProbe();
78+
$probe->mustBeMember();
79+
return $this->circlesManager->getCircles($probe);
80+
} catch (Throwable $e) {
81+
$this->logger->warning('Failed to get user circles: ' . $e->getMessage());
82+
return [];
83+
}
84+
}
85+
}

lib/Helper/UserHelper.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ class UserHelper {
1818
private IUserManager $userManager;
1919

2020
private LoggerInterface $logger;
21+
2122
private IGroupManager $groupManager;
2223

2324
public function __construct(IUserManager $userManager, LoggerInterface $logger, IGroupManager $groupManager) {
2425
$this->userManager = $userManager;
2526
$this->logger = $logger;
2627
$this->groupManager = $groupManager;
2728
}
29+
2830
public function getUserDisplayName(string $userId): string {
2931
try {
3032
$user = $this->getUser($userId);

lib/Middleware/PermissionMiddleware.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function beforeController(Controller $controller, string $methodName) {
5050
$this->assertCanManageContext();
5151
}
5252

53-
protected function assertPermission($controller, $methodName): void {
53+
protected function assertPermission(Controller $controller, string $methodName): void {
5454
$reflectionMethod = new \ReflectionMethod($controller, $methodName);
5555
$permissionReqs = $reflectionMethod->getAttributes(RequirePermission::class);
5656
if ($permissionReqs) {

lib/Service/PermissionsService.php

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
use OCA\Tables\Db\ViewMapper;
1919
use OCA\Tables\Errors\InternalError;
2020
use OCA\Tables\Errors\NotFoundError;
21+
use OCA\Tables\Helper\CircleHelper;
2122
use OCA\Tables\Helper\ConversionHelper;
2223
use OCA\Tables\Helper\UserHelper;
2324
use OCA\Tables\Model\Permissions;
2425
use OCP\AppFramework\Db\DoesNotExistException;
2526
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
2627
use OCP\DB\Exception;
2728
use Psr\Log\LoggerInterface;
29+
use Throwable;
2830

2931
class PermissionsService {
3032
private TableMapper $tableMapper;
@@ -35,11 +37,14 @@ class PermissionsService {
3537

3638
private UserHelper $userHelper;
3739

40+
private CircleHelper $circleHelper;
41+
3842
protected LoggerInterface $logger;
3943

4044
protected ?string $userId = null;
4145

4246
protected bool $isCli = false;
47+
4348
private ContextMapper $contextMapper;
4449

4550
public function __construct(
@@ -50,6 +55,7 @@ public function __construct(
5055
ShareMapper $shareMapper,
5156
ContextMapper $contextMapper,
5257
UserHelper $userHelper,
58+
CircleHelper $circleHelper,
5359
bool $isCLI
5460
) {
5561
$this->tableMapper = $tableMapper;
@@ -60,6 +66,7 @@ public function __construct(
6066
$this->userId = $userId;
6167
$this->isCli = $isCLI;
6268
$this->contextMapper = $contextMapper;
69+
$this->circleHelper = $circleHelper;
6370
}
6471

6572

@@ -420,6 +427,7 @@ public function canReadShare(Share $share, ?string $userId = null): bool {
420427
* @param int $elementId
421428
* @param 'table'|'view' $elementType
422429
* @param string $userId
430+
* @return Permissions
423431
* @throws NotFoundError
424432
*/
425433
public function getSharedPermissionsIfSharedWithMe(int $elementId, string $elementType, string $userId): Permissions {
@@ -436,16 +444,40 @@ public function getSharedPermissionsIfSharedWithMe(int $elementId, string $eleme
436444
$this->logger->warning('Exception occurred: '.$e->getMessage().' Permission denied.');
437445
return new Permissions();
438446
}
439-
$additionalShares = [];
447+
$groupShares = [];
440448
foreach ($userGroups as $userGroup) {
441449
try {
442-
$additionalShares[] = $this->shareMapper->findAllSharesForNodeFor($elementType, $elementId, $userGroup->getGid(), 'group');
450+
$groupShares[] = $this->shareMapper->findAllSharesForNodeFor($elementType, $elementId, $userGroup->getGid(), 'group');
443451
} catch (Exception $e) {
444452
$this->logger->warning('Exception occurred: '.$e->getMessage().' Permission denied.');
445453
return new Permissions();
446454
}
447455
}
448-
$shares = array_merge($shares, ...$additionalShares);
456+
457+
$shares = array_merge($shares, ...$groupShares);
458+
459+
if ($this->circleHelper->isCirclesEnabled()) {
460+
$circleShares = [];
461+
462+
try {
463+
$userCircles = $this->circleHelper->getUserCircles($userId);
464+
} catch (Throwable $e) {
465+
$this->logger->warning('Exception occurred: ' . $e->getMessage() . ' Permission denied.');
466+
return new Permissions();
467+
}
468+
469+
foreach ($userCircles as $userCircle) {
470+
try {
471+
$circleShares[] = $this->shareMapper->findAllSharesForNodeFor($elementType, $elementId, $userCircle->getSingleId(), 'circle');
472+
} catch (Exception $e) {
473+
$this->logger->warning('Exception occurred: ' . $e->getMessage() . ' Permission denied.');
474+
return new Permissions();
475+
}
476+
}
477+
478+
$shares = array_merge($shares, ...$circleShares);
479+
}
480+
449481
if (count($shares) > 0) {
450482
$read = array_reduce($shares, function ($carry, $share) {
451483
return $carry || ($share->getPermissionRead());
@@ -520,7 +552,7 @@ private function hasPermission(int $existingPermissions, string $permissionName)
520552
$constantName = 'PERMISSION_' . strtoupper($permissionName);
521553
try {
522554
$permissionBit = constant(Application::class . "::$constantName");
523-
} catch (\Throwable $t) {
555+
} catch (Throwable $t) {
524556
$this->logger->error('Unexpected permission string {permission}', [
525557
'app' => Application::APP_ID,
526558
'permission' => $permissionName,

0 commit comments

Comments
 (0)