diff --git a/help/custom-profile-fields.md b/help/custom-profile-fields.md index acb232647b..7ab91f2eb6 100644 --- a/help/custom-profile-fields.md +++ b/help/custom-profile-fields.md @@ -98,6 +98,26 @@ it out. {end_tabs} +## Configure whether users can edit custom profile fields + +{!admin-only.md!} + +You can configure whether users in your organization can modify their +own custom profile fields. For example, you may want to restrict editing +if syncing profile fields from an employee directory. + +{start_tabs} + +{settings_tab|profile-field-settings} + +1. In the **Actions** column, click the **pencil** () + icon for the profile field you want to configure. + +1. Toggle **Users can edit this field**. + +4. Click **Save changes**. + +{end_tabs} ## Profile field types diff --git a/web/src/custom_profile_fields_ui.ts b/web/src/custom_profile_fields_ui.ts index 72fb8efe5c..d2295b8ad3 100644 --- a/web/src/custom_profile_fields_ui.ts +++ b/web/src/custom_profile_fields_ui.ts @@ -10,7 +10,7 @@ import {$t} from "./i18n"; import * as people from "./people"; import * as pill_typeahead from "./pill_typeahead"; import * as settings_components from "./settings_components"; -import {realm} from "./state_data"; +import {current_user, realm} from "./state_data"; import * as typeahead_helper from "./typeahead_helper"; import type {UserPillWidget} from "./user_pill"; import * as user_pill from "./user_pill"; @@ -38,6 +38,7 @@ export function append_custom_profile_fields(element_id: string, user_id: number for (const field of all_custom_fields) { let field_value = people.get_custom_profile_data(user_id, field.id); + const editable_by_user = current_user.is_admin || field.editable_by_user; const is_select_field = field.type === all_field_types.SELECT.id; const field_choices = []; @@ -70,6 +71,7 @@ export function append_custom_profile_fields(element_id: string, user_id: number field_choices, for_manage_user_modal: element_id === "#edit-user-form .custom-profile-field-form", is_empty_required_field: field.required && !field_value.value, + editable_by_user, }); $(element_id).append($(html)); } diff --git a/web/src/settings_profile_fields.ts b/web/src/settings_profile_fields.ts index 17323963f8..83f5d7565a 100644 --- a/web/src/settings_profile_fields.ts +++ b/web/src/settings_profile_fields.ts @@ -242,6 +242,7 @@ function open_custom_profile_field_form_modal(): void { ":checked", ), required: $("#profile-field-required").is(":checked"), + editable_by_user: $("#profile_field_editable_by_user").is(":checked"), }; const url = "/json/realm/profile_fields"; const opts = { @@ -461,6 +462,7 @@ function open_edit_form_modal(this: HTMLElement): void { choices, display_in_profile_summary: field.display_in_profile_summary === true, required: field.required, + editable_by_user: field.editable_by_user, is_select_field: field.type === field_types.SELECT.id, is_external_account_field: field.type === field_types.EXTERNAL_ACCOUNT.id, valid_to_display_in_summary: is_valid_to_display_in_summary(field.type), diff --git a/web/src/state_data.ts b/web/src/state_data.ts index 2f607e5c35..cae7cc7a10 100644 --- a/web/src/state_data.ts +++ b/web/src/state_data.ts @@ -34,6 +34,7 @@ export type NarrowTerm = z.output; export const custom_profile_field_schema = z.object({ display_in_profile_summary: z.optional(z.boolean()), + editable_by_user: z.boolean(), field_data: z.string(), hint: z.string(), id: z.number(), diff --git a/web/src/tippyjs.ts b/web/src/tippyjs.ts index 52b627ed31..658271fe52 100644 --- a/web/src/tippyjs.ts +++ b/web/src/tippyjs.ts @@ -762,6 +762,18 @@ export function initialize(): void { }, }); + tippy.delegate("body", { + target: ".settings-profile-user-field.not-editable-by-user-input-wrapper", + content: $t({ + defaultMessage: + "You are not allowed to change this field. Contact an administrator to update it.", + }), + appendTo: () => document.body, + onHidden(instance) { + instance.destroy(); + }, + }); + tippy.delegate("body", { target: ".popover-contains-shift-hotkey", trigger: "mouseenter", diff --git a/web/styles/dark_theme.css b/web/styles/dark_theme.css index a9585b9d47..44cb8d2d50 100644 --- a/web/styles/dark_theme.css +++ b/web/styles/dark_theme.css @@ -409,6 +409,10 @@ .pill-container { border-style: solid; border-width: 1px; + + &.not-editable-by-user { + opacity: 0.5; + } } .settings-profile-user-field { diff --git a/web/styles/input_pill.css b/web/styles/input_pill.css index cd231b26d9..f1fcb332ff 100644 --- a/web/styles/input_pill.css +++ b/web/styles/input_pill.css @@ -164,6 +164,19 @@ perspective: 1000px; } } + + &.not-editable-by-user { + cursor: not-allowed; + background-color: hsl(0deg 0% 93%); + + .pill { + cursor: not-allowed; + } + + .exit { + display: none; + } + } } #compose-direct-recipient .pill-container { diff --git a/web/styles/settings.css b/web/styles/settings.css index cedd82b4fa..8b48621c50 100644 --- a/web/styles/settings.css +++ b/web/styles/settings.css @@ -1545,6 +1545,10 @@ $option_title_width: 180px; .datepicker { cursor: default; } + + & input[disabled].datepicker { + cursor: not-allowed; + } } #show_my_user_profile_modal { @@ -1988,6 +1992,11 @@ $option_title_width: 180px; cursor: default; opacity: 0.7; } + + /* Needed for settings_checkbox partial. */ + .inline { + display: inline; + } } #admin_users_table .deactivated_user, diff --git a/web/templates/settings/add_new_custom_profile_field_form.hbs b/web/templates/settings/add_new_custom_profile_field_form.hbs index baebc61925..9e9f5a68df 100644 --- a/web/templates/settings/add_new_custom_profile_field_form.hbs +++ b/web/templates/settings/add_new_custom_profile_field_form.hbs @@ -50,5 +50,11 @@ {{t 'Required field' }} + {{> settings_checkbox + prefix="profile_field_" + setting_name="editable_by_user" + is_checked=true + label=(t "Users can edit this field") + }} diff --git a/web/templates/settings/custom_user_profile_field.hbs b/web/templates/settings/custom_user_profile_field.hbs index 2dad7ac257..5f0d450c68 100644 --- a/web/templates/settings/custom_user_profile_field.hbs +++ b/web/templates/settings/custom_user_profile_field.hbs @@ -5,30 +5,30 @@ {{ field.hint }} - + {{#if is_long_text_field}} - {{ field_value.value }} + {{ field_value.value }} {{else if is_select_field}} - + {{#each field_choices}} {{ this.text }} {{/each}} {{else if is_user_field }} - - + + {{else if is_date_field }} - + value="{{ field_value.value }}" {{#unless editable_by_user}}disabled{{/unless}}/> + {{#if editable_by_user}}{{/if}} {{else if is_url_field }} - + {{else if is_pronouns_field}} - + {{else}} - + {{/if}} diff --git a/web/templates/settings/edit_custom_profile_field_form.hbs b/web/templates/settings/edit_custom_profile_field_form.hbs index 3e0655c373..53c3de95e0 100644 --- a/web/templates/settings/edit_custom_profile_field_form.hbs +++ b/web/templates/settings/edit_custom_profile_field_form.hbs @@ -52,5 +52,11 @@ {{t 'Required field' }} + {{> settings_checkbox + prefix="id-custom-profile-field-" + setting_name="editable-by-user" + is_checked= editable_by_user + label=(t "Users can edit this field") + }} {{/with}}