From cb2784c0e5e66dd9c226a7e5366f52943736300a Mon Sep 17 00:00:00 2001 From: Stefan Lender Date: Fri, 9 Oct 2026 00:41:09 +0200 Subject: [PATCH] feat(ocp): add Team Activity resource provider and TeamFolder root file id Introduce ITeamActivityResourceProvider as an optional capability for ITeamResourceProvider implementations to resolve their Team resources into Activity scopes, backed by the new TeamActivityScope value object. Also expose the filecache root id on TeamFolder so Activity providers can reference team folder contents. Signed-off-by: Stefan Lender Assisted-by: Copilot:gpt-6.1-sol Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- lib/composer/composer/autoload_classmap.php | 2 + lib/composer/composer/autoload_static.php | 2 + .../Teams/ITeamActivityResourceProvider.php | 28 ++++++++ lib/public/Teams/TeamActivityScope.php | 49 +++++++++++++ lib/public/Teams/TeamFolder.php | 13 +++- tests/lib/Teams/TeamActivityScopeTest.php | 68 +++++++++++++++++++ tests/lib/Teams/TeamFolderTest.php | 12 +++- 7 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 lib/public/Teams/ITeamActivityResourceProvider.php create mode 100644 lib/public/Teams/TeamActivityScope.php create mode 100644 tests/lib/Teams/TeamActivityScopeTest.php diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 6a9a458507cce..b817fcef17792 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1072,10 +1072,12 @@ 'OCP\\TaskProcessing\\TaskTypes\\TextToTextSummary' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/TextToTextSummary.php', 'OCP\\TaskProcessing\\TaskTypes\\TextToTextTopics' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/TextToTextTopics.php', 'OCP\\TaskProcessing\\TaskTypes\\TextToTextTranslate' => $baseDir . '/lib/public/TaskProcessing/TaskTypes/TextToTextTranslate.php', + 'OCP\\Teams\\ITeamActivityResourceProvider' => $baseDir . '/lib/public/Teams/ITeamActivityResourceProvider.php', 'OCP\\Teams\\ITeamFolderProvider' => $baseDir . '/lib/public/Teams/ITeamFolderProvider.php', 'OCP\\Teams\\ITeamManager' => $baseDir . '/lib/public/Teams/ITeamManager.php', 'OCP\\Teams\\ITeamResourceProvider' => $baseDir . '/lib/public/Teams/ITeamResourceProvider.php', 'OCP\\Teams\\Team' => $baseDir . '/lib/public/Teams/Team.php', + 'OCP\\Teams\\TeamActivityScope' => $baseDir . '/lib/public/Teams/TeamActivityScope.php', 'OCP\\Teams\\TeamFolder' => $baseDir . '/lib/public/Teams/TeamFolder.php', 'OCP\\Teams\\TeamResource' => $baseDir . '/lib/public/Teams/TeamResource.php', 'OCP\\Template' => $baseDir . '/lib/public/Template.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index fabed32790e47..9ccf019fe3810 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1118,10 +1118,12 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\TaskProcessing\\TaskTypes\\TextToTextSummary' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/TextToTextSummary.php', 'OCP\\TaskProcessing\\TaskTypes\\TextToTextTopics' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/TextToTextTopics.php', 'OCP\\TaskProcessing\\TaskTypes\\TextToTextTranslate' => __DIR__ . '/../../..' . '/lib/public/TaskProcessing/TaskTypes/TextToTextTranslate.php', + 'OCP\\Teams\\ITeamActivityResourceProvider' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamActivityResourceProvider.php', 'OCP\\Teams\\ITeamFolderProvider' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamFolderProvider.php', 'OCP\\Teams\\ITeamManager' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamManager.php', 'OCP\\Teams\\ITeamResourceProvider' => __DIR__ . '/../../..' . '/lib/public/Teams/ITeamResourceProvider.php', 'OCP\\Teams\\Team' => __DIR__ . '/../../..' . '/lib/public/Teams/Team.php', + 'OCP\\Teams\\TeamActivityScope' => __DIR__ . '/../../..' . '/lib/public/Teams/TeamActivityScope.php', 'OCP\\Teams\\TeamFolder' => __DIR__ . '/../../..' . '/lib/public/Teams/TeamFolder.php', 'OCP\\Teams\\TeamResource' => __DIR__ . '/../../..' . '/lib/public/Teams/TeamResource.php', 'OCP\\Template' => __DIR__ . '/../../..' . '/lib/public/Template.php', diff --git a/lib/public/Teams/ITeamActivityResourceProvider.php b/lib/public/Teams/ITeamActivityResourceProvider.php new file mode 100644 index 0000000000000..cc24c4e25e299 --- /dev/null +++ b/lib/public/Teams/ITeamActivityResourceProvider.php @@ -0,0 +1,28 @@ + $resources Resources from this provider shared with the Team + * @param string $userId The user the activity scopes are resolved for + * @return list + * @since 36.0.0 + */ + public function getActivityScopes(array $resources, string $userId): array; +} diff --git a/lib/public/Teams/TeamActivityScope.php b/lib/public/Teams/TeamActivityScope.php new file mode 100644 index 0000000000000..296b0d3be7096 --- /dev/null +++ b/lib/public/Teams/TeamActivityScope.php @@ -0,0 +1,49 @@ + */ + private array $objectIds; + + /** + * @param list $objectIds + * @param non-empty-string $objectType + * @since 36.0.0 + */ + public function __construct( + private string $objectType, + array $objectIds, + ) { + $this->objectIds = array_values(array_unique($objectIds)); + } + + /** + * @return non-empty-string + * @since 36.0.0 + */ + public function getObjectType(): string { + return $this->objectType; + } + + /** + * @return list + * @since 36.0.0 + */ + public function getObjectIds(): array { + return $this->objectIds; + } +} diff --git a/lib/public/Teams/TeamFolder.php b/lib/public/Teams/TeamFolder.php index 30f6c85f2f196..afd366460a8af 100644 --- a/lib/public/Teams/TeamFolder.php +++ b/lib/public/Teams/TeamFolder.php @@ -25,6 +25,7 @@ public function __construct( private int $id, private string $mountPoint, private ?int $quota = null, + private ?int $rootFileId = null, ) { } @@ -53,7 +54,16 @@ public function getQuota(): ?int { } /** - * @return array{id: int, quota: int|null, mountPoint: string} + * Return the filecache ID of the folder's root, when exposed by the provider. + * + * @since 36.0.0 + */ + public function getRootFileId(): ?int { + return $this->rootFileId; + } + + /** + * @return array{id: int, quota: int|null, mountPoint: string, rootFileId: int|null} * @since 35.0.0 */ #[\Override] @@ -62,6 +72,7 @@ public function jsonSerialize(): array { 'id' => $this->id, 'quota' => $this->quota, 'mountPoint' => $this->mountPoint, + 'rootFileId' => $this->rootFileId, ]; } } diff --git a/tests/lib/Teams/TeamActivityScopeTest.php b/tests/lib/Teams/TeamActivityScopeTest.php new file mode 100644 index 0000000000000..ec5a519210ded --- /dev/null +++ b/tests/lib/Teams/TeamActivityScopeTest.php @@ -0,0 +1,68 @@ +assertSame('files', $scope->getObjectType()); + $this->assertSame([1, 2, 3], $scope->getObjectIds()); + } + + public function testDeduplicatesObjectIds(): void { + $scope = new TeamActivityScope('files', [1, 2, 2, 3, 1]); + + $this->assertSame([1, 2, 3], $scope->getObjectIds()); + } + + public function testAllowsEmptyObjectIds(): void { + $scope = new TeamActivityScope('files', []); + + $this->assertSame([], $scope->getObjectIds()); + } + + public function testReturnedObjectIdsDoNotMutateScope(): void { + $scope = new TeamActivityScope('files', [1, 2, 3]); + $objectIds = $scope->getObjectIds(); + $objectIds[] = 4; + + $this->assertSame([1, 2, 3], $scope->getObjectIds()); + } + + public function testScopeIsFinalAndReadonly(): void { + $class = new \ReflectionClass(TeamActivityScope::class); + + $this->assertTrue($class->isFinal()); + $this->assertTrue($class->isReadOnly()); + } + + public function testScopeIsConsumable(): void { + $class = new \ReflectionClass(TeamActivityScope::class); + $attributes = $class->getAttributes(Consumable::class); + + $this->assertCount(1, $attributes); + $this->assertSame('36.0.0', $attributes[0]->newInstance()->getSince()); + } + + public function testProviderIsImplementable(): void { + $class = new \ReflectionClass(ITeamActivityResourceProvider::class); + $attributes = $class->getAttributes(Implementable::class); + + $this->assertCount(1, $attributes); + $this->assertSame('36.0.0', $attributes[0]->newInstance()->getSince()); + } +} diff --git a/tests/lib/Teams/TeamFolderTest.php b/tests/lib/Teams/TeamFolderTest.php index bc062b98d61e2..11349695e6620 100644 --- a/tests/lib/Teams/TeamFolderTest.php +++ b/tests/lib/Teams/TeamFolderTest.php @@ -19,13 +19,21 @@ public function testSerializesFolderIdentity(): void { $this->assertSame(42, $folder->getId()); $this->assertSame('Engineering', $folder->getMountPoint()); $this->assertNull($folder->getQuota()); - $this->assertSame(['id' => 42, 'quota' => null, 'mountPoint' => 'Engineering'], $folder->jsonSerialize()); + $this->assertNull($folder->getRootFileId()); + $this->assertSame(['id' => 42, 'quota' => null, 'mountPoint' => 'Engineering', 'rootFileId' => null], $folder->jsonSerialize()); } public function testSerializesFolderQuota(): void { $folder = new TeamFolder(42, 'Engineering', 1024); $this->assertSame(1024, $folder->getQuota()); - $this->assertSame(['id' => 42, 'quota' => 1024, 'mountPoint' => 'Engineering'], $folder->jsonSerialize()); + $this->assertSame(['id' => 42, 'quota' => 1024, 'mountPoint' => 'Engineering', 'rootFileId' => null], $folder->jsonSerialize()); + } + + public function testSerializesFolderRootFileId(): void { + $folder = new TeamFolder(42, 'Engineering', 1024, 1337); + + $this->assertSame(1337, $folder->getRootFileId()); + $this->assertSame(['id' => 42, 'quota' => 1024, 'mountPoint' => 'Engineering', 'rootFileId' => 1337], $folder->jsonSerialize()); } }