Page MenuHomeWickedGov Phorge

SpecialMultiLock.php
No OneTemporary

Size
15 KB
Referenced Files
None
Subscribers
None

SpecialMultiLock.php

<?php
namespace MediaWiki\Extension\CentralAuth\Special;
use LogEventsList;
use MediaWiki\Extension\CentralAuth\CentralAuthDatabaseManager;
use MediaWiki\Extension\CentralAuth\CentralAuthUIService;
use MediaWiki\Extension\CentralAuth\User\CentralAuthUser;
use MediaWiki\Html\Html;
use MediaWiki\SpecialPage\SpecialPage;
use MediaWiki\User\UserNameUtils;
use MediaWiki\Xml\Xml;
use Wikimedia\Rdbms\IExpression;
use Wikimedia\Rdbms\LikeValue;
/**
* Special page to allow locking and hiding multiple users
* at one time. Lots of code derived from Special:CentralAuth.
*
* @file
* @ingroup Extensions
*/
class SpecialMultiLock extends SpecialPage {
/** @var bool */
private $mCanSuppress;
/** @var string[] */
private $mGlobalUsers;
/** @var string[]|string|null */
private $mUserNames;
/** @var string */
private $mPrefixSearch;
/** @var bool */
private $mPosted;
/** @var string */
private $mMethod;
/** @var string */
private $mActionLock;
/** @var int */
private $mActionHide;
/** @var string */
private $mReason;
/** @var string[] */
private $mActionUserNames;
private UserNameUtils $userNameUtils;
private CentralAuthDatabaseManager $databaseManager;
private CentralAuthUIService $uiService;
public function __construct(
UserNameUtils $userNameUtils,
CentralAuthDatabaseManager $databaseManager,
CentralAuthUIService $uiService
) {
parent::__construct( 'MultiLock', 'centralauth-lock' );
$this->userNameUtils = $userNameUtils;
$this->databaseManager = $databaseManager;
$this->uiService = $uiService;
}
public function doesWrites() {
return true;
}
/** @inheritDoc */
public function execute( $subpage ) {
$this->setHeaders();
$this->checkPermissions();
$this->mCanSuppress = $this->getContext()->getAuthority()->isAllowed( 'centralauth-suppress' );
$this->getOutput()->addModules( 'ext.centralauth' );
$this->getOutput()->addModuleStyles( 'ext.centralauth.noflash' );
$this->mMethod = $this->getRequest()->getVal( 'wpMethod', '' );
$this->mActionLock = $this->getRequest()->getVal( 'wpActionLock', 'nochange' );
$this->mActionHide = $this->getRequest()->getInt( 'wpActionHide', -1 );
$this->mUserNames = $this->getRequest()->getVal( 'wpTarget', '' );
$this->mPrefixSearch = $this->getRequest()->getVal( 'wpSearchTarget', '' );
$this->mActionUserNames = $this->getRequest()->getArray( 'wpActionTarget' );
$this->mPosted = $this->getRequest()->wasPosted();
$this->mReason = $this->getRequest()->getText( 'wpReasonList' );
$reasonDetail = $this->getRequest()->getText( 'wpReason' );
if ( $this->mReason == 'other' ) {
$this->mReason = $reasonDetail;
} elseif ( $reasonDetail ) {
$this->mReason .= $this->msg( 'colon-separator' )->inContentLanguage()->text() .
$reasonDetail;
}
if ( $this->mUserNames !== '' ) {
$this->mUserNames = explode( "\n", $this->mUserNames );
} else {
$this->mUserNames = [];
}
if ( $this->mPrefixSearch !== '' ) {
$this->mPrefixSearch = $this->getLanguage()->ucfirst( trim( $this->mPrefixSearch ) );
}
if ( $this->mMethod === '' ) {
$this->getOutput()->addWikiMsg( 'centralauth-admin-multi-intro' );
$this->showUsernameForm();
return;
} elseif ( $this->mPosted && $this->mMethod == 'search' && count( $this->mUserNames ) > 0 ) {
$this->showUserTable();
} elseif ( $this->mPosted && $this->mMethod == 'search' && $this->mPrefixSearch !== '' ) {
$this->searchForUsers();
$this->showUserTable();
} elseif ( $this->mPosted && $this->mMethod == 'set-status' &&
is_array( $this->mActionUserNames )
) {
$this->mGlobalUsers = array_unique(
$this->getGlobalUsers( $this->mActionUserNames, true ), SORT_REGULAR
);
$this->setStatus();
$this->showUserTable();
} else {
$this->showError( 'centralauth-admin-multi-username' );
}
$this->showUsernameForm();
$this->showLogExtract();
}
/**
* Get the CentralAuthUsers from lines of text
*
* @param string[] $usernames
* @param bool $fromPrimaryDb
* @return (CentralAuthUser|string|false)[] User object, a HTML error string, or false.
*/
private function getGlobalUsers( $usernames, $fromPrimaryDb = false ) {
$ret = [];
foreach ( $usernames as $username ) {
$username = trim( $username );
if ( $username === '' ) {
$ret[] = false;
continue;
}
if ( $this->userNameUtils->getCanonical( $username ) === false ) {
$ret[] = $this->msg( 'htmlform-user-not-valid', $username )->parse();
continue;
}
$globalUser = $fromPrimaryDb
? CentralAuthUser::getPrimaryInstanceByName( $username )
: CentralAuthUser::getInstanceByName( $username );
if ( !$globalUser->exists()
|| ( !$this->mCanSuppress &&
( $globalUser->isSuppressed() || $globalUser->isHidden() ) )
) {
$ret[] = $this->msg( 'centralauth-admin-nonexistent', $username )->parse();
} else {
$ret[] = $globalUser;
}
}
return $ret;
}
/**
* Search the CentralAuth db for all usernames prefixed with mPrefixSearch
*/
private function searchForUsers() {
$dbr = $this->databaseManager->getCentralReplicaDB();
$where = [
$dbr->expr( 'gu_name', IExpression::LIKE,
new LikeValue( $this->mPrefixSearch, $dbr->anyString() ) )
];
if ( !$this->mCanSuppress ) {
$where['gu_hidden_level'] = CentralAuthUser::HIDDEN_LEVEL_NONE;
}
$result = $dbr->newSelectQueryBuilder()
->select( 'gu_name' )
->from( 'globaluser' )
->where( $where )
->limit( 100 )
->caller( __METHOD__ )
->fetchFieldValues();
foreach ( $result as $name ) {
$this->mUserNames[] = $name;
}
}
/**
* Show the Lock and/or Hide form, appropriate for this admin user's rights.
* The <form> and <fieldset> were started in showTableHeader()
*/
private function showStatusForm() {
$form = '';
$radioLocked =
Xml::radioLabel(
$this->msg( 'centralauth-admin-action-lock-nochange' )->text(),
'wpActionLock',
'nochange',
'mw-centralauth-status-locked-no',
true ) .
'<br />' .
Xml::radioLabel(
$this->msg( 'centralauth-admin-action-lock-unlock' )->text(),
'wpActionLock',
'unlock',
'centralauth-admin-action-lock-unlock',
false ) .
'<br />' .
Xml::radioLabel(
$this->msg( 'centralauth-admin-action-lock-lock' )->text(),
'wpActionLock',
'lock',
'centralauth-admin-action-lock-lock',
false );
$radioHidden =
Xml::radioLabel(
$this->msg( 'centralauth-admin-action-hide-nochange' )->text(),
'wpActionHide',
'-1',
'mw-centralauth-status-hidden-nochange',
true ) .
'<br />';
if ( $this->mCanSuppress ) {
$radioHidden .= Xml::radioLabel(
$this->msg( 'centralauth-admin-action-hide-none' )->text(),
'wpActionHide',
(string)CentralAuthUser::HIDDEN_LEVEL_NONE,
'mw-centralauth-status-hidden-no',
false ) .
'<br />' .
Xml::radioLabel(
$this->msg( 'centralauth-admin-action-hide-lists' )->text(),
'wpActionHide',
(string)CentralAuthUser::HIDDEN_LEVEL_LISTS,
'mw-centralauth-status-hidden-list',
false ) .
'<br />' .
Xml::radioLabel(
$this->msg( 'centralauth-admin-action-hide-oversight' )->text(),
'wpActionHide',
(string)CentralAuthUser::HIDDEN_LEVEL_SUPPRESSED,
'mw-centralauth-status-hidden-oversight',
false
);
}
$reasonList = Xml::listDropdown(
'wpReasonList',
$this->msg( 'centralauth-admin-status-reasons' )->inContentLanguage()->text(),
$this->msg( 'centralauth-admin-reason-other-select' )->inContentLanguage()->text()
);
$reasonField = Xml::input( 'wpReason', 45, false );
$botField = Xml::check( 'markasbot' ) .
$this->msg( 'centralauth-admin-multi-botcheck' )->parse();
$form .= Xml::buildForm(
[
'centralauth-admin-status-locked' => $radioLocked,
'centralauth-admin-status-hidden' => $radioHidden,
'centralauth-admin-reason' => $reasonList,
'centralauth-admin-reason-other' => $reasonField,
'centralauth-admin-multi-bot' => $botField
],
'centralauth-admin-status-submit'
);
$searchlist = $this->mUserNames;
if ( is_array( $this->mUserNames ) ) {
$searchlist = implode( "\n", $this->mUserNames );
}
$form .= Html::hidden( 'wpTarget', $searchlist );
$form .= '</fieldset></form>';
$this->getOutput()->addHTML( $form );
}
/**
* Start admin <form>, and start the table listing usernames to take action on
*/
private function showTableHeader() {
$out = $this->getOutput();
$header = Xml::openElement(
'form',
[
'method' => 'POST',
'action' => $this->getPageTitle()->getFullUrl()
]
);
$header .= Xml::fieldset( $this->msg( 'centralauth-admin-status' )->text() );
$header .= Html::hidden( 'wpMethod', 'set-status' );
$header .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
$header .= $this->msg( 'centralauth-admin-status-intro' )->parseAsBlock();
$header .= Xml::openElement(
'table',
[ 'class' => 'wikitable sortable mw-centralauth-wikislist' ]
);
$header .= '<thead><tr>' .
'<th></th>' .
'<th>' .
$out->getContext()->msg( 'centralauth-admin-username' )->escaped() .
'</th>' .
'<th>' .
$out->getContext()->msg( 'centralauth-admin-info-registered' )->escaped() .
'</th>' .
'<th>' .
$out->getContext()->msg( 'centralauth-admin-info-locked' )->escaped() .
'</th>' .
'<th>' .
$out->getContext()->msg( 'centralauth-admin-info-hidden' )->escaped() .
'</th>' .
'<th>' .
$out->getContext()->msg( 'centralauth-admin-info-editcount' )->escaped() .
'</th>' .
'<th>' .
$out->getContext()->msg( 'centralauth-admin-info-attached' )->escaped() .
'</th>' .
'<th>' .
$out->getContext()->msg( 'centralauth-multilock-homewiki' )->escaped() .
'</th>' .
'</tr></thead>' .
'<tbody>';
$out->addHTML( $header );
$out->addModuleStyles( 'jquery.tablesorter.styles' );
$out->addModules( 'jquery.tablesorter' );
}
/**
* Build the table of users to lock and/or hide
*/
private function showUserTable() {
$this->mGlobalUsers = array_unique(
$this->getGlobalUsers( $this->mUserNames ), SORT_REGULAR
);
$out = $this->getOutput();
if ( count( $this->mGlobalUsers ) < 1 ) {
$this->showError( 'centralauth-admin-multi-notfound' );
return;
}
$this->showTableHeader();
foreach ( $this->mGlobalUsers as $globalUser ) {
$rowtext = Xml::openElement( 'tr' );
if ( $globalUser === false ) {
continue;
} elseif ( $globalUser instanceof CentralAuthUser ) {
$rowtext .= $this->getUserTableRow( $globalUser );
} else {
$rowtext .= Html::rawElement(
'td',
[ 'colspan' => 8 ],
$globalUser
);
}
$rowtext .= Xml::closeElement( 'tr' );
$out->addHTML( $rowtext );
}
$out->addHTML( '</tbody></table>' );
$this->showStatusForm();
}
/**
* @param CentralAuthUser $globalUser
* @return string
*/
private function getUserTableRow( CentralAuthUser $globalUser ) {
$rowHtml = '';
$guName = $globalUser->getName();
$guLink = $this->getLinkRenderer()->makeLink(
SpecialPage::getTitleFor( 'CentralAuth', $guName ),
// Names are known to exist, so this is not really needed
$guName
);
// formatHiddenLevel html escapes its output
$guHidden = $this->uiService->formatHiddenLevel( $this->getContext(), $globalUser->getHiddenLevelInt() );
$accountAge = time() - (int)wfTimestamp( TS_UNIX, $globalUser->getRegistration() );
$guRegister = $this->uiService->prettyTimespan( $this->getContext(), $accountAge );
$guLocked = $this->msg( 'centralauth-admin-status-locked-no' )->text();
if ( $globalUser->isLocked() ) {
$guLocked = $this->msg( 'centralauth-admin-status-locked-yes' )->text();
}
$guEditCount = $this->getLanguage()->formatNum( $globalUser->getGlobalEditCount() );
$guAttachedLocalAccounts = $this->getLanguage()
->formatNum( count( $globalUser->listAttached() ) );
$guHomeWiki = $globalUser->getHomeWiki() ?? '';
$rowHtml .= Html::rawElement( 'td', [],
Html::input(
'wpActionTarget[' . $guName . ']',
$guName,
'checkbox',
[ 'checked' => 'checked' ]
)
);
$rowHtml .= Html::rawElement( 'td', [], $guLink );
$rowHtml .= Html::element( 'td', [ 'data-sort-value' => $accountAge ], $guRegister );
$rowHtml .= Html::element( 'td', [], $guLocked );
$rowHtml .= Html::rawElement( 'td', [], $guHidden );
$rowHtml .= Html::element( 'td', [], $guEditCount );
$rowHtml .= Html::element( 'td', [], $guAttachedLocalAccounts );
$rowHtml .= Html::element( 'td', [], $guHomeWiki );
return $rowHtml;
}
/**
* Lock and/or hide global users and log the activity (if any)
*/
private function setStatus() {
if ( !$this->getUser()->matchEditToken( $this->getRequest()->getVal( 'wpEditToken' ) ) ) {
$this->showError( 'centralauth-token-mismatch' );
return;
}
if ( $this->mActionHide !== -1 && !$this->mCanSuppress ) {
$this->showError( 'centralauth-admin-not-authorized' );
return;
}
$setLocked = null;
$setHidden = null;
if ( $this->mActionLock != 'nochange' ) {
$setLocked = ( $this->mActionLock == 'lock' );
}
if ( $this->mActionHide !== -1 ) {
$setHidden = $this->mActionHide;
}
foreach ( $this->mGlobalUsers as $globalUser ) {
if ( !$globalUser instanceof CentralAuthUser ) {
// Somehow the user submitted a bad user name
$this->showStatusError( $globalUser );
continue;
}
$status = $globalUser->adminLockHide(
$setLocked,
$setHidden,
$this->mReason,
$this->getContext(),
$this->getRequest()->getCheck( 'markasbot' )
);
if ( !$status->isGood() ) {
$this->showStatusError( $status->getWikiText() );
} elseif ( $status->successCount > 0 ) {
$this->showSuccess( 'centralauth-admin-setstatus-success', $globalUser->getName() );
}
}
}
/**
* @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() {
if ( is_array( $this->mUserNames ) ) {
$this->mUserNames = implode( "\n", $this->mUserNames );
}
$form = Xml::tags( 'form',
[
'method' => 'post',
'action' => $this->getPageTitle()->getLocalUrl()
],
Xml::tags( 'fieldset', [],
Xml::element( 'legend', [], $this->msg( 'centralauth-admin-manage' )->text() ) .
Html::hidden( 'wpMethod', 'search' ) .
Xml::element( 'p', [],
$this->msg( 'centralauth-admin-multi-username' )->text()
) .
Xml::textarea( 'wpTarget',
( $this->mPrefixSearch ? '' : $this->mUserNames ), 25, 20 ) .
Xml::element( 'p', [],
$this->msg( 'centralauth-admin-multi-searchprefix' )->text()
) .
Html::input( 'wpSearchTarget', $this->mPrefixSearch ) .
Xml::tags( 'p', [],
Xml::submitButton( $this->msg( 'centralauth-admin-lookup-ro' )->text() )
)
)
);
$this->getOutput()->addHTML( $form );
}
/**
* Show the last 50 log entries
*/
private function showLogExtract() {
$text = '';
$numRows = LogEventsList::showLogExtract(
$text,
[ 'globalauth', 'suppress' ],
'',
'',
[
'conds' => [
// T59253
'log_action' => 'setstatus'
],
'showIfEmpty' => true
] );
if ( $numRows ) {
$this->getOutput()->addHTML(
Xml::fieldset( $this->msg( 'centralauth-admin-logsnippet' )->text(), $text )
);
}
}
/** @inheritDoc */
protected function getGroupName() {
return 'users';
}
}

File Metadata

Mime Type
text/x-php
Expires
Fri, Jul 3, 19:40 (1 d, 2 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
41/72/d130705df373c8c1abab0898a69d
Default Alt Text
SpecialMultiLock.php (15 KB)

Event Timeline