Don't show action links for IP ranges outside block limit
These action links don't work but they appear like they would. So better not show them. Bug: T211910 Change-Id: Ibec312870b3ba225dc26e533e478cd50df37fcaa
This commit is contained in:
parent
da8ace4020
commit
032dc91f47
2 changed files with 170 additions and 67 deletions
|
|
@ -68,6 +68,9 @@ class SpecialContributions extends IncludableSpecialPage {
|
|||
/** @var UserOptionsLookup */
|
||||
private $userOptionsLookup;
|
||||
|
||||
/** @var ContribsPager|null */
|
||||
private $pager = null;
|
||||
|
||||
/**
|
||||
* @param LinkBatchFactory|null $linkBatchFactory
|
||||
* @param PermissionManager|null $permissionManager
|
||||
|
|
@ -145,6 +148,45 @@ class SpecialContributions extends IncludableSpecialPage {
|
|||
$this->opts['newOnly'] = $request->getBool( 'newOnly' );
|
||||
$this->opts['hideMinor'] = $request->getBool( 'hideMinor' );
|
||||
|
||||
$ns = $request->getVal( 'namespace', null );
|
||||
if ( $ns !== null && $ns !== '' && $ns !== 'all' ) {
|
||||
$this->opts['namespace'] = intval( $ns );
|
||||
} else {
|
||||
$this->opts['namespace'] = '';
|
||||
}
|
||||
|
||||
// Backwards compatibility: Before using OOUI form the old HTML form had
|
||||
// fields for nsInvert and associated. These have now been replaced with the
|
||||
// wpFilters query string parameters. These are retained to keep old URIs working.
|
||||
$this->opts['associated'] = $request->getBool( 'associated' );
|
||||
$this->opts['nsInvert'] = (bool)$request->getVal( 'nsInvert' );
|
||||
$nsFilters = $request->getArray( 'wpfilters', null );
|
||||
if ( $nsFilters !== null ) {
|
||||
$this->opts['associated'] = in_array( 'associated', $nsFilters );
|
||||
$this->opts['nsInvert'] = in_array( 'nsInvert', $nsFilters );
|
||||
}
|
||||
|
||||
$this->opts['tagfilter'] = (string)$request->getVal( 'tagfilter' );
|
||||
|
||||
// Allows reverts to have the bot flag in recent changes. It is just here to
|
||||
// be passed in the form at the top of the page
|
||||
if ( MediaWikiServices::getInstance()
|
||||
->getPermissionManager()
|
||||
->userHasRight( $user, 'markbotedits' ) && $request->getBool( 'bot' )
|
||||
) {
|
||||
$this->opts['bot'] = '1';
|
||||
}
|
||||
|
||||
$skip = $request->getText( 'offset' ) || $request->getText( 'dir' ) == 'prev';
|
||||
# Offset overrides year/month selection
|
||||
if ( !$skip ) {
|
||||
$this->opts['year'] = $request->getVal( 'year' );
|
||||
$this->opts['month'] = $request->getVal( 'month' );
|
||||
|
||||
$this->opts['start'] = $request->getVal( 'start' );
|
||||
$this->opts['end'] = $request->getVal( 'end' );
|
||||
}
|
||||
|
||||
$id = 0;
|
||||
if ( ExternalUserNames::isExternal( $target ) ) {
|
||||
$userObj = User::newFromName( $target, false );
|
||||
|
|
@ -185,41 +227,6 @@ class SpecialContributions extends IncludableSpecialPage {
|
|||
}
|
||||
}
|
||||
|
||||
$ns = $request->getVal( 'namespace', null );
|
||||
if ( $ns !== null && $ns !== '' && $ns !== 'all' ) {
|
||||
$this->opts['namespace'] = intval( $ns );
|
||||
} else {
|
||||
$this->opts['namespace'] = '';
|
||||
}
|
||||
|
||||
// Backwards compatibility: Before using OOUI form the old HTML form had
|
||||
// fields for nsInvert and associated. These have now been replaced with the
|
||||
// wpFilters query string parameters. These are retained to keep old URIs working.
|
||||
$this->opts['associated'] = $request->getBool( 'associated' );
|
||||
$this->opts['nsInvert'] = (bool)$request->getVal( 'nsInvert' );
|
||||
$nsFilters = $request->getArray( 'wpfilters', null );
|
||||
if ( $nsFilters !== null ) {
|
||||
$this->opts['associated'] = in_array( 'associated', $nsFilters );
|
||||
$this->opts['nsInvert'] = in_array( 'nsInvert', $nsFilters );
|
||||
}
|
||||
|
||||
$this->opts['tagfilter'] = (string)$request->getVal( 'tagfilter' );
|
||||
|
||||
// Allows reverts to have the bot flag in recent changes. It is just here to
|
||||
// be passed in the form at the top of the page
|
||||
if ( $this->permissionManager->userHasRight( $user, 'markbotedits' ) && $request->getBool( 'bot' ) ) {
|
||||
$this->opts['bot'] = '1';
|
||||
}
|
||||
|
||||
$skip = $request->getText( 'offset' ) || $request->getText( 'dir' ) == 'prev';
|
||||
# Offset overrides year/month selection
|
||||
if ( !$skip ) {
|
||||
$this->opts['year'] = $request->getVal( 'year' );
|
||||
$this->opts['month'] = $request->getVal( 'month' );
|
||||
|
||||
$this->opts['start'] = $request->getVal( 'start' );
|
||||
$this->opts['end'] = $request->getVal( 'end' );
|
||||
}
|
||||
$this->opts = ContribsPager::processDateFilter( $this->opts );
|
||||
|
||||
if ( $this->opts['namespace'] < NS_MAIN ) {
|
||||
|
|
@ -281,33 +288,10 @@ class SpecialContributions extends IncludableSpecialPage {
|
|||
if ( $this->getHookRunner()->onSpecialContributionsBeforeMainOutput(
|
||||
$id, $userObj, $this )
|
||||
) {
|
||||
$pager = new ContribsPager(
|
||||
$this->getContext(), [
|
||||
'target' => $target,
|
||||
'namespace' => $this->opts['namespace'],
|
||||
'tagfilter' => $this->opts['tagfilter'],
|
||||
'start' => $this->opts['start'],
|
||||
'end' => $this->opts['end'],
|
||||
'deletedOnly' => $this->opts['deletedOnly'],
|
||||
'topOnly' => $this->opts['topOnly'],
|
||||
'newOnly' => $this->opts['newOnly'],
|
||||
'hideMinor' => $this->opts['hideMinor'],
|
||||
'nsInvert' => $this->opts['nsInvert'],
|
||||
'associated' => $this->opts['associated'],
|
||||
],
|
||||
$this->getLinkRenderer(),
|
||||
$this->linkBatchFactory,
|
||||
$this->getHookContainer(),
|
||||
$this->permissionManager,
|
||||
$this->loadBalancer,
|
||||
$this->actorMigration,
|
||||
$this->revisionStore,
|
||||
$this->namespaceInfo
|
||||
);
|
||||
if ( !$this->including() ) {
|
||||
$out->addHTML( $this->getForm( $this->opts ) );
|
||||
}
|
||||
|
||||
$pager = $this->getPager();
|
||||
if ( IPUtils::isValidRange( $target ) && !$pager->isQueryableRange( $target ) ) {
|
||||
// Valid range, but outside CIDR limit.
|
||||
$limits = $this->getConfig()->get( 'RangeContributionsCIDRLimit' );
|
||||
|
|
@ -353,7 +337,7 @@ class SpecialContributions extends IncludableSpecialPage {
|
|||
$out->preventClickjacking( $pager->getPreventClickjacking() );
|
||||
|
||||
# Show the appropriate "footer" message - WHOIS tools, etc.
|
||||
if ( IPUtils::isValidRange( $target ) ) {
|
||||
if ( IPUtils::isValidRange( $target ) && $pager->isQueryableRange( $target ) ) {
|
||||
$message = 'sp-contributions-footer-anon-range';
|
||||
} elseif ( IPUtils::isIPAddress( $target ) ) {
|
||||
$message = 'sp-contributions-footer-anon';
|
||||
|
|
@ -404,13 +388,13 @@ class SpecialContributions extends IncludableSpecialPage {
|
|||
$nt = $userObj->getUserPage();
|
||||
$talk = $userObj->getTalkPage();
|
||||
$links = '';
|
||||
if ( $talk ) {
|
||||
$tools = self::getUserLinks(
|
||||
$this,
|
||||
$userObj,
|
||||
$this->permissionManager,
|
||||
$this->getHookRunner()
|
||||
);
|
||||
|
||||
// T211910. Don't show action links if a range is outside block limit
|
||||
$showForIp = IPUtils::isValid( $userObj ) ||
|
||||
( IPUtils::isValidRange( $userObj ) && $this->getPager()->isQueryableRange( $userObj ) );
|
||||
|
||||
if ( $talk && ( $userObj->isRegistered() || $showForIp ) ) {
|
||||
$tools = self::getUserLinks( $this, $userObj );
|
||||
$links = Html::openElement( 'span', [ 'class' => 'mw-changeslist-links' ] );
|
||||
foreach ( $tools as $tool ) {
|
||||
$links .= Html::rawElement( 'span', [], $tool ) . ' ';
|
||||
|
|
@ -812,6 +796,32 @@ class SpecialContributions extends IncludableSpecialPage {
|
|||
->search( UserNamePrefixSearch::AUDIENCE_PUBLIC, $search, $limit, $offset );
|
||||
}
|
||||
|
||||
private function getPager() {
|
||||
if ( $this->pager === null ) {
|
||||
$options = [
|
||||
'target' => $this->opts['target'],
|
||||
'namespace' => $this->opts['namespace'],
|
||||
'tagfilter' => $this->opts['tagfilter'],
|
||||
'start' => $this->opts['start'],
|
||||
'end' => $this->opts['end'],
|
||||
'deletedOnly' => $this->opts['deletedOnly'],
|
||||
'topOnly' => $this->opts['topOnly'],
|
||||
'newOnly' => $this->opts['newOnly'],
|
||||
'hideMinor' => $this->opts['hideMinor'],
|
||||
'nsInvert' => $this->opts['nsInvert'],
|
||||
'associated' => $this->opts['associated'],
|
||||
];
|
||||
|
||||
$this->pager = new ContribsPager(
|
||||
$this->getContext(),
|
||||
$options,
|
||||
$this->getLinkRenderer()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->pager;
|
||||
}
|
||||
|
||||
protected function getGroupName() {
|
||||
return 'users';
|
||||
}
|
||||
|
|
|
|||
93
tests/phpunit/includes/specials/SpecialContributionsTest.php
Normal file
93
tests/phpunit/includes/specials/SpecialContributionsTest.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Ammarpad
|
||||
* @group Database
|
||||
* @covers SpecialContributions
|
||||
*/
|
||||
class SpecialContributionsTest extends SpecialPageTestBase {
|
||||
private $admin;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->setMwGlobals( [
|
||||
'wgRangeContributionsCIDRLimit' => [
|
||||
'IPv4' => 16,
|
||||
'IPv6' => 32,
|
||||
]
|
||||
] );
|
||||
$this->setTemporaryHook(
|
||||
'SpecialContributionsBeforeMainOutput',
|
||||
function () {
|
||||
return;
|
||||
}
|
||||
);
|
||||
$this->admin = $this->getTestSysop()->getUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers SpecialContributions::execute
|
||||
* @dataProvider provideTestExecuteRange
|
||||
*/
|
||||
public function testExecuteRange( $username, $shouldShowLinks ) {
|
||||
list( $html ) = $this->executeSpecialPage( $username, null, 'qqx', $this->admin, true );
|
||||
|
||||
if ( $shouldShowLinks ) {
|
||||
$this->assertStringContainsString( 'blocklink', $html );
|
||||
} else {
|
||||
$this->assertStringNotContainsString( 'blocklink', $html );
|
||||
$this->assertStringContainsString( 'sp-contributions-outofrange', $html );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers SpecialContributions::execute
|
||||
* @dataProvider provideTestExecuteNonRange
|
||||
*/
|
||||
public function testExecuteNonRange( $username, $shouldShowLinks ) {
|
||||
list( $html ) = $this->executeSpecialPage( $username, null, 'qqx', $this->admin, true );
|
||||
|
||||
if ( $shouldShowLinks ) {
|
||||
$this->assertStringContainsString( 'blocklink', $html );
|
||||
} else {
|
||||
$this->assertStringNotContainsString( 'blocklink', $html );
|
||||
}
|
||||
}
|
||||
|
||||
public function provideTestExecuteRange() {
|
||||
yield 'Queryable IPv4 range should have blocklink for admin'
|
||||
=> [ '24.237.208.166/30', true ];
|
||||
yield 'Queryable IPv6 range should have blocklink for admin'
|
||||
=> [ '2001:DB8:0:0:0:0:0:01/43', true ];
|
||||
yield 'Unqueryable IPv4 range should not have blocklink for admin'
|
||||
=> [ '212.35.31.121/14', false ];
|
||||
yield 'Unqueryable IPv6 range should not have blocklink for admin'
|
||||
=> [ '2000::/24', false ];
|
||||
}
|
||||
|
||||
public function provideTestExecuteNonRange() {
|
||||
yield 'Valid IPv4 should have blocklink for admin' => [ '124.24.52.13', true ];
|
||||
yield 'Valid IPv6 should have blocklink for admin' => [ '2001:db8::', true ];
|
||||
yield 'Local user should have blocklink for admin' => [ 'UTSysop', true ];
|
||||
yield 'Invalid IP should not have blocklink for admin' => [ '24.237.222208.166', false ];
|
||||
yield 'External user should not have blocklink for admin' => [ 'imported>UTSysop', false ];
|
||||
yield 'Nonexistent user should not have blocklink for admin' => [ __CLASS__, false ];
|
||||
}
|
||||
|
||||
protected function newSpecialPage(): SpecialContributions {
|
||||
$services = MediaWiki\MediaWikiServices::getInstance();
|
||||
|
||||
return new SpecialContributions(
|
||||
$services->getLinkBatchFactory(),
|
||||
$services->getPermissionManager(),
|
||||
$services->getDBLoadBalancer(),
|
||||
$services->getActorMigration(),
|
||||
$services->getRevisionStore(),
|
||||
$services->getNamespaceInfo(),
|
||||
$services->getUserNameUtils(),
|
||||
$services->getUserNamePrefixSearch(),
|
||||
$services->getUserOptionsLookup()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue