From 3cac29a8e1a2b9d2892348254ee6e730c4eef029 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 9 Oct 2023 14:29:16 -0700 Subject: [PATCH] settings_org: Extract settings_components module. Signed-off-by: Anders Kaseorg --- tools/test-js-with-node | 1 + web/src/admin.js | 15 +- web/src/settings_components.js | 657 ++++++++++++++ web/src/settings_display.js | 8 +- web/src/settings_notifications.js | 9 +- web/src/settings_org.js | 799 +++--------------- .../settings_realm_user_settings_defaults.js | 3 +- web/src/stream_edit.js | 15 +- web/tests/settings_org.test.js | 36 +- 9 files changed, 813 insertions(+), 730 deletions(-) create mode 100644 web/src/settings_components.js diff --git a/tools/test-js-with-node b/tools/test-js-with-node index bea99fb2680a9..d668d39fa2038 100755 --- a/tools/test-js-with-node +++ b/tools/test-js-with-node @@ -188,6 +188,7 @@ EXEMPT_FILES = make_set( "web/src/settings.js", "web/src/settings_account.js", "web/src/settings_bots.js", + "web/src/settings_components.js", "web/src/settings_display.js", "web/src/settings_emoji.ts", "web/src/settings_exports.ts", diff --git a/web/src/admin.js b/web/src/admin.js index ee1e75ced7181..59af1821e0e9a 100644 --- a/web/src/admin.js +++ b/web/src/admin.js @@ -10,6 +10,7 @@ import {page_params} from "./page_params"; import {realm_user_settings_defaults} from "./realm_user_settings_defaults"; import * as settings from "./settings"; import * as settings_bots from "./settings_bots"; +import * as settings_components from "./settings_components"; import * as settings_config from "./settings_config"; import * as settings_data from "./settings_data"; import * as settings_invites from "./settings_invites"; @@ -116,12 +117,14 @@ export function build_page() { realm_add_custom_emoji_policy: page_params.realm_add_custom_emoji_policy, can_add_emojis: settings_data.user_can_add_custom_emoji(), can_create_new_bots: settings_bots.can_create_new_bots(), - realm_message_content_edit_limit_minutes: settings_org.get_realm_time_limits_in_minutes( - "realm_message_content_edit_limit_seconds", - ), - realm_message_content_delete_limit_minutes: settings_org.get_realm_time_limits_in_minutes( - "realm_message_content_delete_limit_seconds", - ), + realm_message_content_edit_limit_minutes: + settings_components.get_realm_time_limits_in_minutes( + "realm_message_content_edit_limit_seconds", + ), + realm_message_content_delete_limit_minutes: + settings_components.get_realm_time_limits_in_minutes( + "realm_message_content_delete_limit_seconds", + ), realm_message_retention_days: page_params.realm_message_retention_days, realm_allow_edit_history: page_params.realm_allow_edit_history, realm_allow_message_editing: page_params.realm_allow_message_editing, diff --git a/web/src/settings_components.js b/web/src/settings_components.js new file mode 100644 index 0000000000000..ec09b47cb8b68 --- /dev/null +++ b/web/src/settings_components.js @@ -0,0 +1,657 @@ +import $ from "jquery"; + +import render_compose_banner from "../templates/compose_banner/compose_banner.hbs"; + +import * as blueslip from "./blueslip"; +import * as compose_banner from "./compose_banner"; +import {$t} from "./i18n"; +import {page_params} from "./page_params"; +import {realm_user_settings_defaults} from "./realm_user_settings_defaults"; +import * as settings_config from "./settings_config"; +import * as stream_data from "./stream_data"; +import * as util from "./util"; + +const MAX_CUSTOM_TIME_LIMIT_SETTING_VALUE = 2147483647; + +export function get_sorted_options_list(option_values_object) { + const options_list = Object.keys(option_values_object).map((key) => ({ + ...option_values_object[key], + key, + })); + let comparator = (x, y) => x.order - y.order; + if (!options_list[0].order) { + comparator = (x, y) => { + const key_x = x.key.toUpperCase(); + const key_y = y.key.toUpperCase(); + if (key_x < key_y) { + return -1; + } + if (key_x > key_y) { + return 1; + } + return 0; + }; + } + options_list.sort(comparator); + return options_list; +} + +export function get_realm_time_limits_in_minutes(property) { + if (page_params[property] === null) { + // This represents "Anytime" case. + return null; + } + let val = (page_params[property] / 60).toFixed(1); + if (Number.parseFloat(val, 10) === Number.parseInt(val, 10)) { + val = Number.parseInt(val, 10); + } + return val.toString(); +} + +export function get_property_value(property_name, for_realm_default_settings, sub) { + if (for_realm_default_settings) { + // realm_user_default_settings are stored in a separate object. + if (property_name === "twenty_four_hour_time") { + return JSON.stringify(realm_user_settings_defaults.twenty_four_hour_time); + } + if ( + property_name === "email_notifications_batching_period_seconds" || + property_name === "email_notification_batching_period_edit_minutes" + ) { + return realm_user_settings_defaults.email_notifications_batching_period_seconds; + } + return realm_user_settings_defaults[property_name]; + } + + if (sub) { + if (property_name === "stream_privacy") { + return stream_data.get_stream_privacy_policy(sub.stream_id); + } + if (property_name === "is_default_stream") { + return stream_data.is_default_stream_id(sub.stream_id); + } + + return sub[property_name]; + } + + if (property_name === "realm_org_join_restrictions") { + if (page_params.realm_emails_restricted_to_domains) { + return "only_selected_domain"; + } + if (page_params.realm_disallow_disposable_email_addresses) { + return "no_disposable_email"; + } + return "no_restriction"; + } + + return page_params[property_name]; +} + +export function extract_property_name($elem, for_realm_default_settings) { + if (for_realm_default_settings) { + // ID for realm_user_default_settings elements are of the form + // "realm_{settings_name}}" because both user and realm default + // settings use the same template and each element should have + // unique id. + return /^realm_(.*)$/.exec($elem.attr("id").replaceAll("-", "_"))[1]; + } + + if ($elem.attr("id").startsWith("id_authmethod")) { + // Authentication Method component IDs include authentication method name + // for uniqueness, anchored to "id_authmethod" prefix, e.g. "id_authmethodapple_". + // We need to strip that whole construct down to extract the actual property name. + // The [\da-z]+ part of the regexp covers the auth method name itself. + // We assume it's not an empty string and can contain only digits and lowercase ASCII letters, + // this is ensured by a respective allowlist-based filter in populate_auth_methods(). + return /^id_authmethod[\da-z]+_(.*)$/.exec($elem.attr("id"))[1]; + } + + return /^id_(.*)$/.exec($elem.attr("id").replaceAll("-", "_"))[1]; +} + +export function get_subsection_property_elements(subsection) { + return [...$(subsection).find(".prop-element")]; +} + +export function set_property_dropdown_value(property_name) { + $(`#id_${CSS.escape(property_name)}`).val(get_property_value(property_name)); +} + +export function change_element_block_display_property(elem_id, show_element) { + const $elem = $(`#${CSS.escape(elem_id)}`); + if (show_element) { + $elem.parent().show(); + } else { + $elem.parent().hide(); + } +} + +export function is_video_chat_provider_jitsi_meet() { + const video_chat_provider_id = Number.parseInt($("#id_realm_video_chat_provider").val(), 10); + const jitsi_meet_id = page_params.realm_available_video_chat_providers.jitsi_meet.id; + return video_chat_provider_id === jitsi_meet_id; +} + +function get_jitsi_server_url_setting_value($input_elem, for_api_data = true) { + // If the video chat provider dropdown is not set to Jitsi, we return + // `realm_jitsi_server_url` to indicate that the property remains unchanged. + // This ensures the appropriate state of the save button and prevents the + // addition of the `jitsi_server_url` in the API data. + if (!is_video_chat_provider_jitsi_meet()) { + return page_params.realm_jitsi_server_url; + } + + const select_elem_val = $input_elem.val(); + if (select_elem_val === "server_default") { + if (!for_api_data) { + return null; + } + return JSON.stringify("default"); + } + + const $custom_input_elem = $("#id_realm_jitsi_server_url_custom_input"); + if (!for_api_data) { + return $custom_input_elem.val(); + } + return JSON.stringify($custom_input_elem.val()); +} + +export function update_custom_value_input(property_name) { + const $dropdown_elem = $(`#id_${CSS.escape(property_name)}`); + const custom_input_elem_id = $dropdown_elem + .parent() + .find(".time-limit-custom-input") + .attr("id"); + + const show_custom_limit_input = $dropdown_elem.val() === "custom_period"; + change_element_block_display_property(custom_input_elem_id, show_custom_limit_input); + if (show_custom_limit_input) { + $(`#${CSS.escape(custom_input_elem_id)}`).val( + get_realm_time_limits_in_minutes(property_name), + ); + } +} + +export function get_time_limit_dropdown_setting_value(property_name) { + if (page_params[property_name] === null) { + return "any_time"; + } + + const valid_limit_values = settings_config.time_limit_dropdown_values.map((x) => x.value); + if (valid_limit_values.includes(page_params[property_name])) { + return page_params[property_name].toString(); + } + + return "custom_period"; +} + +export function set_time_limit_setting(property_name) { + const dropdown_elem_val = get_time_limit_dropdown_setting_value(property_name); + $(`#id_${CSS.escape(property_name)}`).val(dropdown_elem_val); + + const $custom_input = $(`#id_${CSS.escape(property_name)}`) + .parent() + .find(".time-limit-custom-input"); + $custom_input.val(get_realm_time_limits_in_minutes(property_name)); + + change_element_block_display_property( + $custom_input.attr("id"), + dropdown_elem_val === "custom_period", + ); +} + +function get_message_retention_setting_value($input_elem, for_api_data = true) { + const select_elem_val = $input_elem.val(); + if (select_elem_val === "unlimited") { + if (!for_api_data) { + return settings_config.retain_message_forever; + } + return JSON.stringify("unlimited"); + } + + if (select_elem_val === "realm_default") { + if (!for_api_data) { + return null; + } + return JSON.stringify("realm_default"); + } + + const $custom_input = $input_elem.parent().find(".message-retention-setting-custom-input"); + if ($custom_input.val().length === 0) { + return settings_config.retain_message_forever; + } + return Number.parseInt(Number($custom_input.val()), 10); +} + +export function sort_object_by_key(obj) { + const keys = Object.keys(obj).sort(); + const new_obj = {}; + + for (const key of keys) { + new_obj[key] = obj[key]; + } + + return new_obj; +} + +export let default_code_language_widget = null; +export let notifications_stream_widget = null; +export let signup_notifications_stream_widget = null; +export let create_multiuse_invite_group_widget = null; +export let can_remove_subscribers_group_widget = null; + +export function get_widget_for_dropdown_list_settings(property_name) { + switch (property_name) { + case "realm_notifications_stream_id": + return notifications_stream_widget; + case "realm_signup_notifications_stream_id": + return signup_notifications_stream_widget; + case "realm_default_code_block_language": + return default_code_language_widget; + case "realm_create_multiuse_invite_group": + return create_multiuse_invite_group_widget; + case "can_remove_subscribers_group": + return can_remove_subscribers_group_widget; + default: + blueslip.error("No dropdown list widget for property", {property_name}); + return null; + } +} + +export function set_default_code_language_widget(widget) { + default_code_language_widget = widget; +} + +export function set_notifications_stream_widget(widget) { + notifications_stream_widget = widget; +} + +export function set_signup_notifications_stream_widget(widget) { + signup_notifications_stream_widget = widget; +} + +export function set_create_multiuse_invite_group_widget(widget) { + create_multiuse_invite_group_widget = widget; +} + +export function set_can_remove_subscribers_group_widget(widget) { + can_remove_subscribers_group_widget = widget; +} + +export function set_dropdown_list_widget_setting_value(property_name, value) { + const widget = get_widget_for_dropdown_list_settings(property_name); + widget.render(value); +} + +export function get_dropdown_list_widget_setting_value($input_elem) { + const widget_name = extract_property_name($input_elem); + const setting_widget = get_widget_for_dropdown_list_settings(widget_name); + + const setting_value_type = $input_elem.attr("data-setting-value-type"); + if (setting_value_type === "number") { + return Number.parseInt(setting_widget.value(), 10); + } + + return setting_widget.value(); +} + +export function change_save_button_state($element, state) { + function show_hide_element($element, show, fadeout_delay, fadeout_callback) { + if (show) { + $element.removeClass("hide").addClass(".show").fadeIn(300); + return; + } + setTimeout(() => { + $element.fadeOut(300, fadeout_callback); + }, fadeout_delay); + } + + const $saveBtn = $element.find(".save-button"); + const $textEl = $saveBtn.find(".save-discard-widget-button-text"); + + if (state !== "saving") { + $saveBtn.removeClass("saving"); + } + + if (state === "discarded") { + show_hide_element($element, false, 0, () => + enable_or_disable_save_button($element.closest(".settings-subsection-parent")), + ); + return; + } + + let button_text; + let data_status; + let is_show; + switch (state) { + case "unsaved": + button_text = $t({defaultMessage: "Save changes"}); + data_status = "unsaved"; + is_show = true; + + $element.find(".discard-button").show(); + break; + case "saved": + button_text = $t({defaultMessage: "Save changes"}); + data_status = ""; + is_show = false; + break; + case "saving": + button_text = $t({defaultMessage: "Saving"}); + data_status = "saving"; + is_show = true; + + $element.find(".discard-button").hide(); + $saveBtn.addClass("saving"); + break; + case "failed": + button_text = $t({defaultMessage: "Save changes"}); + data_status = "failed"; + is_show = true; + break; + case "succeeded": + button_text = $t({defaultMessage: "Saved"}); + data_status = "saved"; + is_show = false; + break; + } + + $textEl.text(button_text); + $saveBtn.attr("data-status", data_status); + if (state === "unsaved") { + enable_or_disable_save_button($element.closest(".settings-subsection-parent")); + } + show_hide_element($element, is_show, 800); +} + +function get_input_type($input_elem, input_type) { + if (["boolean", "string", "number"].includes(input_type)) { + return input_type; + } + return $input_elem.data("setting-widget-type"); +} + +export function get_input_element_value(input_elem, input_type) { + const $input_elem = $(input_elem); + input_type = get_input_type($input_elem, input_type); + switch (input_type) { + case "boolean": + return $input_elem.prop("checked"); + case "string": + return $input_elem.val().trim(); + case "number": + return Number.parseInt($input_elem.val().trim(), 10); + case "radio-group": { + const selected_val = $input_elem.find("input:checked").val(); + if ($input_elem.data("setting-choice-type") === "number") { + return Number.parseInt(selected_val, 10); + } + return selected_val.trim(); + } + case "time-limit": + return get_time_limit_setting_value($input_elem); + case "jitsi-server-url-setting": + return get_jitsi_server_url_setting_value($input_elem); + case "message-retention-setting": + return get_message_retention_setting_value($input_elem); + case "dropdown-list-widget": + return get_dropdown_list_widget_setting_value($input_elem); + default: + return undefined; + } +} + +export function set_input_element_value($input_elem, value) { + const input_type = get_input_type($input_elem, typeof value); + if (input_type) { + if (input_type === "boolean") { + return $input_elem.prop("checked", value); + } else if (input_type === "string" || input_type === "number") { + return $input_elem.val(value); + } + } + blueslip.error("Failed to set value of property", { + property: extract_property_name($input_elem), + }); + return undefined; +} + +export function get_auth_method_list_data() { + const new_auth_methods = {}; + const $auth_method_rows = $("#id_realm_authentication_methods").find("div.method_row"); + + for (const method_row of $auth_method_rows) { + new_auth_methods[$(method_row).data("method")] = $(method_row) + .find("input") + .prop("checked"); + } + + return new_auth_methods; +} + +export function parse_time_limit($elem) { + return Math.floor(Number.parseFloat(Number($elem.val()), 10).toFixed(1) * 60); +} + +function get_time_limit_setting_value($input_elem, for_api_data = true) { + const select_elem_val = $input_elem.val(); + + if (select_elem_val === "any_time") { + // "unlimited" is sent to API when a user wants to set the setting to + // "Any time" and the message_content_edit_limit_seconds field is "null" + // for that case. + if (!for_api_data) { + return null; + } + return JSON.stringify("unlimited"); + } + + if (select_elem_val !== "custom_period") { + return Number.parseInt(select_elem_val, 10); + } + + const $custom_input_elem = $input_elem.parent().find(".time-limit-custom-input"); + if ($custom_input_elem.val().length === 0) { + // This handles the case where the initial setting value is "Any time" and then + // dropdown is changed to "Custom" where the input box is empty initially and + // thus we do not show the save-discard widget until something is typed in the + // input box. + return null; + } + + if ($input_elem.attr("id") === "id_realm_waiting_period_threshold") { + // For realm waiting period threshold setting, the custom input element contains + // number of days. + return Number.parseInt(Number($custom_input_elem.val()), 10); + } + + return parse_time_limit($custom_input_elem); +} + +export function check_property_changed(elem, for_realm_default_settings, sub) { + const $elem = $(elem); + const property_name = extract_property_name($elem, for_realm_default_settings); + let current_val = get_property_value(property_name, for_realm_default_settings, sub); + let proposed_val; + + switch (property_name) { + case "realm_authentication_methods": + current_val = sort_object_by_key(current_val); + current_val = JSON.stringify(current_val); + proposed_val = get_auth_method_list_data(); + proposed_val = JSON.stringify(proposed_val); + break; + case "realm_notifications_stream_id": + case "realm_signup_notifications_stream_id": + case "realm_default_code_block_language": + case "can_remove_subscribers_group": + case "realm_create_multiuse_invite_group": + proposed_val = get_dropdown_list_widget_setting_value($elem); + break; + case "email_notifications_batching_period_seconds": + proposed_val = get_time_limit_setting_value($elem, false); + break; + case "realm_message_content_edit_limit_seconds": + case "realm_message_content_delete_limit_seconds": + case "realm_move_messages_between_streams_limit_seconds": + case "realm_move_messages_within_stream_limit_seconds": + case "realm_waiting_period_threshold": + proposed_val = get_time_limit_setting_value($elem, false); + break; + case "realm_message_retention_days": + case "message_retention_days": + proposed_val = get_message_retention_setting_value($elem, false); + break; + case "realm_jitsi_server_url": + proposed_val = get_jitsi_server_url_setting_value($elem, false); + break; + case "realm_default_language": + proposed_val = $( + "#org-notifications .language_selection_widget .language_selection_button span", + ).attr("data-language-code"); + break; + case "emojiset": + case "user_list_style": + case "stream_privacy": + proposed_val = get_input_element_value($elem, "radio-group"); + break; + default: + if (current_val !== undefined) { + proposed_val = get_input_element_value($elem, typeof current_val); + } else { + blueslip.error("Element refers to unknown property", {property_name}); + } + } + return current_val !== proposed_val; +} + +function switching_to_private(properties_elements, for_realm_default_settings) { + for (const elem of properties_elements) { + const $elem = $(elem); + const property_name = extract_property_name($elem, for_realm_default_settings); + if (property_name !== "stream_privacy") { + continue; + } + const proposed_val = get_input_element_value($elem, "radio-group"); + return proposed_val === "invite-only-public-history" || proposed_val === "invite-only"; + } + return false; +} + +export function save_discard_widget_status_handler($subsection, for_realm_default_settings, sub) { + $subsection.find(".subsection-failed-status p").hide(); + $subsection.find(".save-button").show(); + const properties_elements = get_subsection_property_elements($subsection); + const show_change_process_button = properties_elements.some((elem) => + check_property_changed(elem, for_realm_default_settings, sub), + ); + + const $save_btn_controls = $subsection.find(".subsection-header .save-button-controls"); + const button_state = show_change_process_button ? "unsaved" : "discarded"; + change_save_button_state($save_btn_controls, button_state); + + // If this widget is for a stream, and the stream isn't currently private + // but being changed to private, and the user changing this setting isn't + // subscribed, we show a warning that they won't be able to access the + // stream after making it private unless they subscribe. + if (!sub) { + return; + } + if ( + button_state === "unsaved" && + !sub.invite_only && + !sub.subscribed && + switching_to_private(properties_elements, for_realm_default_settings) + ) { + if ($("#stream_permission_settings .stream_privacy_warning").length > 0) { + return; + } + const context = { + banner_type: compose_banner.WARNING, + banner_text: $t({ + defaultMessage: + "Only subscribers can access or join private streams, so you will lose access to this stream if you convert it to a private stream while not subscribed to it.", + }), + button_text: $t({defaultMessage: "Subscribe"}), + classname: "stream_privacy_warning", + stream_id: sub.stream_id, + }; + $("#stream_permission_settings .stream-permissions-warning-banner").append( + render_compose_banner(context), + ); + } else { + $("#stream_permission_settings .stream-permissions-warning-banner").empty(); + } +} + +function check_maximum_valid_value($custom_input_elem, property_name) { + let setting_value = Number.parseInt($custom_input_elem.val(), 10); + if ( + property_name === "realm_message_content_edit_limit_seconds" || + property_name === "realm_message_content_delete_limit_seconds" || + property_name === "email_notifications_batching_period_seconds" + ) { + setting_value = parse_time_limit($custom_input_elem); + } + return setting_value <= MAX_CUSTOM_TIME_LIMIT_SETTING_VALUE; +} + +function should_disable_save_button_for_jitsi_server_url_setting() { + if (!is_video_chat_provider_jitsi_meet()) { + return false; + } + + const $dropdown_elem = $("#id_realm_jitsi_server_url"); + const $custom_input_elem = $("#id_realm_jitsi_server_url_custom_input"); + + return $dropdown_elem.val() === "custom" && !util.is_valid_url($custom_input_elem.val(), true); +} + +function should_disable_save_button_for_time_limit_settings(time_limit_settings) { + let disable_save_btn = false; + for (const setting_elem of time_limit_settings) { + const $dropdown_elem = $(setting_elem).find("select"); + const $custom_input_elem = $(setting_elem).find(".time-limit-custom-input"); + const custom_input_elem_val = Number.parseInt(Number($custom_input_elem.val()), 10); + + const for_realm_default_settings = + $dropdown_elem.closest(".settings-section.show").attr("id") === + "realm-user-default-settings"; + const property_name = extract_property_name($dropdown_elem, for_realm_default_settings); + + disable_save_btn = + $dropdown_elem.val() === "custom_period" && + (custom_input_elem_val <= 0 || + Number.isNaN(custom_input_elem_val) || + !check_maximum_valid_value($custom_input_elem, property_name)); + + if ( + $custom_input_elem.val() === "0" && + property_name === "realm_waiting_period_threshold" + ) { + // 0 is a valid value for realm_waiting_period_threshold setting. We specifically + // check for $custom_input_elem.val() to be "0" and not custom_input_elem_val + // because it is 0 even when custom input box is empty. + disable_save_btn = false; + } + + if (disable_save_btn) { + break; + } + } + + return disable_save_btn; +} + +function enable_or_disable_save_button($subsection_elem) { + const time_limit_settings = [...$subsection_elem.find(".time-limit-setting")]; + + let disable_save_btn = false; + if (time_limit_settings.length) { + disable_save_btn = should_disable_save_button_for_time_limit_settings(time_limit_settings); + } else if ($subsection_elem.attr("id") === "org-other-settings") { + disable_save_btn = should_disable_save_button_for_jitsi_server_url_setting(); + } + + $subsection_elem.find(".subsection-changes-save button").prop("disabled", disable_save_btn); +} diff --git a/web/src/settings_display.js b/web/src/settings_display.js index a995ed8376b92..e9a83dac6b9ff 100644 --- a/web/src/settings_display.js +++ b/web/src/settings_display.js @@ -11,7 +11,7 @@ import {$t_html, get_language_list_columns, get_language_name} from "./i18n"; import * as loading from "./loading"; import * as overlays from "./overlays"; import {page_params} from "./page_params"; -import * as settings_org from "./settings_org"; +import * as settings_components from "./settings_components"; import * as settings_ui from "./settings_ui"; import * as ui_report from "./ui_report"; import {user_settings} from "./user_settings"; @@ -75,7 +75,7 @@ function org_notification_default_language_modal_post_render() { ); $language_element.text(new_language); $language_element.attr("data-language-code", setting_value); - settings_org.save_discard_widget_status_handler($("#org-notifications")); + settings_components.save_discard_widget_status_handler($("#org-notifications")); }); } @@ -196,7 +196,7 @@ export function set_up(settings_panel) { const $input_elem = $(e.currentTarget); const setting = $input_elem.attr("name"); const data = {}; - data[setting] = settings_org.get_input_element_value(this); + data[setting] = settings_components.get_input_element_value(this); const $status_element = $input_elem .closest(".subsection-parent") .find(".alert-notification"); @@ -321,7 +321,7 @@ export function update_page(property) { } const $input_elem = $container.find(`[name=${CSS.escape(property)}]`); - settings_org.set_input_element_value($input_elem, value); + settings_components.set_input_element_value($input_elem, value); } export function initialize() { diff --git a/web/src/settings_notifications.js b/web/src/settings_notifications.js index 43a3c816d9833..a0e47f440ae68 100644 --- a/web/src/settings_notifications.js +++ b/web/src/settings_notifications.js @@ -9,8 +9,8 @@ import * as confirm_dialog from "./confirm_dialog"; import {$t, $t_html} from "./i18n"; import * as message_notifications from "./message_notifications"; import {page_params} from "./page_params"; +import * as settings_components from "./settings_components"; import * as settings_config from "./settings_config"; -import * as settings_org from "./settings_org"; import * as settings_ui from "./settings_ui"; import * as stream_data from "./stream_data"; import * as stream_settings_api from "./stream_settings_api"; @@ -88,7 +88,10 @@ export function set_notification_batching_ui($container, setting_seconds, force_ $container.find(".setting_email_notifications_batching_period_seconds").val(select_elem_val); $edit_elem.val(setting_seconds / 60); - settings_org.change_element_block_display_property($edit_elem.attr("id"), show_edit_elem); + settings_components.change_element_block_display_property( + $edit_elem.attr("id"), + show_edit_elem, + ); } export function set_enable_digest_emails_visibility(settings_panel) { @@ -216,7 +219,7 @@ export function set_up(settings_panel) { return; } let setting_name = $input_elem.attr("name"); - let setting_value = settings_org.get_input_element_value(this); + let setting_value = settings_components.get_input_element_value(this); if (setting_name === "email_notifications_batching_period_seconds") { if ($input_elem.val() === "custom_period") { diff --git a/web/src/settings_org.js b/web/src/settings_org.js index 6c43acf7c5f8a..1b0a894c4460e 100644 --- a/web/src/settings_org.js +++ b/web/src/settings_org.js @@ -1,14 +1,12 @@ import $ from "jquery"; import pygments_data from "../generated/pygments_data.json"; -import render_compose_banner from "../templates/compose_banner/compose_banner.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 * as audible_notifications from "./audible_notifications"; import * as blueslip from "./blueslip"; import * as channel from "./channel"; -import * as compose_banner from "./compose_banner"; import {csrf_token} from "./csrf"; import * as dialog_widget from "./dialog_widget"; import * as dropdown_widget from "./dropdown_widget"; @@ -19,17 +17,16 @@ import {page_params} from "./page_params"; import * as realm_icon from "./realm_icon"; import * as realm_logo from "./realm_logo"; import {realm_user_settings_defaults} from "./realm_user_settings_defaults"; +import * as settings_components from "./settings_components"; import * as settings_config from "./settings_config"; import * as settings_data from "./settings_data"; import * as settings_notifications from "./settings_notifications"; import * as settings_realm_domains from "./settings_realm_domains"; import * as settings_realm_user_settings_defaults from "./settings_realm_user_settings_defaults"; import * as settings_ui from "./settings_ui"; -import * as stream_data from "./stream_data"; import * as stream_settings_data from "./stream_settings_data"; import * as ui_report from "./ui_report"; import * as user_groups from "./user_groups"; -import * as util from "./util"; const meta = { loaded: false, @@ -39,7 +36,6 @@ export function reset() { meta.loaded = false; } -const MAX_CUSTOM_TIME_LIMIT_SETTING_VALUE = 2147483647; const DISABLED_STATE_ID = -1; export function maybe_disable_widgets() { @@ -87,50 +83,30 @@ export function maybe_disable_widgets() { .addClass("enabled"); } -export function get_sorted_options_list(option_values_object) { - const options_list = Object.keys(option_values_object).map((key) => ({ - ...option_values_object[key], - key, - })); - let comparator = (x, y) => x.order - y.order; - if (!options_list[0].order) { - comparator = (x, y) => { - const key_x = x.key.toUpperCase(); - const key_y = y.key.toUpperCase(); - if (key_x < key_y) { - return -1; - } - if (key_x > key_y) { - return 1; - } - return 0; - }; - } - options_list.sort(comparator); - return options_list; -} - export function get_organization_settings_options() { const options = {}; - options.common_policy_values = get_sorted_options_list(settings_config.common_policy_values); - options.private_message_policy_values = get_sorted_options_list( + options.common_policy_values = settings_components.get_sorted_options_list( + settings_config.common_policy_values, + ); + options.private_message_policy_values = settings_components.get_sorted_options_list( settings_config.private_message_policy_values, ); - options.wildcard_mention_policy_values = get_sorted_options_list( + options.wildcard_mention_policy_values = settings_components.get_sorted_options_list( settings_config.wildcard_mention_policy_values, ); - options.common_message_policy_values = get_sorted_options_list( + options.common_message_policy_values = settings_components.get_sorted_options_list( settings_config.common_message_policy_values, ); - options.invite_to_realm_policy_values = get_sorted_options_list( + options.invite_to_realm_policy_values = settings_components.get_sorted_options_list( settings_config.email_invite_to_realm_policy_values, ); - options.edit_topic_policy_values = get_sorted_options_list( + options.edit_topic_policy_values = settings_components.get_sorted_options_list( settings_config.edit_topic_policy_values, ); - options.move_messages_between_streams_policy_values = get_sorted_options_list( - settings_config.move_messages_between_streams_policy_values, - ); + options.move_messages_between_streams_policy_values = + settings_components.get_sorted_options_list( + settings_config.move_messages_between_streams_policy_values, + ); return options; } @@ -142,83 +118,6 @@ export function get_org_type_dropdown_options() { return settings_config.all_org_type_values; } -export function get_realm_time_limits_in_minutes(property) { - if (page_params[property] === null) { - // This represents "Anytime" case. - return null; - } - let val = (page_params[property] / 60).toFixed(1); - if (Number.parseFloat(val, 10) === Number.parseInt(val, 10)) { - val = Number.parseInt(val, 10); - } - return val.toString(); -} - -function get_property_value(property_name, for_realm_default_settings, sub) { - if (for_realm_default_settings) { - // realm_user_default_settings are stored in a separate object. - if (property_name === "twenty_four_hour_time") { - return JSON.stringify(realm_user_settings_defaults.twenty_four_hour_time); - } - if ( - property_name === "email_notifications_batching_period_seconds" || - property_name === "email_notification_batching_period_edit_minutes" - ) { - return realm_user_settings_defaults.email_notifications_batching_period_seconds; - } - return realm_user_settings_defaults[property_name]; - } - - if (sub) { - if (property_name === "stream_privacy") { - return stream_data.get_stream_privacy_policy(sub.stream_id); - } - if (property_name === "is_default_stream") { - return stream_data.is_default_stream_id(sub.stream_id); - } - - return sub[property_name]; - } - - if (property_name === "realm_org_join_restrictions") { - if (page_params.realm_emails_restricted_to_domains) { - return "only_selected_domain"; - } - if (page_params.realm_disallow_disposable_email_addresses) { - return "no_disposable_email"; - } - return "no_restriction"; - } - - return page_params[property_name]; -} - -export function extract_property_name($elem, for_realm_default_settings) { - if (for_realm_default_settings) { - // ID for realm_user_default_settings elements are of the form - // "realm_{settings_name}}" because both user and realm default - // settings use the same template and each element should have - // unique id. - return /^realm_(.*)$/.exec($elem.attr("id").replaceAll("-", "_"))[1]; - } - - if ($elem.attr("id").startsWith("id_authmethod")) { - // Authentication Method component IDs include authentication method name - // for uniqueness, anchored to "id_authmethod" prefix, e.g. "id_authmethodapple_". - // We need to strip that whole construct down to extract the actual property name. - // The [\da-z]+ part of the regexp covers the auth method name itself. - // We assume it's not an empty string and can contain only digits and lowercase ASCII letters, - // this is ensured by a respective allowlist-based filter in populate_auth_methods(). - return /^id_authmethod[\da-z]+_(.*)$/.exec($elem.attr("id"))[1]; - } - - return /^id_(.*)$/.exec($elem.attr("id").replaceAll("-", "_"))[1]; -} - -export function get_subsection_property_elements(subsection) { - return [...$(subsection).find(".prop-element")]; -} - const simple_dropdown_properties = [ "realm_create_private_stream_policy", "realm_create_public_stream_policy", @@ -234,19 +133,6 @@ const simple_dropdown_properties = [ "realm_org_type", ]; -function set_property_dropdown_value(property_name) { - $(`#id_${CSS.escape(property_name)}`).val(get_property_value(property_name)); -} - -export function change_element_block_display_property(elem_id, show_element) { - const $elem = $(`#${CSS.escape(elem_id)}`); - if (show_element) { - $elem.parent().show(); - } else { - $elem.parent().hide(); - } -} - function set_realm_waiting_period_setting() { const setting_value = page_params.realm_waiting_period_threshold; const valid_limit_values = settings_config.waiting_period_threshold_dropdown_values.map( @@ -260,45 +146,18 @@ function set_realm_waiting_period_setting() { } $("#id_realm_waiting_period_threshold_custom_input").val(setting_value); - change_element_block_display_property( + settings_components.change_element_block_display_property( "id_realm_waiting_period_threshold_custom_input", $("#id_realm_waiting_period_threshold").val() === "custom_period", ); } -function is_video_chat_provider_jitsi_meet() { - const video_chat_provider_id = Number.parseInt($("#id_realm_video_chat_provider").val(), 10); - const jitsi_meet_id = page_params.realm_available_video_chat_providers.jitsi_meet.id; - return video_chat_provider_id === jitsi_meet_id; -} - -function get_jitsi_server_url_setting_value($input_elem, for_api_data = true) { - // If the video chat provider dropdown is not set to Jitsi, we return - // `realm_jitsi_server_url` to indicate that the property remains unchanged. - // This ensures the appropriate state of the save button and prevents the - // addition of the `jitsi_server_url` in the API data. - if (!is_video_chat_provider_jitsi_meet()) { - return page_params.realm_jitsi_server_url; - } - - const select_elem_val = $input_elem.val(); - if (select_elem_val === "server_default") { - if (!for_api_data) { - return null; - } - return JSON.stringify("default"); - } - - const $custom_input_elem = $("#id_realm_jitsi_server_url_custom_input"); - if (!for_api_data) { - return $custom_input_elem.val(); - } - return JSON.stringify($custom_input_elem.val()); -} - function update_jitsi_server_url_custom_input(dropdown_val) { const custom_input = "id_realm_jitsi_server_url_custom_input"; - change_element_block_display_property(custom_input, dropdown_val === "custom"); + settings_components.change_element_block_display_property( + custom_input, + dropdown_val === "custom", + ); if (dropdown_val !== "custom") { return; @@ -309,7 +168,7 @@ function update_jitsi_server_url_custom_input(dropdown_val) { } function set_jitsi_server_url_dropdown() { - if (!is_video_chat_provider_jitsi_meet()) { + if (!settings_components.is_video_chat_provider_jitsi_meet()) { $("#realm_jitsi_server_url_setting").hide(); return; } @@ -350,52 +209,8 @@ function update_message_edit_sub_settings(is_checked) { ); } -function update_custom_value_input(property_name) { - const $dropdown_elem = $(`#id_${CSS.escape(property_name)}`); - const custom_input_elem_id = $dropdown_elem - .parent() - .find(".time-limit-custom-input") - .attr("id"); - - const show_custom_limit_input = $dropdown_elem.val() === "custom_period"; - change_element_block_display_property(custom_input_elem_id, show_custom_limit_input); - if (show_custom_limit_input) { - $(`#${CSS.escape(custom_input_elem_id)}`).val( - get_realm_time_limits_in_minutes(property_name), - ); - } -} - -function get_time_limit_dropdown_setting_value(property_name) { - if (page_params[property_name] === null) { - return "any_time"; - } - - const valid_limit_values = settings_config.time_limit_dropdown_values.map((x) => x.value); - if (valid_limit_values.includes(page_params[property_name])) { - return page_params[property_name].toString(); - } - - return "custom_period"; -} - -function set_time_limit_setting(property_name) { - const dropdown_elem_val = get_time_limit_dropdown_setting_value(property_name); - $(`#id_${CSS.escape(property_name)}`).val(dropdown_elem_val); - - const $custom_input = $(`#id_${CSS.escape(property_name)}`) - .parent() - .find(".time-limit-custom-input"); - $custom_input.val(get_realm_time_limits_in_minutes(property_name)); - - change_element_block_display_property( - $custom_input.attr("id"), - dropdown_elem_val === "custom_period", - ); -} - function set_msg_edit_limit_dropdown() { - 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) { @@ -432,7 +247,7 @@ function enable_or_disable_related_message_move_time_limit_setting(setting_name, } function set_msg_move_limit_setting(property_name) { - set_time_limit_setting(property_name); + settings_components.set_time_limit_setting(property_name); let disable_setting; if (property_name === "realm_move_messages_within_stream_limit_seconds") { @@ -463,7 +278,7 @@ function set_delete_own_message_policy_dropdown(setting_value) { "id_realm_message_content_delete_limit_seconds", true, ); - const limit_setting_dropdown_value = get_time_limit_dropdown_setting_value( + const limit_setting_dropdown_value = settings_components.get_time_limit_dropdown_setting_value( "realm_message_content_delete_limit_seconds", ); if (limit_setting_dropdown_value === "custom_period") { @@ -476,30 +291,7 @@ function set_delete_own_message_policy_dropdown(setting_value) { } function set_msg_delete_limit_dropdown() { - set_time_limit_setting("realm_message_content_delete_limit_seconds"); -} - -function get_message_retention_setting_value($input_elem, for_api_data = true) { - const select_elem_val = $input_elem.val(); - if (select_elem_val === "unlimited") { - if (!for_api_data) { - return settings_config.retain_message_forever; - } - return JSON.stringify("unlimited"); - } - - if (select_elem_val === "realm_default") { - if (!for_api_data) { - return null; - } - return JSON.stringify("realm_default"); - } - - const $custom_input = $input_elem.parent().find(".message-retention-setting-custom-input"); - if ($custom_input.val().length === 0) { - return settings_config.retain_message_forever; - } - return Number.parseInt(Number($custom_input.val()), 10); + settings_components.set_time_limit_setting("realm_message_content_delete_limit_seconds"); } function get_dropdown_value_for_message_retention_setting(setting_value) { @@ -519,7 +311,7 @@ export function set_message_retention_setting_dropdown(sub) { if (sub !== undefined) { property_name = "message_retention_days"; } - const setting_value = get_property_value(property_name, false, sub); + const setting_value = settings_components.get_property_value(property_name, false, sub); const dropdown_val = get_dropdown_value_for_message_retention_setting(setting_value); const $dropdown_elem = $(`#id_${CSS.escape(property_name)}`); @@ -533,37 +325,37 @@ export function set_message_retention_setting_dropdown(sub) { $custom_input_elem.val(setting_value); } - change_element_block_display_property( + settings_components.change_element_block_display_property( $custom_input_elem.attr("id"), dropdown_val === "custom_period", ); } function set_org_join_restrictions_dropdown() { - const value = get_property_value("realm_org_join_restrictions"); + const value = settings_components.get_property_value("realm_org_join_restrictions"); $("#id_realm_org_join_restrictions").val(value); - change_element_block_display_property( + settings_components.change_element_block_display_property( "allowed_domains_label", value === "only_selected_domain", ); } function set_message_content_in_email_notifications_visibility() { - change_element_block_display_property( + settings_components.change_element_block_display_property( "message_content_in_email_notifications_label", page_params.realm_message_content_allowed_in_email_notifications, ); } function set_digest_emails_weekday_visibility() { - change_element_block_display_property( + settings_components.change_element_block_display_property( "id_realm_digest_weekday", page_params.realm_digest_emails_enabled, ); } function set_create_web_public_stream_dropdown_visibility() { - change_element_block_display_property( + settings_components.change_element_block_display_property( "id_realm_create_web_public_stream_policy", page_params.server_web_public_streams_enabled && page_params.zulip_plan_is_not_limited && @@ -586,17 +378,6 @@ export function populate_realm_domains_label(realm_domains) { $("#allowed_domains_label").text($t({defaultMessage: "Allowed domains: {domains}"}, {domains})); } -function sort_object_by_key(obj) { - const keys = Object.keys(obj).sort(); - const new_obj = {}; - - for (const key of keys) { - new_obj[key] = obj[key]; - } - - return new_obj; -} - function can_configure_auth_methods() { if (settings_data.user_email_not_configured()) { return false; @@ -612,7 +393,7 @@ export function populate_auth_methods(auth_methods) { return; } const $auth_methods_list = $("#id_realm_authentication_methods").expectOne(); - auth_methods = sort_object_by_key(auth_methods); + auth_methods = settings_components.sort_object_by_key(auth_methods); let rendered_auth_method_rows = ""; for (const [auth_method, value] of Object.entries(auth_methods)) { rendered_auth_method_rows += render_settings_admin_auth_methods_list({ @@ -633,7 +414,7 @@ export function populate_auth_methods(auth_methods) { function update_dependent_subsettings(property_name) { if (simple_dropdown_properties.includes(property_name)) { - set_property_dropdown_value(property_name); + settings_components.set_property_dropdown_value(property_name); return; } @@ -665,55 +446,17 @@ function update_dependent_subsettings(property_name) { } } -export let default_code_language_widget = null; -export let notifications_stream_widget = null; -export let signup_notifications_stream_widget = null; -export let create_multiuse_invite_group_widget = null; -export let can_remove_subscribers_group_widget = null; - -export function get_widget_for_dropdown_list_settings(property_name) { - switch (property_name) { - case "realm_notifications_stream_id": - return notifications_stream_widget; - case "realm_signup_notifications_stream_id": - return signup_notifications_stream_widget; - case "realm_default_code_block_language": - return default_code_language_widget; - case "realm_create_multiuse_invite_group": - return create_multiuse_invite_group_widget; - case "can_remove_subscribers_group": - return can_remove_subscribers_group_widget; - default: - blueslip.error("No dropdown list widget for property", {property_name}); - return null; - } -} - -export function set_can_remove_subscribers_group_widget(widget) { - can_remove_subscribers_group_widget = widget; -} - -export function set_dropdown_list_widget_setting_value(property_name, value) { - const widget = get_widget_for_dropdown_list_settings(property_name); - widget.render(value); -} - -export function get_dropdown_list_widget_setting_value($input_elem) { - const widget_name = extract_property_name($input_elem); - const setting_widget = get_widget_for_dropdown_list_settings(widget_name); - - const setting_value_type = $input_elem.attr("data-setting-value-type"); - if (setting_value_type === "number") { - return Number.parseInt(setting_widget.value(), 10); - } - - return setting_widget.value(); -} - export function discard_property_element_changes(elem, for_realm_default_settings, sub) { const $elem = $(elem); - const property_name = extract_property_name($elem, for_realm_default_settings); - const property_value = get_property_value(property_name, for_realm_default_settings, sub); + const property_name = settings_components.extract_property_name( + $elem, + for_realm_default_settings, + ); + const property_value = settings_components.get_property_value( + property_name, + for_realm_default_settings, + sub, + ); switch (property_name) { case "notification_sound": @@ -723,7 +466,7 @@ export function discard_property_element_changes(elem, for_realm_default_setting notification_sound: property_value, }, ); - set_input_element_value($elem, property_value); + settings_components.set_input_element_value($elem, property_value); break; case "realm_authentication_methods": populate_auth_methods(property_value); @@ -733,7 +476,10 @@ export function discard_property_element_changes(elem, for_realm_default_setting case "realm_default_code_block_language": case "can_remove_subscribers_group": case "realm_create_multiuse_invite_group": - set_dropdown_list_widget_setting_value(property_name, property_value); + settings_components.set_dropdown_list_widget_setting_value( + property_name, + property_value, + ); break; case "realm_default_language": $("#org-notifications .language_selection_widget .language_selection_button span").attr( @@ -759,7 +505,7 @@ export function discard_property_element_changes(elem, for_realm_default_setting ); break; case "realm_org_type": - 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 // dropdown menu options whenever page_params.realm_org_type // returns another value. @@ -769,7 +515,7 @@ export function discard_property_element_changes(elem, for_realm_default_setting break; case "realm_message_content_edit_limit_seconds": case "realm_message_content_delete_limit_seconds": - set_time_limit_setting(property_name); + settings_components.set_time_limit_setting(property_name); break; case "realm_move_messages_within_stream_limit_seconds": case "realm_move_messages_between_streams_limit_seconds": @@ -790,7 +536,7 @@ export function discard_property_element_changes(elem, for_realm_default_setting break; default: if (property_value !== undefined) { - set_input_element_value($elem, property_value); + settings_components.set_input_element_value($elem, property_value); } else { blueslip.error("Element refers to unknown property", {property_name}); } @@ -842,320 +588,31 @@ export function sync_realm_settings(property) { } } -export function change_save_button_state($element, state) { - function show_hide_element($element, show, fadeout_delay, fadeout_callback) { - if (show) { - $element.removeClass("hide").addClass(".show").fadeIn(300); - return; - } - setTimeout(() => { - $element.fadeOut(300, fadeout_callback); - }, fadeout_delay); - } - - const $saveBtn = $element.find(".save-button"); - const $textEl = $saveBtn.find(".save-discard-widget-button-text"); - - if (state !== "saving") { - $saveBtn.removeClass("saving"); - } - - if (state === "discarded") { - show_hide_element($element, false, 0, () => - enable_or_disable_save_button($element.closest(".settings-subsection-parent")), - ); - return; - } - - let button_text; - let data_status; - let is_show; - switch (state) { - case "unsaved": - button_text = $t({defaultMessage: "Save changes"}); - data_status = "unsaved"; - is_show = true; - - $element.find(".discard-button").show(); - break; - case "saved": - button_text = $t({defaultMessage: "Save changes"}); - data_status = ""; - is_show = false; - break; - case "saving": - button_text = $t({defaultMessage: "Saving"}); - data_status = "saving"; - is_show = true; - - $element.find(".discard-button").hide(); - $saveBtn.addClass("saving"); - break; - case "failed": - button_text = $t({defaultMessage: "Save changes"}); - data_status = "failed"; - is_show = true; - break; - case "succeeded": - button_text = $t({defaultMessage: "Saved"}); - data_status = "saved"; - is_show = false; - break; - } - - $textEl.text(button_text); - $saveBtn.attr("data-status", data_status); - if (state === "unsaved") { - enable_or_disable_save_button($element.closest(".settings-subsection-parent")); - } - show_hide_element($element, is_show, 800); -} - export function save_organization_settings(data, $save_button, patch_url) { const $subsection_parent = $save_button.closest(".settings-subsection-parent"); const $save_btn_container = $subsection_parent.find(".save-button-controls"); const $failed_alert_elem = $subsection_parent.find(".subsection-failed-status p"); - change_save_button_state($save_btn_container, "saving"); + settings_components.change_save_button_state($save_btn_container, "saving"); channel.patch({ url: patch_url, data, success() { $failed_alert_elem.hide(); - change_save_button_state($save_btn_container, "succeeded"); + settings_components.change_save_button_state($save_btn_container, "succeeded"); }, error(xhr) { - change_save_button_state($save_btn_container, "failed"); + settings_components.change_save_button_state($save_btn_container, "failed"); $save_button.hide(); ui_report.error($t_html({defaultMessage: "Save failed"}), xhr, $failed_alert_elem); }, }); } -function get_input_type($input_elem, input_type) { - if (["boolean", "string", "number"].includes(input_type)) { - return input_type; - } - return $input_elem.data("setting-widget-type"); -} - -export function get_input_element_value(input_elem, input_type) { - const $input_elem = $(input_elem); - input_type = get_input_type($input_elem, input_type); - switch (input_type) { - case "boolean": - return $input_elem.prop("checked"); - case "string": - return $input_elem.val().trim(); - case "number": - return Number.parseInt($input_elem.val().trim(), 10); - case "radio-group": { - const selected_val = $input_elem.find("input:checked").val(); - if ($input_elem.data("setting-choice-type") === "number") { - return Number.parseInt(selected_val, 10); - } - return selected_val.trim(); - } - case "time-limit": - return get_time_limit_setting_value($input_elem); - case "jitsi-server-url-setting": - return get_jitsi_server_url_setting_value($input_elem); - case "message-retention-setting": - return get_message_retention_setting_value($input_elem); - case "dropdown-list-widget": - return get_dropdown_list_widget_setting_value($input_elem); - default: - return undefined; - } -} - -export function set_input_element_value($input_elem, value) { - const input_type = get_input_type($input_elem, typeof value); - if (input_type) { - if (input_type === "boolean") { - return $input_elem.prop("checked", value); - } else if (input_type === "string" || input_type === "number") { - return $input_elem.val(value); - } - } - blueslip.error("Failed to set value of property", { - property: extract_property_name($input_elem), - }); - return undefined; -} - export function set_up() { build_page(); maybe_disable_widgets(); } -function get_auth_method_list_data() { - const new_auth_methods = {}; - const $auth_method_rows = $("#id_realm_authentication_methods").find("div.method_row"); - - for (const method_row of $auth_method_rows) { - new_auth_methods[$(method_row).data("method")] = $(method_row) - .find("input") - .prop("checked"); - } - - return new_auth_methods; -} - -export function parse_time_limit($elem) { - return Math.floor(Number.parseFloat(Number($elem.val()), 10).toFixed(1) * 60); -} - -function get_time_limit_setting_value($input_elem, for_api_data = true) { - const select_elem_val = $input_elem.val(); - - if (select_elem_val === "any_time") { - // "unlimited" is sent to API when a user wants to set the setting to - // "Any time" and the message_content_edit_limit_seconds field is "null" - // for that case. - if (!for_api_data) { - return null; - } - return JSON.stringify("unlimited"); - } - - if (select_elem_val !== "custom_period") { - return Number.parseInt(select_elem_val, 10); - } - - const $custom_input_elem = $input_elem.parent().find(".time-limit-custom-input"); - if ($custom_input_elem.val().length === 0) { - // This handles the case where the initial setting value is "Any time" and then - // dropdown is changed to "Custom" where the input box is empty initially and - // thus we do not show the save-discard widget until something is typed in the - // input box. - return null; - } - - if ($input_elem.attr("id") === "id_realm_waiting_period_threshold") { - // For realm waiting period threshold setting, the custom input element contains - // number of days. - return Number.parseInt(Number($custom_input_elem.val()), 10); - } - - return parse_time_limit($custom_input_elem); -} - -export function check_property_changed(elem, for_realm_default_settings, sub) { - const $elem = $(elem); - const property_name = extract_property_name($elem, for_realm_default_settings); - let current_val = get_property_value(property_name, for_realm_default_settings, sub); - let proposed_val; - - switch (property_name) { - case "realm_authentication_methods": - current_val = sort_object_by_key(current_val); - current_val = JSON.stringify(current_val); - proposed_val = get_auth_method_list_data(); - proposed_val = JSON.stringify(proposed_val); - break; - case "realm_notifications_stream_id": - case "realm_signup_notifications_stream_id": - case "realm_default_code_block_language": - case "can_remove_subscribers_group": - case "realm_create_multiuse_invite_group": - proposed_val = get_dropdown_list_widget_setting_value($elem); - break; - case "email_notifications_batching_period_seconds": - proposed_val = get_time_limit_setting_value($elem, false); - break; - case "realm_message_content_edit_limit_seconds": - case "realm_message_content_delete_limit_seconds": - case "realm_move_messages_between_streams_limit_seconds": - case "realm_move_messages_within_stream_limit_seconds": - case "realm_waiting_period_threshold": - proposed_val = get_time_limit_setting_value($elem, false); - break; - case "realm_message_retention_days": - case "message_retention_days": - proposed_val = get_message_retention_setting_value($elem, false); - break; - case "realm_jitsi_server_url": - proposed_val = get_jitsi_server_url_setting_value($elem, false); - break; - case "realm_default_language": - proposed_val = $( - "#org-notifications .language_selection_widget .language_selection_button span", - ).attr("data-language-code"); - break; - case "emojiset": - case "user_list_style": - case "stream_privacy": - proposed_val = get_input_element_value($elem, "radio-group"); - break; - default: - if (current_val !== undefined) { - proposed_val = get_input_element_value($elem, typeof current_val); - } else { - blueslip.error("Element refers to unknown property", {property_name}); - } - } - return current_val !== proposed_val; -} - -function switching_to_private(properties_elements, for_realm_default_settings) { - for (const elem of properties_elements) { - const $elem = $(elem); - const property_name = extract_property_name($elem, for_realm_default_settings); - if (property_name !== "stream_privacy") { - continue; - } - const proposed_val = get_input_element_value($elem, "radio-group"); - return proposed_val === "invite-only-public-history" || proposed_val === "invite-only"; - } - return false; -} - -export function save_discard_widget_status_handler($subsection, for_realm_default_settings, sub) { - $subsection.find(".subsection-failed-status p").hide(); - $subsection.find(".save-button").show(); - const properties_elements = get_subsection_property_elements($subsection); - const show_change_process_button = properties_elements.some((elem) => - check_property_changed(elem, for_realm_default_settings, sub), - ); - - const $save_btn_controls = $subsection.find(".subsection-header .save-button-controls"); - const button_state = show_change_process_button ? "unsaved" : "discarded"; - change_save_button_state($save_btn_controls, button_state); - - // If this widget is for a stream, and the stream isn't currently private - // but being changed to private, and the user changing this setting isn't - // subscribed, we show a warning that they won't be able to access the - // stream after making it private unless they subscribe. - if (!sub) { - return; - } - if ( - button_state === "unsaved" && - !sub.invite_only && - !sub.subscribed && - switching_to_private(properties_elements, for_realm_default_settings) - ) { - if ($("#stream_permission_settings .stream_privacy_warning").length > 0) { - return; - } - const context = { - banner_type: compose_banner.WARNING, - banner_text: $t({ - defaultMessage: - "Only subscribers can access or join private streams, so you will lose access to this stream if you convert it to a private stream while not subscribed to it.", - }), - button_text: $t({defaultMessage: "Subscribe"}), - classname: "stream_privacy_warning", - stream_id: sub.stream_id, - }; - $("#stream_permission_settings .stream-permissions-warning-banner").append( - render_compose_banner(context), - ); - } else { - $("#stream_permission_settings .stream-permissions-warning-banner").empty(); - } -} - export function init_dropdown_widgets() { const notification_stream_options = () => { const streams = stream_settings_data.get_streams_for_settings_page(); @@ -1175,7 +632,7 @@ export function init_dropdown_widgets() { return options; }; - notifications_stream_widget = new dropdown_widget.DropdownWidget({ + const notifications_stream_widget = new dropdown_widget.DropdownWidget({ widget_name: "realm_notifications_stream_id", get_options: notification_stream_options, $events_container: $("#settings_overlay_container #organization-settings"), @@ -1183,8 +640,8 @@ export function init_dropdown_widgets() { dropdown.hide(); event.preventDefault(); event.stopPropagation(); - notifications_stream_widget.render(); - save_discard_widget_status_handler($("#org-notifications")); + settings_components.notifications_stream_widget.render(); + settings_components.save_discard_widget_status_handler($("#org-notifications")); }, tippy_props: { placement: "bottom-start", @@ -1193,9 +650,10 @@ export function init_dropdown_widgets() { unique_id_type: dropdown_widget.DATA_TYPES.NUMBER, show_disabled_if_current_value_not_in_options: true, }); + settings_components.set_notifications_stream_widget(notifications_stream_widget); notifications_stream_widget.setup(); - signup_notifications_stream_widget = new dropdown_widget.DropdownWidget({ + const signup_notifications_stream_widget = new dropdown_widget.DropdownWidget({ widget_name: "realm_signup_notifications_stream_id", get_options: notification_stream_options, $events_container: $("#settings_overlay_container #organization-settings"), @@ -1203,8 +661,8 @@ export function init_dropdown_widgets() { dropdown.hide(); event.preventDefault(); event.stopPropagation(); - signup_notifications_stream_widget.render(); - save_discard_widget_status_handler($("#org-notifications")); + settings_components.signup_notifications_stream_widget.render(); + settings_components.save_discard_widget_status_handler($("#org-notifications")); }, tippy_props: { placement: "bottom-start", @@ -1213,9 +671,10 @@ export function init_dropdown_widgets() { unique_id_type: dropdown_widget.DATA_TYPES.NUMBER, show_disabled_if_current_value_not_in_options: true, }); + settings_components.set_signup_notifications_stream_widget(signup_notifications_stream_widget); signup_notifications_stream_widget.setup(); - default_code_language_widget = new dropdown_widget.DropdownWidget({ + const default_code_language_widget = new dropdown_widget.DropdownWidget({ widget_name: "realm_default_code_block_language", get_options() { const options = Object.keys(pygments_data.langs).map((x) => ({ @@ -1242,13 +701,14 @@ export function init_dropdown_widgets() { dropdown.hide(); event.preventDefault(); event.stopPropagation(); - default_code_language_widget.render(); - save_discard_widget_status_handler($("#org-other-settings")); + settings_components.default_code_language_widget.render(); + settings_components.save_discard_widget_status_handler($("#org-other-settings")); }, }); + settings_components.set_default_code_language_widget(default_code_language_widget); default_code_language_widget.setup(); - create_multiuse_invite_group_widget = new dropdown_widget.DropdownWidget({ + const create_multiuse_invite_group_widget = new dropdown_widget.DropdownWidget({ widget_name: "realm_create_multiuse_invite_group", get_options: () => user_groups.get_realm_user_groups_for_dropdown_list_widget( @@ -1259,8 +719,8 @@ export function init_dropdown_widgets() { dropdown.hide(); event.preventDefault(); event.stopPropagation(); - create_multiuse_invite_group_widget.render(); - save_discard_widget_status_handler($("#org-join-settings")); + settings_components.create_multiuse_invite_group_widget.render(); + settings_components.save_discard_widget_status_handler($("#org-join-settings")); }, tippy_props: { placement: "bottom-start", @@ -1271,93 +731,29 @@ export function init_dropdown_widgets() { $(dropdown.popper).css("min-width", "300px"); }, }); + settings_components.set_create_multiuse_invite_group_widget( + create_multiuse_invite_group_widget, + ); create_multiuse_invite_group_widget.setup(); } -function check_maximum_valid_value($custom_input_elem, property_name) { - let setting_value = Number.parseInt($custom_input_elem.val(), 10); - if ( - property_name === "realm_message_content_edit_limit_seconds" || - property_name === "realm_message_content_delete_limit_seconds" || - property_name === "email_notifications_batching_period_seconds" - ) { - setting_value = parse_time_limit($custom_input_elem); - } - return setting_value <= MAX_CUSTOM_TIME_LIMIT_SETTING_VALUE; -} - -function should_disable_save_button_for_jitsi_server_url_setting() { - if (!is_video_chat_provider_jitsi_meet()) { - return false; - } - - const $dropdown_elem = $("#id_realm_jitsi_server_url"); - const $custom_input_elem = $("#id_realm_jitsi_server_url_custom_input"); - - return $dropdown_elem.val() === "custom" && !util.is_valid_url($custom_input_elem.val(), true); -} - -function should_disable_save_button_for_time_limit_settings(time_limit_settings) { - let disable_save_btn = false; - for (const setting_elem of time_limit_settings) { - const $dropdown_elem = $(setting_elem).find("select"); - const $custom_input_elem = $(setting_elem).find(".time-limit-custom-input"); - const custom_input_elem_val = Number.parseInt(Number($custom_input_elem.val()), 10); - - const for_realm_default_settings = - $dropdown_elem.closest(".settings-section.show").attr("id") === - "realm-user-default-settings"; - const property_name = extract_property_name($dropdown_elem, for_realm_default_settings); - - disable_save_btn = - $dropdown_elem.val() === "custom_period" && - (custom_input_elem_val <= 0 || - Number.isNaN(custom_input_elem_val) || - !check_maximum_valid_value($custom_input_elem, property_name)); - - if ( - $custom_input_elem.val() === "0" && - property_name === "realm_waiting_period_threshold" - ) { - // 0 is a valid value for realm_waiting_period_threshold setting. We specifically - // check for $custom_input_elem.val() to be "0" and not custom_input_elem_val - // because it is 0 even when custom input box is empty. - disable_save_btn = false; - } - - if (disable_save_btn) { - break; - } - } - - return disable_save_btn; -} - -function enable_or_disable_save_button($subsection_elem) { - const time_limit_settings = [...$subsection_elem.find(".time-limit-setting")]; - - let disable_save_btn = false; - if (time_limit_settings.length) { - disable_save_btn = should_disable_save_button_for_time_limit_settings(time_limit_settings); - } else if ($subsection_elem.attr("id") === "org-other-settings") { - disable_save_btn = should_disable_save_button_for_jitsi_server_url_setting(); - } - - $subsection_elem.find(".subsection-changes-save button").prop("disabled", disable_save_btn); -} - export function populate_data_for_request(subsection, for_realm_default_settings, sub) { let data = {}; - const properties_elements = get_subsection_property_elements(subsection); + const properties_elements = settings_components.get_subsection_property_elements(subsection); for (const input_elem of properties_elements) { const $input_elem = $(input_elem); - if (check_property_changed($input_elem, for_realm_default_settings, sub)) { - const input_value = get_input_element_value($input_elem); + if ( + settings_components.check_property_changed($input_elem, for_realm_default_settings, sub) + ) { + const input_value = settings_components.get_input_element_value($input_elem); if (input_value !== undefined) { let property_name; if (for_realm_default_settings || sub) { - property_name = extract_property_name($input_elem, for_realm_default_settings); + property_name = settings_components.extract_property_name( + $input_elem, + for_realm_default_settings, + ); } else if ($input_elem.attr("id").startsWith("id_authmethod")) { // Authentication Method component IDs include authentication method name // for uniqueness, anchored to "id_authmethod" prefix, e.g. "id_authmethodapple_". @@ -1410,14 +806,17 @@ export function register_save_discard_widget_handlers( if ($(e.target).hasClass("setting_email_notifications_batching_period_seconds")) { const show_elem = $(e.target).val() === "custom_period"; - change_element_block_display_property( + settings_components.change_element_block_display_property( "realm_email_notification_batching_period_edit_minutes", show_elem, ); } const $subsection = $(e.target).closest(".settings-subsection-parent"); - save_discard_widget_status_handler($subsection, for_realm_default_settings); + settings_components.save_discard_widget_status_handler( + $subsection, + for_realm_default_settings, + ); return undefined; }); @@ -1425,11 +824,11 @@ export function register_save_discard_widget_handlers( e.preventDefault(); e.stopPropagation(); const $subsection = $(e.target).closest(".settings-subsection-parent"); - for (const elem of get_subsection_property_elements($subsection)) { + for (const elem of settings_components.get_subsection_property_elements($subsection)) { discard_property_element_changes(elem, for_realm_default_settings); } const $save_btn_controls = $(e.target).closest(".save-button-controls"); - change_save_button_state($save_btn_controls, "discarded"); + settings_components.change_save_button_state($save_btn_controls, "discarded"); }); function get_complete_data_for_subsection(subsection) { @@ -1461,7 +860,9 @@ export function register_save_discard_widget_handlers( } case "auth_settings": data = {}; - data.authentication_methods = JSON.stringify(get_auth_method_list_data()); + data.authentication_methods = JSON.stringify( + settings_components.get_auth_method_list_data(), + ); break; } return data; @@ -1505,7 +906,7 @@ export function build_page() { populate_auth_methods(page_params.realm_authentication_methods); for (const property_name of simple_dropdown_properties) { - set_property_dropdown_value(property_name); + settings_components.set_property_dropdown_value(property_name); } set_realm_waiting_period_setting(); @@ -1536,19 +937,23 @@ export function build_page() { }); $("#id_realm_message_content_edit_limit_seconds").on("change", () => { - update_custom_value_input("realm_message_content_edit_limit_seconds"); + settings_components.update_custom_value_input("realm_message_content_edit_limit_seconds"); }); $("#id_realm_move_messages_between_streams_limit_seconds").on("change", () => { - update_custom_value_input("realm_move_messages_between_streams_limit_seconds"); + settings_components.update_custom_value_input( + "realm_move_messages_between_streams_limit_seconds", + ); }); $("#id_realm_move_messages_within_stream_limit_seconds").on("change", () => { - update_custom_value_input("realm_move_messages_within_stream_limit_seconds"); + settings_components.update_custom_value_input( + "realm_move_messages_within_stream_limit_seconds", + ); }); $("#id_realm_message_content_delete_limit_seconds").on("change", () => { - update_custom_value_input("realm_message_content_delete_limit_seconds"); + settings_components.update_custom_value_input("realm_message_content_delete_limit_seconds"); }); $("#id_realm_video_chat_provider").on("change", () => { @@ -1562,7 +967,7 @@ export function build_page() { $("#id_realm_message_retention_days").on("change", (e) => { const message_retention_setting_dropdown_value = e.target.value; - change_element_block_display_property( + settings_components.change_element_block_display_property( "id_realm_message_retention_custom_input", message_retention_setting_dropdown_value === "custom_period", ); @@ -1570,7 +975,7 @@ export function build_page() { $("#id_realm_waiting_period_threshold").on("change", function () { const waiting_period_threshold = this.value; - change_element_block_display_property( + settings_components.change_element_block_display_property( "id_realm_waiting_period_threshold_custom_input", waiting_period_threshold === "custom_period", ); @@ -1578,7 +983,7 @@ export function build_page() { $("#id_realm_digest_emails_enabled").on("change", (e) => { const digest_emails_enabled = $(e.target).is(":checked"); - change_element_block_display_property( + settings_components.change_element_block_display_property( "id_realm_digest_weekday", digest_emails_enabled === true, ); @@ -1604,7 +1009,7 @@ export function build_page() { $("#org-moving-msgs").on("change", ".move-message-policy-setting", (e) => { const $policy_dropdown_elem = $(e.target); - const property_name = extract_property_name($policy_dropdown_elem); + const property_name = settings_components.extract_property_name($policy_dropdown_elem); const disable_time_limit_setting = message_move_limit_setting_enabled(property_name); let time_limit_setting_name; diff --git a/web/src/settings_realm_user_settings_defaults.js b/web/src/settings_realm_user_settings_defaults.js index 405a02ff26570..f3a6a30ae1799 100644 --- a/web/src/settings_realm_user_settings_defaults.js +++ b/web/src/settings_realm_user_settings_defaults.js @@ -4,6 +4,7 @@ import * as audible_notifications from "./audible_notifications"; import * as overlays from "./overlays"; import {page_params} from "./page_params"; import {realm_user_settings_defaults} from "./realm_user_settings_defaults"; +import * as settings_components from "./settings_components"; import * as settings_display from "./settings_display"; import * as settings_notifications from "./settings_notifications"; import * as settings_org from "./settings_org"; @@ -49,7 +50,7 @@ export function update_page(property) { } const $input_elem = $container.find(`[name=${CSS.escape(property)}]`); - settings_org.set_input_element_value($input_elem, value); + settings_components.set_input_element_value($input_elem, value); } export function set_up() { diff --git a/web/src/stream_edit.js b/web/src/stream_edit.js index 8b3d9a3338e35..07ba724816094 100644 --- a/web/src/stream_edit.js +++ b/web/src/stream_edit.js @@ -22,6 +22,7 @@ import * as keydown_util from "./keydown_util"; import * as narrow_state from "./narrow_state"; import {page_params} from "./page_params"; import * as scroll_util from "./scroll_util"; +import * as settings_components from "./settings_components"; import * as settings_config from "./settings_config"; import * as settings_org from "./settings_org"; import * as stream_color from "./stream_color"; @@ -204,7 +205,7 @@ function setup_dropdown(sub, slim_sub) { event.preventDefault(); event.stopPropagation(); can_remove_subscribers_group_widget.render(); - settings_org.save_discard_widget_status_handler( + settings_components.save_discard_widget_status_handler( $("#stream_permission_settings"), false, slim_sub, @@ -220,7 +221,9 @@ function setup_dropdown(sub, slim_sub) { $(dropdown.popper).css("min-width", "300px"); }, }); - settings_org.set_can_remove_subscribers_group_widget(can_remove_subscribers_group_widget); + settings_components.set_can_remove_subscribers_group_widget( + can_remove_subscribers_group_widget, + ); can_remove_subscribers_group_widget.setup(); } @@ -632,7 +635,7 @@ export function initialize() { $("#streams_overlay_container").on("change", ".stream_message_retention_setting", (e) => { const message_retention_setting_dropdown_value = e.target.value; - settings_org.change_element_block_display_property( + settings_components.change_element_block_display_property( "stream_message_retention_custom_input", message_retention_setting_dropdown_value === "custom_period", ); @@ -652,7 +655,7 @@ export function initialize() { const stream_id = get_stream_id(e.target); const sub = sub_store.get(stream_id); const $subsection = $(e.target).closest(".settings-subsection-parent"); - settings_org.save_discard_widget_status_handler($subsection, false, sub); + settings_components.save_discard_widget_status_handler($subsection, false, sub); if (sub) { stream_ui_updates.update_default_stream_and_stream_privacy_state($subsection); } @@ -688,12 +691,12 @@ export function initialize() { const sub = sub_store.get(stream_id); const $subsection = $(e.target).closest(".settings-subsection-parent"); - for (const elem of settings_org.get_subsection_property_elements($subsection)) { + for (const elem of settings_components.get_subsection_property_elements($subsection)) { settings_org.discard_property_element_changes(elem, false, sub); } stream_ui_updates.update_default_stream_and_stream_privacy_state($subsection); const $save_btn_controls = $(e.target).closest(".save-button-controls"); - settings_org.change_save_button_state($save_btn_controls, "discarded"); + settings_components.change_save_button_state($save_btn_controls, "discarded"); }, ); } diff --git a/web/tests/settings_org.test.js b/web/tests/settings_org.test.js index 79c23d5d0e574..9194e3014fffb 100644 --- a/web/tests/settings_org.test.js +++ b/web/tests/settings_org.test.js @@ -24,6 +24,7 @@ mock_esm("../src/loading", { const settings_config = zrequire("settings_config"); const settings_bots = zrequire("settings_bots"); const settings_account = zrequire("settings_account"); +const settings_components = zrequire("settings_components"); const settings_org = zrequire("settings_org"); const dropdown_widget = zrequire("dropdown_widget"); @@ -225,37 +226,37 @@ function test_change_save_button_state() { $save_button_header.attr("id", "org-msg-editing"); { - settings_org.change_save_button_state($save_button_controls, "unsaved"); + settings_components.change_save_button_state($save_button_controls, "unsaved"); assert.equal($save_button_text.text(), "translated: Save changes"); assert.equal(props.hidden, false); assert.equal($save_button.attr("data-status"), "unsaved"); assert.equal($discard_button.visible(), true); } { - settings_org.change_save_button_state($save_button_controls, "saved"); + settings_components.change_save_button_state($save_button_controls, "saved"); assert.equal($save_button_text.text(), "translated: Save changes"); assert.equal(props.hidden, true); assert.equal($save_button.attr("data-status"), ""); } { - settings_org.change_save_button_state($save_button_controls, "saving"); + settings_components.change_save_button_state($save_button_controls, "saving"); assert.equal($save_button_text.text(), "translated: Saving"); assert.equal($save_button.attr("data-status"), "saving"); assert.equal($save_button.hasClass("saving"), true); assert.equal($discard_button.visible(), false); } { - settings_org.change_save_button_state($save_button_controls, "discarded"); + settings_components.change_save_button_state($save_button_controls, "discarded"); assert.equal(props.hidden, true); } { - settings_org.change_save_button_state($save_button_controls, "succeeded"); + settings_components.change_save_button_state($save_button_controls, "succeeded"); assert.equal(props.hidden, true); assert.equal($save_button.attr("data-status"), "saved"); assert.equal($save_button_text.text(), "translated: Saved"); } { - settings_org.change_save_button_state($save_button_controls, "failed"); + settings_components.change_save_button_state($save_button_controls, "failed"); assert.equal(props.hidden, false); assert.equal($save_button.attr("data-status"), "failed"); assert.equal($save_button_text.text(), "translated: Save changes"); @@ -281,7 +282,7 @@ function test_upload_realm_icon(override, upload_realm_logo_or_icon) { function test_extract_property_name() { $("#id_realm_allow_message_editing").attr("id", "id_realm_allow_message_editing"); assert.equal( - settings_org.extract_property_name($("#id_realm_allow_message_editing")), + settings_components.extract_property_name($("#id_realm_allow_message_editing")), "realm_allow_message_editing", ); @@ -290,13 +291,15 @@ function test_extract_property_name() { "id_realm_message_content_edit_limit_minutes_label", ); assert.equal( - settings_org.extract_property_name($("#id_realm_message_content_edit_limit_minutes_label")), + settings_components.extract_property_name( + $("#id_realm_message_content_edit_limit_minutes_label"), + ), "realm_message_content_edit_limit_minutes_label", ); $("#id-realm-allow-message-deleting").attr("id", "id-realm-allow-message-deleting"); assert.equal( - settings_org.extract_property_name($("#id-realm-allow-message-deleting")), + settings_components.extract_property_name($("#id-realm-allow-message-deleting")), "realm_allow_message_deleting", ); } @@ -391,9 +394,10 @@ function test_parse_time_limit() { const $elem = $("#id_realm_message_content_edit_limit_minutes"); const test_function = (value, expected_value = value) => { $elem.val(value); - page_params.realm_message_content_edit_limit_seconds = settings_org.parse_time_limit($elem); + page_params.realm_message_content_edit_limit_seconds = + settings_components.parse_time_limit($elem); assert.equal( - settings_org.get_realm_time_limits_in_minutes( + settings_components.get_realm_time_limits_in_minutes( "realm_message_content_edit_limit_seconds", ), expected_value, @@ -725,7 +729,10 @@ test("test get_sorted_options_list", () => { description: $t({defaultMessage: "Admins"}), }, ]; - assert.deepEqual(settings_org.get_sorted_options_list(option_values_1), expected_option_values); + assert.deepEqual( + settings_components.get_sorted_options_list(option_values_1), + expected_option_values, + ); const option_values_2 = { by_admins_only: { @@ -758,7 +765,10 @@ test("test get_sorted_options_list", () => { description: $t({defaultMessage: "Admins, moderators and members"}), }, ]; - assert.deepEqual(settings_org.get_sorted_options_list(option_values_2), expected_option_values); + assert.deepEqual( + settings_components.get_sorted_options_list(option_values_2), + expected_option_values, + ); }); test("misc", () => {