settings_org: Convert module to typescript.

This commit is contained in:
evykassirer 2024-10-02 22:13:43 -07:00 committed by Tim Abbott
parent 39137e67f7
commit 269066e10a
9 changed files with 367 additions and 245 deletions

View File

@ -47,7 +47,7 @@ organization in Zulip). The following files are involved in the process:
- `web/templates/settings/organization_permissions_admin.hbs`: defines - `web/templates/settings/organization_permissions_admin.hbs`: defines
the structure of the admin permissions page (checkboxes for each organization the structure of the admin permissions page (checkboxes for each organization
permission setting). permission setting).
- `web/src/settings_org.js`: handles organization setting form submission. - `web/src/settings_org.ts`: handles organization setting form submission.
- `web/src/server_events_dispatch.js`: handles events coming from the server - `web/src/server_events_dispatch.js`: handles events coming from the server
(ex: pushing an organization change to other open browsers and updating (ex: pushing an organization change to other open browsers and updating
the application's state). the application's state).
@ -556,7 +556,7 @@ Then add the new form control in `web/src/admin.js`.
``` ```
The JavaScript code for organization settings and permissions can be found in The JavaScript code for organization settings and permissions can be found in
`web/src/settings_org.js`. `web/src/settings_org.ts`.
In frontend, we have split the `property_types` into three objects: In frontend, we have split the `property_types` into three objects:
@ -667,7 +667,7 @@ frontend tests: [node-based unit tests](../testing/testing-with-node.md) and
[Puppeteer end-to-end tests](../testing/testing-with-puppeteer.md). [Puppeteer end-to-end tests](../testing/testing-with-puppeteer.md).
At the minimum, if you created a new function to update UI in At the minimum, if you created a new function to update UI in
`settings_org.js`, you will need to mock that function in `settings_org.ts`, you will need to mock that function in
`web/tests/dispatch.test.js`. Add the name of the UI `web/tests/dispatch.test.js`. Add the name of the UI
function you created to the following object with `noop` as the value: function you created to the following object with `noop` as the value:

View File

@ -218,7 +218,7 @@ EXEMPT_FILES = make_set(
"web/src/settings_linkifiers.ts", "web/src/settings_linkifiers.ts",
"web/src/settings_muted_users.ts", "web/src/settings_muted_users.ts",
"web/src/settings_notifications.ts", "web/src/settings_notifications.ts",
"web/src/settings_org.js", "web/src/settings_org.ts",
"web/src/settings_panel_menu.js", "web/src/settings_panel_menu.js",
"web/src/settings_playgrounds.ts", "web/src/settings_playgrounds.ts",
"web/src/settings_preferences.ts", "web/src/settings_preferences.ts",

View File

@ -47,7 +47,7 @@ type SettingOptionValue = {
description: string; description: string;
}; };
type SettingOptionValueWithKey = SettingOptionValue & {key: string}; export type SettingOptionValueWithKey = SettingOptionValue & {key: string};
export function get_sorted_options_list( export function get_sorted_options_list(
option_values_object: Record<string, SettingOptionValue>, option_values_object: Record<string, SettingOptionValue>,
@ -80,10 +80,13 @@ export function get_sorted_options_list(
return options_list; return options_list;
} }
type MessageTimeLimitSetting = export type MessageMoveTimeLimitSetting =
| "realm_message_content_edit_limit_seconds"
| "realm_move_messages_between_streams_limit_seconds"
| "realm_move_messages_within_stream_limit_seconds" | "realm_move_messages_within_stream_limit_seconds"
| "realm_move_messages_between_streams_limit_seconds";
export type MessageTimeLimitSetting =
| MessageMoveTimeLimitSetting
| "realm_message_content_edit_limit_seconds"
| "realm_message_content_delete_limit_seconds"; | "realm_message_content_delete_limit_seconds";
export function get_realm_time_limits_in_minutes(property: MessageTimeLimitSetting): string { export function get_realm_time_limits_in_minutes(property: MessageTimeLimitSetting): string {
@ -239,7 +242,7 @@ export const simple_dropdown_realm_settings_schema = realm_schema.pick({
realm_edit_topic_policy: true, realm_edit_topic_policy: true,
realm_org_type: true, realm_org_type: true,
}); });
type SimpleDropdownRealmSettings = z.infer<typeof simple_dropdown_realm_settings_schema>; export type SimpleDropdownRealmSettings = z.infer<typeof simple_dropdown_realm_settings_schema>;
export function set_property_dropdown_value( export function set_property_dropdown_value(
property_name: keyof SimpleDropdownRealmSettings, property_name: keyof SimpleDropdownRealmSettings,
@ -654,7 +657,7 @@ export function change_save_button_state($element: JQuery, state: string): void
}); });
} }
function get_input_type($input_elem: JQuery, input_type?: string): string { export function get_input_type($input_elem: JQuery, input_type?: string): string {
if (input_type !== undefined && ["boolean", "string", "number"].includes(input_type)) { if (input_type !== undefined && ["boolean", "string", "number"].includes(input_type)) {
return input_type; return input_type;
} }

View File

@ -234,7 +234,7 @@ export function set_up(settings_panel: SettingsPanel): void {
if (for_realm_settings) { if (for_realm_settings) {
// For the realm-level defaults page, we use the common // For the realm-level defaults page, we use the common
// settings_org.js handlers, so we can return early here. // settings_org.ts handlers, so we can return early here.
return; return;
} }

View File

@ -1,4 +1,6 @@
import $ from "jquery"; import $ from "jquery";
import assert from "minimalistic-assert";
import {z} from "zod";
import render_settings_deactivate_realm_modal from "../templates/confirm_dialog/confirm_deactivate_realm.hbs"; import render_settings_deactivate_realm_modal from "../templates/confirm_dialog/confirm_deactivate_realm.hbs";
import render_settings_admin_auth_methods_list from "../templates/settings/admin_auth_methods_list.hbs"; import render_settings_admin_auth_methods_list from "../templates/settings/admin_auth_methods_list.hbs";
@ -16,6 +18,14 @@ import * as pygments_data from "./pygments_data";
import * as realm_icon from "./realm_icon"; import * as realm_icon from "./realm_icon";
import * as realm_logo from "./realm_logo"; import * as realm_logo from "./realm_logo";
import {realm_user_settings_defaults} from "./realm_user_settings_defaults"; import {realm_user_settings_defaults} from "./realm_user_settings_defaults";
import {
type MessageMoveTimeLimitSetting,
type SettingOptionValueWithKey,
realm_setting_property_schema,
realm_user_settings_default_properties_schema,
simple_dropdown_realm_settings_schema,
stream_settings_property_schema,
} from "./settings_components";
import * as settings_components from "./settings_components"; import * as settings_components from "./settings_components";
import * as settings_config from "./settings_config"; import * as settings_config from "./settings_config";
import * as settings_data from "./settings_data"; import * as settings_data from "./settings_data";
@ -23,23 +33,27 @@ import * as settings_notifications from "./settings_notifications";
import * as settings_preferences from "./settings_preferences"; import * as settings_preferences from "./settings_preferences";
import * as settings_realm_domains from "./settings_realm_domains"; import * as settings_realm_domains from "./settings_realm_domains";
import * as settings_ui from "./settings_ui"; import * as settings_ui from "./settings_ui";
import {current_user, realm} from "./state_data"; import {current_user, group_setting_value_schema, realm, realm_schema} from "./state_data";
import type {Realm} from "./state_data";
import * as stream_settings_data from "./stream_settings_data"; import * as stream_settings_data from "./stream_settings_data";
import type {StreamSubscription} from "./sub_store";
import type {HTMLSelectOneElement} from "./types";
import * as ui_report from "./ui_report"; import * as ui_report from "./ui_report";
import * as user_groups from "./user_groups"; import * as user_groups from "./user_groups";
import type {UserGroup, UserGroupForDropdownListWidget} from "./user_groups";
import * as util from "./util"; import * as util from "./util";
const meta = { const meta = {
loaded: false, loaded: false,
}; };
export function reset() { export function reset(): void {
meta.loaded = false; meta.loaded = false;
} }
const DISABLED_STATE_ID = -1; const DISABLED_STATE_ID = -1;
export function maybe_disable_widgets() { export function maybe_disable_widgets(): void {
if (current_user.is_owner) { if (current_user.is_owner) {
return; return;
} }
@ -91,31 +105,42 @@ export function maybe_disable_widgets() {
.addClass("control-label-disabled"); .addClass("control-label-disabled");
} }
export function get_organization_settings_options() { type OrganizationSettingsOptions = {
const options = {}; common_policy_values: SettingOptionValueWithKey[];
options.common_policy_values = settings_components.get_sorted_options_list( wildcard_mention_policy_values: SettingOptionValueWithKey[];
common_message_policy_values: SettingOptionValueWithKey[];
invite_to_realm_policy_values: SettingOptionValueWithKey[];
edit_topic_policy_values: SettingOptionValueWithKey[];
move_messages_between_streams_policy_values: SettingOptionValueWithKey[];
};
export function get_organization_settings_options(): OrganizationSettingsOptions {
return {
common_policy_values: settings_components.get_sorted_options_list(
settings_config.common_policy_values, settings_config.common_policy_values,
); ),
options.wildcard_mention_policy_values = settings_components.get_sorted_options_list( wildcard_mention_policy_values: settings_components.get_sorted_options_list(
settings_config.wildcard_mention_policy_values, settings_config.wildcard_mention_policy_values,
); ),
options.common_message_policy_values = settings_components.get_sorted_options_list( common_message_policy_values: settings_components.get_sorted_options_list(
settings_config.common_message_policy_values, settings_config.common_message_policy_values,
); ),
options.invite_to_realm_policy_values = settings_components.get_sorted_options_list( invite_to_realm_policy_values: settings_components.get_sorted_options_list(
settings_config.email_invite_to_realm_policy_values, settings_config.email_invite_to_realm_policy_values,
); ),
options.edit_topic_policy_values = settings_components.get_sorted_options_list( edit_topic_policy_values: settings_components.get_sorted_options_list(
settings_config.edit_topic_policy_values, settings_config.edit_topic_policy_values,
); ),
options.move_messages_between_streams_policy_values = move_messages_between_streams_policy_values: settings_components.get_sorted_options_list(
settings_components.get_sorted_options_list(
settings_config.move_messages_between_streams_policy_values, settings_config.move_messages_between_streams_policy_values,
); ),
return options; };
} }
export function get_org_type_dropdown_options() { type DefinedOrgTypeValues = typeof settings_config.defined_org_type_values;
type AllOrgTypeValues = typeof settings_config.all_org_type_values;
export function get_org_type_dropdown_options(): DefinedOrgTypeValues | AllOrgTypeValues {
const current_org_type = realm.realm_org_type; const current_org_type = realm.realm_org_type;
if (current_org_type !== 0) { if (current_org_type !== 0) {
return settings_config.defined_org_type_values; return settings_config.defined_org_type_values;
@ -123,16 +148,9 @@ export function get_org_type_dropdown_options() {
return settings_config.all_org_type_values; return settings_config.all_org_type_values;
} }
const simple_dropdown_properties = [ const simple_dropdown_properties = simple_dropdown_realm_settings_schema.keyof().options;
"realm_invite_to_stream_policy",
"realm_invite_to_realm_policy",
"realm_wildcard_mention_policy",
"realm_move_messages_between_streams_policy",
"realm_edit_topic_policy",
"realm_org_type",
];
function set_realm_waiting_period_setting() { function set_realm_waiting_period_setting(): void {
const setting_value = realm.realm_waiting_period_threshold; const setting_value = realm.realm_waiting_period_threshold;
const valid_limit_values = settings_config.waiting_period_threshold_dropdown_values.map( const valid_limit_values = settings_config.waiting_period_threshold_dropdown_values.map(
(x) => x.code, (x) => x.code,
@ -151,7 +169,7 @@ function set_realm_waiting_period_setting() {
); );
} }
function update_jitsi_server_url_custom_input(dropdown_val) { function update_jitsi_server_url_custom_input(dropdown_val: string): void {
const custom_input = "id_realm_jitsi_server_url_custom_input"; const custom_input = "id_realm_jitsi_server_url_custom_input";
settings_components.change_element_block_display_property( settings_components.change_element_block_display_property(
custom_input, custom_input,
@ -166,7 +184,7 @@ function update_jitsi_server_url_custom_input(dropdown_val) {
$custom_input_elem.val(realm.realm_jitsi_server_url ?? ""); $custom_input_elem.val(realm.realm_jitsi_server_url ?? "");
} }
function set_jitsi_server_url_dropdown() { function set_jitsi_server_url_dropdown(): void {
if (!settings_components.is_video_chat_provider_jitsi_meet()) { if (!settings_components.is_video_chat_provider_jitsi_meet()) {
$("#realm_jitsi_server_url_setting").hide(); $("#realm_jitsi_server_url_setting").hide();
return; return;
@ -183,19 +201,19 @@ function set_jitsi_server_url_dropdown() {
update_jitsi_server_url_custom_input(dropdown_val); update_jitsi_server_url_custom_input(dropdown_val);
} }
function set_video_chat_provider_dropdown() { function set_video_chat_provider_dropdown(): void {
const chat_provider_id = realm.realm_video_chat_provider; const chat_provider_id = realm.realm_video_chat_provider;
$("#id_realm_video_chat_provider").val(chat_provider_id); $("#id_realm_video_chat_provider").val(chat_provider_id);
set_jitsi_server_url_dropdown(); set_jitsi_server_url_dropdown();
} }
function set_giphy_rating_dropdown() { function set_giphy_rating_dropdown(): void {
const rating_id = realm.realm_giphy_rating; const rating_id = realm.realm_giphy_rating;
$("#id_realm_giphy_rating").val(rating_id); $("#id_realm_giphy_rating").val(rating_id);
} }
function update_message_edit_sub_settings(is_checked) { function update_message_edit_sub_settings(is_checked: boolean): void {
settings_ui.disable_sub_setting_onchange( settings_ui.disable_sub_setting_onchange(
is_checked, is_checked,
"id_realm_message_content_edit_limit_seconds", "id_realm_message_content_edit_limit_seconds",
@ -208,12 +226,18 @@ function update_message_edit_sub_settings(is_checked) {
); );
} }
function set_msg_edit_limit_dropdown() { function set_msg_edit_limit_dropdown(): void {
settings_components.set_time_limit_setting("realm_message_content_edit_limit_seconds"); settings_components.set_time_limit_setting("realm_message_content_edit_limit_seconds");
} }
function message_move_limit_setting_enabled(related_setting_name) { function message_move_limit_setting_enabled(
const setting_value = Number.parseInt($(`#id_${CSS.escape(related_setting_name)}`).val(), 10); related_setting_name: "realm_edit_topic_policy" | "realm_move_messages_between_streams_policy",
): boolean {
const setting_value_string = $<HTMLSelectOneElement>(
`select:not(multiple)#id_${CSS.escape(related_setting_name)}`,
).val();
assert(setting_value_string !== undefined);
const setting_value = Number.parseInt(setting_value_string, 10);
let settings_options; let settings_options;
if (related_setting_name === "realm_edit_topic_policy") { if (related_setting_name === "realm_edit_topic_policy") {
@ -237,15 +261,18 @@ function message_move_limit_setting_enabled(related_setting_name) {
return true; return true;
} }
function enable_or_disable_related_message_move_time_limit_setting(setting_name, disable_setting) { function enable_or_disable_related_message_move_time_limit_setting(
setting_name: MessageMoveTimeLimitSetting,
disable_setting: boolean,
): void {
const $setting_elem = $(`#id_${CSS.escape(setting_name)}`); const $setting_elem = $(`#id_${CSS.escape(setting_name)}`);
const $custom_input_elem = $setting_elem.parent().find(".time-limit-custom-input"); const $custom_input_elem = $setting_elem.parent().find(".time-limit-custom-input");
settings_ui.disable_sub_setting_onchange(disable_setting, $setting_elem.attr("id"), true); settings_ui.disable_sub_setting_onchange(disable_setting, $setting_elem.attr("id")!, true);
settings_ui.disable_sub_setting_onchange(disable_setting, $custom_input_elem.attr("id"), true); settings_ui.disable_sub_setting_onchange(disable_setting, $custom_input_elem.attr("id")!, true);
} }
function set_msg_move_limit_setting(property_name) { function set_msg_move_limit_setting(property_name: MessageMoveTimeLimitSetting): void {
settings_components.set_time_limit_setting(property_name); settings_components.set_time_limit_setting(property_name);
let disable_setting; let disable_setting;
@ -259,7 +286,7 @@ function set_msg_move_limit_setting(property_name) {
enable_or_disable_related_message_move_time_limit_setting(property_name, disable_setting); enable_or_disable_related_message_move_time_limit_setting(property_name, disable_setting);
} }
function message_delete_limit_setting_enabled() { function message_delete_limit_setting_enabled(): boolean {
// This function is used to check whether the time-limit setting // This function is used to check whether the time-limit setting
// should be enabled. The setting is disabled when every user // should be enabled. The setting is disabled when every user
// who is allowed to delete their own messages is also allowed // who is allowed to delete their own messages is also allowed
@ -272,14 +299,17 @@ function message_delete_limit_setting_enabled() {
settings_components.get_dropdown_list_widget_setting_value( settings_components.get_dropdown_list_widget_setting_value(
$("#id_realm_can_delete_any_message_group"), $("#id_realm_can_delete_any_message_group"),
); );
assert(typeof realm_can_delete_any_message_group_id === "number");
const can_delete_any_message_subgroups = user_groups.get_recursive_subgroups( const can_delete_any_message_subgroups = user_groups.get_recursive_subgroups(
user_groups.get_user_group_from_id(realm_can_delete_any_message_group_id), user_groups.get_user_group_from_id(realm_can_delete_any_message_group_id),
); );
assert(can_delete_any_message_subgroups !== undefined);
can_delete_any_message_subgroups.add(realm_can_delete_any_message_group_id); can_delete_any_message_subgroups.add(realm_can_delete_any_message_group_id);
assert(typeof realm_can_delete_own_message_group_id === "number");
return !can_delete_any_message_subgroups.has(realm_can_delete_own_message_group_id); return !can_delete_any_message_subgroups.has(realm_can_delete_own_message_group_id);
} }
function check_disable_message_delete_limit_setting_dropdown() { function check_disable_message_delete_limit_setting_dropdown(): void {
settings_ui.disable_sub_setting_onchange( settings_ui.disable_sub_setting_onchange(
message_delete_limit_setting_enabled(), message_delete_limit_setting_enabled(),
"id_realm_message_content_delete_limit_seconds", "id_realm_message_content_delete_limit_seconds",
@ -294,11 +324,11 @@ function check_disable_message_delete_limit_setting_dropdown() {
} }
} }
function set_msg_delete_limit_dropdown() { function set_msg_delete_limit_dropdown(): void {
settings_components.set_time_limit_setting("realm_message_content_delete_limit_seconds"); settings_components.set_time_limit_setting("realm_message_content_delete_limit_seconds");
} }
function get_dropdown_value_for_message_retention_setting(setting_value) { function get_dropdown_value_for_message_retention_setting(setting_value: number | null): string {
if (setting_value === settings_config.retain_message_forever) { if (setting_value === settings_config.retain_message_forever) {
return "unlimited"; return "unlimited";
} }
@ -310,14 +340,15 @@ function get_dropdown_value_for_message_retention_setting(setting_value) {
return "custom_period"; return "custom_period";
} }
export function set_message_retention_setting_dropdown(sub) { export function set_message_retention_setting_dropdown(sub: StreamSubscription | undefined): void {
let property_name = "realm_message_retention_days"; let property_name: "message_retention_days" | "realm_message_retention_days";
let setting_value; let setting_value: number | null;
if (sub !== undefined) { if (sub !== undefined) {
property_name = "message_retention_days"; property_name = "message_retention_days";
setting_value = settings_components.get_stream_settings_property_value(property_name, sub); setting_value = sub.message_retention_days;
} else { } else {
setting_value = settings_components.get_realm_settings_property_value(property_name); property_name = "realm_message_retention_days";
setting_value = realm.realm_message_retention_days;
} }
const dropdown_val = get_dropdown_value_for_message_retention_setting(setting_value); const dropdown_val = get_dropdown_value_for_message_retention_setting(setting_value);
@ -329,19 +360,21 @@ export function set_message_retention_setting_dropdown(sub) {
.find(".message-retention-setting-custom-input") .find(".message-retention-setting-custom-input")
.val(""); .val("");
if (dropdown_val === "custom_period") { if (dropdown_val === "custom_period") {
assert(setting_value !== null);
$custom_input_elem.val(setting_value); $custom_input_elem.val(setting_value);
} }
settings_components.change_element_block_display_property( settings_components.change_element_block_display_property(
$custom_input_elem.attr("id"), $custom_input_elem.attr("id")!,
dropdown_val === "custom_period", dropdown_val === "custom_period",
); );
} }
function set_org_join_restrictions_dropdown() { function set_org_join_restrictions_dropdown(): void {
const value = settings_components.get_realm_settings_property_value( const value = settings_components.get_realm_settings_property_value(
"realm_org_join_restrictions", "realm_org_join_restrictions",
); );
assert(typeof value === "string");
$("#id_realm_org_join_restrictions").val(value); $("#id_realm_org_join_restrictions").val(value);
settings_components.change_element_block_display_property( settings_components.change_element_block_display_property(
"allowed_domains_label", "allowed_domains_label",
@ -349,21 +382,21 @@ function set_org_join_restrictions_dropdown() {
); );
} }
function set_message_content_in_email_notifications_visibility() { function set_message_content_in_email_notifications_visibility(): void {
settings_components.change_element_block_display_property( settings_components.change_element_block_display_property(
"message_content_in_email_notifications_label", "message_content_in_email_notifications_label",
realm.realm_message_content_allowed_in_email_notifications, realm.realm_message_content_allowed_in_email_notifications,
); );
} }
function set_digest_emails_weekday_visibility() { function set_digest_emails_weekday_visibility(): void {
settings_components.change_element_block_display_property( settings_components.change_element_block_display_property(
"id_realm_digest_weekday", "id_realm_digest_weekday",
realm.realm_digest_emails_enabled, realm.realm_digest_emails_enabled,
); );
} }
function set_create_web_public_stream_dropdown_visibility() { function set_create_web_public_stream_dropdown_visibility(): void {
settings_components.change_element_block_display_property( settings_components.change_element_block_display_property(
"id_realm_can_create_web_public_channel_group", "id_realm_can_create_web_public_channel_group",
realm.server_web_public_streams_enabled && realm.server_web_public_streams_enabled &&
@ -372,7 +405,7 @@ function set_create_web_public_stream_dropdown_visibility() {
); );
} }
export function check_disable_direct_message_initiator_group_dropdown(current_value) { export function check_disable_direct_message_initiator_group_dropdown(current_value: number): void {
if (user_groups.is_empty_group(current_value)) { if (user_groups.is_empty_group(current_value)) {
$("#realm_direct_message_initiator_group_widget").prop("disabled", true); $("#realm_direct_message_initiator_group_widget").prop("disabled", true);
} else { } else {
@ -380,7 +413,9 @@ export function check_disable_direct_message_initiator_group_dropdown(current_va
} }
} }
export function populate_realm_domains_label(realm_domains) { export function populate_realm_domains_label(
realm_domains: {domain: string; allow_subdomains: boolean}[],
): void {
if (!meta.loaded) { if (!meta.loaded) {
return; return;
} }
@ -395,7 +430,7 @@ export function populate_realm_domains_label(realm_domains) {
$("#allowed_domains_label").text($t({defaultMessage: "Allowed domains: {domains}"}, {domains})); $("#allowed_domains_label").text($t({defaultMessage: "Allowed domains: {domains}"}, {domains}));
} }
function can_configure_auth_methods() { function can_configure_auth_methods(): boolean {
if (settings_data.user_email_not_configured()) { if (settings_data.user_email_not_configured()) {
return false; return false;
} }
@ -405,7 +440,7 @@ function can_configure_auth_methods() {
return false; return false;
} }
export function populate_auth_methods(auth_method_to_bool_map) { export function populate_auth_methods(auth_method_to_bool_map: Record<string, boolean>): void {
if (!meta.loaded) { if (!meta.loaded) {
return; return;
} }
@ -421,7 +456,7 @@ export function populate_auth_methods(auth_method_to_bool_map) {
// by request, as an exception) - the organization should be able to disable it // by request, as an exception) - the organization should be able to disable it
// if they don't want it anymore. // if they don't want it anymore.
const cant_be_enabled = const cant_be_enabled =
!realm.realm_authentication_methods[auth_method].available && !value; !realm.realm_authentication_methods[auth_method]!.available && !value;
const render_args = { const render_args = {
method: auth_method, method: auth_method,
@ -434,21 +469,23 @@ export function populate_auth_methods(auth_method_to_bool_map) {
// 1) It contains at least one allowed symbol // 1) It contains at least one allowed symbol
// 2) No two auth method names are identical after this allowlist filtering // 2) No two auth method names are identical after this allowlist filtering
prefix: "id_authmethod" + auth_method.toLowerCase().replaceAll(/[^\da-z]/g, "") + "_", prefix: "id_authmethod" + auth_method.toLowerCase().replaceAll(/[^\da-z]/g, "") + "_",
...(cant_be_enabled && {
unavailable_reason:
realm.realm_authentication_methods[auth_method]!.unavailable_reason,
}),
}; };
if (cant_be_enabled) {
render_args.unavailable_reason =
realm.realm_authentication_methods[auth_method].unavailable_reason;
}
rendered_auth_method_rows += render_settings_admin_auth_methods_list(render_args); rendered_auth_method_rows += render_settings_admin_auth_methods_list(render_args);
} }
$auth_methods_list.html(rendered_auth_method_rows); $auth_methods_list.html(rendered_auth_method_rows);
} }
function update_dependent_subsettings(property_name) { function update_dependent_subsettings(property_name: string): void {
if (simple_dropdown_properties.includes(property_name)) { const parsed_property_name = simple_dropdown_realm_settings_schema
settings_components.set_property_dropdown_value(property_name); .keyof()
.safeParse(property_name);
if (parsed_property_name.success) {
settings_components.set_property_dropdown_value(parsed_property_name.data);
return; return;
} }
@ -490,10 +527,12 @@ function update_dependent_subsettings(property_name) {
} }
} }
export function discard_realm_property_element_changes(elem) { export function discard_realm_property_element_changes(elem: HTMLElement): void {
const $elem = $(elem); const $elem = $(elem);
const property_name = settings_components.extract_property_name($elem); const property_name = settings_components.extract_property_name($elem);
const property_value = settings_components.get_realm_settings_property_value(property_name); const property_value = settings_components.get_realm_settings_property_value(
realm_setting_property_schema.parse(property_name),
);
switch (property_name) { switch (property_name) {
case "realm_authentication_methods": case "realm_authentication_methods":
@ -517,21 +556,26 @@ export function discard_realm_property_element_changes(elem) {
case "realm_can_delete_any_message_group": case "realm_can_delete_any_message_group":
case "realm_can_delete_own_message_group": case "realm_can_delete_own_message_group":
case "realm_can_manage_all_groups": case "realm_can_manage_all_groups":
assert(typeof property_value === "string" || typeof property_value === "number");
settings_components.set_dropdown_list_widget_setting_value( settings_components.set_dropdown_list_widget_setting_value(
property_name, property_name,
property_value, property_value,
); );
break; break;
case "realm_default_language": case "realm_default_language":
assert(typeof property_value === "string");
$("#org-notifications .language_selection_widget .language_selection_button span").attr( $("#org-notifications .language_selection_widget .language_selection_button span").attr(
"data-language-code", "data-language-code",
property_value, property_value,
); );
$("#org-notifications .language_selection_widget .language_selection_button span").text( $("#org-notifications .language_selection_widget .language_selection_button span").text(
get_language_name(property_value), // We know this is defined, since we got the `property_value` from a dropdown
// of valid language options.
get_language_name(property_value)!,
); );
break; break;
case "realm_org_type": case "realm_org_type":
assert(typeof property_value === "number");
settings_components.set_input_element_value($elem, property_value); settings_components.set_input_element_value($elem, property_value);
// Remove 'unspecified' option (value=0) from realm_org_type // Remove 'unspecified' option (value=0) from realm_org_type
// dropdown menu options whenever realm.realm_org_type // dropdown menu options whenever realm.realm_org_type
@ -562,7 +606,10 @@ export function discard_realm_property_element_changes(elem) {
break; break;
default: default:
if (property_value !== undefined) { if (property_value !== undefined) {
settings_components.set_input_element_value($elem, property_value); const validated_property_value = z
.union([z.string(), z.number(), z.boolean()])
.parse(property_value);
settings_components.set_input_element_value($elem, validated_property_value);
} else { } else {
blueslip.error("Element refers to unknown property", {property_name}); blueslip.error("Element refers to unknown property", {property_name});
} }
@ -570,21 +617,26 @@ export function discard_realm_property_element_changes(elem) {
update_dependent_subsettings(property_name); update_dependent_subsettings(property_name);
} }
export function discard_stream_property_element_changes(elem, sub) { export function discard_stream_property_element_changes(
elem: HTMLElement,
sub: StreamSubscription,
): void {
const $elem = $(elem); const $elem = $(elem);
const property_name = settings_components.extract_property_name($elem); const property_name = settings_components.extract_property_name($elem);
const property_value = settings_components.get_stream_settings_property_value( const property_value = settings_components.get_stream_settings_property_value(
property_name, stream_settings_property_schema.parse(property_name),
sub, sub,
); );
switch (property_name) { switch (property_name) {
case "can_remove_subscribers_group": case "can_remove_subscribers_group":
assert(typeof property_value === "number");
settings_components.set_dropdown_list_widget_setting_value( settings_components.set_dropdown_list_widget_setting_value(
property_name, property_name,
property_value, property_value,
); );
break; break;
case "stream_privacy": { case "stream_privacy": {
assert(typeof property_value === "string");
$elem.find(`input[value='${CSS.escape(property_value)}']`).prop("checked", true); $elem.find(`input[value='${CSS.escape(property_value)}']`).prop("checked", true);
// Hide stream privacy warning banner // Hide stream privacy warning banner
@ -601,7 +653,10 @@ export function discard_stream_property_element_changes(elem, sub) {
break; break;
default: default:
if (property_value !== undefined) { if (property_value !== undefined) {
settings_components.set_input_element_value($elem, property_value); const validated_property_value = z
.union([z.string(), z.number(), z.boolean()])
.parse(property_value);
settings_components.set_input_element_value($elem, validated_property_value);
} else { } else {
blueslip.error("Element refers to unknown property", {property_name}); blueslip.error("Element refers to unknown property", {property_name});
} }
@ -609,32 +664,41 @@ export function discard_stream_property_element_changes(elem, sub) {
update_dependent_subsettings(property_name); update_dependent_subsettings(property_name);
} }
export function discard_group_property_element_changes(elem, group) { export function discard_group_property_element_changes(elem: HTMLElement, group: UserGroup): void {
const $elem = $(elem); const $elem = $(elem);
const property_name = settings_components.extract_property_name($elem); const property_name = settings_components.extract_property_name($elem);
const property_value = settings_components.get_group_property_value(property_name, group); const property_value = settings_components.get_group_property_value(
user_groups.user_group_schema.keyof().parse(property_name),
group,
);
const group_widget_settings = [...settings_components.group_setting_widget_map.keys()]; const group_widget_settings = [...settings_components.group_setting_widget_map.keys()];
if (property_name === "can_mention_group") { if (property_name === "can_mention_group") {
assert(typeof property_value === "number" || typeof property_value === "string");
settings_components.set_dropdown_list_widget_setting_value(property_name, property_value); settings_components.set_dropdown_list_widget_setting_value(property_name, property_value);
} else if (group_widget_settings.includes(property_name)) { } else if (group_widget_settings.includes(property_name)) {
const pill_widget = settings_components.get_group_setting_widget(property_name); const pill_widget = settings_components.get_group_setting_widget(property_name);
settings_components.set_group_setting_widget_value(pill_widget, property_value); assert(pill_widget !== null);
} else if (property_value !== undefined) { settings_components.set_group_setting_widget_value(
settings_components.set_input_element_value($elem, property_value); pill_widget,
group_setting_value_schema.parse(property_value),
);
} else { } else {
blueslip.error("Element refers to unknown property", {property_name}); blueslip.error("Element refers to unknown property", {property_name});
} }
update_dependent_subsettings(property_name); update_dependent_subsettings(property_name);
} }
export function discard_realm_default_property_element_changes(elem) { export function discard_realm_default_property_element_changes(elem: HTMLElement): void {
const $elem = $(elem); const $elem = $(elem);
const property_name = settings_components.extract_property_name($elem, true); const property_name = realm_user_settings_default_properties_schema.parse(
settings_components.extract_property_name($elem, true),
);
const property_value = const property_value =
settings_components.get_realm_default_setting_property_value(property_name); settings_components.get_realm_default_setting_property_value(property_name);
switch (property_name) { switch (property_name) {
case "notification_sound": case "notification_sound":
assert(typeof property_value === "string");
audible_notifications.update_notification_sound_source( audible_notifications.update_notification_sound_source(
$("audio#realm-default-notification-sound-audio"), $("audio#realm-default-notification-sound-audio"),
{ {
@ -647,7 +711,9 @@ export function discard_realm_default_property_element_changes(elem) {
case "user_list_style": case "user_list_style":
// Because this widget has a radio button structure, it // Because this widget has a radio button structure, it
// needs custom reset code. // needs custom reset code.
$elem.find(`input[value='${CSS.escape(property_value)}']`).prop("checked", true); $elem
.find(`input[value='${CSS.escape(property_value.toString())}']`)
.prop("checked", true);
break; break;
case "email_notifications_batching_period_seconds": case "email_notifications_batching_period_seconds":
case "email_notification_batching_period_edit_minutes": case "email_notification_batching_period_edit_minutes":
@ -658,7 +724,10 @@ export function discard_realm_default_property_element_changes(elem) {
break; break;
default: default:
if (property_value !== undefined) { if (property_value !== undefined) {
settings_components.set_input_element_value($elem, property_value); const validated_property_value = z
.union([z.string(), z.number(), z.boolean()])
.parse(property_value);
settings_components.set_input_element_value($elem, validated_property_value);
} else { } else {
blueslip.error("Element refers to unknown property", {property_name}); blueslip.error("Element refers to unknown property", {property_name});
} }
@ -666,7 +735,7 @@ export function discard_realm_default_property_element_changes(elem) {
update_dependent_subsettings(property_name); update_dependent_subsettings(property_name);
} }
function discard_realm_settings_subsection_changes($subsection) { function discard_realm_settings_subsection_changes($subsection: JQuery): void {
for (const elem of settings_components.get_subsection_property_elements($subsection)) { for (const elem of settings_components.get_subsection_property_elements($subsection)) {
discard_realm_property_element_changes(elem); discard_realm_property_element_changes(elem);
} }
@ -674,7 +743,10 @@ function discard_realm_settings_subsection_changes($subsection) {
settings_components.change_save_button_state($save_btn_controls, "discarded"); settings_components.change_save_button_state($save_btn_controls, "discarded");
} }
export function discard_stream_settings_subsection_changes($subsection, sub) { export function discard_stream_settings_subsection_changes(
$subsection: JQuery,
sub: StreamSubscription,
): void {
for (const elem of settings_components.get_subsection_property_elements($subsection)) { for (const elem of settings_components.get_subsection_property_elements($subsection)) {
discard_stream_property_element_changes(elem, sub); discard_stream_property_element_changes(elem, sub);
} }
@ -682,7 +754,10 @@ export function discard_stream_settings_subsection_changes($subsection, sub) {
settings_components.change_save_button_state($save_btn_controls, "discarded"); settings_components.change_save_button_state($save_btn_controls, "discarded");
} }
export function discard_group_settings_subsection_changes($subsection, group) { export function discard_group_settings_subsection_changes(
$subsection: JQuery,
group: UserGroup,
): void {
for (const elem of settings_components.get_subsection_property_elements($subsection)) { for (const elem of settings_components.get_subsection_property_elements($subsection)) {
discard_group_property_element_changes(elem, group); discard_group_property_element_changes(elem, group);
} }
@ -690,7 +765,7 @@ export function discard_group_settings_subsection_changes($subsection, group) {
settings_components.change_save_button_state($save_btn_controls, "discarded"); settings_components.change_save_button_state($save_btn_controls, "discarded");
} }
export function discard_realm_default_settings_subsection_changes($subsection) { export function discard_realm_default_settings_subsection_changes($subsection: JQuery): void {
for (const elem of settings_components.get_subsection_property_elements($subsection)) { for (const elem of settings_components.get_subsection_property_elements($subsection)) {
discard_realm_default_property_element_changes(elem); discard_realm_default_property_element_changes(elem);
} }
@ -698,11 +773,11 @@ export function discard_realm_default_settings_subsection_changes($subsection) {
settings_components.change_save_button_state($save_btn_controls, "discarded"); settings_components.change_save_button_state($save_btn_controls, "discarded");
} }
export function deactivate_organization(e) { export function deactivate_organization(e: JQuery.Event): void {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
function do_deactivate_realm() { function do_deactivate_realm(): void {
channel.post({ channel.post({
url: "/json/realm/deactivate", url: "/json/realm/deactivate",
error(xhr) { error(xhr) {
@ -724,7 +799,7 @@ export function deactivate_organization(e) {
}); });
} }
export function sync_realm_settings(property) { export function sync_realm_settings(property: string): void {
if (!meta.loaded) { if (!meta.loaded) {
return; return;
} }
@ -739,14 +814,19 @@ export function sync_realm_settings(property) {
if ($element.length) { if ($element.length) {
const $subsection = $element.closest(".settings-subsection-parent"); const $subsection = $element.closest(".settings-subsection-parent");
if ($subsection.find(".save-button-controls").hasClass("hide")) { if ($subsection.find(".save-button-controls").hasClass("hide")) {
discard_realm_property_element_changes($element); discard_realm_property_element_changes(util.the($element));
} else { } else {
discard_realm_settings_subsection_changes($subsection); discard_realm_settings_subsection_changes($subsection);
} }
} }
} }
export function save_organization_settings(data, $save_button, patch_url, success_continuation) { export function save_organization_settings(
data: Record<string, string | number | boolean>,
$save_button: JQuery,
patch_url: string,
success_continuation: (() => void) | undefined,
): void {
const $subsection_parent = $save_button.closest(".settings-subsection-parent"); const $subsection_parent = $save_button.closest(".settings-subsection-parent");
const $save_btn_container = $subsection_parent.find(".save-button-controls"); const $save_btn_container = $subsection_parent.find(".save-button-controls");
const $failed_alert_elem = $subsection_parent.find(".subsection-failed-status p"); const $failed_alert_elem = $subsection_parent.find(".subsection-failed-status p");
@ -769,17 +849,17 @@ export function save_organization_settings(data, $save_button, patch_url, succes
}); });
} }
export function set_up() { export function set_up(): void {
build_page(); build_page();
maybe_disable_widgets(); maybe_disable_widgets();
} }
function set_up_dropdown_widget( function set_up_dropdown_widget(
setting_name, setting_name: keyof Realm,
setting_options, setting_options: () => dropdown_widget.Option[],
setting_type, setting_type: string,
custom_dropdown_widget_callback, custom_dropdown_widget_callback?: (current_value: string | number | undefined) => void,
) { ): void {
const $save_discard_widget_container = $(`#id_${CSS.escape(setting_name)}`).closest( const $save_discard_widget_container = $(`#id_${CSS.escape(setting_name)}`).closest(
".settings-subsection-parent", ".settings-subsection-parent",
); );
@ -799,21 +879,21 @@ function set_up_dropdown_widget(
widget_name: setting_name, widget_name: setting_name,
get_options: setting_options, get_options: setting_options,
$events_container, $events_container,
item_click_callback(event, dropdown) { item_click_callback(event, dropdown, this_widget) {
dropdown.hide(); dropdown.hide();
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.render(); this_widget.render();
settings_components.save_discard_realm_settings_widget_status_handler( settings_components.save_discard_realm_settings_widget_status_handler(
$save_discard_widget_container, $save_discard_widget_container,
); );
if (custom_dropdown_widget_callback !== undefined) { if (custom_dropdown_widget_callback !== undefined) {
custom_dropdown_widget_callback(this.current_value); custom_dropdown_widget_callback(this_widget.current_value);
} }
}, },
default_id: realm[setting_name], default_id: z.union([z.string(), z.number()]).parse(realm[setting_name]),
unique_id_type, unique_id_type,
text_if_current_value_not_in_options, ...(text_if_current_value_not_in_options && {text_if_current_value_not_in_options}),
on_mount_callback(dropdown) { on_mount_callback(dropdown) {
if (setting_type === "group") { if (setting_type === "group") {
$(dropdown.popper).css("min-width", "300px"); $(dropdown.popper).css("min-width", "300px");
@ -826,18 +906,24 @@ function set_up_dropdown_widget(
setting_dropdown_widget.setup(); setting_dropdown_widget.setup();
} }
export function set_up_dropdown_widget_for_realm_group_settings() { export function set_up_dropdown_widget_for_realm_group_settings(): void {
const realm_group_permission_settings = Object.keys( const realm_group_permission_settings = Object.keys(
realm.server_supported_permission_settings.realm, realm.server_supported_permission_settings.realm,
); );
for (const setting_name of realm_group_permission_settings) { for (const setting_name of realm_group_permission_settings) {
const get_setting_options = () => const get_setting_options = (): UserGroupForDropdownListWidget[] =>
user_groups.get_realm_user_groups_for_dropdown_list_widget(setting_name, "realm"); user_groups.get_realm_user_groups_for_dropdown_list_widget(setting_name, "realm");
let dropdown_list_item_click_callback; let dropdown_list_item_click_callback:
| ((current_value: string | number | undefined) => void)
| undefined;
if (setting_name === "direct_message_permission_group") { if (setting_name === "direct_message_permission_group") {
dropdown_list_item_click_callback = dropdown_list_item_click_callback = (
check_disable_direct_message_initiator_group_dropdown; current_value: string | number | undefined,
): void => {
assert(typeof current_value === "number");
check_disable_direct_message_initiator_group_dropdown(current_value);
};
} else if ( } else if (
setting_name === "can_delete_any_message_group" || setting_name === "can_delete_any_message_group" ||
setting_name === "can_delete_own_message_group" setting_name === "can_delete_own_message_group"
@ -846,7 +932,7 @@ export function set_up_dropdown_widget_for_realm_group_settings() {
} }
set_up_dropdown_widget( set_up_dropdown_widget(
"realm_" + setting_name, realm_schema.keyof().parse("realm_" + setting_name),
get_setting_options, get_setting_options,
"group", "group",
dropdown_list_item_click_callback, dropdown_list_item_click_callback,
@ -854,10 +940,10 @@ export function set_up_dropdown_widget_for_realm_group_settings() {
} }
} }
export function init_dropdown_widgets() { export function init_dropdown_widgets(): void {
const notification_stream_options = () => { const notification_stream_options = (): dropdown_widget.Option[] => {
const streams = stream_settings_data.get_streams_for_settings_page(); const streams = stream_settings_data.get_streams_for_settings_page();
const options = streams.map((stream) => ({ const options: dropdown_widget.Option[] = streams.map((stream) => ({
name: stream.name, name: stream.name,
unique_id: stream.stream_id, unique_id: stream.stream_id,
stream, stream,
@ -889,7 +975,7 @@ export function init_dropdown_widgets() {
"channel", "channel",
); );
const default_code_language_options = () => { const default_code_language_options = (): dropdown_widget.Option[] => {
const options = Object.keys(pygments_data.langs).map((x) => ({ const options = Object.keys(pygments_data.langs).map((x) => ({
name: x, name: x,
unique_id: x, unique_id: x,
@ -914,11 +1000,11 @@ export function init_dropdown_widgets() {
} }
export function register_save_discard_widget_handlers( export function register_save_discard_widget_handlers(
$container, $container: JQuery,
patch_url, patch_url: string,
for_realm_default_settings, for_realm_default_settings: boolean,
) { ): void {
$container.on("change input", "input, select, textarea", function (e) { $container.on("change input", "input, select, textarea", function (this: HTMLElement, e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -956,7 +1042,10 @@ export function register_save_discard_widget_handlers(
return undefined; return undefined;
}); });
$container.on("click", ".subsection-header .subsection-changes-discard button", function (e) { $container.on(
"click",
".subsection-header .subsection-changes-discard button",
function (this: HTMLElement, e) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const $subsection = $(this).closest(".settings-subsection-parent"); const $subsection = $(this).closest(".settings-subsection-parent");
@ -965,17 +1054,22 @@ export function register_save_discard_widget_handlers(
} else { } else {
discard_realm_settings_subsection_changes($subsection); discard_realm_settings_subsection_changes($subsection);
} }
}); },
);
$container.on("click", ".subsection-header .subsection-changes-save button", function (e) { $container.on(
"click",
".subsection-header .subsection-changes-save button",
function (this: HTMLElement, e: JQuery.ClickEvent) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const $save_button = $(this); const $save_button = $(this);
const $subsection_elem = $save_button.closest(".settings-subsection-parent"); const $subsection_elem = $save_button.closest(".settings-subsection-parent");
let data; let data: Record<string, string | number | boolean>;
let success_continuation; let success_continuation;
if (!for_realm_default_settings) { if (!for_realm_default_settings) {
data = settings_components.populate_data_for_realm_settings_request($subsection_elem); data =
settings_components.populate_data_for_realm_settings_request($subsection_elem);
} else { } else {
data = data =
settings_components.populate_data_for_default_realm_settings_request( settings_components.populate_data_for_default_realm_settings_request(
@ -997,10 +1091,11 @@ export function register_save_discard_widget_handlers(
} }
} }
save_organization_settings(data, $save_button, patch_url, success_continuation); save_organization_settings(data, $save_button, patch_url, success_continuation);
}); },
);
} }
export function build_page() { export function build_page(): void {
meta.loaded = true; meta.loaded = true;
loading.make_indicator($("#admin_page_auth_methods_loading_indicator")); loading.make_indicator($("#admin_page_auth_methods_loading_indicator"));
@ -1068,36 +1163,47 @@ export function build_page() {
set_jitsi_server_url_dropdown(); set_jitsi_server_url_dropdown();
}); });
$("#id_realm_jitsi_server_url").on("change", function () { $<HTMLSelectOneElement>("select:not([multiple])#id_realm_jitsi_server_url").on(
"change",
function () {
const dropdown_val = this.value; const dropdown_val = this.value;
update_jitsi_server_url_custom_input(dropdown_val); update_jitsi_server_url_custom_input(dropdown_val);
}); },
);
$("#id_realm_message_retention_days").on("change", function () { $<HTMLSelectOneElement>("select:not([multiple])#id_realm_message_retention_days").on(
"change",
function () {
const message_retention_setting_dropdown_value = this.value; const message_retention_setting_dropdown_value = this.value;
settings_components.change_element_block_display_property( settings_components.change_element_block_display_property(
"id_realm_message_retention_custom_input", "id_realm_message_retention_custom_input",
message_retention_setting_dropdown_value === "custom_period", message_retention_setting_dropdown_value === "custom_period",
); );
}); },
);
$("#id_realm_waiting_period_threshold").on("change", function () { $<HTMLSelectOneElement>("select:not([multiple])#id_realm_waiting_period_threshold").on(
"change",
function () {
const waiting_period_threshold = this.value; const waiting_period_threshold = this.value;
settings_components.change_element_block_display_property( settings_components.change_element_block_display_property(
"id_realm_waiting_period_threshold_custom_input", "id_realm_waiting_period_threshold_custom_input",
waiting_period_threshold === "custom_period", waiting_period_threshold === "custom_period",
); );
}); },
);
$("#id_realm_digest_emails_enabled").on("change", function () { $("#id_realm_digest_emails_enabled").on("change", function () {
const digest_emails_enabled = $(this).is(":checked"); const digest_emails_enabled = $(this).is(":checked");
settings_components.change_element_block_display_property( settings_components.change_element_block_display_property(
"id_realm_digest_weekday", "id_realm_digest_weekday",
digest_emails_enabled === true, digest_emails_enabled,
); );
}); });
$("#id_realm_org_join_restrictions").on("change", function () { $<HTMLSelectOneElement>("select:not([multiple])#id_realm_org_join_restrictions").on(
"change",
function () {
const org_join_restrictions = this.value; const org_join_restrictions = this.value;
const $node = $("#allowed_domains_label").parent(); const $node = $("#allowed_domains_label").parent();
if (org_join_restrictions === "only_selected_domain") { if (org_join_restrictions === "only_selected_domain") {
@ -1108,19 +1214,24 @@ export function build_page() {
} else { } else {
$node.hide(); $node.hide();
} }
},
);
$<HTMLInputElement>("input#id_realm_allow_message_editing").on("change", function () {
update_message_edit_sub_settings(this.checked);
}); });
$("#id_realm_allow_message_editing").on("change", function () { $("#org-moving-msgs").on(
const is_checked = $(this).prop("checked"); "change",
update_message_edit_sub_settings(is_checked); ".move-message-policy-setting",
}); function (this: HTMLElement) {
$("#org-moving-msgs").on("change", ".move-message-policy-setting", function () {
const $policy_dropdown_elem = $(this); const $policy_dropdown_elem = $(this);
const property_name = settings_components.extract_property_name($policy_dropdown_elem); const property_name = z
.enum(["realm_edit_topic_policy", "realm_move_messages_between_streams_policy"])
.parse(settings_components.extract_property_name($policy_dropdown_elem));
const disable_time_limit_setting = message_move_limit_setting_enabled(property_name); const disable_time_limit_setting = message_move_limit_setting_enabled(property_name);
let time_limit_setting_name; let time_limit_setting_name: MessageMoveTimeLimitSetting;
if (property_name === "realm_edit_topic_policy") { if (property_name === "realm_edit_topic_policy") {
time_limit_setting_name = "realm_move_messages_within_stream_limit_seconds"; time_limit_setting_name = "realm_move_messages_within_stream_limit_seconds";
} else { } else {
@ -1131,7 +1242,8 @@ export function build_page() {
time_limit_setting_name, time_limit_setting_name,
disable_time_limit_setting, disable_time_limit_setting,
); );
}); },
);
$("#id_realm_org_join_restrictions").on("click", (e) => { $("#id_realm_org_join_restrictions").on("click", (e) => {
// This prevents the disappearance of modal when there are // This prevents the disappearance of modal when there are
@ -1145,25 +1257,40 @@ export function build_page() {
settings_realm_domains.show_realm_domains_modal(); settings_realm_domains.show_realm_domains_modal();
}); });
function realm_icon_logo_upload_complete($spinner, $upload_text, $delete_button) { function realm_icon_logo_upload_complete(
$spinner: JQuery,
$upload_text: JQuery,
$delete_button: JQuery,
): void {
$spinner.css({visibility: "hidden"}); $spinner.css({visibility: "hidden"});
$upload_text.show(); $upload_text.show();
$delete_button.show(); $delete_button.show();
} }
function realm_icon_logo_upload_start($spinner, $upload_text, $delete_button) { function realm_icon_logo_upload_start(
$spinner: JQuery,
$upload_text: JQuery,
$delete_button: JQuery,
): void {
$spinner.css({visibility: "visible"}); $spinner.css({visibility: "visible"});
$upload_text.hide(); $upload_text.hide();
$delete_button.hide(); $delete_button.hide();
} }
function upload_realm_logo_or_icon($file_input, night, icon) { function upload_realm_logo_or_icon(
$file_input: JQuery<HTMLInputElement>,
night: boolean | null,
icon: boolean,
): void {
const form_data = new FormData(); const form_data = new FormData();
let widget; let widget;
let url; let url;
assert(csrf_token !== undefined);
form_data.append("csrfmiddlewaretoken", csrf_token); form_data.append("csrfmiddlewaretoken", csrf_token);
for (const [i, file] of Array.prototype.entries.call($file_input[0].files)) { const files = util.the($file_input).files;
assert(files !== null);
for (const [i, file] of [...files].entries()) {
form_data.append("file-" + i, file); form_data.append("file-" + i, file);
} }
if (icon) { if (icon) {

View File

@ -247,7 +247,7 @@ export function set_up(settings_panel: SettingsPanel): void {
if (for_realm_settings) { if (for_realm_settings) {
// For the realm-level defaults page, we use the common // For the realm-level defaults page, we use the common
// settings_org.js handlers, so we can return early here. // settings_org.ts handlers, so we can return early here.
return; return;
} }

View File

@ -88,7 +88,7 @@ export function disable_sub_setting_onchange(
is_checked: boolean, is_checked: boolean,
sub_setting_id: string, sub_setting_id: string,
disable_on_uncheck: boolean, disable_on_uncheck: boolean,
include_label: boolean, include_label = false,
): void { ): void {
if ((is_checked && disable_on_uncheck) || (!is_checked && !disable_on_uncheck)) { if ((is_checked && disable_on_uncheck) || (!is_checked && !disable_on_uncheck)) {
$(`#${CSS.escape(sub_setting_id)}`).prop("disabled", false); $(`#${CSS.escape(sub_setting_id)}`).prop("disabled", false);

View File

@ -22,7 +22,7 @@ export const user_group_schema = raw_user_group_schema.extend({
}); });
export type UserGroup = z.infer<typeof user_group_schema>; export type UserGroup = z.infer<typeof user_group_schema>;
type UserGroupForDropdownListWidget = { export type UserGroupForDropdownListWidget = {
name: string; name: string;
unique_id: number; unique_id: number;
}; };

View File

@ -5,7 +5,6 @@ const assert = require("node:assert/strict");
const {$t} = require("./lib/i18n"); const {$t} = require("./lib/i18n");
const {mock_esm, set_global, zrequire} = require("./lib/namespace"); const {mock_esm, set_global, zrequire} = require("./lib/namespace");
const {run_test, noop} = require("./lib/test"); const {run_test, noop} = require("./lib/test");
const blueslip = require("./lib/zblueslip");
const $ = require("./lib/zjquery"); const $ = require("./lib/zjquery");
const realm_icon = mock_esm("../src/realm_icon"); const realm_icon = mock_esm("../src/realm_icon");
@ -291,22 +290,12 @@ function test_sync_realm_settings({override}) {
$.create("save-button-controls-stub").addClass("hide"), $.create("save-button-controls-stub").addClass("hide"),
); );
{
/* Test invalid settings property sync */
const $property_elem = $("#id_realm_invalid_settings_property");
$property_elem.attr("id", "id_realm_invalid_settings_property");
$property_elem.closest = () => $subsection_stub;
$property_elem.length = 1;
blueslip.expect("error", "Element refers to unknown property");
settings_org.sync_realm_settings("invalid_settings_property");
}
function test_common_policy(property_name) { function test_common_policy(property_name) {
const $property_elem = $(`#id_realm_${CSS.escape(property_name)}`); const $property_elem = $(`#id_realm_${CSS.escape(property_name)}`);
$property_elem.length = 1; $property_elem.length = 1;
$property_elem.attr("id", `id_realm_${CSS.escape(property_name)}`); $property_elem.attr("id", `id_realm_${CSS.escape(property_name)}`);
$property_elem.closest = () => $subsection_stub; $property_elem.closest = () => $subsection_stub;
$property_elem[0] = `#id_realm_${CSS.escape(property_name)}`;
/* Each policy is initialized to 'by_members' and then all the values are tested /* Each policy is initialized to 'by_members' and then all the values are tested
in the following order - by_admins_only, by_moderators_only, by_full_members, in the following order - by_admins_only, by_moderators_only, by_full_members,
@ -338,6 +327,7 @@ function test_sync_realm_settings({override}) {
$property_elem.attr("id", "id_realm_message_content_edit_limit_minutes"); $property_elem.attr("id", "id_realm_message_content_edit_limit_minutes");
$property_dropdown_elem.attr("id", "id_realm_message_content_edit_limit_seconds"); $property_dropdown_elem.attr("id", "id_realm_message_content_edit_limit_seconds");
$property_dropdown_elem.closest = () => $subsection_stub; $property_dropdown_elem.closest = () => $subsection_stub;
$property_dropdown_elem[0] = "#id_realm_message_content_edit_limit_seconds";
override(realm, "realm_message_content_edit_limit_seconds", 120); override(realm, "realm_message_content_edit_limit_seconds", 120);
@ -362,6 +352,7 @@ function test_sync_realm_settings({override}) {
$property_elem.length = 1; $property_elem.length = 1;
$property_elem.attr("id", "id_realm_org_join_restrictions"); $property_elem.attr("id", "id_realm_org_join_restrictions");
$property_elem.closest = () => $subsection_stub; $property_elem.closest = () => $subsection_stub;
$property_elem[0] = "#id_realm_org_join_restrictions";
override(realm, "realm_emails_restricted_to_domains", true); override(realm, "realm_emails_restricted_to_domains", true);
override(realm, "realm_disallow_disposable_email_addresses", false); override(realm, "realm_disallow_disposable_email_addresses", false);
@ -529,6 +520,7 @@ test("set_up", ({override, override_rewire}) => {
name: "BigBlueButton", name: "BigBlueButton",
}, },
}); });
override(realm, "realm_message_retention_days", null);
let upload_realm_logo_or_icon; let upload_realm_logo_or_icon;
realm_icon.build_realm_icon_widget = (f) => { realm_icon.build_realm_icon_widget = (f) => {