Add block restriction classes

Partial blocks logic will be used in multiple places. This
classes will group block restriction functionality to avoid
code duplication

Bug: T202036
Change-Id: I675316dddf272fd0d6172ecad3882160752bf780
This commit is contained in:
Dayllan Maza 2018-08-16 00:55:55 -04:00 committed by Moriel Schottlender
parent 6cd66511d2
commit a562611e5b
13 changed files with 1698 additions and 7 deletions

View file

@ -130,6 +130,7 @@ class AutoLoader {
public static function getAutoloadNamespaces() {
return [
'MediaWiki\\Auth\\' => __DIR__ . '/auth/',
'MediaWiki\\Block\\' => __DIR__ . '/block/',
'MediaWiki\\Edit\\' => __DIR__ . '/edit/',
'MediaWiki\\EditPage\\' => __DIR__ . '/editpage/',
'MediaWiki\\Linker\\' => __DIR__ . '/linker/',

View file

@ -22,6 +22,8 @@
use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
use MediaWiki\Block\BlockRestriction;
use MediaWiki\Block\Restriction\Restriction;
use MediaWiki\MediaWikiServices;
class Block {
@ -79,6 +81,12 @@ class Block {
/** @var string|null */
private $systemBlockType;
/** @var bool */
private $isSitewide;
/** @var Restriction[] */
private $restrictions;
# TYPE constants
const TYPE_USER = 1;
const TYPE_IP = 2;
@ -129,6 +137,7 @@ class Block {
'allowUsertalk' => false,
'byText' => '',
'systemBlock' => null,
'sitewide' => true,
];
if ( func_num_args() > 1 || !is_array( $options ) ) {
@ -165,6 +174,7 @@ class Block {
$this->mHideName = (bool)$options['hideName'];
$this->isHardblock( !$options['anonOnly'] );
$this->isAutoblocking( (bool)$options['enableAutoblock'] );
$this->isSitewide( (bool)$options['sitewide'] );
# Prevention measures
$this->prevents( 'sendemail', (bool)$options['blockEmail'] );
@ -236,6 +246,7 @@ class Block {
'ipb_block_email',
'ipb_allow_usertalk',
'ipb_parent_block_id',
'ipb_sitewide',
] + CommentStore::getStore()->getFields( 'ipb_reason' );
}
@ -266,6 +277,7 @@ class Block {
'ipb_block_email',
'ipb_allow_usertalk',
'ipb_parent_block_id',
'ipb_sitewide',
] + $commentQuery['fields'] + $actorQuery['fields'],
'joins' => $commentQuery['joins'] + $actorQuery['joins'],
];
@ -292,6 +304,10 @@ class Block {
&& $this->prevents( 'sendemail' ) == $block->prevents( 'sendemail' )
&& $this->prevents( 'editownusertalk' ) == $block->prevents( 'editownusertalk' )
&& $this->mReason == $block->mReason
&& $this->isSitewide() == $block->isSitewide()
// Block::getRestrictions() may perform a database query, so keep it at
// the end.
&& BlockRestriction::equals( $this->getRestrictions(), $block->getRestrictions() )
);
}
@ -477,6 +493,7 @@ class Block {
$this->isHardblock( !$row->ipb_anon_only );
$this->isAutoblocking( $row->ipb_enable_autoblock );
$this->isSitewide( (bool)$row->ipb_sitewide );
$this->prevents( 'createaccount', $row->ipb_create_account );
$this->prevents( 'sendemail', $row->ipb_block_email );
@ -510,7 +527,11 @@ class Block {
}
$dbw = wfGetDB( DB_MASTER );
BlockRestriction::deleteByParentBlockId( $this->getId() );
$dbw->delete( 'ipblocks', [ 'ipb_parent_block_id' => $this->getId() ], __METHOD__ );
BlockRestriction::deleteByBlockId( $this->getId() );
$dbw->delete( 'ipblocks', [ 'ipb_id' => $this->getId() ], __METHOD__ );
return $dbw->affectedRows() > 0;
@ -546,7 +567,12 @@ class Block {
$dbw->insert( 'ipblocks', $row, __METHOD__, [ 'IGNORE' ] );
$affected = $dbw->affectedRows();
$this->mId = $dbw->insertId();
if ( $affected ) {
$this->setId( $dbw->insertId() );
if ( $this->restrictions ) {
BlockRestriction::insert( $this->restrictions );
}
}
# Don't collide with expired blocks.
# Do this after trying to insert to avoid locking.
@ -564,9 +590,13 @@ class Block {
);
if ( $ids ) {
$dbw->delete( 'ipblocks', [ 'ipb_id' => $ids ], __METHOD__ );
BlockRestriction::deleteByBlockId( $ids );
$dbw->insert( 'ipblocks', $row, __METHOD__, [ 'IGNORE' ] );
$affected = $dbw->affectedRows();
$this->mId = $dbw->insertId();
$this->setId( $dbw->insertId() );
if ( $this->restrictions ) {
BlockRestriction::insert( $this->restrictions );
}
}
}
@ -598,14 +628,24 @@ class Block {
$dbw->startAtomic( __METHOD__ );
$dbw->update(
$result = $dbw->update(
'ipblocks',
$this->getDatabaseArray( $dbw ),
[ 'ipb_id' => $this->getId() ],
__METHOD__
);
$affected = $dbw->affectedRows();
// Only update the restrictions if they have been modified.
if ( $this->restrictions !== null ) {
// An empty array should remove all of the restrictions.
if ( empty( $this->restrictions ) ) {
$success = BlockRestriction::deleteByBlockId( $this->getId() );
} else {
$success = BlockRestriction::update( $this->restrictions );
}
// Update the result. The first false is the result, otherwise, true.
$result = $result && $success;
}
if ( $this->isAutoblocking() ) {
// update corresponding autoblock(s) (T50813)
@ -615,8 +655,14 @@ class Block {
[ 'ipb_parent_block_id' => $this->getId() ],
__METHOD__
);
// Only update the restrictions if they have been modified.
if ( $this->restrictions !== null ) {
BlockRestriction::updateByParentBlockId( $this->getId(), $this->restrictions );
}
} else {
// autoblock no longer required, delete corresponding autoblock(s)
BlockRestriction::deleteByParentBlockId( $this->getId() );
$dbw->delete(
'ipblocks',
[ 'ipb_parent_block_id' => $this->getId() ],
@ -626,12 +672,12 @@ class Block {
$dbw->endAtomic( __METHOD__ );
if ( $affected ) {
if ( $result ) {
$auto_ipd_ids = $this->doRetroactiveAutoblock();
return [ 'id' => $this->mId, 'autoIds' => $auto_ipd_ids ];
}
return false;
return $result;
}
/**
@ -662,7 +708,8 @@ class Block {
'ipb_deleted' => intval( $this->mHideName ), // typecast required for SQLite
'ipb_block_email' => $this->prevents( 'sendemail' ),
'ipb_allow_usertalk' => !$this->prevents( 'editownusertalk' ),
'ipb_parent_block_id' => $this->mParentBlockId
'ipb_parent_block_id' => $this->mParentBlockId,
'ipb_sitewide' => $this->isSitewide(),
] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->mReason )
+ ActorMigration::newMigration()->getInsertValues( $dbw, 'ipb_by', $this->getBlocker() );
@ -865,6 +912,8 @@ class Block {
$autoblock->mHideName = $this->mHideName;
$autoblock->prevents( 'editownusertalk', $this->prevents( 'editownusertalk' ) );
$autoblock->mParentBlockId = $this->mId;
$autoblock->isSitewide( $this->isSitewide() );
$autoblock->setRestrictions( $this->getRestrictions() );
if ( $this->mExpiry == 'infinity' ) {
# Original block was indefinite, start an autoblock now
@ -1014,6 +1063,22 @@ class Block {
return $this->mId;
}
/**
* Set the block ID
*
* @param int $blockId
* @return int
*/
private function setId( $blockId ) {
$this->mId = (int)$blockId;
if ( is_array( $this->restrictions ) ) {
$this->restrictions = BlockRestriction::setBlockId( $blockId, $this->restrictions );
}
return $this;
}
/**
* Get the system block type, if any
* @since 1.29
@ -1061,6 +1126,18 @@ class Block {
: false;
}
/**
* Indicates that the block is a sitewide block. This means the user is
* prohibited from editing any page on the site (other than their own talk
* page).
*
* @param null|bool $x
* @return bool
*/
public function isSitewide( $x = null ) {
return wfSetVar( $this->isSitewide, $x );
}
/**
* Get/set whether the Block prevents a given action
*
@ -1145,6 +1222,7 @@ class Block {
$fname
);
if ( $ids ) {
BlockRestriction::deleteByBlockId( $ids );
$dbw->delete( 'ipblocks', [ 'ipb_id' => $ids ], $fname );
}
}
@ -1648,4 +1726,40 @@ class Block {
$lang->userTimeAndDate( $this->mTimestamp, $context->getUser() ),
];
}
/**
* Get Restrictions.
*
* Getting the restrictions will perform a database query if the restrictions
* are not already loaded.
*
* @return Restriction[]
*/
public function getRestrictions() {
if ( $this->restrictions === null ) {
// If the block id has not been set, then do not attempt to load the
// restrictions.
if ( !$this->mId ) {
return [];
}
$this->restrictions = BlockRestriction::loadByBlockId( $this->mId );
}
return $this->restrictions;
}
/**
* Set Restrictions.
*
* @param Restriction[] $restrictions
*
* @return self
*/
public function setRestrictions( array $restrictions ) {
$this->restrictions = array_filter( $restrictions, function ( $restriction ) {
return $restriction instanceof Restriction;
} );
return $this;
}
}

View file

@ -9027,6 +9027,16 @@ $wgChangeTagsSchemaMigrationStage = MIGRATION_WRITE_BOTH;
*/
$wgTagStatisticsNewTable = false;
/**
* Flag to enable Partial Blocks. This allows an admin to prevent a user from editing specific pages
* or namespaces.
*
* @since 1.32
* @deprecated 1.32
* @var bool
*/
$wgEnablePartialBlocks = false;
/**
* For really cool vim folding this needs to be at the end:
* vim: foldmarker=@{,@} foldmethod=marker

View file

@ -0,0 +1,417 @@
<?php
/**
* Block restriction interface.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\Block;
use MediaWiki\Block\Restriction\PageRestriction;
use MediaWiki\Block\Restriction\Restriction;
use Wikimedia\Rdbms\IResultWrapper;
use Wikimedia\Rdbms\IDatabase;
class BlockRestriction {
/**
* Retrieves the restrictions from the database by block id.
*
* @param int|array $blockId
* @param IDatabase|null $db
* @param array $options Options to pass to the select query.
* @return Restriction[]
*/
public static function loadByBlockId( $blockId, IDatabase $db = null ) {
if ( is_null( $blockId ) || $blockId === [] ) {
return [];
}
$db = $db ?: wfGetDb( DB_REPLICA );
$result = $db->select(
[ 'ipblocks_restrictions', 'page' ],
[ 'ir_ipb_id', 'ir_type', 'ir_value', 'page_namespace', 'page_title' ],
[ 'ir_ipb_id' => $blockId ],
__METHOD__,
[],
[ 'page' => [ 'LEFT JOIN', [ 'ir_type' => PageRestriction::TYPE_ID, 'ir_value=page_id' ] ] ]
);
return self::resultToRestrictions( $result );
}
/**
* Inserts the restrictions into the database.
*
* @param Restriction[] $restrictions
* @return bool
*/
public static function insert( array $restrictions ) {
if ( empty( $restrictions ) ) {
return false;
}
$rows = [];
foreach ( $restrictions as $restriction ) {
if ( !$restriction instanceof Restriction ) {
continue;
}
$rows[] = $restriction->toRow();
}
if ( empty( $rows ) ) {
return false;
}
$dbw = wfGetDB( DB_MASTER );
return $dbw->insert(
'ipblocks_restrictions',
$rows,
__METHOD__,
[ 'IGNORE' ]
);
}
/**
* Updates the list of restrictions. This method does not allow removing all
* of the restrictions. To do that, use ::deleteByBlockId().
*
* @param Restriction[] $restrictions
* @return bool
*/
public static function update( array $restrictions ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->startAtomic( __METHOD__ );
// Organize the restrictions by blockid.
$restrictionList = self::restrictionsByBlockId( $restrictions );
// Load the existing restrictions and organize by block id. Any block ids
// that were passed into this function will be used to load all of the
// existing restrictions. This list might be the same, or may be completely
// different.
$existingList = [];
$blockIds = array_keys( $restrictionList );
if ( !empty( $blockIds ) ) {
$result = $dbw->select(
[ 'ipblocks_restrictions', 'page' ],
[ 'ir_ipb_id', 'ir_type', 'ir_value' ],
[ 'ir_ipb_id' => $blockIds ],
__METHOD__,
[ 'FOR UPDATE' ]
);
$existingList = self::restrictionsByBlockId(
self::resultToRestrictions( $result )
);
}
$result = true;
// Perform the actions on a per block-id basis.
foreach ( $restrictionList as $blockId => $blockRestrictions ) {
// Insert all of the restrictions first, ignoring ones that already exist.
$success = self::insert( $blockRestrictions );
// Update the result. The first false is the result, otherwise, true.
$result = $success && $result;
$restrictionsToRemove = self::restrictionsToRemove(
$existingList[$blockId] ?? [],
$restrictions
);
// Nothing to remove.
if ( empty( $restrictionsToRemove ) ) {
continue;
}
$success = self::delete( $restrictionsToRemove );
// Update the result. The first false is the result, otherwise, true.
$result = $success && $result;
}
$dbw->endAtomic( __METHOD__ );
return $result;
}
/**
* Updates the list of restrictions by parent id.
*
* @param int $parentBlockId
* @param Restriction[] $restrictions
* @return bool
*/
public static function updateByParentBlockId( $parentBlockId, array $restrictions ) {
// If removing all of the restrictions, then just delete them all.
if ( empty( $restrictions ) ) {
return self::deleteByParentBlockId( $parentBlockId );
}
$parentBlockId = (int)$parentBlockId;
$db = wfGetDb( DB_MASTER );
$db->startAtomic( __METHOD__ );
$blockIds = $db->selectFieldValues(
'ipblocks',
'ipb_id',
[ 'ipb_parent_block_id' => $parentBlockId ],
__METHOD__,
[ 'FOR UPDATE' ]
);
$result = true;
foreach ( $blockIds as $id ) {
$success = self::update( self::setBlockId( $id, $restrictions ) );
// Update the result. The first false is the result, otherwise, true.
$result = $success && $result;
}
$db->endAtomic( __METHOD__ );
return $result;
}
/**
* Delete the restrictions.
*
* @param Restriction[]|null $restrictions
* @throws MWException
* @return bool
*/
public static function delete( array $restrictions ) {
$dbw = wfGetDB( DB_MASTER );
$result = true;
foreach ( $restrictions as $restriction ) {
if ( !$restriction instanceof Restriction ) {
continue;
}
$success = $dbw->delete(
'ipblocks_restrictions',
// The restriction row is made up of a compound primary key. Therefore,
// the row and the delete conditions are the same.
$restriction->toRow(),
__METHOD__
);
// Update the result. The first false is the result, otherwise, true.
$result = $success && $result;
}
return $result;
}
/**
* Delete the restrictions by Block ID.
*
* @param int|array $blockId
* @throws MWException
* @return bool
*/
public static function deleteByBlockId( $blockId ) {
$dbw = wfGetDB( DB_MASTER );
return $dbw->delete(
'ipblocks_restrictions',
[ 'ir_ipb_id' => $blockId ],
__METHOD__
);
}
/**
* Delete the restrictions by Parent Block ID.
*
* @param int|array $parentBlockId
* @throws MWException
* @return bool
*/
public static function deleteByParentBlockId( $parentBlockId ) {
$dbw = wfGetDB( DB_MASTER );
return $dbw->deleteJoin(
'ipblocks_restrictions',
'ipblocks',
'ir_ipb_id',
'ipb_id',
[ 'ipb_parent_block_id' => $parentBlockId ],
__METHOD__
);
}
/**
* Checks if two arrays of Restrictions are effectively equal. This is a loose
* equality check as the restrictions do not have to contain the same block
* ids.
*
* @param Restriction[] $a
* @param Restriction[] $b
* @return bool
*/
public static function equals( array $a, array $b ) {
$filter = function ( $restriction ) {
return $restriction instanceof Restriction;
};
// Ensure that every item in the array is a Restriction. This prevents a
// fatal error from calling Restriction::getHash if something in the array
// is not a restriction.
$a = array_filter( $a, $filter );
$b = array_filter( $b, $filter );
$aCount = count( $a );
$bCount = count( $b );
// If the count is different, then they are obviously a different set.
if ( $aCount !== $bCount ) {
return false;
}
// If both sets contain no items, then they are the same set.
if ( $aCount === 0 && $bCount === 0 ) {
return true;
}
$hasher = function ( $r ) {
return $r->getHash();
};
$aHashes = array_map( $hasher, $a );
$bHashes = array_map( $hasher, $b );
sort( $aHashes );
sort( $bHashes );
return $aHashes === $bHashes;
}
/**
* Set the blockId on a set of restrictions and return a new set.
*
* @param int $blockId
* @param Restriction[] $restrictions
* @return Restriction[]
*/
public static function setBlockId( $blockId, array $restrictions ) {
$blockRestrictions = [];
foreach ( $restrictions as $restriction ) {
if ( !$restriction instanceof Restriction ) {
continue;
}
// Clone the restriction so any references to the current restriction are
// not suddenly changed to a different blockId.
$restriction = clone $restriction;
$restriction->setBlockId( $blockId );
$blockRestrictions[] = $restriction;
}
return $blockRestrictions;
}
/**
* Get the restrictions that should be removed, which are existing
* restrictions that are not in the new list of restrictions.
*
* @param Restriction[] $existing
* @param Restriction[] $new
* @return array
*/
private static function restrictionsToRemove( array $existing, array $new ) {
return array_filter( $existing, function ( $e ) use ( $new ) {
foreach ( $new as $restriction ) {
if ( !$restriction instanceof Restriction ) {
continue;
}
if ( $restriction->equals( $e ) ) {
return false;
}
}
return true;
} );
}
/**
* Converts an array of restrictions to an associative array of restrictions
* where the keys are the block ids.
*
* @param Restriction[] $restrictions
* @return array
*/
private static function restrictionsByBlockId( array $restrictions ) {
$blockRestrictions = [];
foreach ( $restrictions as $restriction ) {
// Ensure that all of the items in the array are restrictions.
if ( !$restriction instanceof Restriction ) {
continue;
}
if ( !isset( $blockRestrictions[$restriction->getBlockId()] ) ) {
$blockRestrictions[$restriction->getBlockId()] = [];
}
$blockRestrictions[$restriction->getBlockId()][] = $restriction;
}
return $blockRestrictions;
}
/**
* Convert an Result Wrapper to an array of restrictions.
*
* @param IResultWrapper $result
* @return Restriction[]
*/
private static function resultToRestrictions( IResultWrapper $result ) {
$restrictions = [];
foreach ( $result as $row ) {
$restriction = self::rowToRestriction( $row );
if ( !$restriction ) {
continue;
}
$restrictions[] = $restriction;
}
return $restrictions;
}
/**
* Convert a result row from the database into a restriction object.
*
* @param \stdClass $row
* @return Restriction|null
*/
private static function rowToRestriction( \stdClass $row ) {
switch ( $row->ir_type ) {
case PageRestriction::TYPE_ID:
return PageRestriction::newFromRow( $row );
default:
return null;
}
}
}

View file

@ -0,0 +1,102 @@
<?php
/**
* Abstract block restriction.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\Block\Restriction;
abstract class AbstractRestriction implements Restriction {
/**
* @var int
*/
protected $blockId;
/**
* @var int
*/
protected $value;
/**
* Create Restriction.
*
* @param int $blockId
* @param int $value
*/
public function __construct( $blockId, $value ) {
$this->blockId = (int)$blockId;
$this->value = (int)$value;
}
/**
* {@inheritdoc}
*/
public function getBlockId() {
return $this->blockId;
}
/**
* {@inheritdoc}
*/
public function setBlockId( $blockId ) {
$this->blockId = (int)$blockId;
return $this;
}
/**
* {@inheritdoc}
*/
public function getValue() {
return $this->value;
}
/**
* {@inheritdoc}
*/
public static function newFromRow( \stdClass $row ) {
return new static( $row->ir_ipb_id, $row->ir_value );
}
/**
* {@inheritdoc}
*/
public function toRow() {
return [
'ir_ipb_id' => $this->getBlockId(),
'ir_type' => $this->getTypeId(),
'ir_value' => $this->getValue(),
];
}
/**
* {@inheritdoc}
*/
public function equals( Restriction $other ) {
return $this->getHash() === $other->getHash();
}
/**
* {@inheritdoc}
*/
public function getHash() {
return $this->getType() . '-' . $this->getValue();
}
}

View file

@ -0,0 +1,99 @@
<?php
/**
* A Block restriction object of type 'Page'.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\Block\Restriction;
class PageRestriction extends AbstractRestriction {
const TYPE = 'page';
const TYPE_ID = 1;
/**
* @var \Title
*/
protected $title;
/**
* {@inheritdoc}
*/
public function matches( \Title $title ) {
return $title->equals( $this->getTitle() );
}
/**
* {@inheritdoc}
*/
public function getType() {
return self::TYPE;
}
/**
* {@inheritdoc}
*/
public function getTypeId() {
return self::TYPE_ID;
}
/**
* Set the title.
*
* @param \Title $title
* @return self
*/
public function setTitle( \Title $title ) {
$this->title = $title;
return $this;
}
/**
* Get Title.
*
* @return \Title|null
*/
public function getTitle() {
if ( !$this->title ) {
$this->title = \Title::newFromID( $this->value );
}
return $this->title;
}
/**
* {@inheritdoc}
*/
public static function newFromRow( \stdClass $row ) {
$restriction = parent::newFromRow( $row );
// If the page_namespace and the page_title were provided, add the title to
// the restriction.
if ( isset( $row->page_namespace ) && isset( $row->page_title ) ) {
// Clone the row so it is not mutated.
$row = clone $row;
$row->page_id = $row->ir_value;
$title = \Title::newFromRow( $row );
$restriction->setTitle( $title );
}
return $restriction;
}
}

View file

@ -0,0 +1,100 @@
<?php
/**
* Block restriction interface.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace MediaWiki\Block\Restriction;
interface Restriction {
/**
* Gets the id of the block.
*
* @return int
*/
public function getBlockId();
/**
* Sets the id of the block.
*
* @param int $blockId
* @return self
*/
public function setBlockId( $blockId );
/**
* Gets the value of the restriction.
*
* @return int
*/
public function getValue();
/**
* Gets the type of restriction
*
* @return string
*/
public function getType();
/**
* Gets the id of the type of restriction. This id is used in the database.
*
* @return string
*/
public function getTypeId();
/**
* Creates a new Restriction from a database row.
*
* @return self
*/
public static function newFromRow( \stdClass $row );
/**
* Convert a restriction object into a row array for insertion.
*
* @return array
*/
public function toRow();
/**
* Determine if a restriction matches a given title.
*
* @param \Title $title
* @return bool
*/
public function matches( \Title $title );
/**
* Determine if a restriction equals another restriction.
*
* @param Restriction $other
* @return bool
*/
public function equals( Restriction $other );
/**
* Create a unique hash of the block restriction based on the type and value.
*
* @return string
*/
public function getHash();
}

View file

@ -783,6 +783,7 @@ class SpecialBlock extends FormSpecialPage {
$currentBlock->prevents( 'sendemail', $block->prevents( 'sendemail' ) );
$currentBlock->prevents( 'editownusertalk', $block->prevents( 'editownusertalk' ) );
$currentBlock->mReason = $block->mReason;
$currentBlock->isSitewide( $block->isSitewide() );
$status = $currentBlock->update();

View file

@ -93,6 +93,9 @@ $wgAutoloadClasses += [
'MediaWiki\\Auth\\AuthenticationRequestTestCase' =>
"$testDir/phpunit/includes/auth/AuthenticationRequestTestCase.php",
# tests/phpunit/includes/block
'MediaWiki\\Tests\\Block\\Restriction\\RestrictionTestCase' => "$testDir/phpunit/includes/block/Restriction/RestrictionTestCase.php",
# tests/phpunit/includes/changes
'TestRecentChangesHelper' => "$testDir/phpunit/includes/changes/TestRecentChangesHelper.php",

View file

@ -1,5 +1,7 @@
<?php
use MediaWiki\Block\Restriction\PageRestriction;
/**
* @group Database
* @group Blocking
@ -460,4 +462,152 @@ class BlockTest extends MediaWikiLangTestCase {
}
}
/**
* @covers Block::newFromRow
*/
public function testNewFromRow() {
$badActor = $this->getTestUser()->getUser();
$sysop = $this->getTestSysop()->getUser();
$block = new Block( [
'address' => $badActor->getName(),
'user' => $badActor->getId(),
'by' => $sysop->getId(),
'expiry' => 'infinity',
] );
$block->insert();
$blockQuery = Block::getQueryInfo();
$row = $this->db->select(
$blockQuery['tables'],
$blockQuery['fields'],
[
'ipb_id' => $block->getId(),
],
__METHOD__,
[],
$blockQuery['joins']
)->fetchObject();
$block = Block::newFromRow( $row );
$this->assertInstanceOf( Block::class, $block );
$this->assertEquals( $block->getBy(), $sysop->getId() );
$this->assertEquals( $block->getTarget()->getName(), $badActor->getName() );
$block->delete();
}
/**
* @covers Block::equals
*/
public function testEquals() {
$block = new Block();
$this->assertTrue( $block->equals( $block ) );
$partial = new Block( [
'sitewide' => false,
] );
$this->assertFalse( $block->equals( $partial ) );
}
/**
* @covers Block::isSitewide
*/
public function testIsSitewide() {
$block = new Block();
$this->assertTrue( $block->isSitewide() );
$block = new Block( [
'sitewide' => true,
] );
$this->assertTrue( $block->isSitewide() );
$block = new Block( [
'sitewide' => false,
] );
$this->assertFalse( $block->isSitewide() );
$block = new Block( [
'sitewide' => false,
] );
$block->isSitewide( true );
$this->assertTrue( $block->isSitewide() );
}
/**
* @covers Block::getRestrictions
* @covers Block::setRestrictions
*/
public function testRestrictions() {
$block = new Block();
$restrictions = [
new PageRestriction( 0, 1 )
];
$block->setRestrictions( $restrictions );
$this->assertSame( $restrictions, $block->getRestrictions() );
}
/**
* @covers Block::getRestrictions
* @covers Block::insert
*/
public function testRestrictionsFromDatabase() {
$badActor = $this->getTestUser()->getUser();
$sysop = $this->getTestSysop()->getUser();
$block = new Block( [
'address' => $badActor->getName(),
'user' => $badActor->getId(),
'by' => $sysop->getId(),
'expiry' => 'infinity',
] );
$page = $this->getExistingTestPage( 'Foo' );
$restriction = new PageRestriction( 0, $page->getId() );
$block->setRestrictions( [ $restriction ] );
$block->insert();
// Refresh the block from the database.
$block = Block::newFromID( $block->getId() );
$restrictions = $block->getRestrictions();
$this->assertCount( 1, $restrictions );
$this->assertTrue( $restriction->equals( $restrictions[0] ) );
$block->delete();
}
/**
* @covers Block::insert
*/
public function testInsertExistingBlock() {
$badActor = $this->getTestUser()->getUser();
$sysop = $this->getTestSysop()->getUser();
$block = new Block( [
'address' => $badActor->getName(),
'user' => $badActor->getId(),
'by' => $sysop->getId(),
'expiry' => 'infinity',
] );
$page = $this->getExistingTestPage( 'Foo' );
$restriction = new PageRestriction( 0, $page->getId() );
$block->setRestrictions( [ $restriction ] );
$block->insert();
// Insert the block again, which should result in a failur
$result = $block->insert();
$this->assertFalse( $result );
// Ensure that there are no restrictions where the blockId is 0.
$count = $this->db->selectRowCount(
'ipblocks_restrictions',
'*',
[ 'ir_ipb_id' => 0 ],
__METHOD__
);
$this->assertSame( 0, $count );
$block->delete();
}
}

View file

@ -0,0 +1,556 @@
<?php
namespace MediaWiki\Tests\Block;
use MediaWiki\Block\BlockRestriction;
use MediaWiki\Block\Restriction\PageRestriction;
use MediaWiki\Block\Restriction\Restriction;
/**
* @group Database
* @group Blocking
* @coversDefaultClass \MediaWiki\Block\BlockRestriction
*/
class BlockRestrictionTest extends \MediaWikiLangTestCase {
public function tearDown() {
parent::tearDown();
$this->resetTables();
}
/**
* @covers ::loadByBlockId
* @covers ::resultToRestrictions
* @covers ::rowToRestriction
*/
public function testLoadMultipleRestrictions() {
$block = $this->insertBlock();
$pageFoo = $this->getExistingTestPage( 'Foo' );
$pageBar = $this->getExistingTestPage( 'Bar' );
BlockRestriction::insert( [
new PageRestriction( $block->getId(), $pageFoo->getId() ),
new PageRestriction( $block->getId(), $pageBar->getId() ),
] );
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 2, $restrictions );
}
/**
* @covers ::loadByBlockId
* @covers ::resultToRestrictions
* @covers ::rowToRestriction
*/
public function testWithNoRestrictions() {
$block = $this->insertBlock();
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertEmpty( $restrictions );
}
/**
* @covers ::loadByBlockId
* @covers ::resultToRestrictions
* @covers ::rowToRestriction
*/
public function testWithEmptyParam() {
$restrictions = BlockRestriction::loadByBlockId( [] );
$this->assertEmpty( $restrictions );
}
/**
* @covers ::loadByBlockId
* @covers ::resultToRestrictions
* @covers ::rowToRestriction
*/
public function testIgnoreNotSupportedTypes() {
$block = $this->insertBlock();
$pageFoo = $this->getExistingTestPage( 'Foo' );
$pageBar = $this->getExistingTestPage( 'Bar' );
// valid type
$this->insertRestriction( $block->getId(), PageRestriction::TYPE_ID, $pageFoo->getId() );
// invalid type
$this->insertRestriction( $block->getId(), 9, $pageBar->getId() );
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
}
/**
* @covers ::loadByBlockId
* @covers ::resultToRestrictions
* @covers ::rowToRestriction
*/
public function testMappingRestrictionObject() {
$block = $this->insertBlock();
$title = 'Lady Macbeth';
$page = $this->getExistingTestPage( $title );
BlockRestriction::insert( [
new PageRestriction( $block->getId(), $page->getId() ),
] );
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
list( $pageRestriction ) = $restrictions;
$this->assertInstanceOf( PageRestriction::class, $pageRestriction );
$this->assertEquals( $block->getId(), $pageRestriction->getBlockId() );
$this->assertEquals( $page->getId(), $pageRestriction->getValue() );
$this->assertEquals( $pageRestriction->getType(), PageRestriction::TYPE );
$this->assertEquals( $pageRestriction->getTitle()->getText(), $title );
}
/**
* @covers ::insert
*/
public function testInsert() {
$block = $this->insertBlock();
$pageFoo = $this->getExistingTestPage( 'Foo' );
$pageBar = $this->getExistingTestPage( 'Bar' );
$restrictions = [
new \stdClass(),
new PageRestriction( $block->getId(), $pageFoo->getId() ),
new PageRestriction( $block->getId(), $pageBar->getId() ),
];
$result = BlockRestriction::insert( $restrictions );
$this->assertTrue( $result );
$restrictions = [
new \stdClass(),
];
$result = BlockRestriction::insert( $restrictions );
$this->assertFalse( $result );
$result = BlockRestriction::insert( [] );
$this->assertFalse( $result );
}
/**
* @covers ::insert
*/
public function testInsertTypes() {
$block = $this->insertBlock();
$pageFoo = $this->getExistingTestPage( 'Foo' );
$pageBar = $this->getExistingTestPage( 'Bar' );
$namespace = $this->createMock( Restriction::class );
$namespace->method( 'toRow' )
->willReturn( [
'ir_ipb_id' => $block->getId(),
'ir_type' => 2,
'ir_value' => 0,
] );
$invalid = $this->createMock( Restriction::class );
$invalid->method( 'toRow' )
->willReturn( [
'ir_ipb_id' => $block->getId(),
'ir_type' => 9,
'ir_value' => 42,
] );
$restrictions = [
new \stdClass(),
new PageRestriction( $block->getId(), $pageFoo->getId() ),
new PageRestriction( $block->getId(), $pageBar->getId() ),
$namespace,
$invalid,
];
$result = BlockRestriction::insert( $restrictions );
$this->assertTrue( $result );
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 2, $restrictions );
}
/**
* @covers ::update
* @covers ::restrictionsByBlockId
* @covers ::restrictionsToRemove
*/
public function testUpdateInsert() {
$block = $this->insertBlock();
$pageFoo = $this->getExistingTestPage( 'Foo' );
$pageBar = $this->getExistingTestPage( 'Bar' );
BlockRestriction::insert( [
new PageRestriction( $block->getId(), $pageFoo->getId() ),
] );
BlockRestriction::update( [
new \stdClass(),
new PageRestriction( $block->getId(), $pageBar->getId() ),
] );
$db = wfGetDb( DB_REPLICA );
$result = $db->select(
[ 'ipblocks_restrictions' ],
[ '*' ],
[ 'ir_ipb_id' => $block->getId() ]
);
$this->assertEquals( 1, $result->numRows() );
$row = $result->fetchObject();
$this->assertEquals( $block->getId(), $row->ir_ipb_id );
$this->assertEquals( $pageBar->getId(), $row->ir_value );
}
/**
* @covers ::update
* @covers ::restrictionsByBlockId
* @covers ::restrictionsToRemove
*/
public function testUpdateChange() {
$block = $this->insertBlock();
$page = $this->getExistingTestPage( 'Foo' );
BlockRestriction::update( [
new PageRestriction( $block->getId(), $page->getId() ),
] );
$db = wfGetDb( DB_REPLICA );
$result = $db->select(
[ 'ipblocks_restrictions' ],
[ '*' ],
[ 'ir_ipb_id' => $block->getId() ]
);
$this->assertEquals( 1, $result->numRows() );
$row = $result->fetchObject();
$this->assertEquals( $block->getId(), $row->ir_ipb_id );
$this->assertEquals( $page->getId(), $row->ir_value );
}
/**
* @covers ::update
* @covers ::restrictionsByBlockId
* @covers ::restrictionsToRemove
*/
public function testUpdateNoRestrictions() {
$block = $this->insertBlock();
BlockRestriction::update( [] );
$db = wfGetDb( DB_REPLICA );
$result = $db->select(
[ 'ipblocks_restrictions' ],
[ '*' ],
[ 'ir_ipb_id' => $block->getId() ]
);
$this->assertEquals( 0, $result->numRows() );
}
/**
* @covers ::update
* @covers ::restrictionsByBlockId
* @covers ::restrictionsToRemove
*/
public function testUpdateSame() {
$block = $this->insertBlock();
$page = $this->getExistingTestPage( 'Foo' );
BlockRestriction::insert( [
new PageRestriction( $block->getId(), $page->getId() ),
] );
BlockRestriction::update( [
new PageRestriction( $block->getId(), $page->getId() ),
] );
$db = wfGetDb( DB_REPLICA );
$result = $db->select(
[ 'ipblocks_restrictions' ],
[ '*' ],
[ 'ir_ipb_id' => $block->getId() ]
);
$this->assertEquals( 1, $result->numRows() );
$row = $result->fetchObject();
$this->assertEquals( $block->getId(), $row->ir_ipb_id );
$this->assertEquals( $page->getId(), $row->ir_value );
}
/**
* @covers ::updateByParentBlockId
*/
public function testDeleteAllUpdateByParentBlockId() {
// Create a block and an autoblock (a child block)
$block = $this->insertBlock();
$pageFoo = $this->getExistingTestPage( 'Foo' );
$pageBar = $this->getExistingTestPage( 'Bar' );
BlockRestriction::insert( [
new PageRestriction( $block->getId(), $pageFoo->getId() ),
] );
$autoblockId = $block->doAutoblock( '127.0.0.1' );
// Ensure that the restrictions on the block have not changed.
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
$this->assertEquals( $pageFoo->getId(), $restrictions[0]->getValue() );
// Ensure that the restrictions on the autoblock are the same as the block.
$restrictions = BlockRestriction::loadByBlockId( $autoblockId );
$this->assertCount( 1, $restrictions );
$this->assertEquals( $pageFoo->getId(), $restrictions[0]->getValue() );
// Update the restrictions on the autoblock (but leave the block unchanged)
BlockRestriction::updateByParentBlockId( $block->getId(), [
new PageRestriction( $block->getId(), $pageBar->getId() ),
] );
// Ensure that the restrictions on the block have not changed.
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
$this->assertEquals( $pageFoo->getId(), $restrictions[0]->getValue() );
// Ensure that the restrictions on the autoblock have been updated.
$restrictions = BlockRestriction::loadByBlockId( $autoblockId );
$this->assertCount( 1, $restrictions );
$this->assertEquals( $pageBar->getId(), $restrictions[0]->getValue() );
}
/**
* @covers ::updateByParentBlockId
*/
public function testUpdateByParentBlockId() {
// Create a block and an autoblock (a child block)
$block = $this->insertBlock();
$page = $this->getExistingTestPage( 'Foo' );
BlockRestriction::insert( [
new PageRestriction( $block->getId(), $page->getId() ),
] );
$autoblockId = $block->doAutoblock( '127.0.0.1' );
// Ensure that the restrictions on the block have not changed.
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
// Ensure that the restrictions on the autoblock have not changed.
$restrictions = BlockRestriction::loadByBlockId( $autoblockId );
$this->assertCount( 1, $restrictions );
// Remove the restrictions on the autoblock (but leave the block unchanged)
BlockRestriction::updateByParentBlockId( $block->getId(), [] );
// Ensure that the restrictions on the block have not changed.
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
// Ensure that the restrictions on the autoblock have been updated.
$restrictions = BlockRestriction::loadByBlockId( $autoblockId );
$this->assertCount( 0, $restrictions );
}
/**
* @covers ::updateByParentBlockId
*/
public function testNoAutoblocksUpdateByParentBlockId() {
// Create a block with no autoblock.
$block = $this->insertBlock();
$page = $this->getExistingTestPage( 'Foo' );
BlockRestriction::insert( [
new PageRestriction( $block->getId(), $page->getId() ),
] );
// Ensure that the restrictions on the block have not changed.
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
// Update the restrictions on any autoblocks (there are none).
BlockRestriction::updateByParentBlockId( $block->getId(), $restrictions );
// Ensure that the restrictions on the block have not changed.
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
}
/**
* @covers ::delete
*/
public function testDelete() {
$block = $this->insertBlock();
$page = $this->getExistingTestPage( 'Foo' );
BlockRestriction::insert( [
new PageRestriction( $block->getId(), $page->getId() ),
] );
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
$result = BlockRestriction::delete( array_merge( $restrictions, [ new \stdClass() ] ) );
$this->assertTrue( $result );
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 0, $restrictions );
}
/**
* @covers ::deleteByBlockId
*/
public function testDeleteByBlockId() {
$block = $this->insertBlock();
$page = $this->getExistingTestPage( 'Foo' );
BlockRestriction::insert( [
new PageRestriction( $block->getId(), $page->getId() ),
] );
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
$result = BlockRestriction::deleteByBlockId( $block->getId() );
$this->assertNotFalse( $result );
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 0, $restrictions );
}
/**
* @covers ::deleteByParentBlockId
*/
public function testDeleteByParentBlockId() {
// Create a block with no autoblock.
$block = $this->insertBlock();
$page = $this->getExistingTestPage( 'Foo' );
BlockRestriction::insert( [
new PageRestriction( $block->getId(), $page->getId() ),
] );
$autoblockId = $block->doAutoblock( '127.0.0.1' );
// Ensure that the restrictions on the block have not changed.
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
// Ensure that the restrictions on the autoblock are the same as the block.
$restrictions = BlockRestriction::loadByBlockId( $autoblockId );
$this->assertCount( 1, $restrictions );
// Remove all of the restrictions on the autoblock (but leave the block unchanged).
$result = BlockRestriction::deleteByParentBlockId( $block->getId() );
// NOTE: commented out until https://gerrit.wikimedia.org/r/c/mediawiki/core/+/469324 is merged
//$this->assertTrue( $result );
// Ensure that the restrictions on the block have not changed.
$restrictions = BlockRestriction::loadByBlockId( $block->getId() );
$this->assertCount( 1, $restrictions );
// Ensure that the restrictions on the autoblock have been removed.
$restrictions = BlockRestriction::loadByBlockId( $autoblockId );
$this->assertCount( 0, $restrictions );
}
/**
* @covers ::equals
* @dataProvider equalsDataProvider
*
* @param array $a
* @param array $b
* @param bool $expected
*/
public function testEquals( array $a, array $b, $expected ) {
$this->assertSame( $expected, BlockRestriction::equals( $a, $b ) );
}
public function equalsDataProvider() {
return [
[
[
new \stdClass(),
new PageRestriction( 1, 1 ),
],
[
new \stdClass(),
new PageRestriction( 1, 2 )
],
false,
],
[
[
new PageRestriction( 1, 1 ),
],
[
new PageRestriction( 1, 1 ),
new PageRestriction( 1, 2 )
],
false,
],
[
[],
[],
true,
],
[
[
new PageRestriction( 1, 1 ),
new PageRestriction( 1, 2 ),
new PageRestriction( 2, 3 ),
],
[
new PageRestriction( 2, 3 ),
new PageRestriction( 1, 2 ),
new PageRestriction( 1, 1 ),
],
true
],
];
}
/**
* @covers ::setBlockId
*/
public function testSetBlockId() {
$restrictions = [
new \stdClass(),
new PageRestriction( 1, 1 ),
new PageRestriction( 1, 2 ),
];
$result = BlockRestriction::setBlockId( 2, $restrictions );
$this->assertSame( 1, $restrictions[1]->getBlockId() );
$this->assertSame( 1, $restrictions[2]->getBlockId() );
$this->assertSame( 2, $result[0]->getBlockId() );
$this->assertSame( 2, $result[1]->getBlockId() );
}
protected function insertBlock() {
$badActor = $this->getTestUser()->getUser();
$sysop = $this->getTestSysop()->getUser();
$block = new \Block( [
'address' => $badActor->getName(),
'user' => $badActor->getId(),
'by' => $sysop->getId(),
'expiry' => 'infinity',
'sitewide' => 0,
'enableAutoblock' => true,
] );
$block->insert();
return $block;
}
protected function insertRestriction( $blockId, $type, $value ) {
$this->db->insert( 'ipblocks_restrictions', [
'ir_ipb_id' => $blockId,
'ir_type' => $type,
'ir_value' => $value,
] );
}
protected function resetTables() {
$this->db->delete( 'ipblocks', '*', __METHOD__ );
$this->db->delete( 'ipblocks_restrictions', '*', __METHOD__ );
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace MediaWiki\Tests\Block\Restriction;
use MediaWiki\Block\Restriction\PageRestriction;
/**
* @group Database
* @group Blocking
* @covers \MediaWiki\Block\Restriction\AbstractRestriction
* @covers \MediaWiki\Block\Restriction\PageRestriction
*/
class PageRestrictionTest extends RestrictionTestCase {
public function testMatches() {
$class = $this->getClass();
$page = $this->getExistingTestPage( 'Saturn' );
$restriction = new $class( 1, $page->getId() );
$this->assertTrue( $restriction->matches( $page->getTitle() ) );
$page = $this->getExistingTestPage( 'Mars' );
$this->assertFalse( $restriction->matches( $page->getTitle() ) );
}
public function testGetType() {
$class = $this->getClass();
$restriction = new $class( 1, 2 );
$this->assertEquals( 'page', $restriction->getType() );
}
public function testGetTitle() {
$class = $this->getClass();
$restriction = new $class( 1, 2 );
$title = \Title::newFromText( 'Pluto' );
$title->mArticleID = 2;
$restriction->setTitle( $title );
$this->assertSame( $title, $restriction->getTitle() );
$restriction = new $class( 1, 1 );
$title = \Title::newFromId( 1 );
$this->assertEquals( $title->getArticleId(), $restriction->getTitle()->getArticleId() );
}
public function testNewFromRow() {
$class = $this->getClass();
$restriction = $class::newFromRow( (object)[
'ir_ipb_id' => 1,
'ir_value' => 2,
'page_namespace' => 0,
'page_title' => 'Saturn',
] );
$this->assertSame( 1, $restriction->getBlockId() );
$this->assertSame( 2, $restriction->getValue() );
$this->assertSame( 'Saturn', $restriction->getTitle()->getText() );
}
/**
* {@inheritdoc}
*/
protected function getClass() {
return PageRestriction::class;
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace MediaWiki\Tests\Block\Restriction;
/**
* @group Blocking
*/
abstract class RestrictionTestCase extends \MediaWikiTestCase {
public function testConstruct() {
$class = $this->getClass();
$restriction = new $class( 1, 2 );
$this->assertSame( $restriction->getBlockId(), 1 );
$this->assertSame( $restriction->getValue(), 2 );
}
public function testSetBlockId() {
$class = $this->getClass();
$restriction = new $class( 1, 2 );
$restriction->setBlockId( 10 );
$this->assertSame( $restriction->getBlockId(), 10 );
}
public function testEquals() {
$class = $this->getClass();
// Test two restrictions with the same data.
$restriction = new $class( 1, 2 );
$second = new $class( 1, 2 );
$this->assertTrue( $restriction->equals( $second ) );
// Test two restrictions that implement different classes.
$second = $this->createMock( $this->getClass() );
$this->assertFalse( $restriction->equals( $second ) );
// Not the same block id.
$second = new $class( 2, 2 );
$this->assertTrue( $restriction->equals( $second ) );
// Not the same value.
$second = new $class( 1, 3 );
$this->assertFalse( $restriction->equals( $second ) );
}
public function testNewFromRow() {
$class = $this->getClass();
$restriction = $class::newFromRow( (object)[
'ir_ipb_id' => 1,
'ir_value' => 2,
] );
$this->assertSame( 1, $restriction->getBlockId() );
$this->assertSame( 2, $restriction->getValue() );
}
public function testToRow() {
$class = $this->getClass();
$restriction = new $class( 1, 2 );
$row = $restriction->toRow();
$this->assertSame( 1, $row['ir_ipb_id'] );
$this->assertSame( 2, $row['ir_value'] );
}
/**
* Get the class name of the class that is being tested.
*
* @return string
*/
abstract protected function getClass();
}