Update Special:BlockList to present Partial Block details from the database
The Special:BlockList page will be updated to show details from the Partial Block within the "Block parameters" column. The format of the column will change to make the details more readable. Bug: T197143 Change-Id: Ibd79b049d93e427c2d541f8ef93005847482ef59
This commit is contained in:
parent
9c32d7bcf4
commit
170c49d61c
4 changed files with 327 additions and 1 deletions
|
|
@ -22,6 +22,8 @@
|
||||||
/**
|
/**
|
||||||
* @ingroup Pager
|
* @ingroup Pager
|
||||||
*/
|
*/
|
||||||
|
use MediaWiki\Block\BlockRestriction;
|
||||||
|
use MediaWiki\Block\Restriction\Restriction;
|
||||||
use MediaWiki\MediaWikiServices;
|
use MediaWiki\MediaWikiServices;
|
||||||
use Wikimedia\Rdbms\IResultWrapper;
|
use Wikimedia\Rdbms\IResultWrapper;
|
||||||
|
|
||||||
|
|
@ -30,6 +32,13 @@ class BlockListPager extends TablePager {
|
||||||
protected $conds;
|
protected $conds;
|
||||||
protected $page;
|
protected $page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of restrictions.
|
||||||
|
*
|
||||||
|
* @var Restriction[]
|
||||||
|
*/
|
||||||
|
protected $restrictions = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param SpecialPage $page
|
* @param SpecialPage $page
|
||||||
* @param array $conds
|
* @param array $conds
|
||||||
|
|
@ -72,6 +81,8 @@ class BlockListPager extends TablePager {
|
||||||
'blocklist-nousertalk',
|
'blocklist-nousertalk',
|
||||||
'unblocklink',
|
'unblocklink',
|
||||||
'change-blocklink',
|
'change-blocklink',
|
||||||
|
'blocklist-editing',
|
||||||
|
'blocklist-editing-sitewide',
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ( $keys as $key ) {
|
foreach ( $keys as $key ) {
|
||||||
|
|
@ -179,6 +190,18 @@ class BlockListPager extends TablePager {
|
||||||
|
|
||||||
case 'ipb_params':
|
case 'ipb_params':
|
||||||
$properties = [];
|
$properties = [];
|
||||||
|
|
||||||
|
if ( $this->getConfig()->get( 'EnablePartialBlocks' ) ) {
|
||||||
|
if ( $row->ipb_sitewide ) {
|
||||||
|
$properties[] = htmlspecialchars( $msg['blocklist-editing-sitewide'] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !$row->ipb_sitewide && $this->restrictions ) {
|
||||||
|
$list = $this->getRestrictionListHTML( $this->restrictions, $row );
|
||||||
|
$properties[] = htmlspecialchars( $msg['blocklist-editing'] ) . $list;
|
||||||
|
}
|
||||||
|
|
||||||
if ( $row->ipb_anon_only ) {
|
if ( $row->ipb_anon_only ) {
|
||||||
$properties[] = htmlspecialchars( $msg['anononlyblock'] );
|
$properties[] = htmlspecialchars( $msg['anononlyblock'] );
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +220,17 @@ class BlockListPager extends TablePager {
|
||||||
$properties[] = htmlspecialchars( $msg['blocklist-nousertalk'] );
|
$properties[] = htmlspecialchars( $msg['blocklist-nousertalk'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
$formatted = $language->commaList( $properties );
|
$formatted = Html::rawElement(
|
||||||
|
'ul',
|
||||||
|
[],
|
||||||
|
implode( '', array_map( function ( $prop ) {
|
||||||
|
return HTML::rawElement(
|
||||||
|
'li',
|
||||||
|
[],
|
||||||
|
$prop
|
||||||
|
);
|
||||||
|
}, $properties ) )
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -208,6 +241,47 @@ class BlockListPager extends TablePager {
|
||||||
return $formatted;
|
return $formatted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Restriction List HTML
|
||||||
|
*
|
||||||
|
* @param Restriction[] $restrictions
|
||||||
|
* @param stdClass $row
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private static function getRestrictionListHTML(
|
||||||
|
array $restrictions,
|
||||||
|
stdClass $row
|
||||||
|
) {
|
||||||
|
$items = [];
|
||||||
|
|
||||||
|
foreach ( $restrictions as $restriction ) {
|
||||||
|
if ( $restriction->getBlockId() !== (int)$row->ipb_id ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $restriction->getType() !== 'page' ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$items[] = HTML::rawElement(
|
||||||
|
'li',
|
||||||
|
[],
|
||||||
|
Linker::link( $restriction->getTitle() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $items ) ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return Html::rawElement(
|
||||||
|
'ul',
|
||||||
|
[],
|
||||||
|
implode( '', $items )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function getQueryInfo() {
|
function getQueryInfo() {
|
||||||
$commentQuery = CommentStore::getStore()->getJoin( 'ipb_reason' );
|
$commentQuery = CommentStore::getStore()->getJoin( 'ipb_reason' );
|
||||||
$actorQuery = ActorMigration::newMigration()->getJoin( 'ipb_by' );
|
$actorQuery = ActorMigration::newMigration()->getJoin( 'ipb_by' );
|
||||||
|
|
@ -232,6 +306,7 @@ class BlockListPager extends TablePager {
|
||||||
'ipb_deleted',
|
'ipb_deleted',
|
||||||
'ipb_block_email',
|
'ipb_block_email',
|
||||||
'ipb_allow_usertalk',
|
'ipb_allow_usertalk',
|
||||||
|
'ipb_sitewide',
|
||||||
] + $commentQuery['fields'] + $actorQuery['fields'],
|
] + $commentQuery['fields'] + $actorQuery['fields'],
|
||||||
'conds' => $this->conds,
|
'conds' => $this->conds,
|
||||||
'join_conds' => [
|
'join_conds' => [
|
||||||
|
|
@ -296,6 +371,7 @@ class BlockListPager extends TablePager {
|
||||||
$lb = new LinkBatch;
|
$lb = new LinkBatch;
|
||||||
$lb->setCaller( __METHOD__ );
|
$lb->setCaller( __METHOD__ );
|
||||||
|
|
||||||
|
$partialBlocks = [];
|
||||||
foreach ( $result as $row ) {
|
foreach ( $result as $row ) {
|
||||||
$lb->add( NS_USER, $row->ipb_address );
|
$lb->add( NS_USER, $row->ipb_address );
|
||||||
$lb->add( NS_USER_TALK, $row->ipb_address );
|
$lb->add( NS_USER_TALK, $row->ipb_address );
|
||||||
|
|
@ -304,6 +380,16 @@ class BlockListPager extends TablePager {
|
||||||
$lb->add( NS_USER, $row->by_user_name );
|
$lb->add( NS_USER, $row->by_user_name );
|
||||||
$lb->add( NS_USER_TALK, $row->by_user_name );
|
$lb->add( NS_USER_TALK, $row->by_user_name );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !$row->ipb_sitewide ) {
|
||||||
|
$partialBlocks[] = $row->ipb_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $partialBlocks ) {
|
||||||
|
// Mutations to the $row object are not persisted. The restrictions will
|
||||||
|
// need be stored in a separate store.
|
||||||
|
$this->restrictions = BlockRestriction::loadByBlockId( $partialBlocks );
|
||||||
}
|
}
|
||||||
|
|
||||||
$lb->execute();
|
$lb->execute();
|
||||||
|
|
|
||||||
|
|
@ -2646,6 +2646,8 @@
|
||||||
"createaccountblock": "account creation disabled",
|
"createaccountblock": "account creation disabled",
|
||||||
"emailblock": "email disabled",
|
"emailblock": "email disabled",
|
||||||
"blocklist-nousertalk": "cannot edit own talk page",
|
"blocklist-nousertalk": "cannot edit own talk page",
|
||||||
|
"blocklist-editing": "editing",
|
||||||
|
"blocklist-editing-sitewide": "editing (sitewide)",
|
||||||
"ipblocklist-empty": "The block list is empty.",
|
"ipblocklist-empty": "The block list is empty.",
|
||||||
"ipblocklist-no-results": "The requested IP address or username is not blocked.",
|
"ipblocklist-no-results": "The requested IP address or username is not blocked.",
|
||||||
"blocklink": "block",
|
"blocklink": "block",
|
||||||
|
|
|
||||||
|
|
@ -2848,6 +2848,8 @@
|
||||||
"createaccountblock": "Part of the log entry of user block in [[Special:BlockList]].\n\nSee also:\n* {{msg-mw|Block-log-flags-nocreate}}\n{{Related|Blocklist}}",
|
"createaccountblock": "Part of the log entry of user block in [[Special:BlockList]].\n\nSee also:\n* {{msg-mw|Block-log-flags-nocreate}}\n{{Related|Blocklist}}",
|
||||||
"emailblock": "Part of the log entry of user block in [[Special:BlockList]].\n{{Related|Blocklist}}\n{{Identical|E-mail blocked}}",
|
"emailblock": "Part of the log entry of user block in [[Special:BlockList]].\n{{Related|Blocklist}}\n{{Identical|E-mail blocked}}",
|
||||||
"blocklist-nousertalk": "Used in [[Special:IPBlockList]] when \"Allow this user to edit own talk page while blocked\" option hasn't been flagged.\n\nSee also {{msg-mw|Block-log-flags-nousertalk}}.\n\nPart of the log entry of user block in [[Special:BlockList]].\n\n{{Related|Blocklist}}",
|
"blocklist-nousertalk": "Used in [[Special:IPBlockList]] when \"Allow this user to edit own talk page while blocked\" option hasn't been flagged.\n\nSee also {{msg-mw|Block-log-flags-nousertalk}}.\n\nPart of the log entry of user block in [[Special:BlockList]].\n\n{{Related|Blocklist}}",
|
||||||
|
"blocklist-editing-sitewide": "Used in [[Special:IPBlockList]] when a block is a sitewide block.",
|
||||||
|
"blocklist-editing": "Used in [[Special:IPBlockList]] when a block is not a sitewide block.",
|
||||||
"ipblocklist-empty": "Used in [[Special:BlockList]], if the target is not specified.\n\nSee also:\n* {{msg-mw|Ipblocklist-no-results}}",
|
"ipblocklist-empty": "Used in [[Special:BlockList]], if the target is not specified.\n\nSee also:\n* {{msg-mw|Ipblocklist-no-results}}",
|
||||||
"ipblocklist-no-results": "Used in [[Special:BlockList]], if the target is specified.\n\nSee also:\n* {{msg-mw|Ipblocklist-empty}}",
|
"ipblocklist-no-results": "Used in [[Special:BlockList]], if the target is specified.\n\nSee also:\n* {{msg-mw|Ipblocklist-empty}}",
|
||||||
"blocklink": "Display name for a link that, when selected, leads to a form where a user can be blocked. Used in page history and recent changes pages. Example: \"''UserName (Talk | contribs | '''block''')''\".\n\nUsed as link title in [[Special:Contributions]] and in [[Special:DeletedContributions]].\n\nSee also:\n* {{msg-mw|Sp-contributions-talk}}\n* {{msg-mw|Change-blocklink}}\n* {{msg-mw|Unblocklink}}\n* {{msg-mw|Sp-contributions-blocklog}}\n* {{msg-mw|Sp-contributions-uploads}}\n* {{msg-mw|Sp-contributions-logs}}\n* {{msg-mw|Sp-contributions-deleted}}\n* {{msg-mw|Sp-contributions-userrights}}\n{{Identical|Block}}",
|
"blocklink": "Display name for a link that, when selected, leads to a form where a user can be blocked. Used in page history and recent changes pages. Example: \"''UserName (Talk | contribs | '''block''')''\".\n\nUsed as link title in [[Special:Contributions]] and in [[Special:DeletedContributions]].\n\nSee also:\n* {{msg-mw|Sp-contributions-talk}}\n* {{msg-mw|Change-blocklink}}\n* {{msg-mw|Unblocklink}}\n* {{msg-mw|Sp-contributions-blocklog}}\n* {{msg-mw|Sp-contributions-uploads}}\n* {{msg-mw|Sp-contributions-logs}}\n* {{msg-mw|Sp-contributions-deleted}}\n* {{msg-mw|Sp-contributions-userrights}}\n{{Identical|Block}}",
|
||||||
|
|
|
||||||
236
tests/phpunit/includes/specials/pagers/BlockListPagerTest.php
Normal file
236
tests/phpunit/includes/specials/pagers/BlockListPagerTest.php
Normal file
|
|
@ -0,0 +1,236 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use MediaWiki\Block\Restriction\PageRestriction;
|
||||||
|
use MediaWiki\MediaWikiServices;
|
||||||
|
use Wikimedia\TestingAccessWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group Database
|
||||||
|
* @coversDefaultClass BlockListPager
|
||||||
|
*/
|
||||||
|
class BlockListPagerTest extends MediaWikiTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::formatValue
|
||||||
|
* @dataProvider formatValueEmptyProvider
|
||||||
|
* @dataProvider formatValueDefaultProvider
|
||||||
|
* @param string $name
|
||||||
|
* @param string $value
|
||||||
|
* @param string $expected
|
||||||
|
*/
|
||||||
|
public function testFormatValue( $name, $value, $expected, $row = null ) {
|
||||||
|
$this->setMwGlobals( [
|
||||||
|
'wgEnablePartialBlocks' => false,
|
||||||
|
] );
|
||||||
|
$row = $row ?: new stdClass;
|
||||||
|
$pager = new BlockListPager( new SpecialPage(), [] );
|
||||||
|
$wrappedPager = TestingAccessWrapper::newFromObject( $pager );
|
||||||
|
$wrappedPager->mCurrentRow = $row;
|
||||||
|
|
||||||
|
$formatted = $pager->formatValue( $name, $value );
|
||||||
|
$this->assertEquals( $expected, $formatted );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test empty values.
|
||||||
|
*/
|
||||||
|
public function formatValueEmptyProvider() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'test',
|
||||||
|
'',
|
||||||
|
'Unable to format test',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'ipb_timestamp',
|
||||||
|
wfTimestamp( TS_UNIX ),
|
||||||
|
date( 'H:i, j F Y' ),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'ipb_expiry',
|
||||||
|
'',
|
||||||
|
'infinite<br />0 minutes left',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the default row values.
|
||||||
|
*/
|
||||||
|
public function formatValueDefaultProvider() {
|
||||||
|
$row = (object)[
|
||||||
|
'ipb_user' => 0,
|
||||||
|
'ipb_address' => '127.0.0.1',
|
||||||
|
'ipb_by_text' => 'Admin',
|
||||||
|
'ipb_create_account' => 1,
|
||||||
|
'ipb_auto' => 0,
|
||||||
|
'ipb_anon_only' => 0,
|
||||||
|
'ipb_create_account' => 1,
|
||||||
|
'ipb_enable_autoblock' => 1,
|
||||||
|
'ipb_deleted' => 0,
|
||||||
|
'ipb_block_email' => 0,
|
||||||
|
'ipb_allow_usertalk' => 0,
|
||||||
|
'ipb_sitewide' => 1,
|
||||||
|
];
|
||||||
|
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'test',
|
||||||
|
'',
|
||||||
|
'Unable to format test',
|
||||||
|
$row,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'ipb_timestamp',
|
||||||
|
wfTimestamp( TS_UNIX ),
|
||||||
|
date( 'H:i, j F Y' ),
|
||||||
|
$row,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'ipb_expiry',
|
||||||
|
'',
|
||||||
|
'infinite<br />0 minutes left',
|
||||||
|
$row,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'ipb_by',
|
||||||
|
'',
|
||||||
|
$row->ipb_by_text,
|
||||||
|
$row,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'ipb_params',
|
||||||
|
'',
|
||||||
|
'<ul><li>account creation disabled</li><li>cannot edit own talk page</li></ul>',
|
||||||
|
$row,
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::formatValue
|
||||||
|
*/
|
||||||
|
public function testFormatValueRestrictions() {
|
||||||
|
$pager = new BlockListPager( new SpecialPage(), [] );
|
||||||
|
|
||||||
|
$row = (object)[
|
||||||
|
'ipb_id' => 0,
|
||||||
|
'ipb_user' => 0,
|
||||||
|
'ipb_anon_only' => 0,
|
||||||
|
'ipb_enable_autoblock' => 0,
|
||||||
|
'ipb_create_account' => 0,
|
||||||
|
'ipb_block_email' => 0,
|
||||||
|
'ipb_allow_usertalk' => 1,
|
||||||
|
'ipb_sitewide' => 0,
|
||||||
|
];
|
||||||
|
$wrappedPager = TestingAccessWrapper::newFromObject( $pager );
|
||||||
|
$wrappedPager->mCurrentRow = $row;
|
||||||
|
|
||||||
|
$pageName = 'Victor Frankenstein';
|
||||||
|
$page = $this->insertPage( $pageName );
|
||||||
|
$title = $page['title'];
|
||||||
|
$pageId = $page['id'];
|
||||||
|
|
||||||
|
$restrictions = [
|
||||||
|
( new PageRestriction( 0, $pageId ) )->setTitle( $title )
|
||||||
|
];
|
||||||
|
|
||||||
|
$wrappedPager = TestingAccessWrapper::newFromObject( $pager );
|
||||||
|
$wrappedPager->restrictions = $restrictions;
|
||||||
|
|
||||||
|
$formatted = $pager->formatValue( 'ipb_params', '' );
|
||||||
|
$this->assertEquals( '<ul><li>'
|
||||||
|
. wfMessage( 'blocklist-editing' )->text()
|
||||||
|
. '<ul><li><a href="/index.php/'
|
||||||
|
. $title->getDBKey()
|
||||||
|
. '" title="'
|
||||||
|
. $pageName
|
||||||
|
. '">'
|
||||||
|
. $pageName
|
||||||
|
. '</a></li></ul></li></ul>',
|
||||||
|
$formatted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::preprocessResults
|
||||||
|
*/
|
||||||
|
public function testPreprocessResults() {
|
||||||
|
// Test the Link Cache.
|
||||||
|
$linkCache = MediaWikiServices::getInstance()->getLinkCache();
|
||||||
|
$wrappedlinkCache = TestingAccessWrapper::newFromObject( $linkCache );
|
||||||
|
|
||||||
|
$links = [
|
||||||
|
'User:127.0.0.1',
|
||||||
|
'User_talk:127.0.0.1',
|
||||||
|
'User:Admin',
|
||||||
|
'User_talk:Admin',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ( $links as $link ) {
|
||||||
|
$this->assertNull( $wrappedlinkCache->badLinks->get( $link ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = (object)[
|
||||||
|
'ipb_address' => '127.0.0.1',
|
||||||
|
'by_user_name' => 'Admin',
|
||||||
|
'ipb_sitewide' => 1,
|
||||||
|
'ipb_timestamp' => $this->db->timestamp( wfTimestamp( TS_MW ) ),
|
||||||
|
];
|
||||||
|
$pager = new BlockListPager( new SpecialPage(), [] );
|
||||||
|
$pager->preprocessResults( [ $row ] );
|
||||||
|
|
||||||
|
foreach ( $links as $link ) {
|
||||||
|
$this->assertSame( 1, $wrappedlinkCache->badLinks->get( $link ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Sitewide Blocks.
|
||||||
|
$row = (object)[
|
||||||
|
'ipb_address' => '127.0.0.1',
|
||||||
|
'by_user_name' => 'Admin',
|
||||||
|
'ipb_sitewide' => 1,
|
||||||
|
];
|
||||||
|
$pager = new BlockListPager( new SpecialPage(), [] );
|
||||||
|
$pager->preprocessResults( [ $row ] );
|
||||||
|
|
||||||
|
$this->assertObjectNotHasAttribute( 'ipb_restrictions', $row );
|
||||||
|
|
||||||
|
$pageName = 'Victor Frankenstein';
|
||||||
|
$page = $this->getExistingTestPage( 'Victor Frankenstein' );
|
||||||
|
$title = $page->getTitle();
|
||||||
|
|
||||||
|
$target = '127.0.0.1';
|
||||||
|
|
||||||
|
// Test Partial Blocks Blocks.
|
||||||
|
$block = new Block( [
|
||||||
|
'address' => $target,
|
||||||
|
'by' => $this->getTestSysop()->getUser()->getId(),
|
||||||
|
'reason' => 'Parce que',
|
||||||
|
'expiry' => $this->db->getInfinity(),
|
||||||
|
'sitewide' => false,
|
||||||
|
] );
|
||||||
|
$block->setRestrictions( [
|
||||||
|
new PageRestriction( 0, $page->getId() ),
|
||||||
|
] );
|
||||||
|
$block->insert();
|
||||||
|
|
||||||
|
$result = $this->db->select( 'ipblocks', [ '*' ], [ 'ipb_id' => $block->getId() ] );
|
||||||
|
|
||||||
|
$pager = new BlockListPager( new SpecialPage(), [] );
|
||||||
|
$pager->preprocessResults( $result );
|
||||||
|
|
||||||
|
$wrappedPager = TestingAccessWrapper::newFromObject( $pager );
|
||||||
|
|
||||||
|
$restrictions = $wrappedPager->restrictions;
|
||||||
|
$this->assertInternalType( 'array', $restrictions );
|
||||||
|
|
||||||
|
$restriction = $restrictions[0];
|
||||||
|
$this->assertEquals( $page->getId(), $restriction->getValue() );
|
||||||
|
$this->assertEquals( $page->getId(), $restriction->getTitle()->getArticleId() );
|
||||||
|
$this->assertEquals( $title->getDBKey(), $restriction->getTitle()->getDBKey() );
|
||||||
|
$this->assertEquals( $title->getNamespace(), $restriction->getTitle()->getNamespace() );
|
||||||
|
|
||||||
|
// Delete the block and the restrictions.
|
||||||
|
$block->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue