Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit d6b9e2a

Browse files
authored
Fix config override of other settings levels (#12593)
* Make config override other settings levels and add tests * fix documentation * lint * Use a const for finalLevel. * respect the explicit parameter * Use supportedLevelsAreOrdered for config overrides rather than a separate setting. * Fix typos * Fix mock in UserSetttingsDialog-test * Special case disabling of setting tos use config overrides. * remove logs
1 parent 8e200dc commit d6b9e2a

File tree

5 files changed

+118
-70
lines changed

5 files changed

+118
-70
lines changed

src/components/views/elements/SettingsFlag.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ export default class SettingsFlag extends React.Component<IProps, IState> {
6262
}
6363

6464
private getSettingValue(): boolean {
65+
// If a level defined in props is overridden by a level at a high presedence, it gets disabled
66+
// and we should show the overridding value.
67+
if (
68+
SettingsStore.settingIsOveriddenAtConfigLevel(this.props.name, this.props.roomId ?? null, this.props.level)
69+
) {
70+
return !!SettingsStore.getValue(this.props.name);
71+
}
6572
return !!SettingsStore.getValueAt(
6673
this.props.level,
6774
this.props.name,

src/settings/Settings.tsx

+49-60
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ const LEVELS_ROOM_SETTINGS_WITH_ROOM = [
7171
const LEVELS_ACCOUNT_SETTINGS = [SettingLevel.DEVICE, SettingLevel.ACCOUNT, SettingLevel.CONFIG];
7272
const LEVELS_DEVICE_ONLY_SETTINGS = [SettingLevel.DEVICE];
7373
const LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG = [SettingLevel.DEVICE, SettingLevel.CONFIG];
74+
const LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED = [SettingLevel.CONFIG, SettingLevel.DEVICE];
7475
const LEVELS_UI_FEATURE = [
7576
SettingLevel.CONFIG,
7677
// in future we might have a .well-known level or something
@@ -133,17 +134,6 @@ export type SettingValueType =
133134
export interface IBaseSetting<T extends SettingValueType = SettingValueType> {
134135
isFeature?: false | undefined;
135136

136-
/**
137-
* If true, then the presence of this setting in `config.json` will disable the option in the UI.
138-
*
139-
* In other words, we prevent the user overriding the setting if an explicit value is given in `config.json`.
140-
* XXX: note that users who have already set a non-default value before `config.json` is update will continue
141-
* to use that value (and, indeed, won't be able to change it!): https://github.com/element-hq/element-web/issues/26877
142-
*
143-
* Obviously, this only really makes sense if `supportedLevels` includes {@link SettingLevel.CONFIG}.
144-
*/
145-
configDisablesSetting?: true;
146-
147137
// Display names are strongly recommended for clarity.
148138
// Display name can also be an object for different levels.
149139
displayName?:
@@ -268,70 +258,70 @@ export const SETTINGS: { [setting: string]: ISetting } = {
268258
"feature_msc3531_hide_messages_pending_moderation": {
269259
isFeature: true,
270260
labsGroup: LabGroup.Moderation,
271-
configDisablesSetting: true,
272261
// Requires a reload since this setting is cached in EventUtils
273262
controller: new ReloadOnChangeController(),
274263
displayName: _td("labs|msc3531_hide_messages_pending_moderation"),
275-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
264+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
265+
supportedLevelsAreOrdered: true,
276266
default: false,
277267
},
278268
"feature_report_to_moderators": {
279269
isFeature: true,
280270
labsGroup: LabGroup.Moderation,
281-
configDisablesSetting: true,
282271
displayName: _td("labs|report_to_moderators"),
283272
description: _td("labs|report_to_moderators_description"),
284-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
273+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
274+
supportedLevelsAreOrdered: true,
285275
default: false,
286276
},
287277
"feature_latex_maths": {
288278
isFeature: true,
289279
labsGroup: LabGroup.Messaging,
290-
configDisablesSetting: true,
291280
displayName: _td("labs|latex_maths"),
292-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
281+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
282+
supportedLevelsAreOrdered: true,
293283
default: false,
294284
},
295285
"feature_pinning": {
296286
isFeature: true,
297287
labsGroup: LabGroup.Messaging,
298-
configDisablesSetting: true,
299288
displayName: _td("labs|pinning"),
300-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
289+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
290+
supportedLevelsAreOrdered: true,
301291
default: false,
302292
},
303293
"feature_wysiwyg_composer": {
304294
isFeature: true,
305295
labsGroup: LabGroup.Messaging,
306-
configDisablesSetting: true,
307296
displayName: _td("labs|wysiwyg_composer"),
308297
description: _td("labs|feature_wysiwyg_composer_description"),
309-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
298+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
299+
supportedLevelsAreOrdered: true,
310300
default: false,
311301
},
312302
"feature_mjolnir": {
313303
isFeature: true,
314304
labsGroup: LabGroup.Moderation,
315-
configDisablesSetting: true,
316305
displayName: _td("labs|mjolnir"),
317306
description: _td("labs|currently_experimental"),
318-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
307+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
308+
supportedLevelsAreOrdered: true,
319309
default: false,
320310
},
321311
"feature_custom_themes": {
322312
isFeature: true,
323313
labsGroup: LabGroup.Themes,
324-
configDisablesSetting: true,
325314
displayName: _td("labs|custom_themes"),
326-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
315+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
316+
supportedLevelsAreOrdered: true,
327317
default: false,
328318
},
329319
"feature_dehydration": {
330320
isFeature: true,
331321
labsGroup: LabGroup.Encryption,
332-
configDisablesSetting: true,
333322
displayName: _td("labs|dehydration"),
334-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
323+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
324+
supportedLevelsAreOrdered: true,
335325
default: false,
336326
},
337327
"useOnlyCurrentProfiles": {
@@ -350,25 +340,25 @@ export const SETTINGS: { [setting: string]: ISetting } = {
350340
"feature_html_topic": {
351341
isFeature: true,
352342
labsGroup: LabGroup.Rooms,
353-
configDisablesSetting: true,
354-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
343+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
344+
supportedLevelsAreOrdered: true,
355345
displayName: _td("labs|html_topic"),
356346
default: false,
357347
},
358348
"feature_bridge_state": {
359349
isFeature: true,
360350
labsGroup: LabGroup.Rooms,
361-
configDisablesSetting: true,
362-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
351+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
352+
supportedLevelsAreOrdered: true,
363353
displayName: _td("labs|bridge_state"),
364354
default: false,
365355
},
366356
"feature_jump_to_date": {
367357
isFeature: true,
368358
labsGroup: LabGroup.Messaging,
369-
configDisablesSetting: true,
370359
displayName: _td("labs|jump_to_date"),
371-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
360+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
361+
supportedLevelsAreOrdered: true,
372362
default: false,
373363
controller: new ServerSupportUnstableFeatureController(
374364
"feature_jump_to_date",
@@ -398,8 +388,8 @@ export const SETTINGS: { [setting: string]: ISetting } = {
398388
"feature_sliding_sync": {
399389
isFeature: true,
400390
labsGroup: LabGroup.Developer,
401-
configDisablesSetting: true,
402-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
391+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
392+
supportedLevelsAreOrdered: true,
403393
displayName: _td("labs|sliding_sync"),
404394
description: _td("labs|sliding_sync_description"),
405395
shouldWarn: true,
@@ -414,34 +404,34 @@ export const SETTINGS: { [setting: string]: ISetting } = {
414404
"feature_element_call_video_rooms": {
415405
isFeature: true,
416406
labsGroup: LabGroup.VoiceAndVideo,
417-
configDisablesSetting: true,
418-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
407+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
408+
supportedLevelsAreOrdered: true,
419409
displayName: _td("labs|element_call_video_rooms"),
420410
controller: new ReloadOnChangeController(),
421411
default: false,
422412
},
423413
"feature_group_calls": {
424414
isFeature: true,
425415
labsGroup: LabGroup.VoiceAndVideo,
426-
configDisablesSetting: true,
427-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
416+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
417+
supportedLevelsAreOrdered: true,
428418
displayName: _td("labs|group_calls"),
429419
controller: new ReloadOnChangeController(),
430420
default: false,
431421
},
432422
"feature_disable_call_per_sender_encryption": {
433423
isFeature: true,
434424
labsGroup: LabGroup.VoiceAndVideo,
435-
configDisablesSetting: true,
436-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
425+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
426+
supportedLevelsAreOrdered: true,
437427
displayName: _td("labs|feature_disable_call_per_sender_encryption"),
438428
default: false,
439429
},
440430
"feature_allow_screen_share_only_mode": {
441431
isFeature: true,
442432
labsGroup: LabGroup.VoiceAndVideo,
443-
configDisablesSetting: true,
444-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
433+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
434+
supportedLevelsAreOrdered: true,
445435
description: _td("labs|under_active_development"),
446436
displayName: _td("labs|allow_screen_share_only_mode"),
447437
controller: new ReloadOnChangeController(),
@@ -450,8 +440,8 @@ export const SETTINGS: { [setting: string]: ISetting } = {
450440
"feature_location_share_live": {
451441
isFeature: true,
452442
labsGroup: LabGroup.Messaging,
453-
configDisablesSetting: true,
454-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
443+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
444+
supportedLevelsAreOrdered: true,
455445
displayName: _td("labs|location_share_live"),
456446
description: _td("labs|location_share_live_description"),
457447
shouldWarn: true,
@@ -460,8 +450,8 @@ export const SETTINGS: { [setting: string]: ISetting } = {
460450
"feature_dynamic_room_predecessors": {
461451
isFeature: true,
462452
labsGroup: LabGroup.Rooms,
463-
configDisablesSetting: true,
464-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
453+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
454+
supportedLevelsAreOrdered: true,
465455
displayName: _td("labs|dynamic_room_predecessors"),
466456
description: _td("labs|dynamic_room_predecessors_description"),
467457
shouldWarn: true,
@@ -470,8 +460,8 @@ export const SETTINGS: { [setting: string]: ISetting } = {
470460
[Features.VoiceBroadcast]: {
471461
isFeature: true,
472462
labsGroup: LabGroup.Messaging,
473-
configDisablesSetting: true,
474-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
463+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
464+
supportedLevelsAreOrdered: true,
475465
displayName: _td("labs|voice_broadcast"),
476466
default: false,
477467
},
@@ -483,8 +473,8 @@ export const SETTINGS: { [setting: string]: ISetting } = {
483473
[Features.OidcNativeFlow]: {
484474
isFeature: true,
485475
labsGroup: LabGroup.Developer,
486-
configDisablesSetting: true,
487-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
476+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
477+
supportedLevelsAreOrdered: true,
488478
displayName: _td("labs|oidc_native_flow"),
489479
description: _td("labs|oidc_native_flow_description"),
490480
default: false,
@@ -493,7 +483,6 @@ export const SETTINGS: { [setting: string]: ISetting } = {
493483
// use the rust matrix-sdk-crypto-wasm for crypto.
494484
isFeature: true,
495485
labsGroup: LabGroup.Developer,
496-
// unlike most features, `configDisablesSetting` is false here.
497486
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
498487
displayName: _td("labs|rust_crypto"),
499488
description: () => {
@@ -527,10 +516,10 @@ export const SETTINGS: { [setting: string]: ISetting } = {
527516
"feature_render_reaction_images": {
528517
isFeature: true,
529518
labsGroup: LabGroup.Messaging,
530-
configDisablesSetting: true,
531519
displayName: _td("labs|render_reaction_images"),
532520
description: _td("labs|render_reaction_images_description"),
533-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
521+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
522+
supportedLevelsAreOrdered: true,
534523
default: false,
535524
},
536525
/**
@@ -609,28 +598,28 @@ export const SETTINGS: { [setting: string]: ISetting } = {
609598
"feature_ask_to_join": {
610599
isFeature: true,
611600
labsGroup: LabGroup.Rooms,
612-
configDisablesSetting: true,
613601
default: false,
614602
displayName: _td("labs|ask_to_join"),
615-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
603+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
604+
supportedLevelsAreOrdered: true,
616605
},
617606
"feature_new_room_decoration_ui": {
618607
isFeature: true,
619608
labsGroup: LabGroup.Rooms,
620-
configDisablesSetting: true,
621609
displayName: _td("labs|new_room_decoration_ui"),
622610
description: _td("labs|under_active_development"),
623-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
611+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
612+
supportedLevelsAreOrdered: true,
624613
default: false,
625614
controller: new ReloadOnChangeController(),
626615
},
627616
"feature_notifications": {
628617
isFeature: true,
629618
labsGroup: LabGroup.Messaging,
630-
configDisablesSetting: true,
631619
displayName: _td("labs|notifications"),
632620
description: _td("labs|unrealiable_e2e"),
633-
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
621+
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED,
622+
supportedLevelsAreOrdered: true,
634623
default: false,
635624
},
636625
"useCompactLayout": {

src/settings/SettingsStore.ts

+35-10
Original file line numberDiff line numberDiff line change
@@ -505,41 +505,66 @@ export default class SettingsStore {
505505
* set for a particular room, otherwise it should be supplied.
506506
*
507507
* This takes into account both the value of {@link SettingController#settingDisabled} of the
508-
* `SettingController`, if any; and, for settings where {@link IBaseSetting#configDisablesSetting} is true,
509-
* whether the setting has been given a value in `config.json`.
508+
* `SettingController`, if any; and, for settings where {@link IBaseSetting#supportedLevelsAreOrdered} is true,
509+
* checks whether a level of higher precedence is set.
510510
*
511511
* Typically, if the user cannot set the setting, it should be hidden, to declutter the UI;
512512
* however some settings (typically, the labs flags) are exposed but greyed out, to unveil
513513
* what features are available with the right server support.
514514
*
515515
* @param {string} settingName The name of the setting to check.
516516
* @param {String} roomId The room ID to check in, may be null.
517-
* @param {SettingLevel} level The level to
518-
* check at.
517+
* @param {SettingLevel} level The level to check at.
519518
* @return {boolean} True if the user may set the setting, false otherwise.
520519
*/
521520
public static canSetValue(settingName: string, roomId: string | null, level: SettingLevel): boolean {
521+
const setting = SETTINGS[settingName];
522522
// Verify that the setting is actually a setting
523-
if (!SETTINGS[settingName]) {
523+
if (!setting) {
524524
throw new Error("Setting '" + settingName + "' does not appear to be a setting.");
525525
}
526526

527-
if (SETTINGS[settingName].controller?.settingDisabled) {
527+
if (setting.controller?.settingDisabled) {
528528
return false;
529529
}
530530

531531
// For some config settings (mostly: non-beta features), a value in config.json overrides the local setting
532-
// (ie: we force them as enabled or disabled).
533-
if (SETTINGS[settingName]?.configDisablesSetting) {
534-
const configVal = SettingsStore.getValueAt(SettingLevel.CONFIG, settingName, roomId, true, true);
535-
if (configVal === true || configVal === false) return false;
532+
// (ie: we force them as enabled or disabled). In this case we should not let the user change the setting.
533+
if (
534+
setting?.supportedLevelsAreOrdered &&
535+
SettingsStore.settingIsOveriddenAtConfigLevel(settingName, roomId, level)
536+
) {
537+
return false;
536538
}
537539

538540
const handler = SettingsStore.getHandler(settingName, level);
539541
if (!handler) return false;
540542
return handler.canSetValue(settingName, roomId);
541543
}
542544

545+
/**
546+
* Determines if the setting at the specified level is overidden by one at a config level.
547+
* @param settingName The name of the setting to check.
548+
* @param roomId The room ID to check in, may be null.
549+
* @param level The level to check at.
550+
* @returns
551+
*/
552+
public static settingIsOveriddenAtConfigLevel(
553+
settingName: string,
554+
roomId: string | null,
555+
level: SettingLevel,
556+
): boolean {
557+
const setting = SETTINGS[settingName];
558+
const levelOrders = getLevelOrder(setting);
559+
const configIndex = levelOrders.indexOf(SettingLevel.CONFIG);
560+
const levelIndex = levelOrders.indexOf(level);
561+
if (configIndex === -1 || levelIndex === -1 || configIndex >= levelIndex) {
562+
return false;
563+
}
564+
const configVal = SettingsStore.getValueAt(SettingLevel.CONFIG, settingName, roomId, true, true);
565+
return configVal === true || configVal === false;
566+
}
567+
543568
/**
544569
* Determines if the given level is supported on this device.
545570
* @param {SettingLevel} level The level

test/components/views/dialogs/UserSettingsDialog-test.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ jest.mock("../../../../src/settings/SettingsStore", () => ({
5454
getDescription: jest.fn(),
5555
shouldHaveWarning: jest.fn(),
5656
disabledMessage: jest.fn(),
57+
settingIsOveriddenAtConfigLevel: jest.fn(),
5758
}));
5859

5960
jest.mock("../../../../src/SdkConfig", () => ({

0 commit comments

Comments
 (0)