Page MenuHomePhabricator

Boolean preferences with default value of true cannot be changed if they do not have a key in $wgDefaultUserOptions
Open, MediumPublic

Description

Steps to reproduce:

  1. in the GetPreferences hook, add a preference with type=toggle and default=true
  2. go to Special:Preferences, unset that preference, save.

The preference will not be saved (unless $wgDefaultUserOptions[$preferenceName] is set).

This is caused by the check in User::saveOptions() to only save preferences with non-default values:

$defaultOption = self::getDefaultOption( $key );
if ( ( is_null( $defaultOption ) &&
	!( $value === false || is_null( $value ) ) ) ||
	$value != $defaultOption
) {
	// save
}

User::getDefaultOptions() returns null if it does not find the key in $wgDefaultUserOptions, and $value will be false, so both parts of the condition fail.

If setting $wgDefaultUserOptions is required when adding a new preference, that should be clearly documented (neither mw:Manual:$wgDefaultUserOptions nor mw:Manual:Hooks/GetPreferences gives that impression). Otherwise, the correct logic is IMO something like this:

$siteDefaultOption = self::getDefaultOption( $key );
$defaultOption = Preferences::getPreferences( $user, $context)[$key];
if ( is_null( $siteDefaultOption ) ) {
	$changed = ( $value != $defaultOption );
} else {
	$changed = ( $value != $siteDefaultOption );
}
if ( $changed ) {
	// save
}

(Except that getPreferences depends on the context which is not available here. Ugh...)

Details

Reference
bz62946