The ApiQueryBlocks and ApiQueryUserinfo endpoints will now return whether or not the block is
sitewide or partial. Partial block restrictions can be returned with ApiQueryBlocks.
Bug: T197141
Change-Id: I76eb4cac4dc989c621a00a39996faebd0eb9892c
* @file
*/
+use Wikimedia\Rdbms\IResultWrapper;
+use MediaWiki\Block\BlockRestriction;
+
/**
* Query module to enumerate all user blocks
*
$fld_reason = isset( $prop['reason'] );
$fld_range = isset( $prop['range'] );
$fld_flags = isset( $prop['flags'] );
+ $fld_restrictions = isset( $prop['restrictions'] );
$result = $this->getResult();
$this->addFieldsIf( 'ipb_expiry', $fld_expiry );
$this->addFieldsIf( [ 'ipb_range_start', 'ipb_range_end' ], $fld_range );
$this->addFieldsIf( [ 'ipb_anon_only', 'ipb_create_account', 'ipb_enable_autoblock',
- 'ipb_block_email', 'ipb_deleted', 'ipb_allow_usertalk' ],
+ 'ipb_block_email', 'ipb_deleted', 'ipb_allow_usertalk', 'ipb_sitewide' ],
$fld_flags );
+ $this->addFieldsIf( 'ipb_sitewide', $fld_restrictions );
if ( $fld_reason ) {
$commentQuery = $commentStore->getJoin( 'ipb_reason' );
$res = $this->select( __METHOD__ );
+ $restrictions = [];
+ if ( $fld_restrictions ) {
+ $restrictions = $this->getRestrictionData( $res, $params['limit'] );
+ }
+
$count = 0;
foreach ( $res as $row ) {
if ( ++$count > $params['limit'] ) {
$block['noemail'] = (bool)$row->ipb_block_email;
$block['hidden'] = (bool)$row->ipb_deleted;
$block['allowusertalk'] = (bool)$row->ipb_allow_usertalk;
+ $block['partial'] = !(bool)$row->ipb_sitewide;
+ }
+
+ if ( $fld_restrictions ) {
+ $block['restrictions'] = [];
+ if ( !$row->ipb_sitewide && isset( $restrictions[$row->ipb_id] ) ) {
+ $block['restrictions'] = $restrictions[$row->ipb_id];
+ }
}
+
$fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $block );
if ( !$fit ) {
$this->setContinueEnumParameter( 'continue', "$row->ipb_timestamp|$row->ipb_id" );
return $name;
}
+ /**
+ * Retrieves the restrictions based on the query result.
+ *
+ * @param IResultWrapper $result
+ * @param int $limit
+ *
+ * @return array
+ */
+ private static function getRestrictionData( IResultWrapper $result, $limit ) {
+ $partialIds = [];
+ $count = 0;
+ foreach ( $result as $row ) {
+ if ( ++$count <= $limit && !$row->ipb_sitewide ) {
+ $partialIds[] = (int)$row->ipb_id;
+ }
+ }
+
+ $restrictions = BlockRestriction::loadByBlockId( $partialIds );
+
+ $data = [];
+ $keys = [
+ 'page' => 'pages',
+ 'ns' => 'namespaces',
+ ];
+ foreach ( $restrictions as $restriction ) {
+ $key = $keys[$restriction->getType()];
+ $id = $restriction->getBlockId();
+ switch ( $restriction->getType() ) {
+ case 'page':
+ $value = [ 'id' => $restriction->getValue() ];
+ self::addTitleInfo( $value, $restriction->getTitle() );
+ break;
+ default:
+ $value = $restriction->getValue();
+ }
+
+ if ( !isset( $data[$id][$key] ) ) {
+ $data[$id][$key] = [];
+ ApiResult::setIndexedTagName( $data[$id][$key], $restriction->getType() );
+ }
+ $data[$id][$key][] = $value;
+ }
+
+ return $data;
+ }
+
public function getAllowedParams() {
$blockCIDRLimit = $this->getConfig()->get( 'BlockCIDRLimit' );
'expiry',
'reason',
'range',
- 'flags'
+ 'flags',
+ 'restrictions',
],
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
$vals['blockreason'] = $block->mReason;
$vals['blockedtimestamp'] = wfTimestamp( TS_ISO_8601, $block->mTimestamp );
$vals['blockexpiry'] = ApiResult::formatExpiry( $block->getExpiry(), 'infinite' );
+ $vals['blockpartial'] = !$block->isSitewide();
if ( $block->getSystemBlockType() !== null ) {
$vals['systemblocktype'] = $block->getSystemBlockType();
}
"apihelp-query+blocks-paramvalue-prop-reason": "Adds the reason given for the block.",
"apihelp-query+blocks-paramvalue-prop-range": "Adds the range of IP addresses affected by the block.",
"apihelp-query+blocks-paramvalue-prop-flags": "Tags the ban with (autoblock, anononly, etc.).",
+ "apihelp-query+blocks-paramvalue-prop-restrictions": "Adds the partial block restrictions if the block is not sitewide.",
"apihelp-query+blocks-param-show": "Show only items that meet these criteria.\nFor example, to see only indefinite blocks on IP addresses, set <kbd>$1show=ip|!temp</kbd>.",
"apihelp-query+blocks-example-simple": "List blocks.",
"apihelp-query+blocks-example-users": "List blocks of users <kbd>Alice</kbd> and <kbd>Bob</kbd>.",
"apihelp-query+blocks-paramvalue-prop-reason": "{{doc-apihelp-paramvalue|query+blocks|prop|reason}}",
"apihelp-query+blocks-paramvalue-prop-range": "{{doc-apihelp-paramvalue|query+blocks|prop|range}}",
"apihelp-query+blocks-paramvalue-prop-flags": "{{doc-apihelp-paramvalue|query+blocks|prop|flags}}",
+ "apihelp-query+blocks-paramvalue-prop-restrictions": "{{doc-apihelp-paramvalue|query+blocks|prop|flags}}",
"apihelp-query+blocks-param-show": "{{doc-apihelp-param|query+blocks|show}}",
"apihelp-query+blocks-example-simple": "{{doc-apihelp-example|query+blocks}}",
"apihelp-query+blocks-example-users": "{{doc-apihelp-example|query+blocks}}",
--- /dev/null
+<?php
+
+use MediaWiki\Block\Restriction\PageRestriction;
+
+/**
+ * @group API
+ * @group Database
+ * @group medium
+ *
+ * @covers ApiQueryBlocks
+ */
+class ApiQueryBlocksTest extends ApiTestCase {
+
+ protected $tablesUsed = [
+ 'ipblocks',
+ 'ipblocks_restrictions',
+ ];
+
+ public function testExecute() {
+ list( $data ) = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'blocks',
+ ] );
+ $this->assertEquals( [ 'batchcomplete' => true, 'query' => [ 'blocks' => [] ] ], $data );
+ }
+
+ public function testExecuteBlock() {
+ $badActor = $this->getTestUser()->getUser();
+ $sysop = $this->getTestSysop()->getUser();
+
+ $block = new Block( [
+ 'address' => $badActor->getName(),
+ 'user' => $badActor->getId(),
+ 'by' => $sysop->getId(),
+ 'expiry' => 'infinity',
+ ] );
+
+ $block->insert();
+
+ list( $data ) = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'blocks',
+ ] );
+ $this->arrayHasKey( 'query', $data );
+ $this->arrayHasKey( 'blocks', $data['query'] );
+ $this->assertCount( 1, $data['query']['blocks'] );
+ $subset = [
+ 'id' => $block->getId(),
+ 'user' => $badActor->getName(),
+ 'expiry' => $block->getExpiry(),
+ ];
+ $this->assertArraySubset( $subset, $data['query']['blocks'][0] );
+ }
+
+ public function testExecuteSitewide() {
+ $badActor = $this->getTestUser()->getUser();
+ $sysop = $this->getTestSysop()->getUser();
+
+ $block = new Block( [
+ 'address' => $badActor->getName(),
+ 'user' => $badActor->getId(),
+ 'by' => $sysop->getId(),
+ 'ipb_expiry' => 'infinity',
+ 'ipb_sitewide' => 1,
+ ] );
+
+ $block->insert();
+
+ list( $data ) = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'blocks',
+ ] );
+ $this->arrayHasKey( 'query', $data );
+ $this->arrayHasKey( 'blocks', $data['query'] );
+ $this->assertCount( 1, $data['query']['blocks'] );
+ $subset = [
+ 'id' => $block->getId(),
+ 'user' => $badActor->getName(),
+ 'expiry' => $block->getExpiry(),
+ 'partial' => !$block->isSitewide(),
+ ];
+ $this->assertArraySubset( $subset, $data['query']['blocks'][0] );
+ }
+
+ public function testExecuteRestrictions() {
+ $badActor = $this->getTestUser()->getUser();
+ $sysop = $this->getTestSysop()->getUser();
+
+ $block = new Block( [
+ 'address' => $badActor->getName(),
+ 'user' => $badActor->getId(),
+ 'by' => $sysop->getId(),
+ 'expiry' => 'infinity',
+ 'sitewide' => 0,
+ ] );
+
+ $block->insert();
+
+ $subset = [
+ 'id' => $block->getId(),
+ 'user' => $badActor->getName(),
+ 'expiry' => $block->getExpiry(),
+ ];
+
+ $title = 'Lady Macbeth';
+ $pageData = $this->insertPage( $title );
+ $pageId = $pageData['id'];
+
+ $this->db->insert( 'ipblocks_restrictions', [
+ 'ir_ipb_id' => $block->getId(),
+ 'ir_type' => PageRestriction::TYPE_ID,
+ 'ir_value' => $pageId,
+ ] );
+ $this->db->insert( 'ipblocks_restrictions', [
+ 'ir_ipb_id' => $block->getId(),
+ 'ir_type' => 2,
+ 'ir_value' => 3,
+ ] );
+
+ // Test without requesting restrictions.
+ list( $data ) = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'blocks',
+ ] );
+ $this->arrayHasKey( 'query', $data );
+ $this->arrayHasKey( 'blocks', $data['query'] );
+ $this->assertCount( 1, $data['query']['blocks'] );
+ $flagSubset = array_merge( $subset, [
+ 'partial' => !$block->isSitewide(),
+ ] );
+ $this->assertArraySubset( $flagSubset, $data['query']['blocks'][0] );
+ $this->assertArrayNotHasKey( 'restrictions', $data['query']['blocks'][0] );
+
+ // Test requesting the restrictions.
+ list( $data ) = $this->doApiRequest( [
+ 'action' => 'query',
+ 'list' => 'blocks',
+ 'bkprop' => 'id|user|expiry|restrictions'
+ ] );
+ $this->arrayHasKey( 'query', $data );
+ $this->arrayHasKey( 'blocks', $data['query'] );
+ $this->assertCount( 1, $data['query']['blocks'] );
+ $restrictionsSubset = array_merge( $subset, [
+ 'restrictions' => [
+ 'pages' => [
+ [
+ 'id' => $pageId,
+ 'ns' => 0,
+ 'title' => $title,
+ ],
+ ],
+ ],
+ ] );
+ $this->assertArraySubset( $restrictionsSubset, $data['query']['blocks'][0] );
+ $this->assertArrayNotHasKey( 'partial', $data['query']['blocks'][0] );
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * @group medium
+ * @covers ApiQueryUserInfo
+ */
+class ApiQueryUserInfoTest extends ApiTestCase {
+ public function testGetBlockInfo() {
+ $apiQueryUserInfo = new ApiQueryUserInfo(
+ new ApiQuery( new ApiMain( $this->apiContext ), 'userinfo' ),
+ 'userinfo'
+ );
+
+ $block = new Block();
+ $info = $apiQueryUserInfo->getBlockInfo( $block );
+ $subset = [
+ 'blockid' => null,
+ 'blockedby' => '',
+ 'blockedbyid' => 0,
+ 'blockreason' => '',
+ 'blockexpiry' => 'infinite',
+ 'blockpartial' => false,
+ ];
+ $this->assertArraySubset( $subset, $info );
+ }
+
+ public function testGetBlockInfoPartial() {
+ $apiQueryUserInfo = new ApiQueryUserInfo(
+ new ApiQuery( new ApiMain( $this->apiContext ), 'userinfo' ),
+ 'userinfo'
+ );
+
+ $block = new Block( [
+ 'sitewide' => false,
+ ] );
+ $info = $apiQueryUserInfo->getBlockInfo( $block );
+ $subset = [
+ 'blockid' => null,
+ 'blockedby' => '',
+ 'blockedbyid' => 0,
+ 'blockreason' => '',
+ 'blockexpiry' => 'infinite',
+ 'blockpartial' => true,
+ ];
+ $this->assertArraySubset( $subset, $info );
+ }
+}