From 0a0e84f6a691f83736f7d92603856fc553f3aed7 Mon Sep 17 00:00:00 2001 From: Lennart Joswig Date: Mon, 31 Aug 2026 09:45:30 +0200 Subject: [PATCH] fix(teams): allow hiding the shared folder button via app config Admins can hide the team-page shortcut that creates a personal folder and shares it with the team. The setting is registered in the app config Signed-off-by: Lennart Joswig Co-authored-by: Tomasz Trillo --- lib/ConfigLexicon.php | 8 ++++ lib/Controller/PageController.php | 8 ++++ src/components/CircleDetails.vue | 5 +- src/services/hideTeamSharedFolderCreation.js | 9 ++++ tests/unit/Controller/PageControllerTest.php | 48 ++++++++++++++++++++ 5 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 src/services/hideTeamSharedFolderCreation.js diff --git a/lib/ConfigLexicon.php b/lib/ConfigLexicon.php index 27031c6540..5c52a549f7 100644 --- a/lib/ConfigLexicon.php +++ b/lib/ConfigLexicon.php @@ -33,6 +33,7 @@ class ConfigLexicon implements ILexicon { public const FEDERATIONS_CACHE = 'federations_cache'; public const FEDERATIONS_CACHE_EXPIRES = 'expires'; public const FEDERATIONS_CACHE_DELTA_EXPIRES = 'delta_expires'; + public const HIDE_TEAM_SHARED_FOLDER_CREATION = 'hide_team_shared_folder_creation'; #[\Override] public function getStrictness(): Strictness { @@ -107,6 +108,13 @@ public function getAppConfigs(): array { definition: 'Federations cache update (delta) expiration time.', lazy: true, ), + new Entry( + self::HIDE_TEAM_SHARED_FOLDER_CREATION, + ValueType::BOOL, + defaultRaw: false, + definition: 'Hide the team-page button that creates a personal folder and shares it with the team.', + lazy: true, + ), ]; } diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index a68d8d8ff9..370e706a09 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -9,6 +9,7 @@ use OC\App\CompareVersion; use OCA\Contacts\AppInfo\Application; +use OCA\Contacts\ConfigLexicon; use OCA\Contacts\Service\FederatedInvitesService; use OCA\Contacts\Service\GroupSharingService; use OCA\Contacts\Service\SocialApiService; @@ -16,6 +17,7 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IRequest; use OCP\IUserSession; @@ -29,6 +31,7 @@ public function __construct( IRequest $request, private FederatedInvitesService $federatedInvitesService, private IConfig $config, + private IAppConfig $appConfig, private IInitialState $initialState, private IFactory $languageFactory, private IUserSession $userSession, @@ -83,6 +86,10 @@ public function index(string $token = '', string $providerDomain = ''): Template $isTalkVersionCompatible = $this->compareVersion->isCompatible($talkVersion ? $talkVersion : '0.0.0', 2); $isOcmInvitesEnabled = $this->federatedInvitesService->isOcmInvitesEnabled(); $ocmInvitesConfig = $this->federatedInvitesService->getOcmInvitesConfig(); + $hideTeamSharedFolderCreation = $this->appConfig->getValueBool( + Application::APP_ID, + ConfigLexicon::HIDE_TEAM_SHARED_FOLDER_CREATION, + ); $this->initialState->provideInitialState('isGroupSharingEnabled', $isGroupSharingEnabled); $this->initialState->provideInitialState('locales', $locales); @@ -95,6 +102,7 @@ public function index(string $token = '', string $providerDomain = ''): Template $this->initialState->provideInitialState('isTalkEnabled', $isTalkEnabled && $isTalkVersionCompatible); $this->initialState->provideInitialState('isOcmInvitesEnabled', $isOcmInvitesEnabled); $this->initialState->provideInitialState('ocmInvitesConfig', $ocmInvitesConfig); + $this->initialState->provideInitialState('hideTeamSharedFolderCreation', $hideTeamSharedFolderCreation); Util::addStyle(Application::APP_ID, 'contacts-main'); Util::addScript(Application::APP_ID, 'contacts-main'); diff --git a/src/components/CircleDetails.vue b/src/components/CircleDetails.vue index fb478bcdda..3d219dddf6 100644 --- a/src/components/CircleDetails.vue +++ b/src/components/CircleDetails.vue @@ -151,7 +151,7 @@ -
+

{{ t('contacts', 'Create') }}

@@ -327,6 +327,7 @@ import TeamResourceButton from './CircleDetails/TeamResourceButton.vue' import MemberList from './MemberList/MemberList.vue' import CircleActionsMixin from '../mixins/CircleActionsMixin.js' import { CircleEdit, editCircle } from '../services/circles.ts' +import hideTeamSharedFolderCreation from '../services/hideTeamSharedFolderCreation.js' import logger from '../services/logger.js' import 'cropperjs/dist/cropper.css' @@ -541,7 +542,7 @@ export default { helperText: t('contacts', 'This will create a regular folder shared with the team. To create a Team Folder, please contact your {productName} administrator', { productName: OC.theme.name }), icon: 'FolderOutlineIcon', apiPath: 'files', - enabled: enabledApps.files !== undefined, + enabled: enabledApps.files !== undefined && !hideTeamSharedFolderCreation, }, { id: 'talk', diff --git a/src/services/hideTeamSharedFolderCreation.js b/src/services/hideTeamSharedFolderCreation.js new file mode 100644 index 0000000000..d139be0738 --- /dev/null +++ b/src/services/hideTeamSharedFolderCreation.js @@ -0,0 +1,9 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { loadState } from '@nextcloud/initial-state' + +const hideTeamSharedFolderCreation = loadState('contacts', 'hideTeamSharedFolderCreation', false) +export default hideTeamSharedFolderCreation diff --git a/tests/unit/Controller/PageControllerTest.php b/tests/unit/Controller/PageControllerTest.php index 1cd8ba6375..1200bd40c4 100644 --- a/tests/unit/Controller/PageControllerTest.php +++ b/tests/unit/Controller/PageControllerTest.php @@ -10,12 +10,14 @@ use ChristophWurst\Nextcloud\Testing\TestCase; use OC\App\CompareVersion; use OCA\Contacts\AppInfo\Application; +use OCA\Contacts\ConfigLexicon; use OCA\Contacts\Service\FederatedInvitesService; use OCA\Contacts\Service\GroupSharingService; use OCA\Contacts\Service\SocialApiService; use OCP\App\IAppManager; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IRequest; use OCP\IUser; @@ -33,6 +35,9 @@ class PageControllerTest extends TestCase { /** @var IConfig|MockObject */ private $config; + /** @var IAppConfig|MockObject */ + private $appConfig; + /** @var IInitialState|MockObject */ private $initialStateService; @@ -59,6 +64,7 @@ protected function setUp(): void { $this->request = $this->createMock(IRequest::class); $this->config = $this->createMock(IConfig::class); + $this->appConfig = $this->createMock(IAppConfig::class); $this->initialStateService = $this->createMock(IInitialState::class); $this->languageFactory = $this->createMock(IFactory::class); $this->userSession = $this->createMock(IUserSession::class); @@ -76,6 +82,7 @@ private function buildController(ServerVersion $serverVersion): PageController { $this->request, $this->federatedInvitesService, $this->config, + $this->appConfig, $this->initialStateService, $this->languageFactory, $this->userSession, @@ -99,6 +106,47 @@ public function testIndex() { $this->assertTrue($result instanceof TemplateResponse); } + public function testIndexProvidesHideTeamSharedFolderCreation() { + $user = $this->createMock(IUser::class); + $user->method('getUid')->willReturn('mrstest'); + $this->userSession->method('getUser')->willReturn($user); + + $this->appConfig->method('getValueBool') + ->willReturnCallback(function (string $app, string $key, bool $default = false) { + $this->assertSame(Application::APP_ID, $app); + if ($key === ConfigLexicon::HIDE_TEAM_SHARED_FOLDER_CREATION) { + return true; + } + return $default; + }); + + $states = []; + $this->initialStateService->method('provideInitialState') + ->willReturnCallback(function (string $key, mixed $value) use (&$states): void { + $states[$key] = $value; + }); + + $this->controller->index(); + + $this->assertTrue($states['hideTeamSharedFolderCreation']); + } + + public function testIndexHideTeamSharedFolderCreationDefaultsToFalse() { + $user = $this->createMock(IUser::class); + $user->method('getUid')->willReturn('mrstest'); + $this->userSession->method('getUser')->willReturn($user); + + $states = []; + $this->initialStateService->method('provideInitialState') + ->willReturnCallback(function (string $key, mixed $value) use (&$states): void { + $states[$key] = $value; + }); + + $this->controller->index(); + + $this->assertFalse($states['hideTeamSharedFolderCreation']); + } + public static function teamManagementDataProvider(): array { return [ // [server major version, circles enabled, circles version compatible, expected]