Skip to content

Commit 6879b0e

Browse files
committed
feat(teams): Add Deck resources to Team Activity
Expose shared boards and their cards as Team Activity scopes. Co-authored-by: Copilot <copilot@github.com> GPT-5.6 Sol Signed-off-by: Stefan Lender <Stefan.Lender@dataport.de>
1 parent 45e106b commit 6879b0e

2 files changed

Lines changed: 135 additions & 1 deletion

File tree

‎lib/Teams/DeckTeamResourceProvider.php‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,22 @@
77
use OCA\Deck\AppInfo\Application;
88
use OCA\Deck\Db\Board;
99
use OCA\Deck\Db\BoardMapper;
10+
use OCA\Deck\Db\CardMapper;
1011
use OCP\IURLGenerator;
12+
use OCP\Teams\ITeamActivityResourceProvider;
13+
use OCP\Teams\TeamActivityScope;
1114
use OCP\Teams\TeamResource;
1215

1316
/**
1417
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
1518
* SPDX-License-Identifier: AGPL-3.0-or-later
1619
*/
1720

18-
class DeckTeamResourceProvider implements \OCP\Teams\ITeamResourceProvider {
21+
class DeckTeamResourceProvider implements ITeamActivityResourceProvider {
1922

2023
public function __construct(
2124
private BoardMapper $boardMapper,
25+
private CardMapper $cardMapper,
2226
private IURLGenerator $urlGenerator,
2327
) {
2428
}
@@ -57,6 +61,28 @@ public function getTeamsForResource(string $resourceId): array {
5761
return $this->boardMapper->findTeamsForBoard((int)$resourceId);
5862
}
5963

64+
public function getActivityScopes(array $resources, string $userId): array {
65+
// Read access and deleted objects are checked by DeckProvider via canSee*Activity(), so $userId is not used here.
66+
$boardIds = array_values(array_unique(array_filter(array_map(
67+
static fn (TeamResource $resource): ?int => ctype_digit($resource->getId()) && (int)$resource->getId() > 0 ? (int)$resource->getId() : null,
68+
$resources,
69+
))));
70+
if ($boardIds === []) {
71+
return [];
72+
}
73+
74+
$query = $this->cardMapper->queryCardsByBoards($boardIds);
75+
$query->select('c.id');
76+
$result = $query->executeQuery();
77+
$cardIds = array_map('intval', $result->fetchFirstColumn());
78+
$result->closeCursor();
79+
80+
return [
81+
new TeamActivityScope('deck_board', $boardIds),
82+
new TeamActivityScope('deck_card', $cardIds),
83+
];
84+
}
85+
6086
public function getBoardBulletIcon(Board $board): string {
6187
return '<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" viewBox="0 0 16 16"><g fill="#' . $board->getColor() . '"><rect ry="15" height="14" width="14" y="1" x="1"/></g></svg>';
6288
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Deck\Teams;
11+
12+
use OCA\Deck\Db\BoardMapper;
13+
use OCA\Deck\Db\CardMapper;
14+
use OCP\DB\IResult;
15+
use OCP\DB\QueryBuilder\IQueryBuilder;
16+
use OCP\IURLGenerator;
17+
use OCP\Teams\ITeamResourceProvider;
18+
use OCP\Teams\TeamResource;
19+
use PHPUnit\Framework\MockObject\MockObject;
20+
use PHPUnit\Framework\TestCase;
21+
22+
class DeckTeamResourceProviderTest extends TestCase {
23+
private CardMapper&MockObject $cardMapper;
24+
private ITeamResourceProvider&MockObject $resourceProvider;
25+
private DeckTeamResourceProvider $provider;
26+
27+
protected function setUp(): void {
28+
parent::setUp();
29+
$this->cardMapper = $this->createMock(CardMapper::class);
30+
$this->resourceProvider = $this->createMock(ITeamResourceProvider::class);
31+
$this->provider = new DeckTeamResourceProvider(
32+
$this->createMock(BoardMapper::class),
33+
$this->cardMapper,
34+
$this->createMock(IURLGenerator::class),
35+
);
36+
}
37+
38+
private function resource(string $id): TeamResource {
39+
return new TeamResource($this->resourceProvider, $id, 'Board ' . $id, '/deck/' . $id);
40+
}
41+
42+
private function expectCardIds(array $boardIds, array $cardIds): void {
43+
$query = $this->createMock(IQueryBuilder::class);
44+
$result = $this->createMock(IResult::class);
45+
$this->cardMapper->expects($this->once())->method('queryCardsByBoards')->with($boardIds)->willReturn($query);
46+
$query->expects($this->once())->method('select')->with('c.id')->willReturnSelf();
47+
$query->expects($this->once())->method('executeQuery')->willReturn($result);
48+
$result->expects($this->once())->method('fetchFirstColumn')->willReturn($cardIds);
49+
$result->expects($this->once())->method('closeCursor');
50+
}
51+
52+
public function testActivityScopesContainBoardsAndTheirCards(): void {
53+
$this->expectCardIds([7, 8], [70, 71, 80]);
54+
55+
$scopes = $this->provider->getActivityScopes([
56+
$this->resource('7'),
57+
$this->resource('8'),
58+
$this->resource('not-a-board-id'),
59+
], 'alice');
60+
61+
self::assertCount(2, $scopes);
62+
self::assertSame('deck_board', $scopes[0]->getObjectType());
63+
self::assertSame([7, 8], $scopes[0]->getObjectIds());
64+
self::assertSame('deck_card', $scopes[1]->getObjectType());
65+
self::assertSame([70, 71, 80], $scopes[1]->getObjectIds());
66+
}
67+
68+
public function testActivityScopesWithoutResources(): void {
69+
$this->cardMapper->expects($this->never())->method('queryCardsByBoards');
70+
71+
self::assertSame([], $this->provider->getActivityScopes([], 'alice'));
72+
}
73+
74+
public function testActivityScopesIgnoreInvalidBoardIds(): void {
75+
$this->cardMapper->expects($this->never())->method('queryCardsByBoards');
76+
77+
$scopes = $this->provider->getActivityScopes([
78+
$this->resource('0'),
79+
$this->resource('abc'),
80+
$this->resource('-1'),
81+
$this->resource(''),
82+
], 'alice');
83+
84+
self::assertSame([], $scopes);
85+
}
86+
87+
public function testActivityScopesDeduplicateBoardIds(): void {
88+
$this->expectCardIds([7], [70]);
89+
90+
$scopes = $this->provider->getActivityScopes([
91+
$this->resource('7'),
92+
$this->resource('7'),
93+
], 'alice');
94+
95+
self::assertSame([7], $scopes[0]->getObjectIds());
96+
}
97+
98+
public function testActivityScopesForBoardWithoutCards(): void {
99+
$this->expectCardIds([7], []);
100+
101+
$scopes = $this->provider->getActivityScopes([$this->resource('7')], 'alice');
102+
103+
self::assertCount(2, $scopes);
104+
self::assertSame([7], $scopes[0]->getObjectIds());
105+
self::assertSame('deck_card', $scopes[1]->getObjectType());
106+
self::assertSame([], $scopes[1]->getObjectIds());
107+
}
108+
}

0 commit comments

Comments
 (0)