Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4163362
GlobalPreferencesFactory.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
23 KB
Referenced Files
None
Subscribers
None
GlobalPreferencesFactory.php
View Options
<?php
/**
* Implements global preferences for MediaWiki
*
* @author Kunal Mehta <legoktm@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GPL-2.0-or-later
* @file
* @ingroup Extensions
*
* Partially based off of work by Werdna
* https://www.mediawiki.org/wiki/Special:Code/MediaWiki/49790
*/
namespace
GlobalPreferences
;
use
GlobalPreferences\Services\GlobalPreferencesHookRunner
;
use
LogicException
;
use
MediaWiki\Auth\AuthManager
;
use
MediaWiki\Config\ServiceOptions
;
use
MediaWiki\Context\IContextSource
;
use
MediaWiki\Context\RequestContext
;
use
MediaWiki\HookContainer\HookContainer
;
use
MediaWiki\Html\Html
;
use
MediaWiki\HTMLForm\Field\HTMLCheckMatrix
;
use
MediaWiki\HTMLForm\Field\HTMLSelectOrOtherField
;
use
MediaWiki\Language\ILanguageConverter
;
use
MediaWiki\Language\Language
;
use
MediaWiki\Language\RawMessage
;
use
MediaWiki\Languages\LanguageNameUtils
;
use
MediaWiki\Linker\LinkRenderer
;
use
MediaWiki\MediaWikiServices
;
use
MediaWiki\Permissions\PermissionManager
;
use
MediaWiki\Preferences\DefaultPreferencesFactory
;
use
MediaWiki\SpecialPage\SpecialPage
;
use
MediaWiki\Status\Status
;
use
MediaWiki\Title\NamespaceInfo
;
use
MediaWiki\User\CentralId\CentralIdLookup
;
use
MediaWiki\User\Options\UserOptionsLookup
;
use
MediaWiki\User\User
;
use
MediaWiki\User\UserIdentity
;
use
OOUI\ButtonWidget
;
use
RuntimeException
;
use
WeakReference
;
/**
* Global preferences.
* @package GlobalPreferences
*/
class
GlobalPreferencesFactory
extends
DefaultPreferencesFactory
{
/**
* The suffix appended to preference names
* for the associated preference that tracks whether they have a local override.
*/
public
const
LOCAL_EXCEPTION_SUFFIX
=
'-local-exception'
;
/**
* The suffix appended to preference names for their global counterparts.
*/
public
const
GLOBAL_EXCEPTION_SUFFIX
=
'-global'
;
/**
* @var string[] Names of autoglobal options
*/
protected
$autoGlobals
=
[];
/**
* "bad" preferences that we should remove from
* Special:GlobalPrefs
* @var array
*/
protected
$disallowedPreferences
=
[
// Stored in user table, doesn't work yet
'realname'
,
// @todo Show CA user id / shared user table id?
'userid'
,
// @todo Show CA global groups instead?
'usergroups'
,
// @todo Should global edit count instead?
'editcount'
,
'registrationdate'
,
// Signature could be global, but links in it are too likely to break.
'nickname'
,
'fancysig'
,
];
/**
* Preference types that we should not add a checkbox for
* @var array
*/
protected
$typesPrevented
=
[
'info'
,
'hidden'
,
'api'
,
];
/**
* Preference classes that are allowed to be global
* @var array
*/
protected
$allowedClasses
=
[
// Checking old alias for compatibility with unchanged extensions
\HTMLSelectOrOtherField
::
class
,
HTMLSelectOrOtherField
::
class
,
\MediaWiki\Extension\BetaFeatures\HTMLFeatureField
::
class
,
// Checking old alias for compatibility with unchanged extensions
\HTMLCheckMatrix
::
class
,
HTMLCheckMatrix
::
class
,
];
/**
* Weak reference to the UserIdentity with global ID $this->globalUserId
* @var WeakReference|null
*/
private
$globalUserRef
;
/** @var int|null */
private
$globalUserId
;
private
readonly
GlobalPreferencesHookRunner
$globalPreferencesHookRunner
;
public
function
__construct
(
ServiceOptions
$options
,
Language
$contLang
,
AuthManager
$authManager
,
LinkRenderer
$linkRenderer
,
NamespaceInfo
$nsInfo
,
PermissionManager
$permissionManager
,
ILanguageConverter
$languageConverter
,
LanguageNameUtils
$languageNameUtils
,
HookContainer
$hookContainer
,
UserOptionsLookup
$userOptionsLookup
,
)
{
parent
::
__construct
(
$options
,
$contLang
,
$authManager
,
$linkRenderer
,
$nsInfo
,
$permissionManager
,
$languageConverter
,
$languageNameUtils
,
$hookContainer
,
$userOptionsLookup
);
$this
->
globalPreferencesHookRunner
=
new
GlobalPreferencesHookRunner
(
$hookContainer
);
}
/**
* Sets the list of options for which setting the local value should transparently update
* the global value.
*
* @param string[] $list
*/
public
function
setAutoGlobals
(
array
$list
)
{
$this
->
autoGlobals
=
$list
;
}
/**
* Get all user preferences.
* @param User $user
* @param IContextSource $context The current request context
* @return array|null
*/
public
function
getFormDescriptor
(
User
$user
,
IContextSource
$context
)
{
$globalPrefs
=
$this
->
getGlobalPreferencesValues
(
$user
,
Storage
::
SKIP_CACHE
);
// The above function can return false
$globalPrefNames
=
$globalPrefs
?
array_keys
(
$globalPrefs
)
:
[];
$preferences
=
parent
::
getFormDescriptor
(
$user
,
$context
);
if
(
$this
->
onGlobalPrefsPage
(
$context
)
)
{
if
(
$globalPrefs
===
false
)
{
throw
new
RuntimeException
(
"Attempted to load global preferences page for {$user->getName()} whose "
.
'preference values failed to load'
);
}
return
$this
->
getPreferencesGlobal
(
$user
,
$preferences
,
$globalPrefs
,
$context
);
}
return
$this
->
getPreferencesLocal
(
$user
,
$preferences
,
$globalPrefNames
,
$context
);
}
/**
* Add help-text to the local preferences where they're globalized,
* and add the link to Special:GlobalPreferences to the personal preferences tab.
* @param User $user
* @param mixed[][] $preferences The preferences array.
* @param string[] $globalPrefNames The names of those preferences that are already global.
* @param IContextSource $context The current request context
* @return mixed[][]
*/
protected
function
getPreferencesLocal
(
User
$user
,
array
$preferences
,
array
$globalPrefNames
,
IContextSource
$context
)
{
$this
->
logger
->
debug
(
"Creating local preferences array for '{$user->getName()}'"
);
$modifiedPrefs
=
[];
$userOptionsLookup
=
MediaWikiServices
::
getInstance
()->
getUserOptionsLookup
();
foreach
(
$preferences
as
$name
=>
$def
)
{
$modifiedPrefs
[
$name
]
=
$def
;
// If this is not globalizable, or hasn't been set globally.
if
(
!
isset
(
$def
[
'section'
]
)
||
!
in_array
(
$name
,
$globalPrefNames
)
||
!
$this
->
isGlobalizablePreference
(
$name
,
$def
)
)
{
continue
;
}
$localExName
=
$name
.
UserOptionsLookup
::
LOCAL_EXCEPTION_SUFFIX
;
$localExValueUser
=
$userOptionsLookup
->
getBoolOption
(
$user
,
$localExName
);
// Add a new local exception preference after this one.
$cssClasses
=
[
'mw-globalprefs-local-exception'
,
'mw-globalprefs-local-exception-for-'
.
$name
,
'mw-prefs-search-noindex'
,
];
$section
=
$def
[
'section'
];
$secFragment
=
static
::
getSectionFragmentId
(
$section
);
$labelMsg
=
$context
->
msg
(
'globalprefs-set-local-exception'
,
[
$secFragment
]
);
$modifiedPrefs
[
$localExName
]
=
[
'type'
=>
'toggle'
,
'label-raw'
=>
$labelMsg
->
parse
(),
'default'
=>
$localExValueUser
,
'section'
=>
$section
,
'cssclass'
=>
implode
(
' '
,
$cssClasses
),
'hide-if'
=>
$def
[
'hide-if'
]
??
false
,
'disable-if'
=>
$def
[
'disable-if'
]
??
false
,
];
if
(
isset
(
$def
[
'disable-if'
]
)
)
{
$modifiedPrefs
[
$name
][
'disable-if'
]
=
[
'OR'
,
$def
[
'disable-if'
],
[
'!=='
,
$localExName
,
'1'
]
];
}
else
{
$modifiedPrefs
[
$name
][
'disable-if'
]
=
[
'!=='
,
$localExName
,
'1'
];
}
}
$preferences
=
$modifiedPrefs
;
// Add a link to GlobalPreferences to the local preferences form.
$linkObject
=
new
ButtonWidget
(
[
'href'
=>
SpecialPage
::
getTitleFor
(
'GlobalPreferences'
)->
getLinkURL
(),
'label'
=>
$context
->
msg
(
'globalprefs-info-link'
)->
text
(),
]
);
$link
=
$linkObject
->
toString
();
$preferences
[
'global-info'
]
=
[
'type'
=>
'info'
,
'section'
=>
'personal/info'
,
'label-message'
=>
'globalprefs-info-label'
,
'raw'
=>
true
,
'default'
=>
$link
,
'help-message'
=>
'globalprefs-info-help'
,
];
return
$preferences
;
}
/**
* Add the '-global' counterparts to all preferences, and override the local exception.
* @param User $user
* @param mixed[][] $preferences The preferences array.
* @param mixed[] $globalPrefs The array of global preferences.
* @param IContextSource $context The current request context
* @return mixed[][]
*/
protected
function
getPreferencesGlobal
(
User
$user
,
array
$preferences
,
array
$globalPrefs
,
IContextSource
$context
)
{
$allPrefs
=
[];
$userOptionsLookup
=
MediaWikiServices
::
getInstance
()->
getUserOptionsLookup
();
// Add the "Restore all default preferences" link like on Special:Preferences
// (the normal preference entry is not globalizable)
$allPrefs
[
'restoreprefs'
]
=
[
'type'
=>
'info'
,
'raw'
=>
true
,
'label-message'
=>
'prefs-user-restoreprefs-label'
,
'default'
=>
Html
::
element
(
'a'
,
[
'href'
=>
SpecialPage
::
getTitleFor
(
'GlobalPreferences'
)
->
getSubpage
(
'reset'
)->
getLocalURL
()
],
$context
->
msg
(
'globalprefs-restoreprefs'
)->
text
()
),
'section'
=>
'personal/info'
,
];
// Add all corresponding new global fields.
foreach
(
$preferences
as
$pref
=>
$def
)
{
// Ignore unwanted preferences.
if
(
!
$this
->
isGlobalizablePreference
(
$pref
,
$def
)
)
{
continue
;
}
// If a 'info' preference was allowed (i.e. 'canglobal' is set to true), then we should not add a checkbox
// as it doesn't make sense.
if
(
isset
(
$def
[
'type'
]
)
&&
$def
[
'type'
]
===
'info'
)
{
$allPrefs
[
$pref
]
=
$def
;
continue
;
}
// Create the new preference.
$isGlobal
=
isset
(
$globalPrefs
[
$pref
]
);
$allPrefs
[
$pref
.
static
::
GLOBAL_EXCEPTION_SUFFIX
]
=
[
'type'
=>
'toggle'
,
// Make the tooltip and the label the same, because the label is normally hidden.
'tooltip'
=>
'globalprefs-check-label'
,
'label-message'
=>
'tooltip-globalprefs-check-label'
,
'default'
=>
$isGlobal
,
'section'
=>
$def
[
'section'
],
'cssclass'
=>
'mw-globalprefs-global-check mw-globalprefs-checkbox-for-'
.
$pref
,
'hide-if'
=>
$def
[
'hide-if'
]
??
false
,
'disable-if'
=>
$def
[
'disable-if'
]
??
false
,
];
if
(
isset
(
$def
[
'disable-if'
]
)
)
{
$def
[
'disable-if'
]
=
[
'OR'
,
$def
[
'disable-if'
],
[
'!=='
,
$pref
.
static
::
GLOBAL_EXCEPTION_SUFFIX
,
'1'
]
];
}
else
{
$def
[
'disable-if'
]
=
[
'!=='
,
$pref
.
static
::
GLOBAL_EXCEPTION_SUFFIX
,
'1'
];
}
// If this has a local exception, override it and append a help message to say so.
if
(
$isGlobal
&&
$userOptionsLookup
->
getBoolOption
(
$user
,
$pref
.
UserOptionsLookup
::
LOCAL_EXCEPTION_SUFFIX
)
)
{
$def
[
'default'
]
=
$this
->
getOptionFromUser
(
$pref
,
$def
,
$globalPrefs
);
// Create a link to the relevant section of GlobalPreferences.
$secFragment
=
static
::
getSectionFragmentId
(
$def
[
'section'
]
);
$helpMsg
=
[
'globalprefs-has-local-exception'
,
$secFragment
];
// Merge the help messages.
if
(
isset
(
$def
[
'help'
]
)
)
{
$def
[
'help-messages'
]
=
[
new
RawMessage
(
$def
[
'help'
]
.
'<br />'
),
$helpMsg
];
unset
(
$def
[
'help'
]
);
}
elseif
(
isset
(
$def
[
'help-message'
]
)
)
{
$def
[
'help-messages'
]
=
[
$def
[
'help-message'
],
new
RawMessage
(
'<br />'
),
$helpMsg
];
unset
(
$def
[
'help-message'
]
);
}
elseif
(
isset
(
$def
[
'help-messages'
]
)
)
{
$def
[
'help-messages'
][]
=
new
RawMessage
(
'<br />'
);
$def
[
'help-messages'
][]
=
$helpMsg
;
}
else
{
$def
[
'help-message'
]
=
$helpMsg
;
}
}
$allPrefs
[
$pref
]
=
$def
;
}
return
$allPrefs
;
}
/**
* @inheritDoc
*/
protected
function
saveFormData
(
$formData
,
\PreferencesFormOOUI
$form
,
array
$formDescriptor
)
{
if
(
!
$this
->
onGlobalPrefsPage
(
$form
)
)
{
return
parent
::
saveFormData
(
$formData
,
$form
,
$formDescriptor
);
}
'@phan-var GlobalPreferencesFormOOUI $form'
;
$user
=
$form
->
getModifiedUser
();
// Difference from parent: removed 'editmyprivateinfo'
if
(
!
$this
->
permissionManager
->
userHasRight
(
$user
,
'editmyoptions'
)
)
{
return
Status
::
newFatal
(
'mypreferencesprotected'
);
}
// Filter input
$this
->
applyFilters
(
$formData
,
$formDescriptor
,
'filterFromForm'
);
// In the parent, we remove 'realname', but this is unnecessary
// here because GlobalPreferences removes this elsewhere, so
// the field will not even appear in this form
// Difference from parent: We are not collecting old user settings
foreach
(
$this
->
getSaveBlacklist
()
as
$b
)
{
unset
(
$formData
[
$b
]
);
}
# If users have saved a value for a preference which has subsequently been disabled
# via $wgHiddenPrefs, we don't want to destroy that setting in case the preference
# is subsequently re-enabled
$hiddenPrefs
=
$this
->
options
->
get
(
'HiddenPrefs'
);
$userOptionsLookup
=
MediaWikiServices
::
getInstance
()->
getUserOptionsLookup
();
foreach
(
$hiddenPrefs
as
$pref
)
{
# If the user has not set a non-default value here, the default will be returned
# and subsequently discarded
$formData
[
$pref
]
=
$userOptionsLookup
->
getOption
(
$user
,
$pref
,
null
,
true
);
}
// Difference from parent: We are ignoring RClimit preference; the parent
// checks for changes in that preference to update a hidden one, but the
// RCFilters product is okay with having that be localized
// We are also not resetting unused preferences in the global context.
// Otherwise, users could lose data by editing their global preferences
// on a wiki where some of the preferences don't exist. However, this means
// that preferences for undeployed extensions or removed code are never
// removed from the database...
// Setting the actual preference values:
$prefs
=
[];
$suffixLen
=
strlen
(
self
::
GLOBAL_EXCEPTION_SUFFIX
);
foreach
(
$formData
as
$name
=>
$value
)
{
// If this is the '-global' counterpart to a preference.
if
(
self
::
isGlobalPrefName
(
$name
)
&&
$value
===
true
)
{
// Determine the real name of the preference.
$realName
=
substr
(
$name
,
0
,
-
$suffixLen
);
if
(
array_key_exists
(
$realName
,
$formData
)
)
{
$prefs
[
$realName
]
=
$formData
[
$realName
];
if
(
$prefs
[
$realName
]
===
null
)
{
// Special case: null means don't save this row, which can keep the previous value
$prefs
[
$realName
]
=
''
;
}
}
}
}
$matricesToClear
=
[];
// Now special processing for CheckMatrices
foreach
(
$this
->
findCheckMatrices
(
$formDescriptor
)
as
$name
)
{
$globalName
=
$name
.
self
::
GLOBAL_EXCEPTION_SUFFIX
;
// Find all separate controls for this CheckMatrix
$checkMatrix
=
preg_grep
(
'/^'
.
preg_quote
(
$name
)
.
'/'
,
array_keys
(
$formData
)
);
if
(
array_key_exists
(
$globalName
,
$formData
)
&&
$formData
[
$globalName
]
)
{
// Setting is global, copy the checkmatrices
foreach
(
$checkMatrix
as
$input
)
{
$prefs
[
$input
]
=
$formData
[
$input
];
}
$prefs
[
$name
]
=
true
;
}
else
{
// Remove all the rows for this CheckMatrix
foreach
(
$checkMatrix
as
$input
)
{
unset
(
$prefs
[
$input
]
);
}
$matricesToClear
[]
=
$name
;
}
unset
(
$prefs
[
$globalName
]
);
}
$this
->
setGlobalPreferences
(
$user
,
$prefs
,
$form
->
getContext
(),
$matricesToClear
);
return
true
;
}
/**
* Finds CheckMatrix inputs in a form descriptor
*
* @param array $formDescriptor
* @return string[] Names of CheckMatrix options (parent only, not sub-checkboxes)
*/
private
function
findCheckMatrices
(
array
$formDescriptor
)
{
$result
=
[];
foreach
(
$formDescriptor
as
$name
=>
$info
)
{
if
(
(
isset
(
$info
[
'type'
]
)
&&
$info
[
'type'
]
==
'checkmatrix'
)
||
// Checking old alias for compatibility with unchanged extensions
(
isset
(
$info
[
'class'
]
)
&&
$info
[
'class'
]
==
\HTMLCheckMatrix
::
class
)
||
(
isset
(
$info
[
'class'
]
)
&&
$info
[
'class'
]
==
HTMLCheckMatrix
::
class
)
)
{
$result
[]
=
$name
;
}
}
return
$result
;
}
/**
* Get the HTML fragment identifier for a given preferences section. This is the leading part
* of the provided section name, up to a slash (if there is one).
* @param string $section A section name, as used in a preference definition.
* @return string
*/
public
static
function
getSectionFragmentId
(
$section
)
{
$sectionId
=
preg_replace
(
'#/.*$#'
,
''
,
$section
);
return
'mw-prefsection-'
.
$sectionId
;
}
/**
* Checks whether the given preference is globalizable.
*
* @param string $name Preference name
* @param mixed[] &$info Preference description, by reference to avoid unnecessary cloning
* @return bool
*/
protected
function
isGlobalizablePreference
(
$name
,
&
$info
)
{
// Preferences can opt out of being globalized by setting the 'noglobal' flag.
if
(
isset
(
$info
[
'noglobal'
]
)
&&
$info
[
'noglobal'
]
===
true
)
{
return
false
;
}
// Ignore "is global" checkboxes
if
(
static
::
isGlobalPrefName
(
$name
)
)
{
return
false
;
}
// If a setting can't be changed, don't bother globalizing it
if
(
isset
(
$info
[
'disabled'
]
)
&&
$info
[
'disabled'
]
)
{
return
false
;
}
// Allow explicitly define if a preference can be globalized,
// especially useful for custom field classes.
// TODO: Deprecate 'noglobal' in favour of this param.
if
(
isset
(
$info
[
'canglobal'
]
)
)
{
return
(
bool
)
$info
[
'canglobal'
];
}
$isAllowedType
=
isset
(
$info
[
'type'
]
)
&&
!
in_array
(
$info
[
'type'
],
$this
->
typesPrevented
)
&&
!
in_array
(
$name
,
$this
->
disallowedPreferences
);
$isAllowedClass
=
isset
(
$info
[
'class'
]
)
&&
in_array
(
$info
[
'class'
],
$this
->
allowedClasses
);
return
$isAllowedType
||
$isAllowedClass
;
}
/**
* A convenience function to check if a preference name is for a global one.
* @param string $name The name to check.
* @return bool
*/
public
static
function
isGlobalPrefName
(
$name
)
{
return
str_ends_with
(
$name
,
static
::
GLOBAL_EXCEPTION_SUFFIX
);
}
/**
* A convenience function to check if a preference name is for a local-exception preference.
* @param string $name The name to check.
* @return bool
*/
public
static
function
isLocalPrefName
(
$name
)
{
return
str_ends_with
(
$name
,
UserOptionsLookup
::
LOCAL_EXCEPTION_SUFFIX
);
}
/**
* Checks if the user is globalized.
* @param UserIdentity $user
* @return bool
*/
public
function
isUserGlobalized
(
UserIdentity
$user
)
{
$utils
=
MediaWikiServices
::
getInstance
()->
getUserIdentityUtils
();
return
$utils
->
isNamed
(
$user
)
&&
$this
->
getUserID
(
$user
)
!==
0
;
}
/**
* Gets the user's ID that we're using in the table
* Returns 0 if the user is not global
* @param UserIdentity $user
* @return int
*/
public
function
getUserID
(
UserIdentity
$user
)
{
// Implement a single-element cache where $this->globalUserRef holds the
// cache key of the currently-stored item and $this->globalUserId is
// the value.
if
(
!
$this
->
globalUserRef
||
$this
->
globalUserRef
->
get
()
!==
$user
)
{
$lookup
=
MediaWikiServices
::
getInstance
()->
getCentralIdLookup
();
$this
->
globalUserRef
=
WeakReference
::
create
(
$user
);
$this
->
globalUserId
=
$lookup
->
isOwned
(
$user
)
?
$lookup
->
centralIdFromName
(
$user
->
getName
(),
CentralIdLookup
::
AUDIENCE_RAW
)
:
0
;
}
return
$this
->
globalUserId
;
}
/**
* Get the user's global preferences.
* @param UserIdentity $user
* @param bool $skipCache Whether the preferences should be loaded strictly from DB
* @return false|string[] Array keyed by preference name, or false if not found.
*/
public
function
getGlobalPreferencesValues
(
UserIdentity
$user
,
$skipCache
=
false
)
{
$id
=
$this
->
getUserID
(
$user
);
if
(
!
$id
)
{
$this
->
logger
->
warning
(
"Couldn't find a global ID for user {user}"
,
[
'user'
=>
$user
->
getName
()
]
);
return
false
;
}
$storage
=
$this
->
makeStorage
(
$user
);
return
$storage
->
load
(
$skipCache
);
}
/**
* Save the user's global preferences.
* @param User $user
* @param array $newGlobalPrefs Array keyed by preference name.
* @param IContextSource $context The request context.
* @param string[] $checkMatricesToClear List of check matrix controls that
* need their rows purged
* @return bool True on success, false if the user isn't global.
*/
public
function
setGlobalPreferences
(
User
$user
,
$newGlobalPrefs
,
IContextSource
$context
,
array
$checkMatricesToClear
=
[]
)
{
$id
=
$this
->
getUserID
(
$user
);
if
(
!
$id
)
{
return
false
;
}
// Use a new instance of the current user to fetch the form descriptor because that way
// we're working with the previous user options and not those that are currently in the
// process of being saved (we only want the option names here, so don't care what the
// values are).
$userForDescriptor
=
MediaWikiServices
::
getInstance
()->
getUserFactory
()->
newFromId
(
$user
->
getId
()
);
// Save the global options.
$storage
=
$this
->
makeStorage
(
$user
);
$knownPrefs
=
array_keys
(
$this
->
getFormDescriptor
(
$userForDescriptor
,
$context
)
);
$oldPreferences
=
$this
->
getGlobalPreferencesValues
(
$user
);
$storage
->
save
(
$newGlobalPrefs
,
$knownPrefs
,
$checkMatricesToClear
);
$this
->
globalPreferencesHookRunner
->
onGlobalPreferencesSetGlobalPreferences
(
$user
,
$oldPreferences
,
$newGlobalPrefs
);
$user
->
clearInstanceCache
();
return
true
;
}
/**
* Deletes all of a user's global preferences.
* Assumes that the user is globalized.
* @param User $user
*/
public
function
resetGlobalUserSettings
(
User
$user
)
{
$oldPreferences
=
$this
->
getGlobalPreferencesValues
(
$user
);
$this
->
makeStorage
(
$user
)->
delete
();
$this
->
globalPreferencesHookRunner
->
onGlobalPreferencesSetGlobalPreferences
(
$user
,
$oldPreferences
,
[]
);
}
/**
* Convenience function to check if we're on the global prefs page.
* @param IContextSource|null $context The context to use; if not set main request context is used.
* @return bool
*/
public
function
onGlobalPrefsPage
(
$context
=
null
)
{
$context
=
$context
?:
RequestContext
::
getMain
();
return
$context
->
getTitle
()
&&
$context
->
getTitle
()->
isSpecial
(
'GlobalPreferences'
);
}
/**
* Convenience function to check if we're on the local prefs page.
*
* @param IContextSource|null $context The context to use; if not set main request context is used.
* @return bool
*/
public
function
onLocalPrefsPage
(
$context
=
null
)
{
$context
=
$context
?:
RequestContext
::
getMain
();
return
$context
->
getTitle
()
&&
$context
->
getTitle
()->
isSpecial
(
'Preferences'
);
}
/**
* Processes local user options before they're saved
*
* @param UserIdentity $user
* @param array &$modifiedOptions
* @param array $originalOptions
*/
public
function
handleLocalPreferencesChange
(
UserIdentity
$user
,
array
&
$modifiedOptions
,
array
$originalOptions
)
{
$shouldModify
=
[];
$mergedOptions
=
array_merge
(
$originalOptions
,
$modifiedOptions
);
foreach
(
$this
->
autoGlobals
as
$optName
)
{
// $modifiedOptions can contains options that not actually modified, filter out them
if
(
array_key_exists
(
$optName
,
$modifiedOptions
)
&&
(
!
array_key_exists
(
$optName
,
$originalOptions
)
||
$modifiedOptions
[
$optName
]
!==
$originalOptions
[
$optName
]
)
&&
// And skip options that have local exceptions
!(
$mergedOptions
[
$optName
.
UserOptionsLookup
::
LOCAL_EXCEPTION_SUFFIX
]
??
false
)
)
{
$shouldModify
[
$optName
]
=
$modifiedOptions
[
$optName
];
}
}
// No auto-global options are modified
if
(
!
$shouldModify
)
{
return
;
}
$preferencesChanged
=
false
;
$globals
=
$this
->
getGlobalPreferencesValues
(
$user
,
true
);
if
(
$globals
)
{
foreach
(
$shouldModify
as
$optName
=>
$optVal
)
{
if
(
array_key_exists
(
$optName
,
$globals
)
)
{
$globals
[
$optName
]
=
$optVal
;
$preferencesChanged
=
true
;
}
}
if
(
$preferencesChanged
)
{
$this
->
makeStorage
(
$user
)->
save
(
$globals
,
array_keys
(
$globals
)
);
}
}
}
/**
* Factory for preference storage
*
* @param UserIdentity $user
* @return Storage
*/
protected
function
makeStorage
(
UserIdentity
$user
)
{
$id
=
$this
->
getUserID
(
$user
);
if
(
!
$id
)
{
throw
new
LogicException
(
'User not set or is not global on call to '
.
__METHOD__
);
}
return
new
Storage
(
$id
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Aug 20 2026, 02:01 (10 w, 3 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
aa/cb/a27be0ede49f4ad31c910eb5fa06
Default Alt Text
GlobalPreferencesFactory.php (23 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment