From cad0658e15576edd788aae28a5abfa86391554c4 Mon Sep 17 00:00:00 2001 From: Oliver Schramm Date: Tue, 27 Oct 2026 21:13:58 +0100 Subject: [PATCH] Add config to change default visibility of profile properties Signed-off-by: Oliver Schramm --- config/config.sample.php | 10 ++ lib/private/Profile/ProfileManager.php | 18 ++- tests/lib/Profile/ProfileManagerTest.php | 156 +++++++++++++++++++++++ 3 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 tests/lib/Profile/ProfileManagerTest.php diff --git a/config/config.sample.php b/config/config.sample.php index da3de23d25202..ed8d2cbe2aa00 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -3100,6 +3100,16 @@ */ 'account_manager.default_property_scope' => [], + /* + * Overrides default visibility for profile data. Valid properties and visibilities + * are defined in ``OCP\Accounts\IAccountManager`` and ``OCP\Profile\IProfileManager``. + * Values are merged with defaults from there. + * + * Example: Set phone property to hidden visibility: + * ``[\OCP\Accounts\IAccountManager::PROPERTY_PHONE => \OCP\Profile\IProfileManager::VISIBILITY_HIDE]`` + */ + 'profile_manager.default_property_visibility' => [], + /** * Enable the deprecated Projects feature, superseded by Related Resources since * Nextcloud 25. diff --git a/lib/private/Profile/ProfileManager.php b/lib/private/Profile/ProfileManager.php index 91632073fb2e3..264d4c1f0c793 100644 --- a/lib/private/Profile/ProfileManager.php +++ b/lib/private/Profile/ProfileManager.php @@ -77,6 +77,15 @@ class ProfileManager implements IProfileManager { IAccountManager::PROPERTY_PRONOUNS, ]; + /** + * Array of allowed visibility values + */ + private const array ALLOWED_VISIBILITY = [ + self::VISIBILITY_HIDE, + self::VISIBILITY_SHOW_USERS_ONLY, + self::VISIBILITY_SHOW, + ]; + public function __construct( private IAccountManager $accountManager, private IAppManager $appManager, @@ -319,9 +328,16 @@ private function getDefaultProfileConfig(IUser $targetUser, ?IUser $visitingUser } // Construct the default config for account properties + $configDefaultVisibility = $this->config->getSystemValue('profile_manager.default_property_visibility', []); $propertiesConfig = []; foreach (self::DEFAULT_PROPERTY_VISIBILITY as $property => $visibility) { - $this->applyDefaultProfilePreset($property, $visibility); + if (isset($configDefaultVisibility[$property]) + && in_array($configDefaultVisibility[$property], self::ALLOWED_VISIBILITY, true)) { + $visibility = $configDefaultVisibility[$property]; + } else { + $this->applyDefaultProfilePreset($property, $visibility); + } + $propertiesConfig[$property] = ['visibility' => $visibility]; } diff --git a/tests/lib/Profile/ProfileManagerTest.php b/tests/lib/Profile/ProfileManagerTest.php new file mode 100644 index 0000000000000..1a55c3c816467 --- /dev/null +++ b/tests/lib/Profile/ProfileManagerTest.php @@ -0,0 +1,156 @@ +accountManager = $this->createMock(IAccountManager::class); + $this->appManager = $this->createMock(IAppManager::class); + $this->config = $this->createMock(IConfig::class); + $this->configMapper = $this->createMock(ProfileConfigMapper::class); + $this->container = $this->createMock(ContainerInterface::class); + $this->knownUserService = $this->createMock(KnownUserService::class); + $this->l10nFactory = $this->createMock(IFactory::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->coordinator = $this->createMock(Coordinator::class); + $this->presetManager = $this->createMock(PresetManager::class); + + $this->profileManager = new ProfileManager( + $this->accountManager, + $this->appManager, + $this->config, + $this->configMapper, + $this->container, + $this->knownUserService, + $this->l10nFactory, + $this->logger, + $this->coordinator, + $this->presetManager + ); + } + + public static function dataSetDefaultPropertyScopes(): array { + return [ + [ + [], # returns just IProfileManager::DEFAULT_PROPERTY_VISIBILITY + [ + IAccountManager::PROPERTY_ADDRESS => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_AVATAR => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_BIOGRAPHY => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_DISPLAYNAME => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_HEADLINE => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_ORGANISATION => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_ROLE => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_EMAIL => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_PHONE => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_TWITTER => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_BLUESKY => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_WEBSITE => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_PRONOUNS => IProfileManager::VISIBILITY_SHOW, + ] + ], + [ + [ # should overwrite their respective defaults + IAccountManager::PROPERTY_AVATAR => IProfileManager::VISIBILITY_HIDE, + IAccountManager::PROPERTY_ROLE => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + ], + [ + IAccountManager::PROPERTY_ADDRESS => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_AVATAR => IProfileManager::VISIBILITY_HIDE, + IAccountManager::PROPERTY_BIOGRAPHY => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_DISPLAYNAME => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_HEADLINE => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_ORGANISATION => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_ROLE => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_EMAIL => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_PHONE => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_TWITTER => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_BLUESKY => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_WEBSITE => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_PRONOUNS => IProfileManager::VISIBILITY_SHOW, + ] + ], + [ + [ + IAccountManager::PROPERTY_BLUESKY => 'invalid', # should fall back to default + ], + [ + IAccountManager::PROPERTY_ADDRESS => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_AVATAR => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_BIOGRAPHY => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_DISPLAYNAME => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_HEADLINE => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_ORGANISATION => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_ROLE => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_EMAIL => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_PHONE => IProfileManager::VISIBILITY_SHOW_USERS_ONLY, + IAccountManager::PROPERTY_TWITTER => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_BLUESKY => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_WEBSITE => IProfileManager::VISIBILITY_SHOW, + IAccountManager::PROPERTY_PRONOUNS => IProfileManager::VISIBILITY_SHOW, + ] + ], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataSetDefaultPropertyScopes')] + public function testSetDefaultPropertyVisibility(array $propertyVisibilities, array $expected): void { + $targetUser = $this->createMock(IUser::class); + $this->invokePrivate($this->profileManager, 'sortedActions', [[]]); + $this->presetManager->method('getLexiconPreset')->willReturn(Preset::NONE); + $this->config->expects($this->once())->method('getSystemValue')->with('profile_manager.default_property_visibility', [])->willReturn($propertyVisibilities); + + $result = $this->invokePrivate($this->profileManager, 'getDefaultProfileConfig', [$targetUser, null]); + + foreach ($expected as $key => $expectedValue) { + $resultVisibility = $result[$key]['visibility']; + $this->assertEquals($expectedValue, $resultVisibility, "The result visibility doesn't follow the value set into the config or defaults correctly."); + } + } +}