diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 77fdebecc..95c99b09b 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -32,6 +32,7 @@
use OCA\GroupFolders\Listeners\NodeRenamedListener;
use OCA\GroupFolders\Mount\FolderStorageManager;
use OCA\GroupFolders\Mount\MountProvider;
+use OCA\GroupFolders\Team\GroupFoldersTeamResourceProvider;
use OCA\GroupFolders\Trash\TrashBackend;
use OCA\GroupFolders\Trash\TrashManager;
use OCA\GroupFolders\Versions\GroupVersionsExpireManager;
@@ -92,6 +93,8 @@ public function register(IRegistrationContext $context): void {
$context->registerEventListener(GroupDeletedEvent::class, DeleteListener::class);
$context->registerEventListener(UserDeletedEvent::class, DeleteListener::class);
+ $context->registerTeamResourceProvider(GroupFoldersTeamResourceProvider::class);
+
$context->registerService(MountProvider::class, function (ContainerInterface $c): MountProvider {
/** @var IAppConfig $config */
$config = $c->get(IAppConfig::class);
diff --git a/lib/Team/GroupFoldersTeamResourceProvider.php b/lib/Team/GroupFoldersTeamResourceProvider.php
new file mode 100644
index 000000000..d3a7ae8b8
--- /dev/null
+++ b/lib/Team/GroupFoldersTeamResourceProvider.php
@@ -0,0 +1,104 @@
+';
+
+ /** Per-resource icon: a plain folder, matching the files team resource provider. */
+ private const FOLDER_ICON_SVG = '';
+
+ public function __construct(
+ private readonly IL10N $l,
+ private readonly IURLGenerator $url,
+ private readonly FolderManager $folderManager,
+ ) {
+ }
+
+ public function getId(): string {
+ return Application::APP_ID;
+ }
+
+ public function getName(): string {
+ return $this->l->t('Team folders');
+ }
+
+ public function getIconSvg(): string {
+ return self::PROVIDER_ICON_SVG;
+ }
+
+ public function getSharedWith(string $teamId): array {
+ // Membership is gated by TeamManager before this runs, so we return every
+ // team folder mapped to this circle without re-checking the current user.
+ if ($this->folderManager->getCirclesManager() === null) {
+ return [];
+ }
+
+ $resources = [];
+ foreach ($this->folderManager->getAllFolders() as $folder) {
+ $applicable = $folder->groups[$teamId] ?? null;
+ if ($applicable === null || $applicable['type'] !== 'circle') {
+ continue;
+ }
+
+ $resources[] = new TeamResource(
+ $this,
+ (string)$folder->id,
+ $folder->mountPoint,
+ $this->url->linkToRouteAbsolute('files.view.index', ['dir' => '/' . $folder->mountPoint]),
+ iconSvg: self::FOLDER_ICON_SVG,
+ );
+ }
+
+ return $resources;
+ }
+
+ public function isSharedWithTeam(string $teamId, string $resourceId): bool {
+ if ($this->folderManager->getCirclesManager() === null) {
+ return false;
+ }
+
+ $folder = $this->folderManager->getAllFolders()[(int)$resourceId] ?? null;
+ if ($folder === null) {
+ return false;
+ }
+
+ $applicable = $folder->groups[$teamId] ?? null;
+ return $applicable !== null && $applicable['type'] === 'circle';
+ }
+
+ public function getTeamsForResource(string $resourceId): array {
+ if ($this->folderManager->getCirclesManager() === null) {
+ return [];
+ }
+
+ $folder = $this->folderManager->getAllFolders()[(int)$resourceId] ?? null;
+ if ($folder === null) {
+ return [];
+ }
+
+ $teamIds = [];
+ foreach ($folder->groups as $entityId => $applicable) {
+ if ($applicable['type'] === 'circle') {
+ $teamIds[] = (string)$entityId;
+ }
+ }
+
+ return $teamIds;
+ }
+}
diff --git a/tests/Team/GroupFoldersTeamResourceProviderTest.php b/tests/Team/GroupFoldersTeamResourceProviderTest.php
new file mode 100644
index 000000000..76b7fae3b
--- /dev/null
+++ b/tests/Team/GroupFoldersTeamResourceProviderTest.php
@@ -0,0 +1,142 @@
+folderManager = $this->createMock(FolderManager::class);
+ $this->l10n = $this->createMock(IL10N::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+
+ $this->l10n->method('t')->willReturnArgument(0);
+ $this->urlGenerator->method('linkToRouteAbsolute')
+ ->willReturnCallback(function (string $route, array $args = []): string {
+ $dir = $args['dir'] ?? '';
+ return 'https://cloud.example/' . $route . '?dir=' . (is_string($dir) ? $dir : '');
+ });
+
+ $this->provider = new GroupFoldersTeamResourceProvider(
+ $this->l10n,
+ $this->urlGenerator,
+ $this->folderManager,
+ );
+ }
+
+ /**
+ * @param array $groups
+ */
+ private function folder(int $id, string $mountPoint, array $groups): FolderDefinitionWithMappings {
+ return new FolderDefinitionWithMappings($id, $mountPoint, 0, false, false, 1, 1, [], $groups, []);
+ }
+
+ /**
+ * @return array{displayName: string, permissions: int, type: 'circle'}
+ */
+ private function circleApplicable(string $displayName = 'Team'): array {
+ return ['displayName' => $displayName, 'permissions' => 31, 'type' => 'circle'];
+ }
+
+ /**
+ * @return array{displayName: string, permissions: int, type: 'group'}
+ */
+ private function groupApplicable(string $displayName = 'Group'): array {
+ return ['displayName' => $displayName, 'permissions' => 31, 'type' => 'group'];
+ }
+
+ private function withCircles(): void {
+ $this->folderManager->method('getCirclesManager')->willReturn($this->createMock(CirclesManager::class));
+ }
+
+ public function testStaticIdentity(): void {
+ $this->assertSame('groupfolders', $this->provider->getId());
+ $this->assertSame('Team folders', $this->provider->getName());
+ $this->assertStringContainsString('