feat(profile-picker): add an admin setting in the profile section to toggle the picker and the previews

Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
This commit is contained in:
Julien Veyssier 2026-02-23 14:34:13 +01:00
parent da455ee86e
commit 2fc9eda850
No known key found for this signature in database
GPG key ID: 4141FEE162030638
7 changed files with 115 additions and 7 deletions

View file

@ -12,17 +12,36 @@ use OCA\Profile\AppInfo\Application;
use OCP\Collaboration\Reference\RenderReferenceEvent;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\IAppConfig;
use OCP\Profile\IProfileManager;
use OCP\Util;
/**
* @template-implements IEventListener<RenderReferenceEvent>
*/
class ProfilePickerReferenceListener implements IEventListener {
public function __construct(
private IAppConfig $appConfig,
private IProfileManager $profileManager,
) {
}
public function handle(Event $event): void {
if (!$event instanceof RenderReferenceEvent) {
return;
}
Util::addScript(Application::APP_ID, 'reference');
$profileEnabledGlobally = $this->profileManager->isProfileEnabled();
$profilePickerEnabled = filter_var(
$this->appConfig->getValueString('settings', 'profile_picker_enabled', '1'),
FILTER_VALIDATE_BOOLEAN,
FILTER_NULL_ON_FAILURE,
);
if ($profileEnabledGlobally && $profilePickerEnabled) {
Util::addScript(Application::APP_ID, 'reference');
}
}
}

View file

