2023-10-09 23:29:16 +02:00
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 { realm _user _settings _defaults } from "./realm_user_settings_defaults" ;
import * as settings _config from "./settings_config" ;
2024-02-13 02:08:24 +01:00
import { realm } from "./state_data" ;
2023-10-09 23:29:16 +02:00
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 ) {
2024-02-13 02:08:24 +01:00
if ( realm [ property ] === null ) {
2023-10-09 23:29:16 +02:00
// This represents "Anytime" case.
return null ;
}
2024-02-13 02:08:24 +01:00
let val = ( realm [ property ] / 60 ) . toFixed ( 1 ) ;
2023-10-09 23:29:16 +02:00
if ( Number . parseFloat ( val , 10 ) === Number . parseInt ( val , 10 ) ) {
val = Number . parseInt ( val , 10 ) ;
}
return val . toString ( ) ;
}
2023-11-27 11:36:07 +01:00
export function get _property _value ( property _name , for _realm _default _settings , sub , group ) {
2023-10-09 23:29:16 +02:00
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 ] ;
}
2023-11-27 11:36:07 +01:00
if ( group ) {
return group [ property _name ] ;
}
2023-10-09 23:29:16 +02:00
if ( property _name === "realm_org_join_restrictions" ) {
2024-02-13 02:08:24 +01:00
if ( realm . realm _emails _restricted _to _domains ) {
2023-10-09 23:29:16 +02:00
return "only_selected_domain" ;
}
2024-02-13 02:08:24 +01:00
if ( realm . realm _disallow _disposable _email _addresses ) {
2023-10-09 23:29:16 +02:00
return "no_disposable_email" ;
}
return "no_restriction" ;
}
2024-02-13 02:08:24 +01:00
return realm [ property _name ] ;
2023-10-09 23:29:16 +02:00
}
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_<property_name>".
// 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 ) ;
2024-02-13 02:08:24 +01:00
const jitsi _meet _id = realm . realm _available _video _chat _providers . jitsi _meet . id ;
2023-10-09 23:29:16 +02:00
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 ( ) ) {
2024-02-13 02:08:24 +01:00
return realm . realm _jitsi _server _url ;
2023-10-09 23:29:16 +02:00
}
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 ) {
2024-02-13 02:08:24 +01:00
if ( realm [ property _name ] === null ) {
2023-10-09 23:29:16 +02:00
return "any_time" ;
}
const valid _limit _values = settings _config . time _limit _dropdown _values . map ( ( x ) => x . value ) ;
2024-02-13 02:08:24 +01:00
if ( valid _limit _values . includes ( realm [ property _name ] ) ) {
return realm [ property _name ] . toString ( ) ;
2023-10-09 23:29:16 +02:00
}
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 ;
2024-02-07 12:13:02 +01:00
export let new _stream _announcements _stream _widget = null ;
2024-02-07 17:11:43 +01:00
export let signup _announcements _stream _widget = null ;
2023-10-09 23:29:16 +02:00
export let create _multiuse _invite _group _widget = null ;
export let can _remove _subscribers _group _widget = null ;
2023-11-23 10:14:35 +01:00
export let can _access _all _users _group _widget = null ;
2023-11-27 11:36:07 +01:00
export let can _mention _group _widget = null ;
export let new _group _can _mention _group _widget = null ;
2023-10-09 23:29:16 +02:00
export function get _widget _for _dropdown _list _settings ( property _name ) {
switch ( property _name ) {
2024-02-07 12:13:02 +01:00
case "realm_new_stream_announcements_stream_id" :
return new _stream _announcements _stream _widget ;
2024-02-07 17:11:43 +01:00
case "realm_signup_announcements_stream_id" :
return signup _announcements _stream _widget ;
2023-10-09 23:29:16 +02:00
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 ;
2023-11-23 10:14:35 +01:00
case "realm_can_access_all_users_group" :
return can _access _all _users _group _widget ;
2023-11-27 11:36:07 +01:00
case "can_mention_group" :
return can _mention _group _widget ;
2023-10-09 23:29:16 +02:00
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 ;
}
2024-02-07 12:13:02 +01:00
export function set _new _stream _announcements _stream _widget ( widget ) {
new _stream _announcements _stream _widget = widget ;
2023-10-09 23:29:16 +02:00
}
2024-02-07 17:11:43 +01:00
export function set _signup _announcements _stream _widget ( widget ) {
signup _announcements _stream _widget = widget ;
2023-10-09 23:29:16 +02:00
}
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 ;
}
2023-11-23 10:14:35 +01:00
export function set _can _access _all _users _group _widget ( widget ) {
can _access _all _users _group _widget = widget ;
}
2023-11-27 11:36:07 +01:00
export function set _can _mention _group _widget ( widget ) {
can _mention _group _widget = widget ;
}
export function set _new _group _can _mention _group _widget ( widget ) {
new _group _can _mention _group _widget = widget ;
}
2023-10-09 23:29:16 +02:00
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 ) ;
}
2023-11-27 11:36:07 +01:00
export function check _property _changed ( elem , for _realm _default _settings , sub , group ) {
2023-10-09 23:29:16 +02:00
const $elem = $ ( elem ) ;
const property _name = extract _property _name ( $elem , for _realm _default _settings ) ;
2023-11-27 11:36:07 +01:00
let current _val = get _property _value ( property _name , for _realm _default _settings , sub , group ) ;
2023-10-09 23:29:16 +02:00
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 ;
2024-02-07 12:13:02 +01:00
case "realm_new_stream_announcements_stream_id" :
2024-02-07 17:11:43 +01:00
case "realm_signup_announcements_stream_id" :
2023-10-09 23:29:16 +02:00
case "realm_default_code_block_language" :
case "can_remove_subscribers_group" :
case "realm_create_multiuse_invite_group" :
2023-11-27 11:36:07 +01:00
case "can_mention_group" :
2024-02-08 08:03:38 +01:00
case "realm_can_access_all_users_group" :
2023-10-09 23:29:16 +02:00
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 ;
}
2023-11-27 11:36:07 +01:00
export function save _discard _widget _status _handler (
$subsection ,
for _realm _default _settings ,
sub ,
group ,
) {
2023-10-09 23:29:16 +02:00
$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 ) =>
2023-11-27 11:36:07 +01:00
check _property _changed ( elem , for _realm _default _settings , sub , group ) ,
2023-10-09 23:29:16 +02:00
) ;
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 ) ;
}