summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'CheckUser/specials/SpecialCheckUser.php')
-rw-r--r--CheckUser/specials/SpecialCheckUser.php1417
1 files changed, 791 insertions, 626 deletions
diff --git a/CheckUser/specials/SpecialCheckUser.php b/CheckUser/specials/SpecialCheckUser.php
index 048a0d20..135d8254 100644
--- a/CheckUser/specials/SpecialCheckUser.php
+++ b/CheckUser/specials/SpecialCheckUser.php
@@ -2,22 +2,47 @@
class CheckUser extends SpecialPage {
/**
- * Constructor -- set up the new special page
+ * @var null|array $message Used to cache frequently used messages
*/
+ protected $message = null;
+
public function __construct() {
parent::__construct( 'CheckUser', 'checkuser' );
}
+ public function doesWrites() {
+ return true; // logging
+ }
+
public function execute( $subpage ) {
+ $this->setHeaders();
+ $this->checkPermissions();
+ // Logging and blocking requires writing so stop from here if read-only mode
+ $this->checkReadOnly();
+
+ // Blocked users are not allowed to run checkuser queries (bug T157883)
+ $callingUser = $this->getUser();
+ if ( $callingUser->isBlocked() ) {
+ throw new UserBlockedError( $callingUser->getBlock() );
+ }
+
+ $out = $this->getOutput();
$request = $this->getRequest();
- $this->checkPermissions();
- $this->setHeaders();
+ if ( $this->getUser()->isAllowed( 'checkuser-log' ) ) {
+ $subtitleLink = $this->getLinkRenderer()->makeKnownLink(
+ SpecialPage::getTitleFor( 'CheckUserLog' ),
+ $this->msg( 'checkuser-showlog' )->text()
+ );
+ $out->addSubtitle( $subtitleLink );
+ }
$user = $request->getText( 'user', $request->getText( 'ip', $subpage ) );
$user = trim( $user );
$reason = $request->getText( 'reason' );
- $blockreason = $request->getText( 'blockreason' );
+ $blockreason = $request->getText( 'blockreason', '' );
+ $disableUserTalk = $request->getBool( 'blocktalk', false );
+ $disableEmail = $request->getBool( 'blockemail', false );
$checktype = $request->getVal( 'checktype' );
$period = $request->getInt( 'period' );
$users = $request->getArray( 'users' );
@@ -26,34 +51,36 @@ class CheckUser extends SpecialPage {
$talkTag = $request->getBool( 'usettag' ) ?
trim( $request->getVal( 'talktag' ) ) : '';
- $m = array();
- # An IPv4? An IPv6? CIDR included?
+ $blockParams = [
+ 'reason' => $blockreason,
+ 'talk' => $disableUserTalk,
+ 'email' => $disableEmail,
+ ];
+
+ $ip = $name = $xff = '';
+ $m = [];
if ( IP::isIPAddress( $user ) ) {
+ // A single IP address or an IP range
$ip = IP::sanitizeIP( $user );
- $name = '';
- $xff = '';
- # An IPv4/IPv6 XFF string? CIDR included?
} elseif ( preg_match( '/^(.+)\/xff$/', $user, $m ) && IP::isIPAddress( $m[1] ) ) {
- $ip = '';
- $name = '';
+ // A single IP address or range with XFF string included
$xff = IP::sanitizeIP( $m[1] );
- # A user?
} else {
- $ip = '';
+ // A user?
$name = $user;
- $xff = '';
}
+ $this->showIntroductoryText();
$this->showForm( $user, $reason, $checktype, $ip, $xff, $name, $period );
- # Perform one of the various submit operations...
+ // Perform one of the various submit operations...
if ( $request->wasPosted() ) {
if ( !$this->getUser()->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) {
- $this->getOutput()->wrapWikiMsg( '<div class="error">$1</div>', 'checkuser-token-fail' );
+ $out->wrapWikiMsg( '<div class="error">$1</div>', 'checkuser-token-fail' );
} elseif ( $request->getVal( 'action' ) === 'block' ) {
- $this->doMassUserBlock( $users, $blockreason, $tag, $talkTag );
+ $this->doMassUserBlock( $users, $blockParams, $tag, $talkTag );
} elseif ( !$this->checkReason( $reason ) ) {
- $this->getOutput()->addWikiMsg( 'checkuser-noreason' );
+ $out->addWikiMsg( 'checkuser-noreason' );
} elseif ( $checktype == 'subuserips' ) {
$this->doUserIPsRequest( $name, $reason, $period );
} elseif ( $xff && $checktype == 'subedits' ) {
@@ -68,57 +95,35 @@ class CheckUser extends SpecialPage {
$this->doIPUsersRequest( $ip, false, $reason, $period, $tag, $talkTag );
}
}
- # Add CIDR calculation convenience form
+ // Add CIDR calculation convenience JS form
$this->addJsCIDRForm();
- $this->getOutput()->addModules( 'ext.checkUser' ); // JS
+ $out->addModules( 'ext.checkUser' );
}
- /**
- * As we use the same small set of messages in various methods and that
- * they are called often, we call them once and save them in $this->message
- */
- protected function preCacheMessages() {
- // Precache various messages
- if ( !isset( $this->message ) ) {
- foreach ( array( 'diff', 'hist', 'minoreditletter', 'newpageletter', 'blocklink', 'log' ) as $msg ) {
- $this->message[$msg] = $this->msg( $msg )->escaped();
- }
- }
- }
-
- /**
- * @return Title
- */
- public function getCheckUserLogTitle() {
- if ( !isset( $this->checkUserLogTitle ) ) {
- $this->checkUserLogTitle = SpecialPage::getTitleFor( 'CheckUserLog' );
- }
- return $this->checkUserLogTitle;
- }
-
- protected function showGuide() {
+ protected function showIntroductoryText() {
global $wgCheckUserCIDRLimit;
$this->getOutput()->addWikiText(
$this->msg( 'checkuser-summary',
$wgCheckUserCIDRLimit['IPv4'],
- $wgCheckUserCIDRLimit['IPv6'] )->text() .
- "\n\n[[" . $this->getCheckUserLogTitle()->getPrefixedText() .
- '|' . $this->msg( 'checkuser-showlog' )->text() . ']]'
+ $wgCheckUserCIDRLimit['IPv6']
+ )->text()
);
}
/**
- * @param $user
- * @param $reason
- * @param $checktype
- * @param $ip
- * @param $xff
- * @param $name
- * @param $period
+ * Show the CheckUser query form
+ *
+ * @param string $user
+ * @param string $reason
+ * @param string $checktype
+ * @param string $ip
+ * @param string $xff
+ * @param string $name
+ * @param int $period
*/
protected function showForm( $user, $reason, $checktype, $ip, $xff, $name, $period ) {
$action = htmlspecialchars( $this->getPageTitle()->getLocalUrl() );
- # Fill in requested type if it makes sense
+ // Fill in requested type if it makes sense
$encipusers = $encedits = $encuserips = 0;
if ( $checktype == 'subipusers' && ( $ip || $xff ) ) {
$encipusers = 1;
@@ -126,64 +131,67 @@ class CheckUser extends SpecialPage {
$encuserips = 1;
} elseif ( $checktype == 'subedits' ) {
$encedits = 1;
- # Defaults otherwise
+ // Defaults otherwise
} elseif ( $ip || $xff ) {
$encedits = 1;
} else {
$encuserips = 1;
}
- # Compile our nice form...
- # Username field should fit things like "2001:0db8:85a3:08d3:1319:8a2e:0370:7344/100/xff"
- $this->showGuide(); // explanation text
- $form = Xml::openElement( 'form', array( 'action' => $action,
- 'name' => 'checkuserform', 'id' => 'checkuserform', 'method' => 'post' ) );
+ $form = Xml::openElement( 'form', [ 'action' => $action,
+ 'name' => 'checkuserform', 'id' => 'checkuserform', 'method' => 'post' ] );
$form .= '<fieldset><legend>' . $this->msg( 'checkuser-query' )->escaped() . '</legend>';
- $form .= Xml::openElement( 'table', array( 'style' => 'border:0' ) );
+ $form .= Xml::openElement( 'table', [ 'style' => 'border:0' ] );
$form .= '<tr>';
$form .= '<td>' . $this->msg( 'checkuser-target' )->escaped() . '</td>';
- $form .= '<td>' . Xml::input( 'user', 46, $user, array( 'id' => 'checktarget' ) );
+ // User field should fit things like "2001:0db8:85a3:08d3:1319:8a2e:0370:7344/100/xff"
+ $form .= '<td>' . Xml::input( 'user', 46, $user, [ 'id' => 'checktarget' ] );
$form .= '&#160;' . $this->getPeriodMenu( $period ) . '</td>';
$form .= '</tr><tr>';
$form .= '<td></td>';
- $form .= Xml::openElement( 'td', array( 'class' => 'checkuserradios' ) );
- $form .= Xml::openElement( 'table', array( 'style' => 'border:0' ) );
+ $form .= Xml::openElement( 'td', [ 'class' => 'checkuserradios' ] );
+ $form .= Xml::openElement( 'table', [ 'style' => 'border:0' ] );
$form .= '<tr>';
$form .= '<td>' .
- Xml::radio( 'checktype', 'subuserips', $encuserips, array( 'id' => 'subuserips' ) );
+ Xml::radio( 'checktype', 'subuserips', $encuserips, [ 'id' => 'subuserips' ] );
$form .= ' ' . Xml::label( $this->msg( 'checkuser-ips' )->text(), 'subuserips' ) . '</td>';
$form .= '<td>' .
- Xml::radio( 'checktype', 'subedits', $encedits, array( 'id' => 'subedits' ) );
+ Xml::radio( 'checktype', 'subedits', $encedits, [ 'id' => 'subedits' ] );
$form .= ' ' . Xml::label( $this->msg( 'checkuser-edits' )->text(), 'subedits' ) . '</td>';
$form .= '<td>' .
- Xml::radio( 'checktype', 'subipusers', $encipusers, array( 'id' => 'subipusers' ) );
- $form .= ' ' . Xml::label( $this->msg( 'checkuser-users' )->text(), 'subipusers' ) . '</td>';
+ Xml::radio( 'checktype', 'subipusers', $encipusers, [ 'id' => 'subipusers' ] );
+ $form .= ' ' .
+ Xml::label( $this->msg( 'checkuser-users' )->text(), 'subipusers' ) . '</td>';
$form .= '</tr>';
$form .= Xml::closeElement( 'table' );
$form .= Xml::closeElement( 'td' );
$form .= '</tr><tr>';
$form .= '<td>' . $this->msg( 'checkuser-reason' )->escaped() . '</td>';
$form .= '<td>' . Xml::input( 'reason', 46, $reason,
- array( 'maxlength' => '150', 'id' => 'checkreason' ) );
+ [ 'maxlength' => '150', 'id' => 'checkreason' ] );
$form .= '&#160; &#160;' . Xml::submitButton( $this->msg( 'checkuser-check' )->text(),
- array( 'id' => 'checkusersubmit', 'name' => 'checkusersubmit' ) ) . '</td>';
+ [ 'id' => 'checkusersubmit', 'name' => 'checkusersubmit' ] ) . '</td>';
$form .= '</tr>';
$form .= Xml::closeElement( 'table' );
$form .= '</fieldset>';
$form .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
$form .= Xml::closeElement( 'form' );
- # Output form
+
$this->getOutput()->addHTML( $form );
}
/**
* Get a selector of time period options
- * @param int $selected, selected level
+ * @param int $selected Currently selected option
* @return string
*/
- protected function getPeriodMenu( $selected = null ) {
- $s = '<label for="period">' . $this->msg( 'checkuser-period' )->escaped() . '</label>&#160;';
- $s .= Xml::openElement( 'select', array( 'name' => 'period', 'id' => 'period', 'style' => 'margin-top:.2em;' ) );
+ protected function getPeriodMenu( $selected ) {
+ $s = '<label for="period">' .
+ $this->msg( 'checkuser-period' )->escaped() . '</label>&#160;';
+ $s .= Xml::openElement(
+ 'select',
+ [ 'name' => 'period', 'id' => 'period', 'style' => 'margin-top:.2em;' ]
+ );
$s .= Xml::option( $this->msg( 'checkuser-week-1' )->text(), 7, $selected === 7 );
$s .= Xml::option( $this->msg( 'checkuser-week-2' )->text(), 14, $selected === 14 );
$s .= Xml::option( $this->msg( 'checkuser-month' )->text(), 31, $selected === 31 );
@@ -200,37 +208,65 @@ class CheckUser extends SpecialPage {
'<legend>' . $this->msg( 'checkuser-cidr-label' )->escaped() . '</legend>';
$s .= '<textarea id="mw-checkuser-iplist" dir="ltr" rows="5" cols="50"></textarea><br />';
$s .= $this->msg( 'checkuser-cidr-res' )->escaped() . '&#160;' .
- Xml::input( 'mw-checkuser-cidr-res', 35, '', array( 'id' => 'mw-checkuser-cidr-res' ) ) .
+ Xml::input( 'mw-checkuser-cidr-res', 35, '', [ 'id' => 'mw-checkuser-cidr-res' ] ) .
'&#160;<strong id="mw-checkuser-ipnote"></strong>';
$s .= '</fieldset>';
$this->getOutput()->addHTML( $s );
}
/**
+ * @param string $reason
+ * @return bool
+ */
+ protected function checkReason( $reason ) {
+ global $wgCheckUserForceSummary;
+ return ( !$wgCheckUserForceSummary || strlen( $reason ) );
+ }
+
+ /**
+ * As we use the same small set of messages in various methods and that
+ * they are called often, we call them once and save them in $this->message
+ */
+ protected function preCacheMessages() {
+ if ( $this->message === null ) {
+ $msgKeys = [ 'diff', 'hist', 'minoreditletter', 'newpageletter', 'blocklink', 'log' ];
+ foreach ( $msgKeys as $msg ) {
+ $this->message[$msg] = $this->msg( $msg )->escaped();
+ }
+ }
+ }
+
+ /**
* Block a list of selected users
* @param array $users
- * @param string $reason
+ * @param array $blockParams
* @param string $tag
* @param string $talkTag
*/
- protected function doMassUserBlock( $users, $reason = '', $tag = '', $talkTag = '' ) {
+ protected function doMassUserBlock( $users, $blockParams, $tag = '', $talkTag = '' ) {
global $wgCheckUserMaxBlocks;
- if ( empty( $users ) || $this->getUser()->isBlocked( false ) ) {
+ $usersCount = count( $users );
+ if ( !$this->getUser()->isAllowed( 'block' ) || $this->getUser()->isBlocked()
+ || !$usersCount
+ ) {
$this->getOutput()->addWikiMsg( 'checkuser-block-failure' );
return;
- } elseif ( count( $users ) > $wgCheckUserMaxBlocks ) {
+ } elseif ( $usersCount > $wgCheckUserMaxBlocks ) {
$this->getOutput()->addWikiMsg( 'checkuser-block-limit' );
return;
- } elseif ( !$reason ) {
+ } elseif ( !$blockParams['reason'] ) {
$this->getOutput()->addWikiMsg( 'checkuser-block-noreason' );
return;
}
- $safeUsers = self::doMassUserBlockInternal( $users, $reason, $tag, $talkTag );
- if ( !empty( $safeUsers ) ) {
+
+ $blockedUsers = $this->doMassUserBlockInternal( $users, $blockParams, $tag, $talkTag );
+ $blockedCount = count( $blockedUsers );
+ if ( $blockedCount > 0 ) {
$lang = $this->getLanguage();
- $n = count( $safeUsers );
- $ulist = $lang->listToText( $safeUsers );
- $this->getOutput()->addWikiMsg( 'checkuser-block-success', $ulist, $lang->formatNum( $n ) );
+ $this->getOutput()->addWikiMsg( 'checkuser-block-success',
+ $lang->listToText( $blockedUsers ),
+ $lang->formatNum( $blockedCount )
+ );
} else {
$this->getOutput()->addWikiMsg( 'checkuser-block-failure' );
}
@@ -239,150 +275,183 @@ class CheckUser extends SpecialPage {
/**
* Block a list of selected users
*
- * @param $users Array
- * @param $reason String
- * @param $tag String: replaces user pages
- * @param $talkTag String: replaces user talk pages
- * @return Array: list of html-safe usernames
+ * @param string[] $users
+ * @param array $blockParams
+ * @param string $tag replaces user pages
+ * @param string $talkTag replaces user talk pages
+ * @return string[] List of html-safe usernames which were actually were blocked
*/
- public static function doMassUserBlockInternal( $users, $reason = '', $tag = '', $talkTag = '' ) {
- global $wgUser;
+ protected function doMassUserBlockInternal( $users, array $blockParams,
+ $tag = '', $talkTag = '' ) {
+ global $wgBlockAllowsUTEdit;
- $counter = $blockSize = 0;
- $safeUsers = array();
- $log = new LogPage( 'block' );
+ $currentUser = $this->getUser();
+ $safeUsers = [];
foreach ( $users as $name ) {
- # Enforce limits
- $counter++;
- $blockSize++;
- # Lets not go *too* fast
- if ( $blockSize >= 20 ) {
- $blockSize = 0;
- wfWaitForSlaves( 5 );
- }
$u = User::newFromName( $name, false );
- // If user doesn't exist, it ought to be an IP then
- if ( is_null( $u ) || ( !$u->getId() && !IP::isIPAddress( $u->getName() ) ) ) {
+ // Do some checks to make sure we can block this user first
+ if ( $u === null ) {
+ // Invalid user
+ continue;
+ }
+ $isIP = IP::isIPAddress( $u->getName() );
+ if ( !$u->getId() && !$isIP ) {
+ // Not a registered user or an IP
+ continue;
+ }
+
+ if ( $u->isBlocked() ) {
+ // If the user is already blocked, just leave it as is
continue;
}
+
$userTitle = $u->getUserPage();
$userTalkTitle = $u->getTalkPage();
- $userpage = WikiPage::factory( $userTitle );
- $usertalk = WikiPage::factory( $userTalkTitle );
- $safeUsers[] = '[[' . $userTitle->getPrefixedText() . '|' . $userTitle->getText() . ']]';
- $expirestr = $u->getId() ? 'indefinite' : '1 week';
+ $safeUsers[] = "[[{$userTitle->getPrefixedText()}|{$userTitle->getText()}]]";
+ $expirestr = $isIP ? '1 week' : 'indefinite';
$expiry = SpecialBlock::parseExpiryInput( $expirestr );
- $anonOnly = IP::isIPAddress( $u->getName() ) ? 1 : 0;
// Create the block
$block = new Block();
$block->setTarget( $u );
- $block->setBlocker( $wgUser );
- $block->mReason = $reason;
+ $block->setBlocker( $currentUser );
+ $block->mReason = $blockParams['reason'];
$block->mExpiry = $expiry;
- $block->isHardblock( !IP::isIPAddress( $u->getName() ) );
+ $block->isHardblock( !$isIP );
$block->isAutoblocking( true );
$block->prevents( 'createaccount', true );
- $block->prevents( 'sendemail', false );
- $block->prevents( 'editownusertalk', false );
-
- $oldblock = Block::newFromTarget( $u->getName() );
- if ( !$oldblock ) {
- $block->insert();
- # Prepare log parameters
- $logParams = array();
- $logParams[] = $expirestr;
- $flags = array( 'nocreate' );
- if ( $anonOnly ) {
- $flags[] = 'anononly';
- }
- $logParams[] = implode( ',', $flags );
- # Add log entry
- $log->addEntry( 'block', $userTitle, $reason, $logParams );
- }
- # Tag userpage! (check length to avoid mistakes)
- if ( strlen( $tag ) > 2 ) {
- $flags = 0;
- if ( $userpage->exists() ) {
- $flags |= EDIT_MINOR;
- }
- $userpage->doEditContent( new WikitextContent( $tag ), $reason, $flags );
- }
- if ( strlen( $talkTag ) > 2 ) {
- $flags = 0;
- if ( $usertalk->exists() ) {
- $flags |= EDIT_MINOR;
- }
- $usertalk->doEditContent( new WikitextContent( $talkTag ), $reason, $flags );
- }
+ $block->prevents( 'sendemail',
+ ( SpecialBlock::canBlockEmail( $currentUser ) && $blockParams['email'] )
+ );
+ $block->prevents( 'editownusertalk', ( !$wgBlockAllowsUTEdit || $blockParams['talk'] ) );
+ $status = $block->insert();
+
+ // Prepare log parameters for the block
+ $logParams = [];
+ $logParams['5::duration'] = $expirestr;
+ $logParams['6::flags'] = self::userBlockLogFlags( $isIP, $blockParams );
+
+ $logEntry = new ManualLogEntry( 'block', 'block' );
+ $logEntry->setTarget( $userTitle );
+ $logEntry->setComment( $blockParams['reason'] );
+ $logEntry->setPerformer( $currentUser );
+ $logEntry->setParameters( $logParams );
+ $blockIds = array_merge( [ $status['id'] ], $status['autoIds'] );
+ $logEntry->setRelations( [ 'ipb_id' => $blockIds ] );
+ $logEntry->publish( $logEntry->insert() );
+
+ // Tag user page and user talk page
+ $this->tagPage( $userTitle, $tag, $blockParams['reason'] );
+ $this->tagPage( $userTalkTitle, $talkTag, $blockParams['reason'] );
}
+
return $safeUsers;
}
/**
+ * Return a comma-delimited list of "flags" to be passed to the block log.
+ * Flags are 'anononly', 'nocreate', 'noemail' and 'nousertalk'.
+ * @param bool $anonOnly
+ * @param array $blockParams
+ * @return string
+ */
+ protected static function userBlockLogFlags( $anonOnly, array $blockParams ) {
+ global $wgBlockAllowsUTEdit;
+ $flags = [];
+
+ if ( $anonOnly ) {
+ $flags[] = 'anononly';
+ }
+
+ $flags[] = 'nocreate';
+
+ if ( $blockParams['email'] ) {
+ $flags[] = 'noemail';
+ }
+
+ if ( $wgBlockAllowsUTEdit && $blockParams['talk'] ) {
+ $flags[] = 'nousertalk';
+ }
+
+ return implode( ',', $flags );
+ }
+
+ /**
+ * Make an edit to the given page with the tag provided
+ *
+ * @param Title $title
+ * @param string $tag
+ * @param string $summary
+ */
+ protected function tagPage( Title $title, $tag, $summary ) {
+ // Check length to avoid mistakes
+ if ( strlen( $tag ) > 2 ) {
+ $page = WikiPage::factory( $title );
+ $flags = 0;
+ if ( $page->exists() ) {
+ $flags |= EDIT_MINOR;
+ }
+ $page->doEditContent( new WikitextContent( $tag ), $summary,
+ $flags, false, $this->getUser() );
+ }
+ }
+
+ /**
* Give a "no matches found for X" message.
* If $checkLast, then mention the last edit by this user or IP.
*
- * @param $userName
+ * @param string $userName
* @param bool $checkLast
- * @return String
+ * @return string
*/
protected function noMatchesMessage( $userName, $checkLast = true ) {
if ( $checkLast ) {
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$user_id = User::idFromName( $userName );
if ( $user_id ) {
$revEdit = $dbr->selectField( 'revision',
'rev_timestamp',
- array( 'rev_user' => $user_id ),
+ [ 'rev_user' => $user_id ],
__METHOD__,
- array( 'ORDER BY' => 'rev_timestamp DESC' )
+ [ 'ORDER BY' => 'rev_timestamp DESC' ]
);
$logEdit = $dbr->selectField( 'logging',
'log_timestamp',
- array( 'log_user' => $user_id ),
+ [ 'log_user' => $user_id ],
__METHOD__,
- array( 'ORDER BY' => 'log_timestamp DESC' )
+ [ 'ORDER BY' => 'log_timestamp DESC' ]
);
} else {
$revEdit = $dbr->selectField( 'revision',
'rev_timestamp',
- array( 'rev_user_text' => $userName ),
+ [ 'rev_user_text' => $userName ],
__METHOD__,
- array( 'ORDER BY' => 'rev_timestamp DESC' )
+ [ 'ORDER BY' => 'rev_timestamp DESC' ]
);
$logEdit = false; // no log_user_text index
}
$lastEdit = max( $revEdit, $logEdit );
if ( $lastEdit ) {
+ $lastEditTime = wfTimestamp( TS_MW, $lastEdit );
$lang = $this->getLanguage();
- $lastEditDate = $lang->date( wfTimestamp( TS_MW, $lastEdit ), true );
- $lastEditTime = $lang->time( wfTimestamp( TS_MW, $lastEdit ), true );
// FIXME: don't pass around parsed messages
- return $this->msg( 'checkuser-nomatch-edits', $lastEditDate, $lastEditTime )->parseAsBlock();
+ return $this->msg( 'checkuser-nomatch-edits',
+ $lang->date( $lastEditTime, true ),
+ $lang->time( $lastEditTime, true )
+ )->parseAsBlock();
}
}
return $this->msg( 'checkuser-nomatch' )->parseAsBlock();
}
/**
- * @param $reason
- * @return bool
- */
- protected function checkReason( $reason ) {
- global $wgCheckUserForceSummary;
- return ( !$wgCheckUserForceSummary || strlen( $reason ) );
- }
-
- /**
- * FIXME: documentation out of date
- * @param string $ip <???
- * @param bool $xfor <???
+ * Show all the IPs used by a user
+ *
+ * @param string $user
* @param string $reason
- * Get all IPs used by a user
- * Shows first and last date and number of edits
+ * @param int $period
*/
- protected function doUserIPsRequest( $user , $reason = '', $period = 0 ) {
+ protected function doUserIPsRequest( $user, $reason = '', $period = 0 ) {
$out = $this->getOutput();
$userTitle = Title::newFromText( $user, NS_USER );
@@ -390,54 +459,50 @@ class CheckUser extends SpecialPage {
// normalize the username
$user = $userTitle->getText();
}
- # IPs are passed in as a blank string
+ // IPs are passed in as a blank string
if ( !$user ) {
$out->addWikiMsg( 'nouserspecified' );
return;
}
- # Get ID, works better than text as user may have been renamed
+ // Get ID, works better than text as user may have been renamed
$user_id = User::idFromName( $user );
- # If user is not IP or nonexistent
+ // If user is not IP or nonexistent
if ( !$user_id ) {
- $out->addWikiMsgArray( 'nosuchusershort', $user );
+ $out->addWikiMsg( 'nosuchusershort', $user );
return;
}
- # Record check...
- if ( !self::addLogEntry( 'userips', 'user', $user, $reason, $user_id ) ) {
- // FIXME: addWikiMsg
- $out->addHTML( '<p>' . $this->msg( 'checkuser-log-fail' )->escaped() . '</p>' );
- }
+ // Record check...
+ self::addLogEntry( 'userips', 'user', $user, $reason, $user_id );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$time_conds = $this->getTimeConds( $period );
- # Ordering by the latest timestamp makes a small filesort on the IP list
+ // Ordering by the latest timestamp makes a small filesort on the IP list
$ret = $dbr->select(
'cu_changes',
- array(
+ [
'cuc_ip',
'cuc_ip_hex',
'COUNT(*) AS count',
'MIN(cuc_timestamp) AS first',
'MAX(cuc_timestamp) AS last',
- ),
- array( 'cuc_user' => $user_id, $time_conds ),
+ ],
+ [ 'cuc_user' => $user_id, $time_conds ],
__METHOD__,
- array(
+ [
'ORDER BY' => 'last DESC',
'GROUP BY' => 'cuc_ip,cuc_ip_hex',
'LIMIT' => 5001,
'USE INDEX' => 'cuc_user_ip_time',
- )
+ ]
);
if ( !$dbr->numRows( $ret ) ) {
$s = $this->noMatchesMessage( $user ) . "\n";
} else {
- $blockip = SpecialPage::getTitleFor( 'Block' );
- $ips_edits = array();
+ $ips_edits = [];
$counter = 0;
foreach ( $ret as $row ) {
if ( $counter >= 5000 ) {
@@ -455,46 +520,46 @@ class CheckUser extends SpecialPage {
set_time_limit( 60 );
wfRestoreWarnings();
- $logs = SpecialPage::getTitleFor( 'Log' );
$s = '<div id="checkuserresults"><ul>';
foreach ( $ips_edits as $ip => $edits ) {
$s .= '<li>';
- $s .= '<a href="' .
- htmlspecialchars( $this->getPageTitle()->getLocalURL( array(
+ $s .= $this->getSelfLink( $ip,
+ [
'user' => $ip,
- 'reason' => $reason
- ) ) ) . '">' .
- htmlspecialchars( $ip ) . '</a>';
- $s .= ' (<a href="' . htmlspecialchars( $blockip->getLocalURL( 'ip=' . urlencode( $ip ) ) ) . '">' .
- $this->msg( 'blocklink' )->escaped() . '</a>)';
- if ( $ips_first[$ip] == $ips_last[$ip] ) {
- $s .= ' (' . $this->getLanguage()->timeanddate( wfTimestamp( TS_MW, $ips_first[$ip] ), true ) . ') ';
- } else {
- $lang = $this->getLanguage();
- $s .= ' (' . $lang->timeanddate( wfTimestamp( TS_MW, $ips_first[$ip] ), true ) .
- ' -- ' . $lang->timeanddate( wfTimestamp( TS_MW, $ips_last[$ip] ), true ) . ') ';
- }
+ 'reason' => $reason,
+ ]
+ );
+ $s .= ' ' . $this->msg( 'parentheses' )->rawParams(
+ $this->getLinkRenderer()->makeKnownLink(
+ SpecialPage::getTitleFor( 'Block', $ip ),
+ $this->msg( 'blocklink' )->text()
+ )
+ )->escaped();
+ $s .= ' ' . $this->getTimeRangeString( $ips_first[$ip], $ips_last[$ip] ) . ' ';
$s .= ' <strong>[' . $edits . ']</strong>';
- # If we get some results, it helps to know if the IP in general
- # has a lot more edits, e.g. "tip of the iceberg"...
+ // If we get some results, it helps to know if the IP in general
+ // has a lot more edits, e.g. "tip of the iceberg"...
$ipedits = $dbr->estimateRowCount( 'cu_changes', '*',
- array( 'cuc_ip_hex' => $ips_hex[$ip], $time_conds ),
+ [ 'cuc_ip_hex' => $ips_hex[$ip], $time_conds ],
__METHOD__ );
- # If small enough, get a more accurate count
+ // If small enough, get a more accurate count
if ( $ipedits <= 1000 ) {
$ipedits = $dbr->selectField( 'cu_changes', 'COUNT(*)',
- array( 'cuc_ip_hex' => $ips_hex[$ip], $time_conds ),
+ [ 'cuc_ip_hex' => $ips_hex[$ip], $time_conds ],
__METHOD__ );
}
if ( $ipedits > $ips_edits[$ip] ) {
- $s .= ' <i>(' . $this->msg( 'checkuser-ipeditcount', $ipedits )->escaped() . ')</i>';
+ $s .= ' <i>(' .
+ $this->msg( 'checkuser-ipeditcount' )->numParams( $ipedits )->escaped() .
+ ')</i>';
}
- # If this IP is blocked, give a link to the block log
+ // If this IP is blocked, give a link to the block log
$s .= $this->getIPBlockInfo( $ip );
$s .= '<div style="margin-left:5%">';
- $s .= '<small>' . $this->msg( 'checkuser-toollinks', urlencode( $ip ) )->parse() . '</small>';
+ $s .= '<small>' . $this->msg( 'checkuser-toollinks', urlencode( $ip ) )->parse() .
+ '</small>';
$s .= '</div>';
$s .= "</li>\n";
}
@@ -506,88 +571,90 @@ class CheckUser extends SpecialPage {
protected function getIPBlockInfo( $ip ) {
$block = Block::newFromTarget( null, $ip, false );
if ( $block instanceof Block ) {
- if ( $block->getType() == Block::TYPE_RANGE ) {
- $userpage = Title::makeTitle( NS_USER, $block->getTarget() );
- $blocklog = Linker::linkKnown(
- SpecialPage::getTitleFor( 'Log' ),
- $this->msg( 'checkuser-blocked' )->escaped(),
- array(),
- array(
- 'type' => 'block',
- 'page' => $userpage->getPrefixedText()
- )
- );
- return ' <strong>(' . $blocklog . ' - ' . $block->getTarget() . ')</strong>';
- } elseif ( $block->getType() == Block::TYPE_AUTO ) {
- $blocklog = Linker::linkKnown(
- SpecialPage::getTitleFor( 'BlockList' ),
- $this->msg( 'checkuser-blocked' )->escaped(),
- array(),
- array( 'ip' => "#{$block->getId()}" )
- );
- return ' <strong>(' . $blocklog . ')</strong>';
- } else {
- $userpage = Title::makeTitle( NS_USER, $block->getTarget() );
- $blocklog = Linker::linkKnown(
- SpecialPage::getTitleFor( 'Log' ),
- $this->msg( 'checkuser-blocked' )->escaped(),
- array(),
- array(
- 'type' => 'block',
- 'page' => $userpage->getPrefixedText()
- )
- );
- return ' <strong>(' . $blocklog . ')</strong>';
- }
+ return $this->getBlockFlag( $block );
}
return '';
}
/**
+ * Get a link to block information about the passed block for displaying to the user.
+ *
+ * @param Block $block
+ * @return string
+ */
+ protected function getBlockFlag( Block $block ) {
+ if ( $block->getType() == Block::TYPE_AUTO ) {
+ $ret = $this->getLinkRenderer()->makeKnownLink(
+ SpecialPage::getTitleFor( 'BlockList' ),
+ $this->msg( 'checkuser-blocked' )->text(),
+ [],
+ [ 'wpTarget' => "#{$block->getId()}" ]
+ );
+ } else {
+ $userPage = Title::makeTitle( NS_USER, $block->getTarget() );
+ $ret = $this->getLinkRenderer()->makeKnownLink(
+ SpecialPage::getTitleFor( 'Log' ),
+ $this->msg( 'checkuser-blocked' )->text(),
+ [],
+ [
+ 'type' => 'block',
+ 'page' => $userPage->getPrefixedText()
+ ]
+ );
+ }
+
+ // Add the blocked range if the block is on a range
+ if ( $block->getType() == Block::TYPE_RANGE ) {
+ $ret .= ' - ' . htmlspecialchars( $block->getTarget() );
+ }
+
+ return '<strong>' .
+ $this->msg( 'parentheses' )->rawParams( $ret )->escaped()
+ . '</strong>';
+ }
+
+ /**
+ * Shows all changes made by an IP address or range
+ *
* @param string $ip
- * @param bool $xfor
+ * @param bool $xfor if query is for XFF
* @param string $reason
- * FIXME: $period ???
- * Shows all edits in Recent Changes by this IP (or range) and who made them
+ * @param int $period
*/
protected function doIPEditsRequest( $ip, $xfor = false, $reason = '', $period = 0 ) {
$out = $this->getOutput();
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
- # Invalid IPs are passed in as a blank string
+ // Invalid IPs are passed in as a blank string
$ip_conds = self::getIpConds( $dbr, $ip, $xfor );
if ( !$ip || $ip_conds === false ) {
$out->addWikiMsg( 'badipaddress' );
return;
}
- $logType = 'ipedits';
- if ( $xfor ) {
- $logType .= '-xff';
- }
- # Record check...
- if ( !self::addLogEntry( $logType, 'ip', $ip, $reason ) ) {
- $out->addWikiMsg( 'checkuser-log-fail' );
- }
+ $logType = $xfor ? 'ipedits-xff' : 'ipedits';
+
+ // Record check in the logs
+ self::addLogEntry( $logType, 'ip', $ip, $reason );
$ip_conds = $dbr->makeList( $ip_conds, LIST_AND );
$time_conds = $this->getTimeConds( $period );
- # Ordered in descent by timestamp. Can cause large filesorts on range scans.
- # Check how many rows will need sorting ahead of time to see if this is too big.
- # Also, if we only show 5000, too many will be ignored as well.
+ // Ordered in descent by timestamp. Can cause large filesorts on range scans.
+ // Check how many rows will need sorting ahead of time to see if this is too big.
+ // Also, if we only show 5000, too many will be ignored as well.
$index = $xfor ? 'cuc_xff_hex_time' : 'cuc_ip_hex_time';
if ( strpos( $ip, '/' ) !== false ) {
- # Quick index check only OK if no time constraint
+ // Quick index check only OK if no time constraint
if ( $period ) {
$rangecount = $dbr->selectField( 'cu_changes', 'COUNT(*)',
- array( $ip_conds, $time_conds ),
+ [ $ip_conds, $time_conds ],
__METHOD__,
- array( 'USE INDEX' => $index ) );
+ [ 'USE INDEX' => $index ] );
} else {
$rangecount = $dbr->estimateRowCount( 'cu_changes', '*',
- array( $ip_conds ),
+ [ $ip_conds ],
__METHOD__,
- array( 'USE INDEX' => $index ) );
+ [ 'USE INDEX' => $index ] );
}
// Sorting might take some time...make sure it is there
wfSuppressWarnings();
@@ -595,20 +662,26 @@ class CheckUser extends SpecialPage {
wfRestoreWarnings();
}
$counter = 0;
- # See what is best to do after testing the waters...
+ // See what is best to do after testing the waters...
if ( isset( $rangecount ) && $rangecount > 5000 ) {
- $ret = $dbr->select( 'cu_changes',
- array( 'cuc_ip_hex', 'COUNT(*) AS count', 'MIN(cuc_timestamp) AS first', 'MAX(cuc_timestamp) AS last' ),
- array( $ip_conds, $time_conds ),
+ $ret = $dbr->select(
+ 'cu_changes',
+ [
+ 'cuc_ip_hex',
+ 'COUNT(*) AS count',
+ 'MIN(cuc_timestamp) AS first',
+ 'MAX(cuc_timestamp) AS last'
+ ],
+ [ $ip_conds, $time_conds ],
__METHOD__,
- array(
+ [
'GROUP BY' => 'cuc_ip_hex',
'ORDER BY' => 'cuc_ip_hex',
'LIMIT' => 5001,
'USE INDEX' => $index,
- )
+ ]
);
- # List out each IP that has edits
+ // List out each IP that has edits
$s = $this->msg( 'checkuser-too-many' )->parseAsBlock();
$s .= '<ol>';
foreach ( $ret as $row ) {
@@ -616,27 +689,22 @@ class CheckUser extends SpecialPage {
$out->addWikiMsg( 'checkuser-limited' );
break;
}
- # Convert the IP hexes into normal form
+ // Convert the IP hexes into normal form
if ( strpos( $row->cuc_ip_hex, 'v6-' ) !== false ) {
$ip = substr( $row->cuc_ip_hex, 3 );
$ip = IP::HextoOctet( $ip );
} else {
- $ip = long2ip( wfBaseConvert( $row->cuc_ip_hex, 16, 10, 8 ) );
+ $ip = long2ip( Wikimedia\base_convert( $row->cuc_ip_hex, 16, 10, 8 ) );
}
- $s .= '<li><a href="' .
- htmlspecialchars( $this->getPageTitle()->getLocalURL( array(
+ $s .= '<li>';
+ $s .= $this->getSelfLink( $ip,
+ [
'user' => $ip,
'reason' => $reason,
'checktype' => 'subipusers'
- ) ) ) .
- '">' . $ip . '</a>';
- if ( $row->first == $row->last ) {
- $s .= ' (' . $this->getLanguage()->timeanddate( wfTimestamp( TS_MW, $row->first ), true ) . ') ';
- } else {
- $lang = $this->getLanguage();
- $s .= ' (' . $lang->timeanddate( wfTimestamp( TS_MW, $row->first ), true ) .
- ' -- ' . $lang->timeanddate( wfTimestamp( TS_MW, $row->last ), true ) . ') ';
- }
+ ]
+ );
+ $s .= ' ' . $this->getTimeRangeString( $row->first, $row->last ) . ' ';
$s .= ' [<strong>' . $row->count . "</strong>]</li>\n";
++$counter;
}
@@ -650,40 +718,42 @@ class CheckUser extends SpecialPage {
return;
}
- # OK, do the real query...
+ // OK, do the real query...
$ret = $dbr->select(
'cu_changes',
- array(
- 'cuc_namespace', 'cuc_title', 'cuc_user', 'cuc_user_text', 'cuc_comment', 'cuc_actiontext',
- 'cuc_timestamp', 'cuc_minor', 'cuc_page_id', 'cuc_type', 'cuc_this_oldid',
- 'cuc_last_oldid', 'cuc_ip', 'cuc_xff', 'cuc_agent'
- ),
- array( $ip_conds, $time_conds ),
+ [
+ 'cuc_namespace', 'cuc_title', 'cuc_user', 'cuc_user_text', 'cuc_comment',
+ 'cuc_actiontext', 'cuc_timestamp', 'cuc_minor', 'cuc_page_id', 'cuc_type',
+ 'cuc_this_oldid', 'cuc_last_oldid', 'cuc_ip', 'cuc_xff', 'cuc_agent'
+ ],
+ [ $ip_conds, $time_conds ],
__METHOD__,
- array(
+ [
'ORDER BY' => 'cuc_timestamp DESC',
'LIMIT' => 5001,
'USE INDEX' => $index,
- )
+ ]
);
if ( !$dbr->numRows( $ret ) ) {
$s = $this->noMatchesMessage( $ip, !$xfor ) . "\n";
} else {
- # Cache common messages
+ // Cache common messages
$this->preCacheMessages();
- # Try to optimize this query
+ // Try to optimize this query
$lb = new LinkBatch;
foreach ( $ret as $row ) {
$userText = str_replace( ' ', '_', $row->cuc_user_text );
- $lb->add( $row->cuc_namespace, $row->cuc_title );
+ if ( $row->cuc_title !== '' ) {
+ $lb->add( $row->cuc_namespace, $row->cuc_title );
+ }
$lb->add( NS_USER, $userText );
$lb->add( NS_USER_TALK, $userText );
}
$lb->execute();
$ret->seek( 0 );
- # List out the edits
+ // List out the edits
$s = '<div id="checkuserresults">';
foreach ( $ret as $row ) {
if ( $counter >= 5000 ) {
@@ -700,9 +770,26 @@ class CheckUser extends SpecialPage {
}
/**
+ * @param ResultWrapper $rows Results with cuc_namespace and cuc_title field
+ */
+ protected function doLinkCache( $rows ) {
+ $lb = new LinkBatch();
+ $lb->setCaller( __METHOD__ );
+ foreach ( $rows as $row ) {
+ if ( $row->cuc_title !== '' ) {
+ $lb->add( $row->cuc_namespace, $row->cuc_title );
+ }
+ }
+ $lb->execute();
+ $rows->seek( 0 );
+ }
+
+ /**
+ * Shows all changes made by a particular user
+ *
* @param string $user
* @param string $reason
- * Shows all edits in Recent Changes by this user
+ * @param int $period
*/
protected function doUserEditsRequest( $user, $reason = '', $period = 0 ) {
$out = $this->getOutput();
@@ -712,70 +799,64 @@ class CheckUser extends SpecialPage {
// normalize the username
$user = $userTitle->getText();
}
- # IPs are passed in as a blank string
+ // IPs are passed in as a blank string
if ( !$user ) {
$out->addWikiMsg( 'nouserspecified' );
return;
}
- # Get ID, works better than text as user may have been renamed
+ // Get ID, works better than text as user may have been renamed
$user_id = User::idFromName( $user );
- # If user is not IP or nonexistent
+ // If user is not IP or nonexistent
if ( !$user_id ) {
$s = $this->msg( 'nosuchusershort', $user )->parseAsBlock();
$out->addHTML( $s );
return;
}
- # Record check...
- if ( !self::addLogEntry( 'useredits', 'user', $user, $reason, $user_id ) ) {
- $out->addHTML( '<p>' . $this->msg( 'checkuser-log-fail' )->escaped() . '</p>' );
- }
+ // Record check...
+ self::addLogEntry( 'useredits', 'user', $user, $reason, $user_id );
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$user_cond = "cuc_user = '$user_id'";
$time_conds = $this->getTimeConds( $period );
- # Ordered in descent by timestamp. Causes large filesorts if there are many edits.
- # Check how many rows will need sorting ahead of time to see if this is too big.
- # If it is, sort by IP,time to avoid the filesort.
+ // Ordered in descent by timestamp. Causes large filesorts if there are many edits.
+ // Check how many rows will need sorting ahead of time to see if this is too big.
+ // If it is, sort by IP,time to avoid the filesort.
if ( $period ) {
$count = $dbr->selectField( 'cu_changes', 'COUNT(*)',
- array( $user_cond, $time_conds ),
+ [ $user_cond, $time_conds ],
__METHOD__,
- array( 'USE INDEX' => 'cuc_user_ip_time' ) );
+ [ 'USE INDEX' => 'cuc_user_ip_time' ] );
} else {
$count = $dbr->estimateRowCount( 'cu_changes', '*',
- array( $user_cond, $time_conds ),
+ [ $user_cond, $time_conds ],
__METHOD__,
- array( 'USE INDEX' => 'cuc_user_ip_time' ) );
+ [ 'USE INDEX' => 'cuc_user_ip_time' ] );
}
- # Cache common messages
+ // Cache common messages
$this->preCacheMessages();
- # See what is best to do after testing the waters...
+ // See what is best to do after testing the waters...
if ( $count > 5000 ) {
$out->addHTML( $this->msg( 'checkuser-limited' )->parse() );
$ret = $dbr->select(
'cu_changes',
'*',
- array( $user_cond, $time_conds ),
+ [ $user_cond, $time_conds ],
__METHOD__,
- array(
+ [
'ORDER BY' => 'cuc_ip ASC, cuc_timestamp DESC',
'LIMIT' => 5000,
'USE INDEX' => 'cuc_user_ip_time'
- )
+ ]
);
- # Try to optimize this query
- $lb = new LinkBatch;
- foreach ( $ret as $row ) {
- $lb->add( $row->cuc_namespace, $row->cuc_title );
- }
- $lb->execute();
- $ret->seek( 0 );
+ // Try to optimize this query
+ $this->doLinkCache( $ret );
$s = '';
foreach ( $ret as $row ) {
- if ( !$ip = htmlspecialchars( $row->cuc_ip ) ) {
+ $ip = htmlspecialchars( $row->cuc_ip );
+ if ( !$ip ) {
continue;
}
if ( !isset( $lastIP ) ) {
@@ -798,30 +879,24 @@ class CheckUser extends SpecialPage {
set_time_limit( 60 );
wfRestoreWarnings();
- # OK, do the real query...
+ // OK, do the real query...
$ret = $dbr->select(
'cu_changes',
'*',
- array( $user_cond, $time_conds ),
+ [ $user_cond, $time_conds ],
__METHOD__,
- array(
+ [
'ORDER BY' => 'cuc_timestamp DESC',
'LIMIT' => 5000,
'USE INDEX' => 'cuc_user_ip_time'
- )
+ ]
);
if ( !$dbr->numRows( $ret ) ) {
$s = $this->noMatchesMessage( $user ) . "\n";
} else {
- # Try to optimize this query
- $lb = new LinkBatch;
- foreach ( $ret as $row ) {
- $lb->add( $row->cuc_namespace, $row->cuc_title );
- }
- $lb->execute();
- $ret->seek( 0 );
- # List out the edits
+ $this->doLinkCache( $ret );
+ // List out the edits
$s = '<div id="checkuserresults">';
foreach ( $ret as $row ) {
$s .= $this->CUChangesLine( $row, $reason );
@@ -833,53 +908,53 @@ class CheckUser extends SpecialPage {
}
/**
+ * Lists all users in recent changes who used an IP, newest to oldest down
+ * Outputs usernames, latest and earliest found edit date, and count
+ * List unique IPs used for each user in time order, list corresponding user agent
+ *
* @param string $ip
* @param bool $xfor
* @param string $reason
* @param int $period
* @param string $tag
* @param string $talkTag
- * Lists all users in recent changes who used an IP, newest to oldest down
- * Outputs usernames, latest and earliest found edit date, and count
- * List unique IPs used for each user in time order, list corresponding user agent
*/
- protected function doIPUsersRequest( $ip, $xfor = false, $reason = '', $period = 0, $tag = '', $talkTag = '' ) {
+ protected function doIPUsersRequest(
+ $ip, $xfor = false, $reason = '', $period = 0, $tag = '', $talkTag = ''
+ ) {
+ global $wgMemc;
$out = $this->getOutput();
+ $dbr = wfGetDB( DB_REPLICA );
- $dbr = wfGetDB( DB_SLAVE );
- # Invalid IPs are passed in as a blank string
+ // Invalid IPs are passed in as a blank string
$ip_conds = self::getIpConds( $dbr, $ip, $xfor );
if ( !$ip || $ip_conds === false ) {
$out->addWikiMsg( 'badipaddress' );
return;
}
- $logType = 'ipusers';
- if ( $xfor ) {
- $logType .= '-xff';
- }
- # Log the check...
- if ( !self::addLogEntry( $logType, 'ip', $ip, $reason ) ) {
- $out->addHTML( '<p>' . $this->msg( 'checkuser-log-fail' )->escaped() . '</p>' );
- }
+ $logType = $xfor ? 'ipusers-xff' : 'ipusers';
+
+ // Log the check...
+ self::addLogEntry( $logType, 'ip', $ip, $reason );
$ip_conds = $dbr->makeList( $ip_conds, LIST_AND );
$time_conds = $this->getTimeConds( $period );
$index = $xfor ? 'cuc_xff_hex_time' : 'cuc_ip_hex_time';
- # Ordered in descent by timestamp. Can cause large filesorts on range scans.
- # Check how many rows will need sorting ahead of time to see if this is too big.
+ // Ordered in descent by timestamp. Can cause large filesorts on range scans.
+ // Check how many rows will need sorting ahead of time to see if this is too big.
if ( strpos( $ip, '/' ) !== false ) {
- # Quick index check only OK if no time constraint
+ // Quick index check only OK if no time constraint
if ( $period ) {
$rangecount = $dbr->selectField( 'cu_changes', 'COUNT(*)',
- array( $ip_conds, $time_conds ),
+ [ $ip_conds, $time_conds ],
__METHOD__,
- array( 'USE INDEX' => $index ) );
+ [ 'USE INDEX' => $index ] );
} else {
$rangecount = $dbr->estimateRowCount( 'cu_changes', '*',
- array( $ip_conds ),
+ [ $ip_conds ],
__METHOD__,
- array( 'USE INDEX' => $index ) );
+ [ 'USE INDEX' => $index ] );
}
// Sorting might take some time...make sure it is there
wfSuppressWarnings();
@@ -890,20 +965,20 @@ class CheckUser extends SpecialPage {
if ( isset( $rangecount ) && $rangecount > 10000 ) {
$ret = $dbr->select(
'cu_changes',
- array(
+ [
'cuc_ip_hex', 'COUNT(*) AS count',
'MIN(cuc_timestamp) AS first', 'MAX(cuc_timestamp) AS last'
- ),
- array( $ip_conds, $time_conds ),
+ ],
+ [ $ip_conds, $time_conds ],
__METHOD__,
- array(
+ [
'GROUP BY' => 'cuc_ip_hex',
'ORDER BY' => 'cuc_ip_hex',
'LIMIT' => 5001,
'USE INDEX' => $index,
- )
+ ]
);
- # List out each IP that has edits
+ // List out each IP that has edits
$s = '<h5>' . $this->msg( 'checkuser-too-many' )->escaped() . '</h5>';
$s .= '<ol>';
$counter = 0;
@@ -912,27 +987,22 @@ class CheckUser extends SpecialPage {
$out->addHTML( $this->msg( 'checkuser-limited' )->parseAsBlock() );
break;
}
- # Convert the IP hexes into normal form
+ // Convert the IP hexes into normal form
if ( strpos( $row->cuc_ip_hex, 'v6-' ) !== false ) {
$ip = substr( $row->cuc_ip_hex, 3 );
- $ip = IP::HextoOctet( $ip );
+ $ip = IP::hexToOctet( $ip );
} else {
- $ip = long2ip( wfBaseConvert( $row->cuc_ip_hex, 16, 10, 8 ) );
+ $ip = long2ip( Wikimedia\base_convert( $row->cuc_ip_hex, 16, 10, 8 ) );
}
- $s .= '<li><a href="' .
- htmlspecialchars( $this->getPageTitle()->getLocalURL( array(
+ $s .= '<li>';
+ $s .= $this->getSelfLink( $ip,
+ [
'user' => $ip,
'reason' => $reason,
'checktype' => 'subipusers'
- ) ) ) .
- '">' . $ip . '</a>';
- if ( $row->first == $row->last ) {
- $s .= ' (' . $this->getLanguage()->timeanddate( wfTimestamp( TS_MW, $row->first ), true ) . ') ';
- } else {
- $lang = $this->getLanguage();
- $s .= ' (' . $lang->timeanddate( wfTimestamp( TS_MW, $row->first ), true ) .
- ' -- ' . $lang->timeanddate( wfTimestamp( TS_MW, $row->last ), true ) . ') ';
- }
+ ]
+ );
+ $s .= ' ' . $this->getTimeRangeString( $row->first, $row->last ) . ' ';
// @todo FIXME: Hard coded brackets.
$s .= ' [<strong>' . $row->count . "</strong>]</li>\n";
++$counter;
@@ -947,24 +1017,27 @@ class CheckUser extends SpecialPage {
return;
}
- global $wgMemc;
- # OK, do the real query...
-
+ // OK, do the real query...
$ret = $dbr->select(
'cu_changes',
- array(
+ [
'cuc_user_text', 'cuc_timestamp', 'cuc_user', 'cuc_ip', 'cuc_agent', 'cuc_xff'
- ),
- array( $ip_conds, $time_conds ),
+ ],
+ [ $ip_conds, $time_conds ],
__METHOD__,
- array(
+ [
'ORDER BY' => 'cuc_timestamp DESC',
'LIMIT' => 10000,
'USE INDEX' => $index,
- )
+ ]
);
- $users_first = $users_last = $users_edits = $users_ids = array();
+ $users_first = [];
+ $users_last = [];
+ $users_edits = [];
+ $users_ids = [];
+ $users_agentsets = [];
+ $users_infosets = [];
if ( !$dbr->numRows( $ret ) ) {
$s = $this->noMatchesMessage( $ip, !$xfor ) . "\n";
} else {
@@ -973,19 +1046,19 @@ class CheckUser extends SpecialPage {
$users_last[$row->cuc_user_text] = $row->cuc_timestamp;
$users_edits[$row->cuc_user_text] = 0;
$users_ids[$row->cuc_user_text] = $row->cuc_user;
- $users_infosets[$row->cuc_user_text] = array();
- $users_agentsets[$row->cuc_user_text] = array();
+ $users_infosets[$row->cuc_user_text] = [];
+ $users_agentsets[$row->cuc_user_text] = [];
}
$users_edits[$row->cuc_user_text] += 1;
$users_first[$row->cuc_user_text] = $row->cuc_timestamp;
- # Treat blank or NULL xffs as empty strings
+ // Treat blank or NULL xffs as empty strings
$xff = empty( $row->cuc_xff ) ? null : $row->cuc_xff;
- $xff_ip_combo = array( $row->cuc_ip, $xff );
- # Add this IP/XFF combo for this username if it's not already there
+ $xff_ip_combo = [ $row->cuc_ip, $xff ];
+ // Add this IP/XFF combo for this username if it's not already there
if ( !in_array( $xff_ip_combo, $users_infosets[$row->cuc_user_text] ) ) {
$users_infosets[$row->cuc_user_text][] = $xff_ip_combo;
}
- # Add this agent string if it's not already there; 10 max.
+ // Add this agent string if it's not already there; 10 max.
if ( count( $users_agentsets[$row->cuc_user_text] ) < 10 ) {
if ( !in_array( $row->cuc_agent, $users_agentsets[$row->cuc_user_text] ) ) {
$users_agentsets[$row->cuc_user_text][] = $row->cuc_agent;
@@ -993,75 +1066,72 @@ class CheckUser extends SpecialPage {
}
}
+ // @todo FIXME: This form (and checkboxes) shouldn't be initiated for users without 'block' right
$action = htmlspecialchars( $this->getPageTitle()->getLocalURL( 'action=block' ) );
$s = "<form name='checkuserblock' id='checkuserblock' action=\"$action\" method='post'>";
$s .= '<div id="checkuserresults"><ul>';
foreach ( $users_edits as $name => $count ) {
$s .= '<li>';
- $s .= Xml::check( 'users[]', false, array( 'value' => $name ) ) . '&#160;';
- # Load user object
+ $s .= Xml::check( 'users[]', false, [ 'value' => $name ] ) . '&#160;';
+ // Load user object
$user = User::newFromName( $name, false );
- # Add user tool links
- $s .= Linker::userLink( - 1 , $name ) . Linker::userToolLinks( - 1 , $name );
- # Add CheckUser link
- $s .= ' (<a href="' . htmlspecialchars( $this->getPageTitle()->getLocalURL( array(
- 'user' => $name,
- 'reason' => $reason
- ) ) ) . '">' . $this->msg( 'checkuser-check' )->escaped() . '</a>)';
- # Show edit time range
- if ( $users_first[$name] == $users_last[$name] ) {
- // @todo FIXME: Hard coded parentheses.
- $s .= ' (' . $this->getLanguage()->timeanddate( wfTimestamp( TS_MW, $users_first[$name] ), true ) . ') ';
- } else {
- // @todo FIXME: Hard coded parentheses.
- $lang = $this->getLanguage();
- $s .= ' (' . $lang->timeanddate( wfTimestamp( TS_MW, $users_first[$name] ), true ) .
- ' -- ' . $lang->timeanddate( wfTimestamp( TS_MW, $users_last[$name] ), true ) . ') ';
- }
- # Total edit count
+ // Add user page and tool links
+ $s .= Linker::userLink( -1, $name ) . ' ';
+ $ip = IP::isIPAddress( $name ) ? $name : '';
+ $linksMsgKey = $ip ? 'checkuser-userlinks-ip' : 'checkuser-userlinks';
+ $s .= $this->msg( $linksMsgKey, $name )->parse();
+ // Add CheckUser link
+ $s .= ' ' . $this->msg( 'parentheses' )->rawParams(
+ $this->getSelfLink(
+ $this->msg( 'checkuser-check' )->text(),
+ [
+ 'user' => $name,
+ 'reason' => $reason
+ ]
+ )
+ )->escaped();
+ // Show edit time range
+ $s .= ' ' . $this->getTimeRangeString( $users_first[$name], $users_last[$name] ) . ' ';
+ // Total edit count
// @todo FIXME: i18n issue: Hard coded brackets.
$s .= ' [<strong>' . $count . '</strong>]<br />';
- # Check if this user or IP is blocked. If so, give a link to the block log...
- $ip = IP::isIPAddress( $name ) ? $name : '';
+ // Check if this user or IP is blocked. If so, give a link to the block log...
$flags = $this->userBlockFlags( $ip, $users_ids[$name], $user );
- # Check how many accounts the user made recently?
+ // Check how many accounts the user made recently
if ( $ip ) {
$key = wfMemcKey( 'acctcreate', 'ip', $ip );
$count = intval( $wgMemc->get( $key ) );
if ( $count ) {
// @todo FIXME: i18n issue: Hard coded brackets.
- $flags[] = '<strong>[' . $this->msg( 'checkuser-accounts' )->numParams( $count )->escaped() . ']</strong>';
+ $flags[] = '<strong>[' .
+ $this->msg( 'checkuser-accounts' )->numParams( $count )->escaped() .
+ ']</strong>';
}
}
$s .= implode( ' ', $flags );
$s .= '<ol>';
- # List out each IP/XFF combo for this username
+ // List out each IP/XFF combo for this username
for ( $i = ( count( $users_infosets[$name] ) - 1 ); $i >= 0; $i-- ) {
$set = $users_infosets[$name][$i];
- # IP link
+ // IP link
$s .= '<li>';
- $s .= '<a href="' .
- htmlspecialchars( $this->getPageTitle()->getLocalURL( 'user=' . urlencode( $set[0] ) ) ) .
- '">' . htmlspecialchars( $set[0] ) . '</a>';
- # XFF string, link to /xff search
+ $s .= $this->getSelfLink( $set[0], [ 'user' => $set[0] ] );
+ // XFF string, link to /xff search
if ( $set[1] ) {
- # Flag our trusted proxies
+ // Flag our trusted proxies
list( $client ) = CheckUserHooks::getClientIPfromXFF( $set[1] );
// XFF was trusted if client came from it
$trusted = ( $client === $row->cuc_ip );
$c = $trusted ? '#F0FFF0' : '#FFFFCC';
- $s .= '&#160;&#160;&#160;<span style="background-color: ' . $c . '"><strong>XFF</strong>: ';
- $s .= Linker::linkKnown(
- $this->getPageTitle(),
- htmlspecialchars( $set[1] ),
- array(),
- array( 'user' => $client . '/xff' )
- ) . '</span>';
+ $s .= '&#160;&#160;&#160;<span style="background-color: ' . $c .
+ '"><strong>XFF</strong>: ';
+ $s .= $this->getSelfLink( $set[1], [ 'user' => $client . '/xff' ] ) .
+ '</span>';
}
$s .= "</li>\n";
}
$s .= '</ol><br /><ol>';
- # List out each agent for this username
+ // List out each agent for this username
for ( $i = ( count( $users_agentsets[$name] ) - 1 ); $i >= 0; $i-- ) {
$agent = $users_agentsets[$name][$i];
$s .= '<li><i>' . htmlspecialchars( $agent ) . "</i></li>\n";
@@ -1071,113 +1141,152 @@ class CheckUser extends SpecialPage {
}
$s .= "</ul></div>\n";
if ( $this->getUser()->isAllowed( 'block' ) && !$this->getUser()->isBlocked() ) {
- $s .= "<fieldset>\n";
- $s .= '<legend>' . $this->msg( 'checkuser-massblock' )->escaped() . "</legend>\n";
- $s .= '<p>' . $this->msg( 'checkuser-massblock-text' )->parse() . "</p>\n";
- $s .= '<table><tr>' .
- '<td>' . Xml::check( 'usetag', false, array( 'id' => 'usetag' ) ) . '</td>' .
- '<td>' . Xml::label( $this->msg( 'checkuser-blocktag' )->escaped(), 'usetag' ) . '</td>' .
- '<td>' . Xml::input( 'tag', 46, $tag, array( 'id' => 'blocktag' ) ) . '</td>' .
- '</tr><tr>' .
- '<td>' . Xml::check( 'usettag', false, array( 'id' => 'usettag' ) ) . '</td>' .
- '<td>' . Xml::label( $this->msg( 'checkuser-blocktag-talk' )->escaped(), 'usettag' ) . '</td>' .
- '<td>' . Xml::input( 'talktag', 46, $talkTag, array( 'id' => 'talktag' ) ) . '</td>' .
- '</tr></table>';
- $s .= '<p>' . $this->msg( 'checkuser-reason' )->escaped() . '&#160;';
- $s .= Xml::input( 'blockreason', 46, '', array( 'maxlength' => '150', 'id' => 'blockreason' ) );
- $s .= '&#160;' . Xml::submitButton( $this->msg( 'checkuser-massblock-commit' )->escaped(),
- array( 'id' => 'checkuserblocksubmit', 'name' => 'checkuserblock' ) ) . "</p>\n";
- $s .= "</fieldset>\n";
+ // FIXME: The block <form> is currently added for users without 'block' right
+ // - only the user-visible form is shown appropriately
+ $s .= $this->getBlockForm( $tag, $talkTag );
+ $s .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
}
- $s .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() );
- $s .= '</form>';
+ $s .= "</form>\n";
}
$out->addHTML( $s );
}
/**
- * @param $ip
- * @param $userId
- * @param $user User
+ * @param string $tag
+ * @param string $talkTag
+ * @return string
+ */
+ protected function getBlockForm( $tag, $talkTag ) {
+ global $wgBlockAllowsUTEdit, $wgCheckUserCAMultiLock;
+ if ( $wgCheckUserCAMultiLock !== false ) {
+ if ( !class_exists( 'CentralAuthUser' ) ) {
+ // $wgCheckUserCAMultiLock shouldn't be enabled if CA is not loaded
+ throw new Exception( '$wgCheckUserCAMultiLock requires CentralAuth extension.' );
+ }
+
+ $caUserGroups = CentralAuthUser::getInstance( $this->getUser() )->getGlobalGroups();
+ // Only load the script for users in the configured global group(s)
+ if ( count( array_intersect( $wgCheckUserCAMultiLock['groups'], $caUserGroups ) ) ) {
+ $out = $this->getOutput();
+ $out->addModules( 'ext.checkUser.caMultiLock' );
+ $centralMLUrl = WikiMap::getForeignURL(
+ $wgCheckUserCAMultiLock['centralDB'],
+ // Use canonical name instead of local name so that it works
+ // even if the local language is different from central wiki
+ 'Special:MultiLock'
+ );
+ if ( $centralMLUrl === false ) {
+ throw new Exception(
+ "Could not retrieve URL for {$wgCheckUserCAMultiLock['centralDB']}"
+ );
+ }
+ $out->addJsConfigVars( 'wgCUCAMultiLockCentral', $centralMLUrl );
+ }
+ }
+
+ $s = "<fieldset>\n";
+ $s .= '<legend>' . $this->msg( 'checkuser-massblock' )->escaped() . "</legend>\n";
+ $s .= $this->msg( 'checkuser-massblock-text' )->parseAsBlock() . "\n";
+ $s .= '<table><tr>' .
+ '<td>' . Xml::check( 'usetag', false, [ 'id' => 'usetag' ] ) . '</td>' .
+ '<td>' . Xml::label( $this->msg( 'checkuser-blocktag' )->escaped(), 'usetag' ) .
+ '</td>' .
+ '<td>' . Xml::input( 'tag', 46, $tag, [ 'id' => 'blocktag' ] ) . '</td>' .
+ '</tr><tr>' .
+ '<td>' . Xml::check( 'usettag', false, [ 'id' => 'usettag' ] ) . '</td>' .
+ '<td>' . Xml::label( $this->msg( 'checkuser-blocktag-talk' )->escaped(), 'usettag' ) .
+ '</td>' .
+ '<td>' . Xml::input( 'talktag', 46, $talkTag, [ 'id' => 'talktag' ] ) . '</td>';
+ if ( $wgBlockAllowsUTEdit ) {
+ $s .= '</tr><tr>' .
+ '<td>' . Xml::check( 'blocktalk', false, [ 'id' => 'blocktalk' ] ) . '</td>' .
+ '<td>' . Xml::label( $this->msg( 'checkuser-blocktalk' )->escaped(), 'blocktalk' ) .
+ '</td>';
+ }
+ if ( SpecialBlock::canBlockEmail( $this->getUser() ) ) {
+ $s .= '</tr><tr>' .
+ '<td>' . Xml::check( 'blockemail', false, [ 'id' => 'blockemail' ] ) . '</td>' .
+ '<td>' . Xml::label( $this->msg( 'checkuser-blockemail' )->escaped(), 'blockemail' )
+ . '</td>';
+ }
+ $s .= '</tr></table>';
+ $s .= '<p>' . $this->msg( 'checkuser-reason' )->escaped() . '&#160;';
+ $s .= Xml::input( 'blockreason', 46, '', [ 'maxlength' => '150', 'id' => 'blockreason' ] );
+ $s .= '&#160;' . Xml::submitButton( $this->msg( 'checkuser-massblock-commit' )->escaped(),
+ [ 'id' => 'checkuserblocksubmit', 'name' => 'checkuserblock' ] ) . "</p>\n";
+ $s .= "</fieldset>\n";
+
+ return $s;
+ }
+
+ /**
+ * Get an HTML link (<a> element) to Special:CheckUser
+ *
+ * @param string $text content to use within <a> tag
+ * @param array $params query parameters to use in the URL
+ * @return string
+ */
+ private function getSelfLink( $text, array $params ) {
+ static $title;
+ if ( $title === null ) {
+ $title = $this->getPageTitle();
+ }
+ return $this->getLinkRenderer()->makeKnownLink(
+ $title,
+ $text,
+ [],
+ $params
+ );
+ }
+
+ /**
+ * @param string $ip
+ * @param int $userId
+ * @param User $user
* @return array
*/
protected function userBlockFlags( $ip, $userId, $user ) {
- global $wgAuth;
- static $logs, $blocklist;
- $logs = SpecialPage::getTitleFor( 'Log' );
- $blocklist = SpecialPage::getTitleFor( 'BlockList' );
+ $flags = [];
+
$block = Block::newFromTarget( $user, $ip, false );
- $flags = array();
if ( $block instanceof Block ) {
- // Range blocked?
- if ( $block->getType() == Block::TYPE_RANGE ) {
- $userpage = Title::makeTitle( NS_USER, $block->getTarget() );
- $blocklog = Linker::linkKnown(
- $logs,
- $this->msg( 'checkuser-blocked' )->escaped(),
- array(),
- array(
- 'type' => 'block',
- 'page' => $userpage->getPrefixedText()
- )
- );
- $flags[] = '<strong>(' . $blocklog . ' - ' . $block->getTarget() . ')</strong>';
- // Auto blocked?
- } elseif ( $block->getType() == Block::TYPE_AUTO ) {
- $blocklog = Linker::linkKnown(
- $blocklist,
- $this->msg( 'checkuser-blocked' )->escaped(),
- array(),
- array( 'ip' => "#{$block->getId()}" )
- );
- // @todo FIXME: Hard coded parentheses.
- $flags[] = '<strong>(' . $blocklog . ')</strong>';
- } else {
- $userpage = $user->getUserPage();
- $blocklog =Linker::linkKnown(
- $logs,
- $this->msg( 'checkuser-blocked' )->escaped(),
- array(),
- array(
- 'type' => 'block',
- 'page' => $userpage->getPrefixedText()
- )
- );
- // @todo FIXME: Hard coded parentheses.
- $flags[] = '<strong>(' . $blocklog . ')</strong>';
- }
- // IP that is blocked on all wikis?
+ // Locally blocked
+ $flags[] = $this->getBlockFlag( $block );
} elseif ( $ip == $user->getName() && $user->isBlockedGlobally( $ip ) ) {
+ // Globally blocked IP
$flags[] = '<strong>(' . $this->msg( 'checkuser-gblocked' )->escaped() . ')</strong>';
} elseif ( self::userWasBlocked( $user->getName() ) ) {
+ // Previously blocked
$userpage = $user->getUserPage();
- $blocklog = Linker::linkKnown(
- $logs,
- $this->msg( 'checkuser-wasblocked' )->escaped(),
- array(),
- array(
- 'type'=> 'block',
+ $blocklog = $this->getLinkRenderer()->makeKnownLink(
+ SpecialPage::getTitleFor( 'Log' ),
+ $this->msg( 'checkuser-wasblocked' )->text(),
+ [],
+ [
+ 'type' => 'block',
'page' => $userpage->getPrefixedText()
- )
+ ]
);
// @todo FIXME: Hard coded parentheses.
$flags[] = '<strong>(' . $blocklog . ')</strong>';
}
- # Show if account is local only
- $authUser = $wgAuth->getUserInstance( $user );
- if ( $user->getId() && $authUser->getId() === 0 ) {
+ // Show if account is local only
+ if ( $user->getId() &&
+ CentralIdLookup::factory()
+ ->centralIdFromLocalUser( $user, CentralIdLookup::AUDIENCE_RAW ) === 0
+ ) {
// @todo FIXME: i18n issue: Hard coded parentheses.
$flags[] = '<strong>(' . $this->msg( 'checkuser-localonly' )->escaped() . ')</strong>';
}
- # Check for extra user rights...
+ // Check for extra user rights...
if ( $userId ) {
if ( $user->isLocked() ) {
// @todo FIXME: i18n issue: Hard coded parentheses.
$flags[] = '<b>(' . $this->msg( 'checkuser-locked' )->escaped() . ')</b>';
}
- $list = array();
+ $list = [];
foreach ( $user->getGroups() as $group ) {
$list[] = self::buildGroupLink( $group, $user->getName() );
}
@@ -1192,14 +1301,15 @@ class CheckUser extends SpecialPage {
}
/**
- * @param Row $row
+ * Get a streamlined recent changes line with IP data
+ *
+ * @param stdClass $row
* @param string $reason
- * @return a streamlined recent changes line with IP data
+ * @return string
*/
protected function CUChangesLine( $row, $reason ) {
- static $cuTitle, $flagCache;
- $cuTitle = SpecialPage::getTitleFor( 'CheckUser' );
- # Add date headers as needed
+ static $flagCache;
+ // Add date headers as needed
$date = $this->getLanguage()->date( wfTimestamp( TS_MW, $row->cuc_timestamp ), true, true );
if ( !isset( $this->lastdate ) ) {
$this->lastdate = $date;
@@ -1211,14 +1321,16 @@ class CheckUser extends SpecialPage {
$line = '';
}
$line .= '<li>';
- # Create diff/hist/page links
+ // Create diff/hist/page links
$line .= $this->getLinksFromRow( $row );
- # Show date
- $line .= ' . . ' . $this->getLanguage()->time( wfTimestamp( TS_MW, $row->cuc_timestamp ), true, true ) . ' . . ';
- # Userlinks
+ // Show date
+ $line .= ' . . ' .
+ $this->getLanguage()->time( wfTimestamp( TS_MW, $row->cuc_timestamp ), true, true )
+ . ' . . ';
+ // Userlinks
$line .= Linker::userLink( $row->cuc_user, $row->cuc_user_text );
$line .= Linker::userToolLinks( $row->cuc_user, $row->cuc_user_text );
- # Get block info
+ // Get block info
if ( isset( $flagCache[$row->cuc_user_text] ) ) {
$flags = $flagCache[$row->cuc_user_text];
} else {
@@ -1227,46 +1339,43 @@ class CheckUser extends SpecialPage {
$flags = $this->userBlockFlags( $ip, $row->cuc_user, $user );
$flagCache[$row->cuc_user_text] = $flags;
}
- # Add any block information
+ // Add any block information
if ( count( $flags ) ) {
$line .= ' ' . implode( ' ', $flags );
}
- # Action text, hackish ...
+ // Action text, hackish ...
if ( $row->cuc_actiontext ) {
$line .= ' ' . Linker::formatComment( $row->cuc_actiontext ) . ' ';
}
- # Comment
+ // Comment
$line .= Linker::commentBlock( $row->cuc_comment );
$line .= '<br />&#160; &#160; &#160; &#160; <small>';
- # IP
- $line .= ' <strong>IP</strong>: ' . Linker::linkKnown(
- $cuTitle,
- htmlspecialchars( $row->cuc_ip ),
- array(),
- array(
+ // IP
+ $line .= ' <strong>IP</strong>: ';
+ $line .= $this->getSelfLink( $row->cuc_ip,
+ [
'user' => $row->cuc_ip,
'reason' => $reason
- )
+ ]
);
- # XFF
+ // XFF
if ( $row->cuc_xff != null ) {
- # Flag our trusted proxies
+ // Flag our trusted proxies
list( $client ) = CheckUserHooks::getClientIPfromXFF( $row->cuc_xff );
$trusted = ( $client === $row->cuc_ip ); // XFF was trusted if client came from it
$c = $trusted ? '#F0FFF0' : '#FFFFCC';
- $line .= '&#160;&#160;&#160;<span class="mw-checkuser-xff" style="background-color: ' . $c . '">' .
+ $line .= '&#160;&#160;&#160;';
+ $line .= '<span class="mw-checkuser-xff" style="background-color: ' . $c . '">' .
'<strong>XFF</strong>: ';
- $line .= Linker::linkKnown(
- $cuTitle,
- htmlspecialchars( $row->cuc_xff ),
- array(),
- array(
+ $line .= $this->getSelfLink( $row->cuc_xff,
+ [
'user' => $client . '/xff',
'reason' => $reason
- )
- ) . '</span>';
+ ]
+ );
+ $line .= '</span>';
}
- # User agent
+ // User agent
$line .= '&#160;&#160;&#160;<span class="mw-checkuser-agent" style="color:#888;">' .
htmlspecialchars( $row->cuc_agent ) . '</span>';
@@ -1276,80 +1385,118 @@ class CheckUser extends SpecialPage {
}
/**
- * @param $row
- * @create diff/hist/page link
+ * Get formatted timestamp(s) to show the time of first and last change.
+ * If both timestamps are the same, it will be shown only once.
+ *
+ * @param string $first Timestamp of the first change
+ * @param string $last Timestamp of the last change
+ * @return string
+ */
+ protected function getTimeRangeString( $first, $last ) {
+ $s = $this->getFormattedTimestamp( $first );
+ if ( $first !== $last ) {
+ // @todo i18n issue - hardcoded string
+ $s .= ' -- ';
+ $s .= $this->getFormattedTimestamp( $last );
+ }
+ return $this->msg( 'parentheses' )->rawParams( $s )->escaped();
+ }
+
+ /**
+ * Get a formatted timestamp string in the current language
+ * for displaying to the user.
+ *
+ * @param string $timestamp
+ * @return string
+ */
+ protected function getFormattedTimestamp( $timestamp ) {
+ return $this->getLanguage()->timeanddate(
+ wfTimestamp( TS_MW, $timestamp ), true
+ );
+ }
+
+ /**
+ * @param stdClass $row
+ * @return string diff, hist and page other links related to the change
*/
protected function getLinksFromRow( $row ) {
- $links = array();
+ $links = [];
// Log items
if ( $row->cuc_type == RC_LOG ) {
$title = Title::makeTitle( $row->cuc_namespace, $row->cuc_title );
// @todo FIXME: Hard coded parentheses.
- $links['log'] = '(' . Linker::linkKnown(
+ $links['log'] = '(' . $this->getLinkRenderer()->makeKnownLink(
SpecialPage::getTitleFor( 'Log' ),
$this->message['log'],
- array(),
- array( 'page' => $title->getPrefixedText() )
+ [],
+ [ 'page' => $title->getPrefixedText() ]
) . ')';
} else {
$title = Title::makeTitle( $row->cuc_namespace, $row->cuc_title );
- # New pages
+ // New pages
if ( $row->cuc_type == RC_NEW ) {
$links['diff'] = '(' . $this->message['diff'] . ') ';
} else {
- # Diff link
+ // Diff link
// @todo FIXME: Hard coded parentheses.
- $links['diff'] = ' (' . Linker::linkKnown(
+ $links['diff'] = ' (' . $this->getLinkRenderer()->makeKnownLink(
$title,
$this->message['diff'],
- array(),
- array(
+ [],
+ [
'curid' => $row->cuc_page_id,
'diff' => $row->cuc_this_oldid,
'oldid' => $row->cuc_last_oldid
- )
+ ]
) . ') ';
}
- # History link
+ // History link
// @todo FIXME: Hard coded parentheses.
- $links['history'] = ' (' . Linker::linkKnown(
+ $links['history'] = ' (' . $this->getLinkRenderer()->makeKnownLink(
$title,
$this->message['hist'],
- array(),
- array(
+ [],
+ [
'curid' => $row->cuc_page_id,
'action' => 'history'
- )
+ ]
) . ') . . ';
- # Some basic flags
+ // Some basic flags
if ( $row->cuc_type == RC_NEW ) {
- $links['newpage'] = '<span class="newpage">' . $this->message['newpageletter'] . '</span>';
+ $links['newpage'] = '<span class="newpage">' . $this->message['newpageletter'] .
+ '</span>';
}
if ( $row->cuc_minor ) {
- $links['minor'] = '<span class="minor">' . $this->message['minoreditletter'] . '</span>';
+ $links['minor'] = '<span class="minor">' . $this->message['minoreditletter'] .
+ '</span>';
}
- # Page link
- $links['title'] = Linker::link( $title );
+ // Page link
+ $links['title'] = $this->getLinkRenderer()->makeLink( $title );
}
- Hooks::run( 'SpecialCheckUserGetLinksFromRow', array( $this, $row, &$links ) );
+ Hooks::run( 'SpecialCheckUserGetLinksFromRow', [ $this, $row, &$links ] );
if ( is_array( $links ) ) {
return implode( ' ', $links );
} else {
- wfDebugLog( __CLASS__, __METHOD__ . ': Expected array from SpecialCheckUserGetLinksFromRow $links param, but received ' . gettype( $links ) );
+ wfDebugLog( __CLASS__,
+ __METHOD__ . ': Expected array from SpecialCheckUserGetLinksFromRow $links param,'
+ . ' but received ' . gettype( $links )
+ );
return '';
}
}
protected static function userWasBlocked( $name ) {
$userpage = Title::makeTitle( NS_USER, $name );
- return wfGetDB( DB_SLAVE )->selectField( 'logging', '1',
- array( 'log_type' => array( 'block', 'suppress' ),
+ return (bool)wfGetDB( DB_REPLICA )->selectField( 'logging', '1',
+ [
+ 'log_type' => [ 'block', 'suppress' ],
'log_action' => 'block',
'log_namespace' => $userpage->getNamespace(),
- 'log_title' => $userpage->getDBkey() ),
+ 'log_title' => $userpage->getDBkey()
+ ],
__METHOD__,
- array( 'USE INDEX' => 'page_time' ) );
+ [ 'USE INDEX' => 'page_time' ] );
}
/**
@@ -1359,56 +1506,45 @@ class CheckUser extends SpecialPage {
* @param string $username
* @return string
*/
- protected static function buildGroupLink( $group, $username = '#' ) {
- static $cache = array();
+ protected static function buildGroupLink( $group, $username ) {
+ static $cache = [];
if ( !isset( $cache[$group] ) ) {
- $cache[$group] = User::makeGroupLinkHtml( $group, User::getGroupMember( $group, $username ) );
+ $cache[$group] = UserGroupMembership::getLink(
+ $group, RequestContext::getMain(), 'html'
+ );
}
return $cache[$group];
}
/**
- * @param DatabaseBase $db
- * @param string $ip
+ * @param IDatabase $db
+ * @param string $target an IP address or CIDR range
* @param string|bool $xfor
- * @return mixed array/false conditions
+ * @return array|false array for valid conditions, false if invalid
*/
- public static function getIpConds( $db, $ip, $xfor = false ) {
+ public static function getIpConds( $db, $target, $xfor = false ) {
global $wgCheckUserCIDRLimit;
- $type = ( $xfor ) ? 'xff' : 'ip';
- // IPv4 CIDR, 16-32 bits
- $matches = array();
- if ( preg_match( '#^(\d+\.\d+\.\d+\.\d+)/(\d+)$#', $ip, $matches ) ) {
- if ( $matches[2] < $wgCheckUserCIDRLimit['IPv4'] || $matches[2] > 32 ) {
- return false; // invalid
+ $type = $xfor ? 'xff' : 'ip';
+ if ( IP::isValidRange( $target ) ) {
+ list( $ip, $range ) = explode( '/', $target, 2 );
+ list( $start, $end ) = IP::parseRange( $target );
+ if ( ( IP::isIPv4( $ip ) && $range < $wgCheckUserCIDRLimit['IPv4'] ) ||
+ ( IP::isIPv6( $ip ) && $range < $wgCheckUserCIDRLimit['IPv6'] ) ) {
+ return false; // range is too wide
}
- list( $start, $end ) = IP::parseRange( $ip );
- return array( 'cuc_' . $type . '_hex BETWEEN ' . $db->addQuotes( $start ) . ' AND ' . $db->addQuotes( $end ) );
- } elseif ( preg_match( '#^\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}/(\d+)$#', $ip, $matches ) ) {
- // IPv6 CIDR, 48-128 bits
- if ( $matches[1] < $wgCheckUserCIDRLimit['IPv6'] || $matches[1] > 128 ) {
- return false; // invalid
- }
- list( $start, $end ) = IP::parseRange( $ip );
- return array( 'cuc_' . $type . '_hex BETWEEN ' . $db->addQuotes( $start ) . ' AND ' . $db->addQuotes( $end ) );
- } elseif ( preg_match( '#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#', $ip ) ) {
- // 32 bit IPv4
- $ip_hex = IP::toHex( $ip );
- return array( 'cuc_' . $type . '_hex' => $ip_hex );
- } elseif ( preg_match( '#^\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}$#', $ip ) ) {
- // 128 bit IPv6
- $ip_hex = IP::toHex( $ip );
- return array( 'cuc_' . $type . '_hex' => $ip_hex );
+ return [ 'cuc_' . $type . '_hex BETWEEN ' . $db->addQuotes( $start ) .
+ ' AND ' . $db->addQuotes( $end ) ];
+ } elseif ( IP::isValid( $target ) ) {
+ return [ "cuc_{$type}_hex" => IP::toHex( $target ) ];
}
- // throw away this query, incomplete IP, these don't get through the entry point anyway
- return false; // invalid
+ return false; // invalid IP
}
protected function getTimeConds( $period ) {
if ( !$period ) {
return '1 = 1';
}
- $dbr = wfGetDB( DB_SLAVE );
+ $dbr = wfGetDB( DB_REPLICA );
$cutoff_unixtime = time() - ( $period * 24 * 3600 );
$cutoff_unixtime = $cutoff_unixtime - ( $cutoff_unixtime % 86400 );
$cutoff = $dbr->addQuotes( $dbr->timestamp( $cutoff_unixtime ) );
@@ -1416,7 +1552,7 @@ class CheckUser extends SpecialPage {
}
public static function addLogEntry( $logType, $targetType, $target, $reason, $targetID = 0 ) {
- global $wgUser;
+ $user = RequestContext::getMain()->getUser();
if ( $targetType == 'ip' ) {
list( $rangeStart, $rangeEnd ) = IP::parseRange( $target );
@@ -1428,27 +1564,56 @@ class CheckUser extends SpecialPage {
$targetHex = $rangeStart = $rangeEnd = '';
}
- $dbw = wfGetDB( DB_MASTER );
- $cul_id = $dbw->nextSequenceValue( 'cu_log_cul_id_seq' );
- $dbw->insert( 'cu_log',
- array(
- 'cul_id' => $cul_id,
- 'cul_timestamp' => $dbw->timestamp(),
- 'cul_user' => $wgUser->getID(),
- 'cul_user_text' => $wgUser->getName(),
- 'cul_reason' => $reason,
- 'cul_type' => $logType,
- 'cul_target_id' => $targetID,
- 'cul_target_text' => $target,
- 'cul_target_hex' => $targetHex,
- 'cul_range_start' => $rangeStart,
- 'cul_range_end' => $rangeEnd,
- ), __METHOD__ );
- return true;
+ $timestamp = time();
+ $data = [
+ 'cul_user' => $user->getId(),
+ 'cul_user_text' => $user->getName(),
+ 'cul_reason' => $reason,
+ 'cul_type' => $logType,
+ 'cul_target_id' => $targetID,
+ 'cul_target_text' => $target,
+ 'cul_target_hex' => $targetHex,
+ 'cul_range_start' => $rangeStart,
+ 'cul_range_end' => $rangeEnd
+ ];
+
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $data, $timestamp ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->insert(
+ 'cu_log',
+ [
+ 'cul_timestamp' => $dbw->timestamp( $timestamp )
+ ] + $data,
+ __METHOD__
+ );
+ },
+ DeferredUpdates::PRESEND // fail on error and show no output
+ );
+ }
+
+ /**
+ * 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 ) {
+ if ( !class_exists( 'UserNamePrefixSearch' ) ) { // check for version 1.27
+ return [];
+ }
+ $user = User::newFromName( $search );
+ if ( !$user ) {
+ // No prefix suggestion for invalid user
+ return [];
+ }
+ // Autocomplete subpage as user list - public to allow caching
+ return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
}
protected function getGroupName() {
return 'users';
}
}
-