@ -15,6 +15,7 @@ use OCP\Collaboration\Reference\ADiscoverableReferenceProvider;
use OCP\Collaboration\Reference\IReference;
use OCP\Collaboration\Reference\Reference;
use OCP\IAppConfig;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUserManager;
@ -22,6 +23,7 @@ use OCP\Profile\IProfileManager;
class ProfilePickerReferenceProvider extends ADiscoverableReferenceProvider {
public const RICH_OBJECT_TYPE = 'profile_widget';
private bool $enabled;
public function __construct(
private IL10N $l10n,
@ -29,8 +31,16 @@ class ProfilePickerReferenceProvider extends ADiscoverableReferenceProvider {
private IUserManager $userManager,
private IAccountManager $accountManager,
private IProfileManager $profileManager,
private IAppConfig $appConfig,
private ?string $userId,
) {
$profileEnabledGlobally = $this->profileManager->isProfileEnabled();
$profilePickerEnabled = filter_var(
$this->appConfig->getValueString('settings', 'profile_picker_enabled', '1'),
FILTER_VALIDATE_BOOLEAN,
FILTER_NULL_ON_FAILURE,
);
$this->enabled = $profileEnabledGlobally && $profilePickerEnabled;
}
/**
@ -65,6 +75,9 @@ class ProfilePickerReferenceProvider extends ADiscoverableReferenceProvider {
* @inheritDoc
*/
public function matchReference(string $referenceText): bool {
if (!$this->enabled) {
return false;
}
return $this->getObjectId($referenceText) !== null;
}

View file

@ -53,6 +53,7 @@ class Server implements IDelegatedSettings {
// Profile page
$this->initialStateService->provideInitialState('profileEnabledGlobally', $this->profileManager->isProfileEnabled());
$this->initialStateService->provideInitialState('profileEnabledByDefault', $this->isProfileEnabledByDefault($this->config));
$this->initialStateService->provideInitialState('profilePickerEnabled', $this->isProfilePickerEnabled($this->config));
// Basic settings
$this->initialStateService->provideInitialState('restrictSystemTagsCreationToAdmin', $this->appConfig->getValueBool('systemtags', 'restrict_creation_to_admin', false));

View file

@ -11,9 +11,9 @@
{{ t('settings', 'Profile') }}
</h2>
<p class="settings-hint">
<NcNoteCard type="info">
{{ t('settings', 'Enable or disable profile by default for new accounts.') }}
</p>
</NcNoteCard>
<NcCheckboxRadioSwitch
v-model="initialProfileEnabledByDefault"
@ -21,6 +21,17 @@
@update:modelValue="onProfileDefaultChange">
{{ t('settings', 'Enable') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch
v-model="initialProfilePickerEnabled"
type="switch"
@update:modelValue="onProfilePickerChange">
{{ t('settings', 'Enable the profile picker') }}
</NcCheckboxRadioSwitch>
<NcNoteCard type="info">
{{ t('settings', 'Enable or disable the profile picker in the Smart Picker and the profile link previews.') }}
</NcNoteCard>
</div>
</template>
@ -28,22 +39,26 @@
import { showError } from '@nextcloud/dialogs'
import { loadState } from '@nextcloud/initial-state'
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
import logger from '../../logger.ts'
import { saveProfileDefault } from '../../service/ProfileService.js'
import { saveProfileDefault, saveProfilePicker } from '../../service/ProfileService.js'
import { validateBoolean } from '../../utils/validate.js'
const profileEnabledByDefault = loadState('settings', 'profileEnabledByDefault', true)
const profilePickerEnabled = loadState('settings', 'profilePickerEnabled', true)
export default {
name: 'ProfileSettings',
components: {
NcCheckboxRadioSwitch,
NcNoteCard,
},
data() {
return {
initialProfileEnabledByDefault: profileEnabledByDefault,
initialProfilePickerEnabled: profilePickerEnabled,
}
},
@ -59,6 +74,7 @@ export default {
const responseData = await saveProfileDefault(isEnabled)
this.handleResponse({
isEnabled,
key: 'initialProfileEnabledByDefault',
status: responseData.ocs?.meta?.status,
})
} catch (e) {
@ -69,9 +85,31 @@ export default {
}
},
handleResponse({ isEnabled, status, errorMessage, error }) {
async onProfilePickerChange(isEnabled) {
if (validateBoolean(isEnabled)) {
await this.updateProfilePicker(isEnabled)
}
},
async updateProfilePicker(isEnabled) {
try {
const responseData = await saveProfilePicker(isEnabled)
this.handleResponse({
isEnabled,
key: 'initialProfilePickerEnabled',
status: responseData.ocs?.meta?.status,
})
} catch (e) {
this.handleResponse({
errorMessage: t('settings', 'Unable to update profile picker setting'),
error: e,
})
}
},
handleResponse({ isEnabled, key, status, errorMessage, error }) {
if (status === 'ok') {
this.initialProfileEnabledByDefault = isEnabled
this[key] = isEnabled
} else {
showError(errorMessage)
logger.error(errorMessage, error)
@ -80,3 +118,9 @@ export default {
},
}
</script>
<style scoped lang="scss">
#profile-settings {
max-width: 600px;
}
</style>

View file

@ -52,3 +52,27 @@ export async function saveProfileDefault(isEnabled) {
return res.data
}
/**
* Save profile picker default
*
* @param {boolean} isEnabled the default
* @return {object}
*/
export async function saveProfilePicker(isEnabled) {
// Convert to string for compatibility
isEnabled = isEnabled ? '1' : '0'
const url = generateOcsUrl('/apps/provisioning_api/api/v1/config/apps/{appId}/{key}', {
appId: 'settings',
key: 'profile_picker_enabled',
})
await confirmPassword()
const res = await axios.post(url, {
value: isEnabled,
})
return res.data
}

View file

@ -48,7 +48,6 @@
"twofactor_totp",
"updatenotification",
"user_ldap",
"user_picker",
"user_status",
"viewer",
"weather_status",

View file

@ -19,4 +19,12 @@ trait TProfileHelper {
FILTER_NULL_ON_FAILURE,
);
}
protected function isProfilePickerEnabled(IConfig $config): ?bool {
return filter_var(
$config->getAppValue('settings', 'profile_picker_enabled', '1'),
FILTER_VALIDATE_BOOLEAN,
FILTER_NULL_ON_FAILURE,
);
}
}