Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4028477
SpecialCentralAuth.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
41 KB
Referenced Files
None
Subscribers
None
SpecialCentralAuth.php
View Options
<?php
namespace
MediaWiki\Extension\CentralAuth\Special
;
use
DateInterval
;
use
Exception
;
use
InvalidArgumentException
;
use
LogEventsList
;
use
MediaWiki\Block\Restriction\ActionRestriction
;
use
MediaWiki\Block\Restriction\NamespaceRestriction
;
use
MediaWiki\Block\Restriction\PageRestriction
;
use
MediaWiki\CommentFormatter\CommentFormatter
;
use
MediaWiki\CommentStore\CommentStore
;
use
MediaWiki\Context\DerivativeContext
;
use
MediaWiki\Extension\CentralAuth\CentralAuthDatabaseManager
;
use
MediaWiki\Extension\CentralAuth\CentralAuthUIService
;
use
MediaWiki\Extension\CentralAuth\GlobalRename\GlobalRenameFactory
;
use
MediaWiki\Extension\CentralAuth\Hooks\CentralAuthHookRunner
;
use
MediaWiki\Extension\CentralAuth\User\CentralAuthGlobalRegistrationProvider
;
use
MediaWiki\Extension\CentralAuth\User\CentralAuthUser
;
use
MediaWiki\Extension\CentralAuth\Widget\HTMLGlobalUserTextField
;
use
MediaWiki\Extension\GlobalBlocking\GlobalBlockingServices
;
use
MediaWiki\Extension\GlobalBlocking\Services\GlobalBlockLookup
;
use
MediaWiki\Html\Html
;
use
MediaWiki\HTMLForm\HTMLForm
;
use
MediaWiki\MediaWikiServices
;
use
MediaWiki\Message\Message
;
use
MediaWiki\Parser\Sanitizer
;
use
MediaWiki\Registration\ExtensionRegistry
;
use
MediaWiki\SpecialPage\SpecialPage
;
use
MediaWiki\Title\NamespaceInfo
;
use
MediaWiki\Title\Title
;
use
MediaWiki\User\Registration\UserRegistrationLookup
;
use
MediaWiki\User\TempUser\TempUserConfig
;
use
MediaWiki\User\User
;
use
MediaWiki\User\UserFactory
;
use
MediaWiki\User\UserGroupMembership
;
use
MediaWiki\User\UserNameUtils
;
use
MediaWiki\Utils\MWTimestamp
;
use
MediaWiki\WikiMap\WikiMap
;
use
MediaWiki\WikiMap\WikiReference
;
use
MediaWiki\Xml\Xml
;
use
OOUI\FieldLayout
;
use
OOUI\FieldsetLayout
;
use
OOUI\HtmlSnippet
;
use
OOUI\PanelLayout
;
use
OOUI\Widget
;
use
Wikimedia\Message\MessageSpecifier
;
use
Wikimedia\Rdbms\IExpression
;
use
Wikimedia\Rdbms\LikeValue
;
class
SpecialCentralAuth
extends
SpecialPage
{
/** @var string */
private
$mUserName
;
/** @var bool */
private
$mCanUnmerge
;
/** @var bool */
private
$mCanLock
;
/** @var bool */
private
$mCanSuppress
;
/** @var bool */
private
$mCanEdit
;
/** @var bool */
private
$mCanChangeGroups
;
/**
* @var CentralAuthUser
*/
private
$mGlobalUser
;
/**
* @var array[]
*/
private
$mAttachedLocalAccounts
;
/**
* @var array
*/
private
$mUnattachedLocalAccounts
;
/** @var string */
private
$mMethod
;
/** @var bool */
private
$mPosted
;
/** @var string[] */
private
$mWikis
;
private
CommentFormatter
$commentFormatter
;
private
NamespaceInfo
$namespaceInfo
;
private
TempUserConfig
$tempUserConfig
;
private
UserFactory
$userFactory
;
private
UserNameUtils
$userNameUtils
;
private
UserRegistrationLookup
$userRegistrationLookup
;
private
CentralAuthDatabaseManager
$databaseManager
;
private
CentralAuthUIService
$uiService
;
private
GlobalRenameFactory
$globalRenameFactory
;
public
function
__construct
(
CommentFormatter
$commentFormatter
,
NamespaceInfo
$namespaceInfo
,
TempUserConfig
$tempUserConfig
,
UserFactory
$userFactory
,
UserNameUtils
$userNameUtils
,
UserRegistrationLookup
$userRegistrationLookup
,
CentralAuthDatabaseManager
$databaseManager
,
CentralAuthUIService
$uiService
,
GlobalRenameFactory
$globalRenameFactory
)
{
parent
::
__construct
(
'CentralAuth'
);
$this
->
commentFormatter
=
$commentFormatter
;
$this
->
namespaceInfo
=
$namespaceInfo
;
$this
->
tempUserConfig
=
$tempUserConfig
;
$this
->
userFactory
=
$userFactory
;
$this
->
userNameUtils
=
$userNameUtils
;
$this
->
userRegistrationLookup
=
$userRegistrationLookup
;
$this
->
databaseManager
=
$databaseManager
;
$this
->
uiService
=
$uiService
;
$this
->
globalRenameFactory
=
$globalRenameFactory
;
}
public
function
doesWrites
()
{
return
true
;
}
/** @inheritDoc */
public
function
execute
(
$subpage
)
{
$this
->
setHeaders
();
$this
->
addHelpLink
(
'Extension:CentralAuth'
);
$authority
=
$this
->
getContext
()->
getAuthority
();
$this
->
mCanUnmerge
=
$authority
->
isAllowed
(
'centralauth-unmerge'
);
$this
->
mCanLock
=
$authority
->
isAllowed
(
'centralauth-lock'
);
$this
->
mCanSuppress
=
$authority
->
isAllowed
(
'centralauth-suppress'
);
$this
->
mCanEdit
=
$this
->
mCanUnmerge
||
$this
->
mCanLock
||
$this
->
mCanSuppress
;
$this
->
mCanChangeGroups
=
$authority
->
isAllowed
(
'globalgroupmembership'
);
$this
->
getOutput
()->
setPageTitleMsg
(
$this
->
msg
(
$this
->
mCanEdit
?
'centralauth'
:
'centralauth-ro'
)
);
$this
->
getOutput
()->
addModules
(
'ext.centralauth'
);
$this
->
getOutput
()->
addModuleStyles
(
'ext.centralauth.misc.styles'
);
$this
->
getOutput
()->
addJsConfigVars
(
'wgMergeMethodDescriptions'
,
$this
->
getMergeMethodDescriptions
()
);
$this
->
mUserName
=
trim
(
str_replace
(
'_'
,
' '
,
$this
->
getRequest
()->
getText
(
'target'
,
$subpage
??
''
)
)
);
$this
->
mUserName
=
$this
->
getContentLanguage
()->
ucfirst
(
$this
->
mUserName
);
$this
->
mPosted
=
$this
->
getRequest
()->
wasPosted
();
$this
->
mMethod
=
$this
->
getRequest
()->
getVal
(
'wpMethod'
);
$this
->
mWikis
=
(
array
)
$this
->
getRequest
()->
getArray
(
'wpWikis'
);
// If wpReasonList is specified then for backwards compatability with the old format of the admin status form,
// the value of wpReason needs to be moved to wpReason-other and the value of wpReasonList needs to be moved
// to wpReason.
if
(
$this
->
getRequest
()->
getVal
(
'wpReasonList'
)
)
{
$this
->
getRequest
()->
setVal
(
'wpReason-other'
,
$this
->
getRequest
()->
getVal
(
'wpReason'
)
);
$this
->
getRequest
()->
setVal
(
'wpReason'
,
$this
->
getRequest
()->
getVal
(
'wpReasonList'
)
);
$this
->
getRequest
()->
unsetVal
(
'wpReasonList'
);
}
// Possible demo states
// success, all accounts merged
// successful login, some accounts merged, others left
// successful login, others left
// not account owner, others left
// is owner / is not owner
// did / did not merge some accounts
// do / don't have more accounts to merge
if
(
$this
->
mUserName
===
''
)
{
# First time through
$this
->
getOutput
()->
addWikiMsg
(
'centralauth-admin-intro'
);
$this
->
showUsernameForm
();
return
;
}
$userPage
=
Title
::
makeTitleSafe
(
NS_USER
,
$this
->
mUserName
);
if
(
$userPage
)
{
$localUser
=
User
::
newFromName
(
$userPage
->
getText
(),
false
);
$this
->
getSkin
()->
setRelevantUser
(
$localUser
);
}
// per T49991
$this
->
getOutput
()->
setHTMLTitle
(
$this
->
msg
(
'pagetitle'
,
$this
->
msg
(
$this
->
mCanEdit
?
'centralauth-admin-title'
:
'centralauth-admin-title-ro'
,
$this
->
mUserName
)->
plain
()
)
);
$canonUsername
=
$this
->
userNameUtils
->
getCanonical
(
$this
->
mUserName
);
if
(
$canonUsername
===
false
)
{
$this
->
showNonexistentError
();
return
;
}
$globalUser
=
$this
->
getRequest
()->
wasPosted
()
?
CentralAuthUser
::
getPrimaryInstanceByName
(
$this
->
mUserName
)
:
CentralAuthUser
::
getInstanceByName
(
$this
->
mUserName
);
$this
->
mGlobalUser
=
$globalUser
;
if
(
(
$globalUser
->
isSuppressed
()
||
$globalUser
->
isHidden
()
)
&&
!
$this
->
mCanSuppress
)
{
// Claim that there's nothing if the global account is hidden and the user is not
// allowed to see it.
$this
->
showNonexistentError
();
return
;
}
$continue
=
true
;
if
(
$this
->
mCanEdit
&&
$this
->
mPosted
)
{
$this
->
databaseManager
->
assertNotReadOnly
();
$continue
=
$this
->
doSubmit
();
}
// Show just a user friendly message when a rename is in progress
try
{
$this
->
mAttachedLocalAccounts
=
$globalUser
->
queryAttached
();
}
catch
(
Exception
$e
)
{
if
(
$globalUser
->
renameInProgress
()
)
{
$this
->
showRenameInProgressError
();
return
;
}
// Rethrow
throw
$e
;
}
$this
->
mUnattachedLocalAccounts
=
$globalUser
->
queryUnattached
();
if
(
!
$globalUser
->
exists
()
&&
!
count
(
$this
->
mUnattachedLocalAccounts
)
)
{
// Nothing to see here
$this
->
showNonexistentError
();
return
;
}
$this
->
showUsernameForm
();
if
(
$continue
&&
$globalUser
->
exists
()
)
{
$this
->
showInfo
();
$this
->
showGlobalBlockingExemptWikisList
();
if
(
$this
->
mCanLock
)
{
$this
->
showStatusForm
();
}
if
(
$this
->
mCanUnmerge
)
{
$this
->
showDeleteGlobalAccountForm
();
}
$this
->
showLogExtract
();
$this
->
showWikiLists
();
}
elseif
(
$continue
&&
!
$globalUser
->
exists
()
)
{
// No global account, but we can still list the local ones
$this
->
showError
(
'centralauth-admin-nonexistent'
,
$this
->
mUserName
);
$this
->
showWikiLists
();
}
}
private
function
showNonexistentError
()
{
$this
->
showError
(
'centralauth-admin-nonexistent'
,
$this
->
mUserName
);
$this
->
showUsernameForm
();
}
private
function
showRenameInProgressError
()
{
$this
->
showError
(
'centralauth-admin-rename-in-progress'
,
$this
->
mUserName
);
$renameStatus
=
$this
->
globalRenameFactory
->
newGlobalRenameUserStatus
(
$this
->
mUserName
);
$names
=
$renameStatus
->
getNames
();
$this
->
uiService
->
showRenameLogExtract
(
$this
->
getContext
(),
$names
[
1
]
);
}
/**
* @return bool Returns true if the normal form should be displayed
*/
public
function
doSubmit
()
{
$deleted
=
false
;
$globalUser
=
$this
->
mGlobalUser
;
$request
=
$this
->
getRequest
();
$givenState
=
$request
->
getVal
(
'wpUserState'
);
$stateCheck
=
$givenState
===
$globalUser
->
getStateHash
(
true
);
if
(
!
$this
->
getUser
()->
matchEditToken
(
$request
->
getVal
(
'wpEditToken'
)
)
)
{
$this
->
showError
(
'centralauth-token-mismatch'
);
}
elseif
(
$this
->
mMethod
==
'unmerge'
&&
$this
->
mCanUnmerge
)
{
$status
=
$globalUser
->
adminUnattach
(
$this
->
mWikis
);
if
(
!
$status
->
isGood
()
)
{
$this
->
showStatusError
(
$status
->
getWikiText
()
);
}
else
{
$this
->
showSuccess
(
'centralauth-admin-unmerge-success'
,
$this
->
getLanguage
()->
formatNum
(
$status
->
successCount
),
/* deprecated */
$status
->
successCount
);
}
}
elseif
(
$this
->
mMethod
==
'delete'
&&
$this
->
mCanUnmerge
)
{
$status
=
$globalUser
->
adminDelete
(
$request
->
getVal
(
'reason'
),
$this
->
getUser
()
);
if
(
!
$status
->
isGood
()
)
{
$this
->
showStatusError
(
$status
->
getWikiText
()
);
}
else
{
$this
->
showSuccess
(
'centralauth-admin-delete-success'
,
$this
->
mUserName
);
$deleted
=
true
;
}
}
elseif
(
$this
->
mMethod
==
'set-status'
&&
!
$stateCheck
)
{
$this
->
showError
(
'centralauth-state-mismatch'
);
}
elseif
(
$this
->
mMethod
==
'set-status'
&&
$this
->
mCanLock
)
{
$setLocked
=
$request
->
getBool
(
'wpStatusLocked'
);
$setHidden
=
$request
->
getInt
(
'wpStatusHidden'
,
-
1
);
$reason
=
$request
->
getText
(
'wpReason'
);
$reasonDetail
=
$request
->
getText
(
'wpReason-other'
);
if
(
$reason
==
'other'
)
{
$reason
=
$reasonDetail
;
}
elseif
(
$reasonDetail
)
{
$reason
.=
$this
->
msg
(
'colon-separator'
)->
inContentLanguage
()->
text
()
.
$reasonDetail
;
}
$status
=
$globalUser
->
adminLockHide
(
$setLocked
,
$setHidden
,
$reason
,
$this
->
getContext
()
);
// Tell the user what happened
if
(
!
$status
->
isGood
()
)
{
$this
->
showStatusError
(
$status
->
getWikiText
()
);
}
elseif
(
$status
->
successCount
>
0
)
{
$this
->
showSuccess
(
'centralauth-admin-setstatus-success'
,
$this
->
mUserName
);
}
}
else
{
$this
->
showError
(
'centralauth-admin-bad-input'
);
}
return
!
$deleted
;
}
/**
* @param string $wikitext
*/
private
function
showStatusError
(
$wikitext
)
{
$out
=
$this
->
getOutput
();
$out
->
addHTML
(
Html
::
errorBox
(
$out
->
parseAsInterface
(
$wikitext
)
)
);
}
/**
* @param string $key
* @param mixed ...$params
*/
private
function
showError
(
$key
,
...
$params
)
{
$this
->
getOutput
()->
addHTML
(
Html
::
errorBox
(
$this
->
msg
(
$key
,
...
$params
)->
parse
()
)
);
}
/**
* @param string $key
* @param mixed ...$params
*/
private
function
showSuccess
(
$key
,
...
$params
)
{
$this
->
getOutput
()->
addHTML
(
Html
::
successBox
(
$this
->
msg
(
$key
,
...
$params
)->
parse
()
)
);
}
private
function
showUsernameForm
()
{
$lookup
=
$this
->
msg
(
$this
->
mCanEdit
?
'centralauth-admin-lookup-rw'
:
'centralauth-admin-lookup-ro'
)->
text
();
$formDescriptor
=
[
'user'
=>
[
'class'
=>
HTMLGlobalUserTextField
::
class
,
'name'
=>
'target'
,
'label-message'
=>
'centralauth-admin-username'
,
'size'
=>
25
,
'id'
=>
'target'
,
'default'
=>
$this
->
mUserName
,
'required'
=>
true
]
];
$legend
=
$this
->
msg
(
$this
->
mCanEdit
?
'centralauth-admin-manage'
:
'centralauth-admin-view'
)->
text
();
$context
=
new
DerivativeContext
(
$this
->
getContext
()
);
// Remove subpage
$context
->
setTitle
(
$this
->
getPageTitle
()
);
$htmlForm
=
HTMLForm
::
factory
(
'ooui'
,
$formDescriptor
,
$context
);
$htmlForm
->
setMethod
(
'get'
)
->
setSubmitText
(
$lookup
)
->
setSubmitID
(
'centralauth-submit-find'
)
->
setWrapperLegend
(
$legend
)
->
prepareForm
()
->
displayForm
(
false
);
}
private
function
showInfo
()
{
$attribs
=
$this
->
getInfoFields
();
// Give grep a chance to find the usages:
// centralauth-admin-info-username, centralauth-admin-info-registered,
// centralauth-admin-info-editcount, centralauth-admin-info-locked,
// centralauth-admin-info-hidden, centralauth-admin-info-groups
$content
=
Xml
::
openElement
(
"ul"
);
foreach
(
$attribs
as
$key
=>
[
'label'
=>
$msg
,
'data'
=>
$data
]
)
{
$content
.=
Html
::
rawElement
(
'li'
,
[
'id'
=>
"mw-centralauth-admin-info-$key"
],
$this
->
msg
(
'centralauth-admin-info-line'
)
->
params
(
$this
->
msg
(
$msg
)->
escaped
()
)
->
rawParams
(
$data
)
->
parse
()
);
}
$content
.=
Xml
::
closeElement
(
"ul"
);
$this
->
getOutput
()->
addHTML
(
$this
->
getFramedFieldsetLayout
(
$content
,
'centralauth-admin-info-header'
,
'mw-centralauth-info'
)
);
}
/**
* @return array Array of arrays where each array has two keys: 'label' containing the message
* key or Message object to be used as the label, and 'data' which is the already HTML escaped
* value that is associated with the label. The first dimension keys are field names, but are currently
* not used in the UI or for generating messages.
* @phan-return array<string,array{label:string|Message,data:string}>
*/
private
function
getInfoFields
()
{
$globalUser
=
$this
->
mGlobalUser
;
$reg
=
$globalUser
->
getRegistration
();
$age
=
$this
->
uiService
->
prettyTimespan
(
$this
->
getContext
(),
(
int
)
wfTimestamp
(
TS_UNIX
)
-
(
int
)
wfTimestamp
(
TS_UNIX
,
$reg
)
);
$attribs
=
[
'username'
=>
htmlspecialchars
(
$globalUser
->
getName
()
),
'registered'
=>
htmlspecialchars
(
$this
->
getLanguage
()->
timeanddate
(
$reg
,
true
)
.
" ($age)"
),
'editcount'
=>
htmlspecialchars
(
$this
->
getLanguage
()->
formatNum
(
$this
->
evaluateTotalEditcount
()
)
),
'attached'
=>
htmlspecialchars
(
$this
->
getLanguage
()->
formatNum
(
count
(
$this
->
mAttachedLocalAccounts
)
)
),
];
if
(
// Renaming self is not allowed.
$globalUser
->
getName
()
!==
$this
->
getContext
()->
getUser
()->
getName
()
&&
$this
->
getContext
()->
getAuthority
()->
isAllowed
(
'centralauth-rename'
)
)
{
$renameLink
=
$this
->
getLinkRenderer
()->
makeKnownLink
(
SpecialPage
::
getTitleFor
(
'GlobalRenameUser'
,
$globalUser
->
getName
()
),
$this
->
msg
(
'centralauth-admin-info-username-rename'
)->
text
()
);
$attribs
[
'username'
]
.=
$this
->
msg
(
'word-separator'
)->
escaped
();
$attribs
[
'username'
]
.=
$this
->
msg
(
'parentheses'
)->
rawParams
(
$renameLink
)->
escaped
();
}
if
(
count
(
$this
->
mUnattachedLocalAccounts
)
)
{
$attribs
[
'unattached'
]
=
htmlspecialchars
(
$this
->
getLanguage
()->
formatNum
(
count
(
$this
->
mUnattachedLocalAccounts
)
)
);
}
if
(
$globalUser
->
isLocked
()
)
{
$attribs
[
'locked'
]
=
$this
->
msg
(
'centralauth-admin-yes'
)->
escaped
();
}
if
(
$this
->
mCanSuppress
)
{
$attribs
[
'hidden'
]
=
$this
->
uiService
->
formatHiddenLevel
(
$this
->
getContext
(),
$globalUser
->
getHiddenLevelInt
()
);
}
if
(
$this
->
tempUserConfig
->
isTempName
(
$globalUser
->
getName
()
)
)
{
$localUser
=
$this
->
userFactory
->
newFromName
(
$globalUser
->
getName
()
);
// if the central user is valid, the local username is too, but Phan doesn't know that
'@phan-var User $localUser'
;
$registrationDate
=
$this
->
userRegistrationLookup
->
getRegistration
(
$localUser
,
CentralAuthGlobalRegistrationProvider
::
TYPE
);
$expirationDays
=
$this
->
tempUserConfig
->
getExpireAfterDays
();
if
(
$registrationDate
&&
$expirationDays
)
{
// Add one day to account for the expiration script running daily
$expirationDate
=
MWTimestamp
::
getInstance
(
$registrationDate
)
->
add
(
new
DateInterval
(
'P'
.
(
$expirationDays
+
1
)
.
'D'
)
);
if
(
$expirationDate
->
getTimestamp
()
<
MWTimestamp
::
time
()
)
{
$attribs
[
'expired'
]
=
htmlspecialchars
(
$this
->
getLanguage
()
->
userTimeAndDate
(
$expirationDate
->
timestamp
,
$localUser
)
);
}
}
}
// Convert the values of the existing $attribs array into arrays, where the value is placed in the 'data'
// key and the 'label' is the message key generated from the associated key.
$attribsWithMessageKeys
=
[];
foreach
(
$attribs
as
$key
=>
$value
)
{
$attribsWithMessageKeys
[
$key
]
=
[
'label'
=>
"centralauth-admin-info-$key"
,
'data'
=>
$value
,
];
}
$groups
=
$globalUser
->
getGlobalGroupsWithExpiration
();
if
(
$groups
)
{
$groupLinks
=
[];
// Ensure temporary groups are displayed first, to avoid ambiguity like
// "first, second (expires at some point)" (unclear if only second expires or if both expire)
uasort
(
$groups
,
static
fn
(
$first
,
$second
)
=>
(
bool
)
$second
<=>
(
bool
)
$first
);
$uiLanguage
=
$this
->
getLanguage
();
$uiUser
=
$this
->
getUser
();
foreach
(
$groups
as
$group
=>
$expiry
)
{
$link
=
$this
->
getLinkRenderer
()->
makeLink
(
SpecialPage
::
getTitleFor
(
'GlobalGroupPermissions'
,
$group
),
$uiLanguage
->
getGroupName
(
$group
)
);
if
(
$expiry
)
{
$link
=
$this
->
msg
(
'group-membership-link-with-expiry'
)
->
rawParams
(
$link
)
->
params
(
$uiLanguage
->
userTimeAndDate
(
$expiry
,
$uiUser
)
)
->
escaped
();
}
$groupLinks
[]
=
$link
;
}
$attribsWithMessageKeys
[
'groups'
]
=
[
'label'
=>
$this
->
msg
(
'centralauth-admin-info-groups'
)->
numParams
(
count
(
$groups
)
),
'data'
=>
$uiLanguage
->
commaList
(
$groupLinks
),
];
}
if
(
$this
->
mCanChangeGroups
)
{
if
(
!
isset
(
$attribsWithMessageKeys
[
'groups'
]
)
)
{
$attribsWithMessageKeys
[
'groups'
]
=
[
'label'
=>
$this
->
msg
(
'centralauth-admin-info-groups'
)->
numParams
(
0
),
'data'
=>
$this
->
msg
(
'rightsnone'
)->
escaped
(),
];
}
$manageGroupsLink
=
$this
->
getLinkRenderer
()->
makeKnownLink
(
SpecialPage
::
getTitleFor
(
'GlobalGroupMembership'
,
$globalUser
->
getName
()
),
$this
->
msg
(
'centralauth-admin-info-groups-manage'
)->
text
()
);
$attribsWithMessageKeys
[
'groups'
][
'data'
]
.=
$this
->
msg
(
'word-separator'
)->
escaped
();
$attribsWithMessageKeys
[
'groups'
][
'data'
]
.=
$this
->
msg
(
'parentheses'
)
->
rawParams
(
$manageGroupsLink
)->
escaped
();
}
$caHookRunner
=
new
CentralAuthHookRunner
(
$this
->
getHookContainer
()
);
$caHookRunner
->
onCentralAuthInfoFields
(
$globalUser
,
$this
->
getContext
(),
$attribsWithMessageKeys
);
return
$attribsWithMessageKeys
;
}
private
function
showGlobalBlockingExemptWikisList
()
{
$tableRows
=
$this
->
getGlobalBlockingExemptWikiTableRows
();
if
(
!
$tableRows
)
{
return
;
}
$header
=
Xml
::
openElement
(
'thead'
)
.
Xml
::
openElement
(
'tr'
);
$header
.=
Html
::
element
(
'th'
,
[],
$this
->
msg
(
'centralauth-admin-globalblock-exempt-list-wiki-heading'
)->
text
()
);
$header
.=
Html
::
element
(
'th'
,
[
'class'
=>
'unsortable'
],
$this
->
msg
(
'centralauth-admin-globalblock-exempt-list-reason-heading'
)->
text
()
);
$header
.=
Xml
::
closeElement
(
'tr'
)
.
Xml
::
closeElement
(
'thead'
);
$body
=
Html
::
rawElement
(
'tbody'
,
[],
$tableRows
);
$tableHtml
=
Html
::
rawElement
(
'table'
,
[
'class'
=>
'wikitable sortable mw-centralauth-globalblock-exempt-list-table'
],
$header
.
$body
);
$this
->
getOutput
()->
addHTML
(
$this
->
getFramedFieldsetLayout
(
$tableHtml
,
'centralauth-admin-globalblock-exempt-list'
,
'mw-centralauth-globalblock-exempt-list'
)
);
}
private
function
getGlobalBlockingExemptWikiTableRows
():
string
{
// There can be no global blocks if the GlobalBlocking extension is not loaded.
if
(
!
ExtensionRegistry
::
getInstance
()->
isLoaded
(
'GlobalBlocking'
)
)
{
return
''
;
}
$globalBlockingServices
=
GlobalBlockingServices
::
wrap
(
MediaWikiServices
::
getInstance
()
);
$globalBlock
=
$globalBlockingServices
->
getGlobalBlockLookup
()
->
getGlobalBlockingBlock
(
null
,
$this
->
mGlobalUser
->
getId
(),
GlobalBlockLookup
::
SKIP_LOCAL_DISABLE_CHECK
);
// If the user is not globally blocked, then their global block cannot be locally disabled (so there will be
// no rows to display).
if
(
$globalBlock
===
null
)
{
return
''
;
}
$html
=
''
;
$queryWikis
=
array_merge
(
$this
->
mAttachedLocalAccounts
,
$this
->
mUnattachedLocalAccounts
);
foreach
(
$queryWikis
as
$wiki
)
{
// Check if the global block is disabled on the given wiki, and if it is then add it to the table HTML.
$localBlockStatus
=
$globalBlockingServices
->
getGlobalBlockLocalStatusLookup
()
->
getLocalWhitelistInfo
(
$globalBlock
->
gb_id
,
null
,
$wiki
[
'wiki'
]
);
if
(
$localBlockStatus
===
false
)
{
continue
;
}
$row
=
Html
::
rawElement
(
'td'
,
[],
$this
->
foreignUserLink
(
$wiki
[
'wiki'
]
)
)
.
Html
::
element
(
'td'
,
[],
$localBlockStatus
[
'reason'
]
);
$html
.=
Html
::
rawElement
(
'tr'
,
[],
$row
);
}
return
$html
;
}
/**
* Generates a {@link FieldLayout} that can be used in a HTMLFormInfo field instance when 'rawrow' is true.
* This is useful in the case that the HTML contains elements which cannot appear inside a label element.
*
* @param string $html The HTML that we want to use in the 'info' field
* @return FieldLayout An instance suitable for use in an 'info' field with 'rawrow' set to true
*/
private
function
getFieldLayoutForHtmlContent
(
string
$html
):
FieldLayout
{
return
new
FieldLayout
(
new
Widget
(
[
'content'
=>
new
HtmlSnippet
(
$html
)
]
)
);
}
private
function
showWikiLists
()
{
$showUnmergeCheckboxes
=
$this
->
mCanUnmerge
&&
$this
->
mGlobalUser
->
exists
();
$formDescriptor
=
[
'wikilist'
=>
[
'type'
=>
'info'
,
'raw'
=>
true
,
// We need to use a "rawrow" here to prevent the table element being wrapped by a label element.
'rawrow'
=>
true
,
// When using "rawrow" we need to provide the HTML content via a FieldLayout.
'default'
=>
$this
->
getFieldLayoutForHtmlContent
(
$this
->
getWikiListsTable
(
$showUnmergeCheckboxes
)
),
],
'Method'
=>
[
'type'
=>
'hidden'
,
'default'
=>
'unmerge'
,
],
];
if
(
$showUnmergeCheckboxes
)
{
$formDescriptor
[
'submit'
]
=
[
'type'
=>
'submit'
,
'buttonlabel-message'
=>
'centralauth-admin-unmerge'
,
'flags'
=>
[
'progressive'
],
];
}
$context
=
new
DerivativeContext
(
$this
->
getContext
()
);
$htmlForm
=
HTMLForm
::
factory
(
'ooui'
,
$formDescriptor
,
$context
);
$htmlForm
->
setAction
(
$this
->
getPageTitle
()->
getFullURL
(
[
'target'
=>
$this
->
mUserName
]
)
)
->
suppressDefaultSubmit
()
->
setWrapperLegendMsg
(
$showUnmergeCheckboxes
?
'centralauth-admin-list-legend-rw'
:
'centralauth-admin-list-legend-ro'
)
->
setId
(
'mw-centralauth-merged'
)
->
prepareForm
()
->
displayForm
(
false
);
}
/**
* @param bool $showUnmergeCheckboxes Whether the checkboxes to allow the user to unmerge a local account should
* be shown in the table.
* @return string The HTML for the table of local accounts
*/
private
function
getWikiListsTable
(
bool
$showUnmergeCheckboxes
):
string
{
$columns
=
[
// centralauth-admin-list-localwiki
"localwiki"
,
// centralauth-admin-list-attached-on
"attached-on"
,
// centralauth-admin-list-method
"method"
,
// centralauth-admin-list-blocked
"blocked"
,
// centralauth-admin-list-editcount
"editcount"
,
// centralauth-admin-list-groups
"groups"
,
];
$header
=
Xml
::
openElement
(
'thead'
)
.
Xml
::
openElement
(
'tr'
);
if
(
$showUnmergeCheckboxes
)
{
$header
.=
Html
::
element
(
'th'
,
[
'class'
=>
'unsortable'
]
);
}
foreach
(
$columns
as
$c
)
{
$header
.=
Html
::
element
(
'th'
,
[],
$this
->
msg
(
"centralauth-admin-list-$c"
)->
text
()
);
}
$header
.=
Xml
::
closeElement
(
'tr'
)
.
Xml
::
closeElement
(
'thead'
);
$body
=
Html
::
rawElement
(
'tbody'
,
[],
$this
->
listAccounts
(
$this
->
mAttachedLocalAccounts
,
$showUnmergeCheckboxes
)
.
$this
->
listAccounts
(
$this
->
mUnattachedLocalAccounts
,
$showUnmergeCheckboxes
)
);
return
Html
::
rawElement
(
'table'
,
[
'class'
=>
'wikitable sortable mw-centralauth-wikislist'
],
$header
.
$body
);
}
/**
* @param array[] $list The result of {@link CentralAuthUser::queryAttached} or
* {@link CentralAuthUser::queryUnattached()}
* @return string The HTML body table rows
*/
private
function
listAccounts
(
array
$list
,
bool
$showUnmergeCheckboxes
):
string
{
ksort
(
$list
);
return
implode
(
"
\n
"
,
array_map
(
function
(
$row
)
use
(
$showUnmergeCheckboxes
)
{
return
$this
->
listWikiItem
(
$row
,
$showUnmergeCheckboxes
);
},
$list
)
);
}
/**
* @param array $row The an item from an array returned by either {@link CentralAuthUser::queryAttached} or
* {@link CentralAuthUser::queryUnattached()}
* @return string The HTML row that represents the provided array
*/
private
function
listWikiItem
(
array
$row
,
bool
$showUnmergeCheckboxes
):
string
{
$html
=
Xml
::
openElement
(
'tr'
);
if
(
$showUnmergeCheckboxes
)
{
if
(
!
empty
(
$row
[
'attachedMethod'
]
)
)
{
$html
.=
Html
::
rawElement
(
'td'
,
[],
$this
->
adminCheck
(
$row
[
'wiki'
]
)
);
}
else
{
// Account is unattached, don't show checkbox to detach
$html
.=
Xml
::
element
(
'td'
);
}
}
$html
.=
Html
::
rawElement
(
'td'
,
[],
$this
->
foreignUserLink
(
$row
[
'wiki'
]
)
);
$attachedTimestamp
=
$row
[
'attachedTimestamp'
]
??
''
;
$html
.=
$this
->
getAttachedTimestampField
(
$attachedTimestamp
);
if
(
empty
(
$row
[
'attachedMethod'
]
)
)
{
$attachedMethod
=
$this
->
msg
(
'centralauth-admin-unattached'
)->
parse
();
}
else
{
$attachedMethod
=
$this
->
formatMergeMethod
(
$row
[
'attachedMethod'
]
);
}
$html
.=
Html
::
rawElement
(
'td'
,
[
'class'
=>
'mw-centralauth-wikislist-method'
],
$attachedMethod
);
$html
.=
Html
::
rawElement
(
'td'
,
[],
$this
->
formatBlockStatus
(
$row
)
)
.
Html
::
rawElement
(
'td'
,
[
'class'
=>
'mw-centralauth-wikislist-editcount'
],
$this
->
formatEditcount
(
$row
)
)
.
Html
::
rawElement
(
'td'
,
[],
$this
->
formatGroups
(
$row
)
)
.
Xml
::
closeElement
(
'tr'
);
return
$html
;
}
/**
* @param string|null $attachedTimestamp
*
* @return string
*/
private
function
getAttachedTimestampField
(
$attachedTimestamp
)
{
if
(
!
$attachedTimestamp
)
{
$html
=
Xml
::
openElement
(
'td'
,
[
'data-sort-value'
=>
'0'
]
)
.
$this
->
msg
(
'centralauth-admin-unattached'
)->
parse
();
}
else
{
$html
=
Xml
::
openElement
(
'td'
,
[
'data-sort-value'
=>
$attachedTimestamp
]
)
.
// visible date and time in users preference
htmlspecialchars
(
$this
->
getLanguage
()->
timeanddate
(
$attachedTimestamp
,
true
)
);
}
$html
.=
Xml
::
closeElement
(
'td'
);
return
$html
;
}
/**
* @param string $method
* @return string
*/
private
function
formatMergeMethod
(
$method
)
{
// Give grep a chance to find the usages:
// centralauth-merge-method-primary, centralauth-merge-method-empty,
// centralauth-merge-method-mail, centralauth-merge-method-password,
// centralauth-merge-method-admin, centralauth-merge-method-new,
// centralauth-merge-method-login
$brief
=
$this
->
msg
(
"centralauth-merge-method-{$method}"
)->
text
();
$html
=
Html
::
element
(
'img'
,
[
'src'
=>
$this
->
getConfig
()->
get
(
'ExtensionAssetsPath'
)
.
"/CentralAuth/images/icons/merged-{$method}.png"
,
'alt'
=>
$brief
,
'title'
=>
$brief
,
]
)
.
Html
::
element
(
'span'
,
[
'class'
=>
'merge-method-help'
,
'title'
=>
$brief
,
'data-centralauth-mergemethod'
=>
$method
],
$this
->
msg
(
'centralauth-merge-method-questionmark'
)->
text
()
);
return
$html
;
}
/**
* @param array $row
* @return string
*/
private
function
formatBlockStatus
(
$row
)
{
$additionalHtml
=
''
;
if
(
isset
(
$row
[
'blocked'
]
)
&&
$row
[
'blocked'
]
)
{
$optionMessage
=
$this
->
formatBlockParams
(
$row
);
if
(
$row
[
'block-expiry'
]
==
'infinity'
)
{
$text
=
$this
->
msg
(
'centralauth-admin-blocked2-indef'
)->
text
();
}
else
{
$expiry
=
$this
->
getLanguage
()->
timeanddate
(
$row
[
'block-expiry'
],
true
);
$expiryd
=
$this
->
getLanguage
()->
date
(
$row
[
'block-expiry'
],
true
);
$expiryt
=
$this
->
getLanguage
()->
time
(
$row
[
'block-expiry'
],
true
);
$text
=
$this
->
msg
(
'centralauth-admin-blocked2'
,
$expiry
,
$expiryd
,
$expiryt
)
->
text
();
}
if
(
$row
[
'block-reason'
]
)
{
$reason
=
Sanitizer
::
escapeHtmlAllowEntities
(
$row
[
'block-reason'
]
);
$reason
=
$this
->
commentFormatter
->
formatLinks
(
$reason
,
null
,
false
,
$row
[
'wiki'
]
);
$msg
=
$this
->
msg
(
'centralauth-admin-blocked-reason'
);
$msg
->
rawParams
(
'<span class="plainlinks">'
.
$reason
.
'</span>'
);
$additionalHtml
.=
Html
::
rawElement
(
'br'
)
.
$msg
->
parse
();
}
$additionalHtml
.=
' '
.
$optionMessage
;
}
else
{
$text
=
$this
->
msg
(
'centralauth-admin-notblocked'
)->
text
();
}
return
self
::
foreignLink
(
$row
[
'wiki'
],
'Special:Log/block'
,
$text
,
$this
->
msg
(
'centralauth-admin-blocklog'
)->
text
(),
'page=User:'
.
urlencode
(
$this
->
mUserName
)
)
.
$additionalHtml
;
}
/**
* Format a block's parameters.
*
* @see BlockListPager::formatValue()
*
* @param array $row
* @return string
*/
private
function
formatBlockParams
(
$row
)
{
global
$wgConf
;
// Ensure all the data is loaded before trying to use.
$wgConf
->
loadFullData
();
$properties
=
[];
if
(
$row
[
'block-sitewide'
]
)
{
$properties
[]
=
$this
->
msg
(
'blocklist-editing-sitewide'
)->
escaped
();
}
if
(
!
$row
[
'block-sitewide'
]
&&
$row
[
'block-restrictions'
]
)
{
$list
=
$this
->
getRestrictionListHTML
(
$row
);
if
(
$list
)
{
$properties
[]
=
$this
->
msg
(
'blocklist-editing'
)->
escaped
()
.
$list
;
}
}
$options
=
[
'anononly'
=>
'anononlyblock'
,
'nocreate'
=>
'createaccountblock'
,
'noautoblock'
=>
'noautoblockblock'
,
'noemail'
=>
'emailblock'
,
'nousertalk'
=>
'blocklist-nousertalk'
,
];
foreach
(
$options
as
$option
=>
$msg
)
{
if
(
$row
[
'block-'
.
$option
]
)
{
$properties
[]
=
$this
->
msg
(
$msg
)->
escaped
();
}
}
if
(
!
$properties
)
{
return
''
;
}
return
Html
::
rawElement
(
'ul'
,
[],
implode
(
''
,
array_map
(
static
function
(
$prop
)
{
return
Html
::
rawElement
(
'li'
,
[],
$prop
);
},
$properties
)
)
);
}
/**
* @see BlockListPager::getRestrictionListHTML()
*
* @param array $row
*
* @return string
*/
private
function
getRestrictionListHTML
(
array
$row
)
{
$count
=
array_reduce
(
$row
[
'block-restrictions'
],
static
function
(
$carry
,
$restriction
)
{
$carry
[
$restriction
->
getType
()]
+=
1
;
return
$carry
;
},
[
PageRestriction
::
TYPE
=>
0
,
NamespaceRestriction
::
TYPE
=>
0
,
ActionRestriction
::
TYPE
=>
0
,
]
);
$restrictions
=
[];
foreach
(
$count
as
$type
=>
$value
)
{
if
(
$value
===
0
)
{
continue
;
}
$restrictions
[]
=
Html
::
rawElement
(
'li'
,
[],
self
::
foreignLink
(
$row
[
'wiki'
],
'Special:BlockList/'
.
$row
[
'name'
],
$this
->
msg
(
'centralauth-block-editing-'
.
$type
,
$value
)->
text
()
)
);
}
if
(
count
(
$restrictions
)
===
0
)
{
return
''
;
}
return
Html
::
rawElement
(
'ul'
,
[],
implode
(
''
,
$restrictions
)
);
}
/**
* @param array $row
* @return string
* @throws Exception
*/
private
function
formatEditcount
(
$row
)
{
$wiki
=
WikiMap
::
getWiki
(
$row
[
'wiki'
]
);
if
(
!
$wiki
)
{
throw
new
InvalidArgumentException
(
"Invalid wiki: {$row['wiki']}"
);
}
$wikiname
=
$wiki
->
getDisplayName
();
$editCount
=
$this
->
getLanguage
()->
formatNum
(
intval
(
$row
[
'editCount'
]
)
);
return
self
::
foreignLink
(
$row
[
'wiki'
],
'Special:Contributions/'
.
$this
->
mUserName
,
$editCount
,
$this
->
msg
(
'centralauth-foreign-contributions'
)
->
params
(
$editCount
,
$wikiname
)->
text
()
);
}
/**
* @param array $row
* @return string
*/
private
function
formatGroups
(
$row
)
{
if
(
!
count
(
$row
[
'groupMemberships'
]
)
)
{
return
''
;
}
// We place temporary groups before non-expiring groups in the list.
// This is to avoid the ambiguity of something like
// "sysop, bureaucrat (temporary)" -- users might wonder whether the
// "temporary" indication applies to both groups, or just the last one
$listTemporary
=
[];
$list
=
[];
/** @var UserGroupMembership $ugm */
foreach
(
$row
[
'groupMemberships'
]
as
$group
=>
$ugm
)
{
if
(
$ugm
->
getExpiry
()
)
{
$listTemporary
[]
=
$this
->
msg
(
'centralauth-admin-group-temporary'
,
wfEscapeWikitext
(
$group
)
)->
parse
();
}
else
{
$list
[]
=
htmlspecialchars
(
$group
);
}
}
return
$this
->
getLanguage
()->
commaList
(
array_merge
(
$listTemporary
,
$list
)
);
}
/**
* @param string|WikiReference $wikiID
* @param string $title
* @param string $text not HTML escaped
* @param string $hint
* @param string $params
* @return string
* @throws Exception
*/
public
static
function
foreignLink
(
$wikiID
,
$title
,
$text
,
$hint
=
''
,
$params
=
''
)
{
if
(
$wikiID
instanceof
WikiReference
)
{
$wiki
=
$wikiID
;
}
else
{
$wiki
=
WikiMap
::
getWiki
(
$wikiID
);
if
(
!
$wiki
)
{
throw
new
InvalidArgumentException
(
"Invalid wiki: $wikiID"
);
}
}
$url
=
$wiki
->
getFullUrl
(
$title
);
if
(
$params
)
{
$url
.=
'?'
.
$params
;
}
return
Xml
::
element
(
'a'
,
[
'href'
=>
$url
,
'title'
=>
$hint
,
],
$text
);
}
/**
* @param string $wikiID
* @return string
* @throws Exception
*/
private
function
foreignUserLink
(
$wikiID
)
{
$wiki
=
WikiMap
::
getWiki
(
$wikiID
);
if
(
!
$wiki
)
{
throw
new
InvalidArgumentException
(
"Invalid wiki: $wikiID"
);
}
$wikiname
=
$wiki
->
getDisplayName
();
return
self
::
foreignLink
(
$wiki
,
$this
->
namespaceInfo
->
getCanonicalName
(
NS_USER
)
.
':'
.
$this
->
mUserName
,
$wikiname
,
$this
->
msg
(
'centralauth-foreign-link'
,
$this
->
mUserName
,
$wikiname
)->
text
()
);
}
/**
* @param string $wikiID
* @return string
*/
private
function
adminCheck
(
$wikiID
)
{
return
Xml
::
check
(
'wpWikis[]'
,
false
,
[
'value'
=>
$wikiID
]
);
}
/**
* Generates a form for managing deleting a global account which contains a description, a reason field
* and destructive submit button.
*/
private
function
showDeleteGlobalAccountForm
()
{
$formDescriptor
=
[
'info'
=>
[
'type'
=>
'info'
,
'raw'
=>
true
,
'default'
=>
$this
->
msg
(
'centralauth-admin-delete-description'
)->
parseAsBlock
(),
'cssclass'
=>
'mw-centralauth-admin-delete-intro'
,
],
'reason'
=>
[
'type'
=>
'text'
,
'label-message'
=>
'centralauth-admin-reason'
,
'id'
=>
"delete-reason"
,
'name'
=>
'reason'
,
],
'Method'
=>
[
'type'
=>
'hidden'
,
'default'
=>
'delete'
,
],
'submit'
=>
[
'type'
=>
'submit'
,
'buttonlabel-message'
=>
'centralauth-admin-delete-button'
,
'flags'
=>
[
'progressive'
,
'destructive'
]
],
];
$context
=
new
DerivativeContext
(
$this
->
getContext
()
);
$htmlForm
=
HTMLForm
::
factory
(
'ooui'
,
$formDescriptor
,
$context
);
$htmlForm
->
setAction
(
$this
->
getPageTitle
()->
getFullURL
(
[
'target'
=>
$this
->
mUserName
]
)
)
->
suppressDefaultSubmit
()
->
setWrapperLegendMsg
(
'centralauth-admin-delete-title'
)
->
setId
(
'mw-centralauth-delete'
)
->
prepareForm
()
->
displayForm
(
false
);
}
private
function
showStatusForm
()
{
// Allows locking, hiding, locking and hiding.
$formDescriptor
=
[
'intro'
=>
[
'type'
=>
'info'
,
'raw'
=>
true
,
'default'
=>
$this
->
msg
(
'centralauth-admin-status-intro'
)->
parseAsBlock
(),
'cssclass'
=>
'mw-centralauth-admin-status-intro'
,
'section'
=>
'intro'
,
],
'StatusLocked'
=>
[
'type'
=>
'radio'
,
'label-message'
=>
'centralauth-admin-status-locked'
,
'options-messages'
=>
[
'centralauth-admin-status-locked-no'
=>
0
,
'centralauth-admin-status-locked-yes'
=>
1
,
],
'default'
=>
(
int
)
$this
->
mGlobalUser
->
isLocked
(),
'id'
=>
'mw-centralauth-admin-status-locked'
,
'section'
=>
'lockedhidden'
,
],
'StatusHidden'
=>
[
'type'
=>
'radio'
,
'label-message'
=>
'centralauth-admin-status-hidden'
,
'options-messages'
=>
[
'centralauth-admin-status-hidden-no'
=>
CentralAuthUser
::
HIDDEN_LEVEL_NONE
,
],
'default'
=>
$this
->
mGlobalUser
->
getHiddenLevelInt
(),
'id'
=>
'mw-centralauth-admin-status-hidden'
,
'section'
=>
'lockedhidden'
,
],
'Reason'
=>
[
'type'
=>
'selectandother'
,
'maxlength'
=>
CommentStore
::
COMMENT_CHARACTER_LIMIT
,
'label-message'
=>
'centralauth-admin-reason'
,
'options-message'
=>
'centralauth-admin-status-reasons'
,
'other-message'
=>
'centralauth-admin-reason-other-select'
,
'id'
=>
'mw-centralauth-admin-reason'
,
],
'Method'
=>
[
'type'
=>
'hidden'
,
'default'
=>
'set-status'
,
],
'UserState'
=>
[
'type'
=>
'hidden'
,
'default'
=>
$this
->
mGlobalUser
->
getStateHash
(
false
),
],
'submit'
=>
[
'type'
=>
'submit'
,
'buttonlabel-message'
=>
'centralauth-admin-status-submit'
,
'flags'
=>
[
'progressive'
],
],
];
if
(
$this
->
mCanSuppress
)
{
$formDescriptor
[
'StatusHidden'
][
'options-messages'
]
+=
[
'centralauth-admin-status-hidden-list'
=>
CentralAuthUser
::
HIDDEN_LEVEL_LISTS
,
'centralauth-admin-status-hidden-oversight'
=>
CentralAuthUser
::
HIDDEN_LEVEL_SUPPRESSED
,
];
}
$context
=
new
DerivativeContext
(
$this
->
getContext
()
);
$htmlForm
=
HTMLForm
::
factory
(
'ooui'
,
$formDescriptor
,
$context
);
$htmlForm
->
setAction
(
$this
->
getPageTitle
()->
getFullURL
(
[
'target'
=>
$this
->
mUserName
]
)
)
->
suppressDefaultSubmit
()
->
setId
(
'mw-centralauth-admin-status'
)
->
setWrapperLegendMsg
(
'centralauth-admin-status'
)
->
prepareForm
()
->
displayForm
(
false
);
}
/**
* Gets a framed and padded fieldset that contains the given HTML.
*
* Used over HTMLForm as we need to avoid adding hidden fields like "wpEditToken" and form elements to parts of
* the page that are not forms.
*
* @param string $html The HTML to be wrapped with the fieldset
* @param string|MessageSpecifier $fieldsetLegendMsg The message to use as the fieldset legend
* @param string|null $fieldsetId The ID for the fieldset, or null for no ID
* @return string HTML
*/
private
function
getFramedFieldsetLayout
(
string
$html
,
$fieldsetLegendMsg
,
?
string
$fieldsetId
=
null
):
string
{
$fieldset
=
new
FieldsetLayout
(
[
'label'
=>
$this
->
msg
(
$fieldsetLegendMsg
)->
text
(),
'items'
=>
[
new
Widget
(
[
'content'
=>
new
HtmlSnippet
(
$html
),
]
),
],
'id'
=>
$fieldsetId
,
]
);
return
new
PanelLayout
(
[
'classes'
=>
[
'mw-htmlform-ooui-wrapper'
],
'expanded'
=>
false
,
'padded'
=>
true
,
'framed'
=>
true
,
'content'
=>
$fieldset
,
]
);
}
private
function
showLogExtract
()
{
$user
=
$this
->
mGlobalUser
->
getName
();
$title
=
Title
::
newFromText
(
$this
->
namespaceInfo
->
getCanonicalName
(
NS_USER
)
.
":{$user}@global"
);
if
(
!
$title
)
{
// Don't fatal even if a Title couldn't be generated
// because we've invalid usernames too :/
return
;
}
$logTypes
=
[
'globalauth'
];
if
(
$this
->
mCanSuppress
)
{
$logTypes
[]
=
'suppress'
;
}
$html
=
''
;
$numRows
=
LogEventsList
::
showLogExtract
(
$html
,
$logTypes
,
$title
->
getPrefixedText
(),
''
,
[
'showIfEmpty'
=>
true
]
);
if
(
$numRows
)
{
$this
->
getOutput
()->
addHTML
(
$this
->
getFramedFieldsetLayout
(
$html
,
'centralauth-admin-logsnippet'
)
);
return
;
}
if
(
$this
->
mGlobalUser
->
isLocked
()
)
{
$logOtherWikiMsg
=
$this
->
msg
(
'centralauth-admin-log-otherwiki'
)
->
params
(
$this
->
mGlobalUser
->
getName
()
);
if
(
!
$logOtherWikiMsg
->
isDisabled
()
)
{
$this
->
getOutput
()->
addHTML
(
Html
::
warningBox
(
$logOtherWikiMsg
->
parse
(),
'centralauth-admin-log-otherwiki'
)
);
}
}
}
/**
* @return int
*/
private
function
evaluateTotalEditcount
()
{
$total
=
0
;
foreach
(
$this
->
mAttachedLocalAccounts
as
$acc
)
{
$total
+=
$acc
[
'editCount'
];
}
return
$total
;
}
/**
* @return array[]
*/
private
function
getMergeMethodDescriptions
()
{
// Give grep a chance to find the usages:
// centralauth-merge-method-primary, centralauth-merge-method-new,
// centralauth-merge-method-empty, centralauth-merge-method-password,
// centralauth-merge-method-mail, centralauth-merge-method-admin,
// centralauth-merge-method-login
// Give grep a chance to find the usages:
// centralauth-merge-method-primary-desc, centralauth-merge-method-new-desc,
// centralauth-merge-method-empty-desc, centralauth-merge-method-password-desc,
// centralauth-merge-method-mail-desc, centralauth-merge-method-admin-desc,
// centralauth-merge-method-login-desc
$mergeMethodDescriptions
=
[];
foreach
(
[
'primary'
,
'new'
,
'empty'
,
'password'
,
'mail'
,
'admin'
,
'login'
]
as
$method
)
{
$mergeMethodDescriptions
[
$method
]
=
[
'short'
=>
$this
->
getLanguage
()->
ucfirst
(
$this
->
msg
(
"centralauth-merge-method-{$method}"
)->
escaped
()
),
'desc'
=>
$this
->
msg
(
"centralauth-merge-method-{$method}-desc"
)->
escaped
()
];
}
return
$mergeMethodDescriptions
;
}
/**
* Return an array of subpages beginning with $search that this special page will accept.
*
* @param string $search Prefix to search for
* @param int $limit Maximum number of results to return (usually 10)
* @param int $offset Number of results to skip (usually 0)
* @return string[] Matching subpages
*/
public
function
prefixSearchSubpages
(
$search
,
$limit
,
$offset
)
{
$search
=
$this
->
userNameUtils
->
getCanonical
(
$search
);
if
(
!
$search
)
{
// No prefix suggestion for invalid user
return
[];
}
$dbr
=
$this
->
databaseManager
->
getCentralReplicaDB
();
// Autocomplete subpage as user list - non-hidden users to allow caching
return
$dbr
->
newSelectQueryBuilder
()
->
select
(
'gu_name'
)
->
from
(
'globaluser'
)
->
where
(
[
$dbr
->
expr
(
'gu_name'
,
IExpression
::
LIKE
,
new
LikeValue
(
$search
,
$dbr
->
anyString
()
)
),
'gu_hidden_level'
=>
CentralAuthUser
::
HIDDEN_LEVEL_NONE
,
]
)
->
orderBy
(
'gu_name'
)
->
limit
(
$limit
)
->
offset
(
$offset
)
->
caller
(
__METHOD__
)
->
fetchFieldValues
();
}
/** @inheritDoc */
protected
function
getGroupName
()
{
return
'users'
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Aug 14 2026, 21:35 (12 w, 6 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
66/2a/7e1ed77187a9687a6e71582fdf1e
Default Alt Text
SpecialCentralAuth.php (41 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment