Rename Block to MediaWiki\Block\DatabaseBlock
authorThalia <thalia.e.chan@googlemail.com>
Mon, 13 May 2019 14:18:07 +0000 (15:18 +0100)
committerThalia <thalia.e.chan@googlemail.com>
Tue, 28 May 2019 11:20:48 +0000 (12:20 +0100)
Keep Block as a deprecated class alias for DatabaseBlock.
Update calls to the Block constructor and Block static
methods from external classes.

Also update documentation in several places that refer to
blocks as Blocks.

Bug: T222737
Change-Id: I6d96b63ca0a84bee19486471e0a16a53a79d768a

49 files changed:
RELEASE-NOTES-1.34
autoload.php
includes/Block.php [deleted file]
includes/EditPage.php
includes/api/ApiBase.php
includes/api/ApiBlock.php
includes/api/ApiUnblock.php
includes/auth/AuthManager.php
includes/auth/CheckBlocksSecondaryAuthenticationProvider.php
includes/block/AbstractBlock.php
includes/block/BlockManager.php
includes/block/BlockRestrictionStore.php
includes/block/DatabaseBlock.php [new file with mode: 0644]
includes/block/Restriction/NamespaceRestriction.php
includes/block/Restriction/PageRestriction.php
includes/page/Article.php
includes/specials/SpecialBlock.php
includes/specials/SpecialBlockList.php
includes/specials/SpecialContributions.php
includes/specials/SpecialDeletedContributions.php
includes/specials/SpecialUnblock.php
includes/specials/pagers/BlockListPager.php
includes/user/User.php
maintenance/cleanupBlocks.php
tests/phpunit/includes/BlockTest.php [deleted file]
tests/phpunit/includes/Permissions/PermissionManagerTest.php
tests/phpunit/includes/TitlePermissionTest.php
tests/phpunit/includes/actions/ActionTest.php
tests/phpunit/includes/api/ApiBaseTest.php
tests/phpunit/includes/api/ApiBlockInfoTraitTest.php
tests/phpunit/includes/api/ApiBlockTest.php
tests/phpunit/includes/api/ApiEditPageTest.php
tests/phpunit/includes/api/ApiMoveTest.php
tests/phpunit/includes/api/ApiQueryBlocksTest.php
tests/phpunit/includes/api/ApiQueryInfoTest.php
tests/phpunit/includes/api/ApiQueryUserInfoTest.php
tests/phpunit/includes/api/ApiUnblockTest.php
tests/phpunit/includes/api/ApiUserrightsTest.php
tests/phpunit/includes/auth/AuthManagerTest.php
tests/phpunit/includes/auth/CheckBlocksSecondaryAuthenticationProviderTest.php
tests/phpunit/includes/block/BlockManagerTest.php
tests/phpunit/includes/block/BlockRestrictionStoreTest.php
tests/phpunit/includes/block/DatabaseBlockTest.php [new file with mode: 0644]
tests/phpunit/includes/specialpage/FormSpecialPageTestCase.php
tests/phpunit/includes/specials/SpecialBlockTest.php
tests/phpunit/includes/specials/pagers/BlockListPagerTest.php
tests/phpunit/includes/user/LocalIdLookupTest.php
tests/phpunit/includes/user/PasswordResetTest.php
tests/phpunit/includes/user/UserTest.php

index 7ad34fe..0a9db22 100644 (file)
@@ -222,9 +222,10 @@ because of Phabricator reports.
 * The getSubjectPage, getTalkPage, and getOtherPage of Title are deprecated.
   Use NamespaceInfo's getSubjectPage, getTalkPage, and getAssociatedPage.
 * MWMessagePack class, no longer used, has been deprecated in 1.34.
-* The Block class is separated into Block (for blocks stored in the database),
-  and SystemBlock (for temporary blocks created by the system). SystemBlock
-  should be used when creating any temporary blocks.
+* The Block class is separated into DatabaseBlock (for blocks stored in the
+  database), and SystemBlock (for temporary blocks created by the system).
+  SystemBlock should be used when creating any temporary blocks. Block is
+  a deprecated alias for DatabaseBlock.
 * Parser::$mConf is deprecated. It will be removed entirely in a later version.
   Some context can be found at T224165.
 * Constructing Parser directly is deprecated. Obtain one from ParserFactory.
index 486239d..a289aaf 100644 (file)
@@ -208,7 +208,7 @@ $wgAutoloadLocalClasses = [
        'BitmapHandler_ClientOnly' => __DIR__ . '/includes/media/BitmapHandler_ClientOnly.php',
        'BitmapMetadataHandler' => __DIR__ . '/includes/media/BitmapMetadataHandler.php',
        'Blob' => __DIR__ . '/includes/libs/rdbms/encasing/Blob.php',
-       'Block' => __DIR__ . '/includes/Block.php',
+       'Block' => __DIR__ . '/includes/block/DatabaseBlock.php',
        'BlockLevelPass' => __DIR__ . '/includes/parser/BlockLevelPass.php',
        'BlockListPager' => __DIR__ . '/includes/specials/pagers/BlockListPager.php',
        'BlockLogFormatter' => __DIR__ . '/includes/logging/BlockLogFormatter.php',
diff --git a/includes/Block.php b/includes/Block.php
deleted file mode 100644 (file)
index 0f5e8dd..0000000
+++ /dev/null
@@ -1,1572 +0,0 @@
-<?php
-/**
- * Class for blocks stored in the database.
- *
- * 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
- */
-
-use Wikimedia\Rdbms\Database;
-use Wikimedia\Rdbms\IDatabase;
-use MediaWiki\Block\AbstractBlock;
-use MediaWiki\Block\BlockRestrictionStore;
-use MediaWiki\Block\Restriction\Restriction;
-use MediaWiki\Block\Restriction\NamespaceRestriction;
-use MediaWiki\Block\Restriction\PageRestriction;
-use MediaWiki\MediaWikiServices;
-
-/**
- * Blocks (as opposed to system blocks) are stored in the database, may
- * give rise to autoblocks and may be tracked with cookies. Blocks are
- * more customizable than system blocks: they may be hardblocks, and
- * they may be sitewide or partial.
- */
-class Block extends AbstractBlock {
-       /** @var bool */
-       public $mAuto;
-
-       /** @var int */
-       public $mParentBlockId;
-
-       /** @var int */
-       private $mId;
-
-       /** @var bool */
-       private $mFromMaster;
-
-       /** @var int Hack for foreign blocking (CentralAuth) */
-       private $forcedTargetID;
-
-       /** @var bool */
-       private $isHardblock;
-
-       /** @var bool */
-       private $isAutoblocking;
-
-       /** @var Restriction[] */
-       private $restrictions;
-
-       /**
-        * Create a new block with specified option parameters on a user, IP or IP range.
-        *
-        * @param array $options Parameters of the block:
-        *     user int             Override target user ID (for foreign users)
-        *     auto bool            Is this an automatic block?
-        *     expiry string        Timestamp of expiration of the block or 'infinity'
-        *     anonOnly bool        Only disallow anonymous actions
-        *     createAccount bool   Disallow creation of new accounts
-        *     enableAutoblock bool Enable automatic blocking
-        *     hideName bool        Hide the target user name
-        *     blockEmail bool      Disallow sending emails
-        *     allowUsertalk bool   Allow the target to edit its own talk page
-        *     sitewide bool        Disallow editing all pages and all contribution
-        *                          actions, except those specifically allowed by
-        *                          other block flags
-        *
-        * @since 1.26 $options array
-        */
-       public function __construct( array $options = [] ) {
-               parent::__construct( $options );
-
-               $defaults = [
-                       'user'            => null,
-                       'auto'            => false,
-                       'expiry'          => '',
-                       'anonOnly'        => false,
-                       'createAccount'   => false,
-                       'enableAutoblock' => false,
-                       'hideName'        => false,
-                       'blockEmail'      => false,
-                       'allowUsertalk'   => false,
-                       'sitewide'        => true,
-               ];
-
-               $options += $defaults;
-
-               if ( $this->target instanceof User && $options['user'] ) {
-                       # Needed for foreign users
-                       $this->forcedTargetID = $options['user'];
-               }
-
-               $this->setExpiry( wfGetDB( DB_REPLICA )->decodeExpiry( $options['expiry'] ) );
-
-               # Boolean settings
-               $this->mAuto = (bool)$options['auto'];
-               $this->setHideName( (bool)$options['hideName'] );
-               $this->isHardblock( !$options['anonOnly'] );
-               $this->isAutoblocking( (bool)$options['enableAutoblock'] );
-               $this->isSitewide( (bool)$options['sitewide'] );
-               $this->isEmailBlocked( (bool)$options['blockEmail'] );
-               $this->isCreateAccountBlocked( (bool)$options['createAccount'] );
-               $this->isUsertalkEditAllowed( (bool)$options['allowUsertalk'] );
-
-               $this->mFromMaster = false;
-       }
-
-       /**
-        * Load a block from the block id.
-        *
-        * @param int $id Block id to search for
-        * @return Block|null
-        */
-       public static function newFromID( $id ) {
-               $dbr = wfGetDB( DB_REPLICA );
-               $blockQuery = self::getQueryInfo();
-               $res = $dbr->selectRow(
-                       $blockQuery['tables'],
-                       $blockQuery['fields'],
-                       [ 'ipb_id' => $id ],
-                       __METHOD__,
-                       [],
-                       $blockQuery['joins']
-               );
-               if ( $res ) {
-                       return self::newFromRow( $res );
-               } else {
-                       return null;
-               }
-       }
-
-       /**
-        * Return the list of ipblocks fields that should be selected to create
-        * a new block.
-        * @deprecated since 1.31, use self::getQueryInfo() instead.
-        * @return array
-        */
-       public static function selectFields() {
-               global $wgActorTableSchemaMigrationStage;
-
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
-                       // If code is using this instead of self::getQueryInfo(), there's a
-                       // decent chance it's going to try to directly access
-                       // $row->ipb_by or $row->ipb_by_text and we can't give it
-                       // useful values here once those aren't being used anymore.
-                       throw new BadMethodCallException(
-                               'Cannot use ' . __METHOD__
-                                       . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
-                       );
-               }
-
-               wfDeprecated( __METHOD__, '1.31' );
-               return [
-                       'ipb_id',
-                       'ipb_address',
-                       'ipb_by',
-                       'ipb_by_text',
-                       'ipb_by_actor' => 'NULL',
-                       'ipb_timestamp',
-                       'ipb_auto',
-                       'ipb_anon_only',
-                       'ipb_create_account',
-                       'ipb_enable_autoblock',
-                       'ipb_expiry',
-                       'ipb_deleted',
-                       'ipb_block_email',
-                       'ipb_allow_usertalk',
-                       'ipb_parent_block_id',
-                       'ipb_sitewide',
-               ] + CommentStore::getStore()->getFields( 'ipb_reason' );
-       }
-
-       /**
-        * Return the tables, fields, and join conditions to be selected to create
-        * a new block object.
-        * @since 1.31
-        * @return array With three keys:
-        *   - tables: (string[]) to include in the `$table` to `IDatabase->select()`
-        *   - fields: (string[]) to include in the `$vars` to `IDatabase->select()`
-        *   - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
-        */
-       public static function getQueryInfo() {
-               $commentQuery = CommentStore::getStore()->getJoin( 'ipb_reason' );
-               $actorQuery = ActorMigration::newMigration()->getJoin( 'ipb_by' );
-               return [
-                       'tables' => [ 'ipblocks' ] + $commentQuery['tables'] + $actorQuery['tables'],
-                       'fields' => [
-                               'ipb_id',
-                               'ipb_address',
-                               'ipb_timestamp',
-                               'ipb_auto',
-                               'ipb_anon_only',
-                               'ipb_create_account',
-                               'ipb_enable_autoblock',
-                               'ipb_expiry',
-                               'ipb_deleted',
-                               'ipb_block_email',
-                               'ipb_allow_usertalk',
-                               'ipb_parent_block_id',
-                               'ipb_sitewide',
-                       ] + $commentQuery['fields'] + $actorQuery['fields'],
-                       'joins' => $commentQuery['joins'] + $actorQuery['joins'],
-               ];
-       }
-
-       /**
-        * Check if two blocks are effectively equal.  Doesn't check irrelevant things like
-        * the blocking user or the block timestamp, only things which affect the blocked user
-        *
-        * @param Block $block
-        *
-        * @return bool
-        */
-       public function equals( Block $block ) {
-               return (
-                       (string)$this->target == (string)$block->target
-                       && $this->type == $block->type
-                       && $this->mAuto == $block->mAuto
-                       && $this->isHardblock() == $block->isHardblock()
-                       && $this->isCreateAccountBlocked() == $block->isCreateAccountBlocked()
-                       && $this->getExpiry() == $block->getExpiry()
-                       && $this->isAutoblocking() == $block->isAutoblocking()
-                       && $this->getHideName() == $block->getHideName()
-                       && $this->isEmailBlocked() == $block->isEmailBlocked()
-                       && $this->isUsertalkEditAllowed() == $block->isUsertalkEditAllowed()
-                       && $this->getReason() == $block->getReason()
-                       && $this->isSitewide() == $block->isSitewide()
-                       // Block::getRestrictions() may perform a database query, so keep it at
-                       // the end.
-                       && $this->getBlockRestrictionStore()->equals(
-                               $this->getRestrictions(), $block->getRestrictions()
-                       )
-               );
-       }
-
-       /**
-        * Load a block from the database which affects the already-set $this->target:
-        *     1) A block directly on the given user or IP
-        *     2) A rangeblock encompassing the given IP (smallest first)
-        *     3) An autoblock on the given IP
-        * @param User|string|null $vagueTarget Also search for blocks affecting this target.  Doesn't
-        *     make any sense to use TYPE_AUTO / TYPE_ID here. Leave blank to skip IP lookups.
-        * @throws MWException
-        * @return bool Whether a relevant block was found
-        */
-       protected function newLoad( $vagueTarget = null ) {
-               $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_REPLICA );
-
-               if ( $this->type !== null ) {
-                       $conds = [
-                               'ipb_address' => [ (string)$this->target ],
-                       ];
-               } else {
-                       $conds = [ 'ipb_address' => [] ];
-               }
-
-               # Be aware that the != '' check is explicit, since empty values will be
-               # passed by some callers (T31116)
-               if ( $vagueTarget != '' ) {
-                       list( $target, $type ) = self::parseTarget( $vagueTarget );
-                       switch ( $type ) {
-                               case self::TYPE_USER:
-                                       # Slightly weird, but who are we to argue?
-                                       $conds['ipb_address'][] = (string)$target;
-                                       break;
-
-                               case self::TYPE_IP:
-                                       $conds['ipb_address'][] = (string)$target;
-                                       $conds[] = self::getRangeCond( IP::toHex( $target ) );
-                                       $conds = $db->makeList( $conds, LIST_OR );
-                                       break;
-
-                               case self::TYPE_RANGE:
-                                       list( $start, $end ) = IP::parseRange( $target );
-                                       $conds['ipb_address'][] = (string)$target;
-                                       $conds[] = self::getRangeCond( $start, $end );
-                                       $conds = $db->makeList( $conds, LIST_OR );
-                                       break;
-
-                               default:
-                                       throw new MWException( "Tried to load block with invalid type" );
-                       }
-               }
-
-               $blockQuery = self::getQueryInfo();
-               $res = $db->select(
-                       $blockQuery['tables'], $blockQuery['fields'], $conds, __METHOD__, [], $blockQuery['joins']
-               );
-
-               # This result could contain a block on the user, a block on the IP, and a russian-doll
-               # set of rangeblocks.  We want to choose the most specific one, so keep a leader board.
-               $bestRow = null;
-
-               # Lower will be better
-               $bestBlockScore = 100;
-
-               foreach ( $res as $row ) {
-                       $block = self::newFromRow( $row );
-
-                       # Don't use expired blocks
-                       if ( $block->isExpired() ) {
-                               continue;
-                       }
-
-                       # Don't use anon only blocks on users
-                       if ( $this->type == self::TYPE_USER && !$block->isHardblock() ) {
-                               continue;
-                       }
-
-                       if ( $block->getType() == self::TYPE_RANGE ) {
-                               # This is the number of bits that are allowed to vary in the block, give
-                               # or take some floating point errors
-                               $target = $block->getTarget();
-                               $max = IP::isIPv6( $target ) ? 128 : 32;
-                               list( $network, $bits ) = IP::parseCIDR( $target );
-                               $size = $max - $bits;
-
-                               # Rank a range block covering a single IP equally with a single-IP block
-                               $score = self::TYPE_RANGE - 1 + ( $size / $max );
-
-                       } else {
-                               $score = $block->getType();
-                       }
-
-                       if ( $score < $bestBlockScore ) {
-                               $bestBlockScore = $score;
-                               $bestRow = $row;
-                       }
-               }
-
-               if ( $bestRow !== null ) {
-                       $this->initFromRow( $bestRow );
-                       return true;
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Get a set of SQL conditions which will select rangeblocks encompassing a given range
-        * @param string $start Hexadecimal IP representation
-        * @param string|null $end Hexadecimal IP representation, or null to use $start = $end
-        * @return string
-        */
-       public static function getRangeCond( $start, $end = null ) {
-               if ( $end === null ) {
-                       $end = $start;
-               }
-               # Per T16634, we want to include relevant active rangeblocks; for
-               # rangeblocks, we want to include larger ranges which enclose the given
-               # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
-               # so we can improve performance by filtering on a LIKE clause
-               $chunk = self::getIpFragment( $start );
-               $dbr = wfGetDB( DB_REPLICA );
-               $like = $dbr->buildLike( $chunk, $dbr->anyString() );
-
-               # Fairly hard to make a malicious SQL statement out of hex characters,
-               # but stranger things have happened...
-               $safeStart = $dbr->addQuotes( $start );
-               $safeEnd = $dbr->addQuotes( $end );
-
-               return $dbr->makeList(
-                       [
-                               "ipb_range_start $like",
-                               "ipb_range_start <= $safeStart",
-                               "ipb_range_end >= $safeEnd",
-                       ],
-                       LIST_AND
-               );
-       }
-
-       /**
-        * Get the component of an IP address which is certain to be the same between an IP
-        * address and a rangeblock containing that IP address.
-        * @param string $hex Hexadecimal IP representation
-        * @return string
-        */
-       protected static function getIpFragment( $hex ) {
-               global $wgBlockCIDRLimit;
-               if ( substr( $hex, 0, 3 ) == 'v6-' ) {
-                       return 'v6-' . substr( substr( $hex, 3 ), 0, floor( $wgBlockCIDRLimit['IPv6'] / 4 ) );
-               } else {
-                       return substr( $hex, 0, floor( $wgBlockCIDRLimit['IPv4'] / 4 ) );
-               }
-       }
-
-       /**
-        * Given a database row from the ipblocks table, initialize
-        * member variables
-        * @param stdClass $row A row from the ipblocks table
-        */
-       protected function initFromRow( $row ) {
-               $this->setTarget( $row->ipb_address );
-               $this->setBlocker( User::newFromAnyId(
-                       $row->ipb_by, $row->ipb_by_text, $row->ipb_by_actor ?? null
-               ) );
-
-               $this->setTimestamp( wfTimestamp( TS_MW, $row->ipb_timestamp ) );
-               $this->mAuto = $row->ipb_auto;
-               $this->setHideName( $row->ipb_deleted );
-               $this->mId = (int)$row->ipb_id;
-               $this->mParentBlockId = $row->ipb_parent_block_id;
-
-               // I wish I didn't have to do this
-               $db = wfGetDB( DB_REPLICA );
-               $this->setExpiry( $db->decodeExpiry( $row->ipb_expiry ) );
-               $this->setReason(
-                       CommentStore::getStore()
-                       // Legacy because $row may have come from self::selectFields()
-                       ->getCommentLegacy( $db, 'ipb_reason', $row )->text
-               );
-
-               $this->isHardblock( !$row->ipb_anon_only );
-               $this->isAutoblocking( $row->ipb_enable_autoblock );
-               $this->isSitewide( (bool)$row->ipb_sitewide );
-
-               $this->isCreateAccountBlocked( $row->ipb_create_account );
-               $this->isEmailBlocked( $row->ipb_block_email );
-               $this->isUsertalkEditAllowed( $row->ipb_allow_usertalk );
-       }
-
-       /**
-        * Create a new Block object from a database row
-        * @param stdClass $row Row from the ipblocks table
-        * @return Block
-        */
-       public static function newFromRow( $row ) {
-               $block = new Block;
-               $block->initFromRow( $row );
-               return $block;
-       }
-
-       /**
-        * Delete the row from the IP blocks table.
-        *
-        * @throws MWException
-        * @return bool
-        */
-       public function delete() {
-               if ( wfReadOnly() ) {
-                       return false;
-               }
-
-               if ( !$this->getId() ) {
-                       throw new MWException( "Block::delete() requires that the mId member be filled\n" );
-               }
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               $this->getBlockRestrictionStore()->deleteByParentBlockId( $this->getId() );
-               $dbw->delete( 'ipblocks', [ 'ipb_parent_block_id' => $this->getId() ], __METHOD__ );
-
-               $this->getBlockRestrictionStore()->deleteByBlockId( $this->getId() );
-               $dbw->delete( 'ipblocks', [ 'ipb_id' => $this->getId() ], __METHOD__ );
-
-               return $dbw->affectedRows() > 0;
-       }
-
-       /**
-        * Insert a block into the block table. Will fail if there is a conflicting
-        * block (same name and options) already in the database.
-        *
-        * @param IDatabase|null $dbw If you have one available
-        * @return bool|array False on failure, assoc array on success:
-        *      ('id' => block ID, 'autoIds' => array of autoblock IDs)
-        */
-       public function insert( $dbw = null ) {
-               global $wgBlockDisablesLogin;
-
-               if ( !$this->getBlocker() || $this->getBlocker()->getName() === '' ) {
-                       throw new MWException( 'Cannot insert a block without a blocker set' );
-               }
-
-               wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
-
-               if ( $dbw === null ) {
-                       $dbw = wfGetDB( DB_MASTER );
-               }
-
-               self::purgeExpired();
-
-               $row = $this->getDatabaseArray( $dbw );
-
-               $dbw->insert( 'ipblocks', $row, __METHOD__, [ 'IGNORE' ] );
-               $affected = $dbw->affectedRows();
-               if ( $affected ) {
-                       $this->setId( $dbw->insertId() );
-                       if ( $this->restrictions ) {
-                               $this->getBlockRestrictionStore()->insert( $this->restrictions );
-                       }
-               }
-
-               # Don't collide with expired blocks.
-               # Do this after trying to insert to avoid locking.
-               if ( !$affected ) {
-                       # T96428: The ipb_address index uses a prefix on a field, so
-                       # use a standard SELECT + DELETE to avoid annoying gap locks.
-                       $ids = $dbw->selectFieldValues( 'ipblocks',
-                               'ipb_id',
-                               [
-                                       'ipb_address' => $row['ipb_address'],
-                                       'ipb_user' => $row['ipb_user'],
-                                       'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() )
-                               ],
-                               __METHOD__
-                       );
-                       if ( $ids ) {
-                               $dbw->delete( 'ipblocks', [ 'ipb_id' => $ids ], __METHOD__ );
-                               $this->getBlockRestrictionStore()->deleteByBlockId( $ids );
-                               $dbw->insert( 'ipblocks', $row, __METHOD__, [ 'IGNORE' ] );
-                               $affected = $dbw->affectedRows();
-                               $this->setId( $dbw->insertId() );
-                               if ( $this->restrictions ) {
-                                       $this->getBlockRestrictionStore()->insert( $this->restrictions );
-                               }
-                       }
-               }
-
-               if ( $affected ) {
-                       $auto_ipd_ids = $this->doRetroactiveAutoblock();
-
-                       if ( $wgBlockDisablesLogin && $this->target instanceof User ) {
-                               // Change user login token to force them to be logged out.
-                               $this->target->setToken();
-                               $this->target->saveSettings();
-                       }
-
-                       return [ 'id' => $this->mId, 'autoIds' => $auto_ipd_ids ];
-               }
-
-               return false;
-       }
-
-       /**
-        * Update a block in the DB with new parameters.
-        * The ID field needs to be loaded first.
-        *
-        * @return bool|array False on failure, array on success:
-        *   ('id' => block ID, 'autoIds' => array of autoblock IDs)
-        */
-       public function update() {
-               wfDebug( "Block::update; timestamp {$this->mTimestamp}\n" );
-               $dbw = wfGetDB( DB_MASTER );
-
-               $dbw->startAtomic( __METHOD__ );
-
-               $result = $dbw->update(
-                       'ipblocks',
-                       $this->getDatabaseArray( $dbw ),
-                       [ 'ipb_id' => $this->getId() ],
-                       __METHOD__
-               );
-
-               // 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 = $this->getBlockRestrictionStore()->deleteByBlockId( $this->getId() );
-                       } else {
-                               $success = $this->getBlockRestrictionStore()->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)
-                       $dbw->update(
-                               'ipblocks',
-                               $this->getAutoblockUpdateArray( $dbw ),
-                               [ 'ipb_parent_block_id' => $this->getId() ],
-                               __METHOD__
-                       );
-
-                       // Only update the restrictions if they have been modified.
-                       if ( $this->restrictions !== null ) {
-                               $this->getBlockRestrictionStore()->updateByParentBlockId( $this->getId(), $this->restrictions );
-                       }
-               } else {
-                       // autoblock no longer required, delete corresponding autoblock(s)
-                       $this->getBlockRestrictionStore()->deleteByParentBlockId( $this->getId() );
-                       $dbw->delete(
-                               'ipblocks',
-                               [ 'ipb_parent_block_id' => $this->getId() ],
-                               __METHOD__
-                       );
-               }
-
-               $dbw->endAtomic( __METHOD__ );
-
-               if ( $result ) {
-                       $auto_ipd_ids = $this->doRetroactiveAutoblock();
-                       return [ 'id' => $this->mId, 'autoIds' => $auto_ipd_ids ];
-               }
-
-               return $result;
-       }
-
-       /**
-        * Get an array suitable for passing to $dbw->insert() or $dbw->update()
-        * @param IDatabase $dbw
-        * @return array
-        */
-       protected function getDatabaseArray( IDatabase $dbw ) {
-               $expiry = $dbw->encodeExpiry( $this->getExpiry() );
-
-               if ( $this->forcedTargetID ) {
-                       $uid = $this->forcedTargetID;
-               } else {
-                       $uid = $this->target instanceof User ? $this->target->getId() : 0;
-               }
-
-               $a = [
-                       'ipb_address'          => (string)$this->target,
-                       'ipb_user'             => $uid,
-                       'ipb_timestamp'        => $dbw->timestamp( $this->getTimestamp() ),
-                       'ipb_auto'             => $this->mAuto,
-                       'ipb_anon_only'        => !$this->isHardblock(),
-                       'ipb_create_account'   => $this->isCreateAccountBlocked(),
-                       'ipb_enable_autoblock' => $this->isAutoblocking(),
-                       'ipb_expiry'           => $expiry,
-                       'ipb_range_start'      => $this->getRangeStart(),
-                       'ipb_range_end'        => $this->getRangeEnd(),
-                       'ipb_deleted'          => intval( $this->getHideName() ), // typecast required for SQLite
-                       'ipb_block_email'      => $this->isEmailBlocked(),
-                       'ipb_allow_usertalk'   => $this->isUsertalkEditAllowed(),
-                       'ipb_parent_block_id'  => $this->mParentBlockId,
-                       'ipb_sitewide'         => $this->isSitewide(),
-               ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->getReason() )
-                       + ActorMigration::newMigration()->getInsertValues( $dbw, 'ipb_by', $this->getBlocker() );
-
-               return $a;
-       }
-
-       /**
-        * @param IDatabase $dbw
-        * @return array
-        */
-       protected function getAutoblockUpdateArray( IDatabase $dbw ) {
-               return [
-                       'ipb_create_account'   => $this->isCreateAccountBlocked(),
-                       'ipb_deleted'          => (int)$this->getHideName(), // typecast required for SQLite
-                       'ipb_allow_usertalk'   => $this->isUsertalkEditAllowed(),
-                       'ipb_sitewide'         => $this->isSitewide(),
-               ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->getReason() )
-                       + ActorMigration::newMigration()->getInsertValues( $dbw, 'ipb_by', $this->getBlocker() );
-       }
-
-       /**
-        * Retroactively autoblocks the last IP used by the user (if it is a user)
-        * blocked by this Block.
-        *
-        * @return array Block IDs of retroactive autoblocks made
-        */
-       protected function doRetroactiveAutoblock() {
-               $blockIds = [];
-               # If autoblock is enabled, autoblock the LAST IP(s) used
-               if ( $this->isAutoblocking() && $this->getType() == self::TYPE_USER ) {
-                       wfDebug( "Doing retroactive autoblocks for " . $this->getTarget() . "\n" );
-
-                       $continue = Hooks::run(
-                               'PerformRetroactiveAutoblock', [ $this, &$blockIds ] );
-
-                       if ( $continue ) {
-                               self::defaultRetroactiveAutoblock( $this, $blockIds );
-                       }
-               }
-               return $blockIds;
-       }
-
-       /**
-        * Retroactively autoblocks the last IP used by the user (if it is a user)
-        * blocked by this Block. This will use the recentchanges table.
-        *
-        * @param Block $block
-        * @param array &$blockIds
-        */
-       protected static function defaultRetroactiveAutoblock( Block $block, array &$blockIds ) {
-               global $wgPutIPinRC;
-
-               // No IPs are in recentchanges table, so nothing to select
-               if ( !$wgPutIPinRC ) {
-                       return;
-               }
-
-               // Autoblocks only apply to TYPE_USER
-               if ( $block->getType() !== self::TYPE_USER ) {
-                       return;
-               }
-               $target = $block->getTarget(); // TYPE_USER => always a User object
-
-               $dbr = wfGetDB( DB_REPLICA );
-               $rcQuery = ActorMigration::newMigration()->getWhere( $dbr, 'rc_user', $target, false );
-
-               $options = [ 'ORDER BY' => 'rc_timestamp DESC' ];
-
-               // Just the last IP used.
-               $options['LIMIT'] = 1;
-
-               $res = $dbr->select(
-                       [ 'recentchanges' ] + $rcQuery['tables'],
-                       [ 'rc_ip' ],
-                       $rcQuery['conds'],
-                       __METHOD__,
-                       $options,
-                       $rcQuery['joins']
-               );
-
-               if ( !$res->numRows() ) {
-                       # No results, don't autoblock anything
-                       wfDebug( "No IP found to retroactively autoblock\n" );
-               } else {
-                       foreach ( $res as $row ) {
-                               if ( $row->rc_ip ) {
-                                       $id = $block->doAutoblock( $row->rc_ip );
-                                       if ( $id ) {
-                                               $blockIds[] = $id;
-                                       }
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Checks whether a given IP is on the autoblock whitelist.
-        * TODO: this probably belongs somewhere else, but not sure where...
-        *
-        * @param string $ip The IP to check
-        * @return bool
-        */
-       public static function isWhitelistedFromAutoblocks( $ip ) {
-               // Try to get the autoblock_whitelist from the cache, as it's faster
-               // than getting the msg raw and explode()'ing it.
-               $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
-               $lines = $cache->getWithSetCallback(
-                       $cache->makeKey( 'ip-autoblock', 'whitelist' ),
-                       $cache::TTL_DAY,
-                       function ( $curValue, &$ttl, array &$setOpts ) {
-                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
-
-                               return explode( "\n",
-                                       wfMessage( 'autoblock_whitelist' )->inContentLanguage()->plain() );
-                       }
-               );
-
-               wfDebug( "Checking the autoblock whitelist..\n" );
-
-               foreach ( $lines as $line ) {
-                       # List items only
-                       if ( substr( $line, 0, 1 ) !== '*' ) {
-                               continue;
-                       }
-
-                       $wlEntry = substr( $line, 1 );
-                       $wlEntry = trim( $wlEntry );
-
-                       wfDebug( "Checking $ip against $wlEntry..." );
-
-                       # Is the IP in this range?
-                       if ( IP::isInRange( $ip, $wlEntry ) ) {
-                               wfDebug( " IP $ip matches $wlEntry, not autoblocking\n" );
-                               return true;
-                       } else {
-                               wfDebug( " No match\n" );
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Autoblocks the given IP, referring to this Block.
-        *
-        * @param string $autoblockIP The IP to autoblock.
-        * @return int|bool Block ID if an autoblock was inserted, false if not.
-        */
-       public function doAutoblock( $autoblockIP ) {
-               # If autoblocks are disabled, go away.
-               if ( !$this->isAutoblocking() ) {
-                       return false;
-               }
-
-               # Check for presence on the autoblock whitelist.
-               if ( self::isWhitelistedFromAutoblocks( $autoblockIP ) ) {
-                       return false;
-               }
-
-               // Avoid PHP 7.1 warning of passing $this by reference
-               $block = $this;
-               # Allow hooks to cancel the autoblock.
-               if ( !Hooks::run( 'AbortAutoblock', [ $autoblockIP, &$block ] ) ) {
-                       wfDebug( "Autoblock aborted by hook.\n" );
-                       return false;
-               }
-
-               # It's okay to autoblock. Go ahead and insert/update the block...
-
-               # Do not add a *new* block if the IP is already blocked.
-               $ipblock = self::newFromTarget( $autoblockIP );
-               if ( $ipblock ) {
-                       # Check if the block is an autoblock and would exceed the user block
-                       # if renewed. If so, do nothing, otherwise prolong the block time...
-                       if ( $ipblock->mAuto && // @todo Why not compare $ipblock->mExpiry?
-                               $this->getExpiry() > self::getAutoblockExpiry( $ipblock->getTimestamp() )
-                       ) {
-                               # Reset block timestamp to now and its expiry to
-                               # $wgAutoblockExpiry in the future
-                               $ipblock->updateTimestamp();
-                       }
-                       return false;
-               }
-
-               # Make a new block object with the desired properties.
-               $autoblock = new Block;
-               wfDebug( "Autoblocking {$this->getTarget()}@" . $autoblockIP . "\n" );
-               $autoblock->setTarget( $autoblockIP );
-               $autoblock->setBlocker( $this->getBlocker() );
-               $autoblock->setReason(
-                       wfMessage( 'autoblocker', $this->getTarget(), $this->getReason() )
-                               ->inContentLanguage()->plain()
-               );
-               $timestamp = wfTimestampNow();
-               $autoblock->setTimestamp( $timestamp );
-               $autoblock->mAuto = 1;
-               $autoblock->isCreateAccountBlocked( $this->isCreateAccountBlocked() );
-               # Continue suppressing the name if needed
-               $autoblock->setHideName( $this->getHideName() );
-               $autoblock->isUsertalkEditAllowed( $this->isUsertalkEditAllowed() );
-               $autoblock->mParentBlockId = $this->mId;
-               $autoblock->isSitewide( $this->isSitewide() );
-               $autoblock->setRestrictions( $this->getRestrictions() );
-
-               if ( $this->getExpiry() == 'infinity' ) {
-                       # Original block was indefinite, start an autoblock now
-                       $autoblock->setExpiry( self::getAutoblockExpiry( $timestamp ) );
-               } else {
-                       # If the user is already blocked with an expiry date, we don't
-                       # want to pile on top of that.
-                       $autoblock->setExpiry( min( $this->getExpiry(), self::getAutoblockExpiry( $timestamp ) ) );
-               }
-
-               # Insert the block...
-               $status = $autoblock->insert();
-               return $status
-                       ? $status['id']
-                       : false;
-       }
-
-       /**
-        * Check if a block has expired. Delete it if it is.
-        * @return bool
-        */
-       public function deleteIfExpired() {
-               if ( $this->isExpired() ) {
-                       wfDebug( "Block::deleteIfExpired() -- deleting\n" );
-                       $this->delete();
-                       $retVal = true;
-               } else {
-                       wfDebug( "Block::deleteIfExpired() -- not expired\n" );
-                       $retVal = false;
-               }
-
-               return $retVal;
-       }
-
-       /**
-        * Has the block expired?
-        * @return bool
-        */
-       public function isExpired() {
-               $timestamp = wfTimestampNow();
-               wfDebug( "Block::isExpired() checking current " . $timestamp . " vs $this->mExpiry\n" );
-
-               if ( !$this->getExpiry() ) {
-                       return false;
-               } else {
-                       return $timestamp > $this->getExpiry();
-               }
-       }
-
-       /**
-        * Is the block address valid (i.e. not a null string?)
-        *
-        * @deprecated since 1.33 No longer needed in core.
-        * @return bool
-        */
-       public function isValid() {
-               wfDeprecated( __METHOD__, '1.33' );
-               return $this->getTarget() != null;
-       }
-
-       /**
-        * Update the timestamp on autoblocks.
-        */
-       public function updateTimestamp() {
-               if ( $this->mAuto ) {
-                       $this->setTimestamp( wfTimestamp() );
-                       $this->setExpiry( self::getAutoblockExpiry( $this->getTimestamp() ) );
-
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->update( 'ipblocks',
-                               [ /* SET */
-                                       'ipb_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
-                                       'ipb_expiry' => $dbw->timestamp( $this->getExpiry() ),
-                               ],
-                               [ /* WHERE */
-                                       'ipb_id' => $this->getId(),
-                               ],
-                               __METHOD__
-                       );
-               }
-       }
-
-       /**
-        * Get the IP address at the start of the range in Hex form
-        * @throws MWException
-        * @return string IP in Hex form
-        */
-       public function getRangeStart() {
-               switch ( $this->type ) {
-                       case self::TYPE_USER:
-                               return '';
-                       case self::TYPE_IP:
-                               return IP::toHex( $this->target );
-                       case self::TYPE_RANGE:
-                               list( $start, /*...*/ ) = IP::parseRange( $this->target );
-                               return $start;
-                       default:
-                               throw new MWException( "Block with invalid type" );
-               }
-       }
-
-       /**
-        * Get the IP address at the end of the range in Hex form
-        * @throws MWException
-        * @return string IP in Hex form
-        */
-       public function getRangeEnd() {
-               switch ( $this->type ) {
-                       case self::TYPE_USER:
-                               return '';
-                       case self::TYPE_IP:
-                               return IP::toHex( $this->target );
-                       case self::TYPE_RANGE:
-                               list( /*...*/, $end ) = IP::parseRange( $this->target );
-                               return $end;
-                       default:
-                               throw new MWException( "Block with invalid type" );
-               }
-       }
-
-       /**
-        * @inheritDoc
-        */
-       public function getId() {
-               return $this->mId;
-       }
-
-       /**
-        * Set the block ID
-        *
-        * @param int $blockId
-        * @return self
-        */
-       private function setId( $blockId ) {
-               $this->mId = (int)$blockId;
-
-               if ( is_array( $this->restrictions ) ) {
-                       $this->restrictions = $this->getBlockRestrictionStore()->setBlockId(
-                               $blockId, $this->restrictions
-                       );
-               }
-
-               return $this;
-       }
-
-       /**
-        * Get/set a flag determining whether the master is used for reads
-        *
-        * @param bool|null $x
-        * @return bool
-        */
-       public function fromMaster( $x = null ) {
-               return wfSetVar( $this->mFromMaster, $x );
-       }
-
-       /**
-        * Get/set whether the Block is a hardblock (affects logged-in users on a given IP/range)
-        * @param bool|null $x
-        * @return bool
-        */
-       public function isHardblock( $x = null ) {
-               wfSetVar( $this->isHardblock, $x );
-
-               # You can't *not* hardblock a user
-               return $this->getType() == self::TYPE_USER
-                       ? true
-                       : $this->isHardblock;
-       }
-
-       /**
-        * @param null|bool $x
-        * @return bool
-        */
-       public function isAutoblocking( $x = null ) {
-               wfSetVar( $this->isAutoblocking, $x );
-
-               # You can't put an autoblock on an IP or range as we don't have any history to
-               # look over to get more IPs from
-               return $this->getType() == self::TYPE_USER
-                       ? $this->isAutoblocking
-                       : false;
-       }
-
-       /**
-        * Get the block name, but with autoblocked IPs hidden as per standard privacy policy
-        * @return string Text is escaped
-        */
-       public function getRedactedName() {
-               if ( $this->mAuto ) {
-                       return Html::element(
-                               'span',
-                               [ 'class' => 'mw-autoblockid' ],
-                               wfMessage( 'autoblockid', $this->mId )->text()
-                       );
-               } else {
-                       return htmlspecialchars( $this->getTarget() );
-               }
-       }
-
-       /**
-        * Get a timestamp of the expiry for autoblocks
-        *
-        * @param string|int $timestamp
-        * @return string
-        */
-       public static function getAutoblockExpiry( $timestamp ) {
-               global $wgAutoblockExpiry;
-
-               return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
-       }
-
-       /**
-        * Purge expired blocks from the ipblocks table
-        */
-       public static function purgeExpired() {
-               if ( wfReadOnly() ) {
-                       return;
-               }
-
-               DeferredUpdates::addUpdate( new AutoCommitUpdate(
-                       wfGetDB( DB_MASTER ),
-                       __METHOD__,
-                       function ( IDatabase $dbw, $fname ) {
-                               $ids = $dbw->selectFieldValues( 'ipblocks',
-                                       'ipb_id',
-                                       [ 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
-                                       $fname
-                               );
-                               if ( $ids ) {
-                                       $blockRestrictionStore = MediaWikiServices::getInstance()->getBlockRestrictionStore();
-                                       $blockRestrictionStore->deleteByBlockId( $ids );
-
-                                       $dbw->delete( 'ipblocks', [ 'ipb_id' => $ids ], $fname );
-                               }
-                       }
-               ) );
-       }
-
-       /**
-        * Given a target and the target's type, get an existing Block object if possible.
-        * @param string|User|int $specificTarget A block target, which may be one of several types:
-        *     * A user to block, in which case $target will be a User
-        *     * An IP to block, in which case $target will be a User generated by using
-        *       User::newFromName( $ip, false ) to turn off name validation
-        *     * An IP range, in which case $target will be a String "123.123.123.123/18" etc
-        *     * The ID of an existing block, in the format "#12345" (since pure numbers are valid
-        *       usernames
-        *     Calling this with a user, IP address or range will not select autoblocks, and will
-        *     only select a block where the targets match exactly (so looking for blocks on
-        *     1.2.3.4 will not select 1.2.0.0/16 or even 1.2.3.4/32)
-        * @param string|User|int|null $vagueTarget As above, but we will search for *any* block which
-        *     affects that target (so for an IP address, get ranges containing that IP; and also
-        *     get any relevant autoblocks). Leave empty or blank to skip IP-based lookups.
-        * @param bool $fromMaster Whether to use the DB_MASTER database
-        * @return Block|null (null if no relevant block could be found).  The target and type
-        *     of the returned Block will refer to the actual block which was found, which might
-        *     not be the same as the target you gave if you used $vagueTarget!
-        */
-       public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromMaster = false ) {
-               list( $target, $type ) = self::parseTarget( $specificTarget );
-               if ( $type == self::TYPE_ID || $type == self::TYPE_AUTO ) {
-                       return self::newFromID( $target );
-
-               } elseif ( $target === null && $vagueTarget == '' ) {
-                       # We're not going to find anything useful here
-                       # Be aware that the == '' check is explicit, since empty values will be
-                       # passed by some callers (T31116)
-                       return null;
-
-               } elseif ( in_array(
-                       $type,
-                       [ self::TYPE_USER, self::TYPE_IP, self::TYPE_RANGE, null ] )
-               ) {
-                       $block = new Block();
-                       $block->fromMaster( $fromMaster );
-
-                       if ( $type !== null ) {
-                               $block->setTarget( $target );
-                       }
-
-                       if ( $block->newLoad( $vagueTarget ) ) {
-                               return $block;
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * Get all blocks that match any IP from an array of IP addresses
-        *
-        * @param array $ipChain List of IPs (strings), usually retrieved from the
-        *     X-Forwarded-For header of the request
-        * @param bool $isAnon Exclude anonymous-only blocks if false
-        * @param bool $fromMaster Whether to query the master or replica DB
-        * @return array Array of Blocks
-        * @since 1.22
-        */
-       public static function getBlocksForIPList( array $ipChain, $isAnon, $fromMaster = false ) {
-               if ( $ipChain === [] ) {
-                       return [];
-               }
-
-               $conds = [];
-               $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
-               foreach ( array_unique( $ipChain ) as $ipaddr ) {
-                       # Discard invalid IP addresses. Since XFF can be spoofed and we do not
-                       # necessarily trust the header given to us, make sure that we are only
-                       # checking for blocks on well-formatted IP addresses (IPv4 and IPv6).
-                       # Do not treat private IP spaces as special as it may be desirable for wikis
-                       # to block those IP ranges in order to stop misbehaving proxies that spoof XFF.
-                       if ( !IP::isValid( $ipaddr ) ) {
-                               continue;
-                       }
-                       # Don't check trusted IPs (includes local CDNs which will be in every request)
-                       if ( $proxyLookup->isTrustedProxy( $ipaddr ) ) {
-                               continue;
-                       }
-                       # Check both the original IP (to check against single blocks), as well as build
-                       # the clause to check for rangeblocks for the given IP.
-                       $conds['ipb_address'][] = $ipaddr;
-                       $conds[] = self::getRangeCond( IP::toHex( $ipaddr ) );
-               }
-
-               if ( $conds === [] ) {
-                       return [];
-               }
-
-               if ( $fromMaster ) {
-                       $db = wfGetDB( DB_MASTER );
-               } else {
-                       $db = wfGetDB( DB_REPLICA );
-               }
-               $conds = $db->makeList( $conds, LIST_OR );
-               if ( !$isAnon ) {
-                       $conds = [ $conds, 'ipb_anon_only' => 0 ];
-               }
-               $blockQuery = self::getQueryInfo();
-               $rows = $db->select(
-                       $blockQuery['tables'],
-                       array_merge( [ 'ipb_range_start', 'ipb_range_end' ], $blockQuery['fields'] ),
-                       $conds,
-                       __METHOD__,
-                       [],
-                       $blockQuery['joins']
-               );
-
-               $blocks = [];
-               foreach ( $rows as $row ) {
-                       $block = self::newFromRow( $row );
-                       if ( !$block->isExpired() ) {
-                               $blocks[] = $block;
-                       }
-               }
-
-               return $blocks;
-       }
-
-       /**
-        * From a list of multiple blocks, find the most exact and strongest Block.
-        *
-        * The logic for finding the "best" block is:
-        *  - Blocks that match the block's target IP are preferred over ones in a range
-        *  - Hardblocks are chosen over softblocks that prevent account creation
-        *  - Softblocks that prevent account creation are chosen over other softblocks
-        *  - Other softblocks are chosen over autoblocks
-        *  - If there are multiple exact or range blocks at the same level, the one chosen
-        *    is random
-        * This should be used when $blocks were retrieved from the user's IP address
-        * and $ipChain is populated from the same IP address information.
-        *
-        * @param array $blocks Array of Block objects
-        * @param array $ipChain List of IPs (strings). This is used to determine how "close"
-        *     a block is to the server, and if a block matches exactly, or is in a range.
-        *     The order is furthest from the server to nearest e.g., (Browser, proxy1, proxy2,
-        *     local-cdn, ...)
-        * @throws MWException
-        * @return Block|null The "best" block from the list
-        */
-       public static function chooseBlock( array $blocks, array $ipChain ) {
-               if ( $blocks === [] ) {
-                       return null;
-               } elseif ( count( $blocks ) == 1 ) {
-                       return $blocks[0];
-               }
-
-               // Sort hard blocks before soft ones and secondarily sort blocks
-               // that disable account creation before those that don't.
-               usort( $blocks, function ( Block $a, Block $b ) {
-                       $aWeight = (int)$a->isHardblock() . (int)$a->appliesToRight( 'createaccount' );
-                       $bWeight = (int)$b->isHardblock() . (int)$b->appliesToRight( 'createaccount' );
-                       return strcmp( $bWeight, $aWeight ); // highest weight first
-               } );
-
-               $blocksListExact = [
-                       'hard' => false,
-                       'disable_create' => false,
-                       'other' => false,
-                       'auto' => false
-               ];
-               $blocksListRange = [
-                       'hard' => false,
-                       'disable_create' => false,
-                       'other' => false,
-                       'auto' => false
-               ];
-               $ipChain = array_reverse( $ipChain );
-
-               /** @var Block $block */
-               foreach ( $blocks as $block ) {
-                       // Stop searching if we have already have a "better" block. This
-                       // is why the order of the blocks matters
-                       if ( !$block->isHardblock() && $blocksListExact['hard'] ) {
-                               break;
-                       } elseif ( !$block->appliesToRight( 'createaccount' ) && $blocksListExact['disable_create'] ) {
-                               break;
-                       }
-
-                       foreach ( $ipChain as $checkip ) {
-                               $checkipHex = IP::toHex( $checkip );
-                               if ( (string)$block->getTarget() === $checkip ) {
-                                       if ( $block->isHardblock() ) {
-                                               $blocksListExact['hard'] = $blocksListExact['hard'] ?: $block;
-                                       } elseif ( $block->appliesToRight( 'createaccount' ) ) {
-                                               $blocksListExact['disable_create'] = $blocksListExact['disable_create'] ?: $block;
-                                       } elseif ( $block->mAuto ) {
-                                               $blocksListExact['auto'] = $blocksListExact['auto'] ?: $block;
-                                       } else {
-                                               $blocksListExact['other'] = $blocksListExact['other'] ?: $block;
-                                       }
-                                       // We found closest exact match in the ip list, so go to the next Block
-                                       break;
-                               } elseif ( array_filter( $blocksListExact ) == []
-                                       && $block->getRangeStart() <= $checkipHex
-                                       && $block->getRangeEnd() >= $checkipHex
-                               ) {
-                                       if ( $block->isHardblock() ) {
-                                               $blocksListRange['hard'] = $blocksListRange['hard'] ?: $block;
-                                       } elseif ( $block->appliesToRight( 'createaccount' ) ) {
-                                               $blocksListRange['disable_create'] = $blocksListRange['disable_create'] ?: $block;
-                                       } elseif ( $block->mAuto ) {
-                                               $blocksListRange['auto'] = $blocksListRange['auto'] ?: $block;
-                                       } else {
-                                               $blocksListRange['other'] = $blocksListRange['other'] ?: $block;
-                                       }
-                                       break;
-                               }
-                       }
-               }
-
-               if ( array_filter( $blocksListExact ) == [] ) {
-                       $blocksList = &$blocksListRange;
-               } else {
-                       $blocksList = &$blocksListExact;
-               }
-
-               $chosenBlock = null;
-               if ( $blocksList['hard'] ) {
-                       $chosenBlock = $blocksList['hard'];
-               } elseif ( $blocksList['disable_create'] ) {
-                       $chosenBlock = $blocksList['disable_create'];
-               } elseif ( $blocksList['other'] ) {
-                       $chosenBlock = $blocksList['other'];
-               } elseif ( $blocksList['auto'] ) {
-                       $chosenBlock = $blocksList['auto'];
-               } else {
-                       throw new MWException( "Proxy block found, but couldn't be classified." );
-               }
-
-               return $chosenBlock;
-       }
-
-       /**
-        * @inheritDoc
-        *
-        * Autoblocks have whichever type corresponds to their target, so to detect if a block is an
-        * autoblock, we have to check the mAuto property instead.
-        */
-       public function getType() {
-               return $this->mAuto
-                       ? self::TYPE_AUTO
-                       : parent::getType();
-       }
-
-       /**
-        * Set the 'BlockID' cookie to this block's ID and expiry time. The cookie's expiry will be
-        * the same as the block's, to a maximum of 24 hours.
-        *
-        * @since 1.29
-        *
-        * @param WebResponse $response The response on which to set the cookie.
-        */
-       public function setCookie( WebResponse $response ) {
-               // Calculate the default expiry time.
-               $maxExpiryTime = wfTimestamp( TS_MW, wfTimestamp() + ( 24 * 60 * 60 ) );
-
-               // Use the Block's expiry time only if it's less than the default.
-               $expiryTime = $this->getExpiry();
-               if ( $expiryTime === 'infinity' || $expiryTime > $maxExpiryTime ) {
-                       $expiryTime = $maxExpiryTime;
-               }
-
-               // Set the cookie. Reformat the MediaWiki datetime as a Unix timestamp for the cookie.
-               $expiryValue = DateTime::createFromFormat( 'YmdHis', $expiryTime )->format( 'U' );
-               $cookieOptions = [ 'httpOnly' => false ];
-               $cookieValue = $this->getCookieValue();
-               $response->setCookie( 'BlockID', $cookieValue, $expiryValue, $cookieOptions );
-       }
-
-       /**
-        * Unset the 'BlockID' cookie.
-        *
-        * @since 1.29
-        *
-        * @param WebResponse $response The response on which to unset the cookie.
-        */
-       public static function clearCookie( WebResponse $response ) {
-               $response->clearCookie( 'BlockID', [ 'httpOnly' => false ] );
-       }
-
-       /**
-        * Get the BlockID cookie's value for this block. This is usually the block ID concatenated
-        * with an HMAC in order to avoid spoofing (T152951), but if wgSecretKey is not set will just
-        * be the block ID.
-        *
-        * @since 1.29
-        *
-        * @return string The block ID, probably concatenated with "!" and the HMAC.
-        */
-       public function getCookieValue() {
-               $config = RequestContext::getMain()->getConfig();
-               $id = $this->getId();
-               $secretKey = $config->get( 'SecretKey' );
-               if ( !$secretKey ) {
-                       // If there's no secret key, don't append a HMAC.
-                       return $id;
-               }
-               $hmac = MWCryptHash::hmac( $id, $secretKey, false );
-               $cookieValue = $id . '!' . $hmac;
-               return $cookieValue;
-       }
-
-       /**
-        * Get the stored ID from the 'BlockID' cookie. The cookie's value is usually a combination of
-        * the ID and a HMAC (see Block::setCookie), but will sometimes only be the ID.
-        *
-        * @since 1.29
-        *
-        * @param string $cookieValue The string in which to find the ID.
-        *
-        * @return int|null The block ID, or null if the HMAC is present and invalid.
-        */
-       public static function getIdFromCookieValue( $cookieValue ) {
-               // Extract the ID prefix from the cookie value (may be the whole value, if no bang found).
-               $bangPos = strpos( $cookieValue, '!' );
-               $id = ( $bangPos === false ) ? $cookieValue : substr( $cookieValue, 0, $bangPos );
-               // Get the site-wide secret key.
-               $config = RequestContext::getMain()->getConfig();
-               $secretKey = $config->get( 'SecretKey' );
-               if ( !$secretKey ) {
-                       // If there's no secret key, just use the ID as given.
-                       return $id;
-               }
-               $storedHmac = substr( $cookieValue, $bangPos + 1 );
-               $calculatedHmac = MWCryptHash::hmac( $id, $secretKey, false );
-               if ( $calculatedHmac === $storedHmac ) {
-                       return $id;
-               } else {
-                       return null;
-               }
-       }
-
-       /**
-        * @inheritDoc
-        *
-        * Build different messages for autoblocks and partial blocks.
-        */
-       public function getPermissionsError( IContextSource $context ) {
-               $params = $this->getBlockErrorParams( $context );
-
-               $msg = 'blockedtext';
-               if ( $this->mAuto ) {
-                       $msg = 'autoblockedtext';
-               } elseif ( !$this->isSitewide() ) {
-                       $msg = 'blockedtext-partial';
-               }
-
-               array_unshift( $params, $msg );
-
-               return $params;
-       }
-
-       /**
-        * Get Restrictions.
-        *
-        * Getting the restrictions will perform a database query if the restrictions
-        * are not already loaded.
-        *
-        * @since 1.33
-        * @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 = $this->getBlockRestrictionStore()->loadByBlockId( $this->mId );
-               }
-
-               return $this->restrictions;
-       }
-
-       /**
-        * Set Restrictions.
-        *
-        * @since 1.33
-        * @param Restriction[] $restrictions
-        * @return self
-        */
-       public function setRestrictions( array $restrictions ) {
-               $this->restrictions = array_filter( $restrictions, function ( $restriction ) {
-                       return $restriction instanceof Restriction;
-               } );
-
-               return $this;
-       }
-
-       /**
-        * @inheritDoc
-        */
-       public function appliesToTitle( Title $title ) {
-               if ( $this->isSitewide() ) {
-                       return true;
-               }
-
-               $restrictions = $this->getRestrictions();
-               foreach ( $restrictions as $restriction ) {
-                       if ( $restriction->matches( $title ) ) {
-                               return true;
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * @inheritDoc
-        */
-       public function appliesToNamespace( $ns ) {
-               if ( $this->isSitewide() ) {
-                       return true;
-               }
-
-               // Blocks do not apply to virtual namespaces.
-               if ( $ns < 0 ) {
-                       return false;
-               }
-
-               $restriction = $this->findRestriction( NamespaceRestriction::TYPE, $ns );
-
-               return (bool)$restriction;
-       }
-
-       /**
-        * @inheritDoc
-        */
-       public function appliesToPage( $pageId ) {
-               if ( $this->isSitewide() ) {
-                       return true;
-               }
-
-               // If the pageId is not over zero, the block cannot apply to it.
-               if ( $pageId <= 0 ) {
-                       return false;
-               }
-
-               $restriction = $this->findRestriction( PageRestriction::TYPE, $pageId );
-
-               return (bool)$restriction;
-       }
-
-       /**
-        * Find Restriction by type and value.
-        *
-        * @param string $type
-        * @param int $value
-        * @return Restriction|null
-        */
-       private function findRestriction( $type, $value ) {
-               $restrictions = $this->getRestrictions();
-               foreach ( $restrictions as $restriction ) {
-                       if ( $restriction->getType() !== $type ) {
-                               continue;
-                       }
-
-                       if ( $restriction->getValue() === $value ) {
-                               return $restriction;
-                       }
-               }
-
-               return null;
-       }
-
-       /**
-        * @inheritDoc
-        */
-       public function shouldTrackWithCookie( $isAnon ) {
-               $config = RequestContext::getMain()->getConfig();
-               switch ( $this->getType() ) {
-                       case self::TYPE_IP:
-                       case self::TYPE_RANGE:
-                               return $isAnon && $config->get( 'CookieSetOnIpBlock' );
-                       case self::TYPE_USER:
-                               return !$isAnon && $config->get( 'CookieSetOnAutoblock' ) && $this->isAutoblocking();
-                       default:
-                               return false;
-               }
-       }
-
-       /**
-        * Get a BlockRestrictionStore instance
-        *
-        * @return BlockRestrictionStore
-        */
-       private function getBlockRestrictionStore() : BlockRestrictionStore {
-               return MediaWikiServices::getInstance()->getBlockRestrictionStore();
-       }
-}
index 47a8b5b..a42e243 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  */
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\EditPage\TextboxBuilder;
 use MediaWiki\EditPage\TextConflictHelper;
 use MediaWiki\Logger\LoggerFactory;
@@ -2622,13 +2623,13 @@ ERROR;
                        $username = explode( '/', $this->mTitle->getText(), 2 )[0];
                        $user = User::newFromName( $username, false /* allow IP users */ );
                        $ip = User::isIP( $username );
-                       $block = Block::newFromTarget( $user, $user );
+                       $block = DatabaseBlock::newFromTarget( $user, $user );
                        if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
                                $out->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>",
                                        [ 'userpage-userdoesnotexist', wfEscapeWikiText( $username ) ] );
                        } elseif (
                                !is_null( $block ) &&
-                               $block->getType() != Block::TYPE_AUTO &&
+                               $block->getType() != DatabaseBlock::TYPE_AUTO &&
                                ( $block->isSitewide() || $user->isBlockedFrom( $this->mTitle ) )
                        ) {
                                // Show log extract if the user is sitewide blocked or is partially
index 7cb2dbf..dd1a6db 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use MediaWiki\Block\AbstractBlock;
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\MediaWikiServices;
 use Wikimedia\Rdbms\IDatabase;
 
@@ -2034,7 +2035,7 @@ abstract class ApiBase extends ContextSource {
         */
        public function dieBlocked( AbstractBlock $block ) {
                // Die using the appropriate message depending on block type
-               if ( $block->getType() == Block::TYPE_AUTO ) {
+               if ( $block->getType() == DatabaseBlock::TYPE_AUTO ) {
                        $this->dieWithError(
                                'apierror-autoblocked',
                                'autoblocked',
index f7c2bce..c71de40 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * API module that facilitates the blocking of users. Requires API write mode
  * to be enabled.
@@ -82,7 +84,7 @@ class ApiBlock extends ApiBase {
 
                        // T40633 - if the target is a user (not an IP address), but it
                        // doesn't exist or is unusable, error.
-                       if ( $type === Block::TYPE_USER &&
+                       if ( $type === DatabaseBlock::TYPE_USER &&
                                ( $target->isAnon() /* doesn't exist */ || !User::isUsableName( $params['user'] ) )
                        ) {
                                $this->dieWithError( [ 'nosuchusershort', $params['user'] ], 'nosuchuser' );
@@ -136,8 +138,8 @@ class ApiBlock extends ApiBase {
                $res['user'] = $params['user'];
                $res['userID'] = $target instanceof User ? $target->getId() : 0;
 
-               $block = Block::newFromTarget( $target, null, true );
-               if ( $block instanceof Block ) {
+               $block = DatabaseBlock::newFromTarget( $target, null, true );
+               if ( $block instanceof DatabaseBlock ) {
                        $res['expiry'] = ApiResult::formatExpiry( $block->getExpiry(), 'infinite' );
                        $res['id'] = $block->getId();
                } else {
index 1c72b67..5cef194 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * API module that facilitates the unblocking of users. Requires API write mode
  * to be enabled.
@@ -78,14 +80,14 @@ class ApiUnblock extends ApiBase {
                        'Reason' => $params['reason'],
                        'Tags' => $params['tags']
                ];
-               $block = Block::newFromTarget( $data['Target'] );
+               $block = DatabaseBlock::newFromTarget( $data['Target'] );
                $retval = SpecialUnblock::processUnblock( $data, $this->getContext() );
                if ( $retval !== true ) {
                        $this->dieStatus( $this->errorArrayToStatus( $retval ) );
                }
 
                $res['id'] = $block->getId();
-               $target = $block->getType() == Block::TYPE_AUTO ? '' : $block->getTarget();
+               $target = $block->getType() == DatabaseBlock::TYPE_AUTO ? '' : $block->getTarget();
                $res['user'] = $target instanceof User ? $target->getName() : $target;
                $res['userid'] = $target instanceof User ? $target->getId() : 0;
                $res['reason'] = $params['reason'];
index 5915d35..e03c590 100644 (file)
@@ -24,6 +24,7 @@
 namespace MediaWiki\Auth;
 
 use Config;
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\MediaWikiServices;
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerInterface;
@@ -1010,7 +1011,7 @@ class AuthManager implements LoggerAwareInterface {
                                $block->getByName()
                        ];
 
-                       if ( $block->getType() === \Block::TYPE_RANGE ) {
+                       if ( $block->getType() === DatabaseBlock::TYPE_RANGE ) {
                                $errorMessage = 'cantcreateaccount-range-text';
                                $errorParams[] = $this->getRequest()->getIP();
                        } else {
index 3e26097..92b06a4 100644 (file)
@@ -22,6 +22,7 @@
 namespace MediaWiki\Auth;
 
 use Config;
+use MediaWiki\Block\DatabaseBlock;
 use StatusValue;
 
 /**
@@ -95,7 +96,7 @@ class CheckBlocksSecondaryAuthenticationProvider extends AbstractSecondaryAuthen
                                $block->getByName()
                        ];
 
-                       if ( $block->getType() === \Block::TYPE_RANGE ) {
+                       if ( $block->getType() === DatabaseBlock::TYPE_RANGE ) {
                                $errorMessage = 'cantcreateaccount-range-text';
                                $errorParams[] = $this->manager->getRequest()->getIP();
                        } else {
index a931d7a..fb49dfc 100644 (file)
@@ -28,7 +28,7 @@ use Title;
 use User;
 
 /**
- * @since 1.34 Factored out from Block.
+ * @since 1.34 Factored out from DatabaseBlock (previously Block).
  */
 abstract class AbstractBlock {
        /** @var string */
@@ -56,7 +56,7 @@ abstract class AbstractBlock {
        protected $target;
 
        /**
-        * @var int Block::TYPE_ constant. After the block has been loaded
+        * @var int AbstractBlock::TYPE_ constant. After the block has been loaded
         * from the database, this can only be USER, IP or RANGE.
         */
        protected $type;
@@ -228,9 +228,9 @@ abstract class AbstractBlock {
        }
 
        /**
-        * Determine whether the Block prevents a given right. A right
+        * Determine whether the block prevents a given right. A right
         * may be blacklisted or whitelisted, or determined from a
-        * property on the Block object. For certain rights, the property
+        * property on the block object. For certain rights, the property
         * may be overridden according to global configs.
         *
         * @since 1.33
@@ -275,7 +275,7 @@ abstract class AbstractBlock {
        }
 
        /**
-        * Get/set whether the Block prevents a given action
+        * Get/set whether the block prevents a given action
         *
         * @deprecated since 1.33, use appliesToRight to determine block
         *  behaviour, and specific methods to get/set properties
@@ -335,13 +335,13 @@ abstract class AbstractBlock {
        }
 
        /**
-        * From an existing Block, get the target and the type of target.
+        * From an existing block, get the target and the type of target.
         * Note that, except for null, it is always safe to treat the target
         * as a string; for User objects this will return User::__toString()
         * which in turn gives User::getName().
         *
         * @param string|int|User|null $target
-        * @return array [ User|String|null, Block::TYPE_ constant|null ]
+        * @return array [ User|String|null, AbstractBlock::TYPE_ constant|null ]
         */
        public static function parseTarget( $target ) {
                # We may have been through this before
@@ -395,25 +395,25 @@ abstract class AbstractBlock {
 
        /**
         * Get the type of target for this particular block.
-        * @return int Block::TYPE_ constant, will never be TYPE_ID
+        * @return int AbstractBlock::TYPE_ constant, will never be TYPE_ID
         */
        public function getType() {
                return $this->type;
        }
 
        /**
-        * Get the target and target type for this particular Block.  Note that for autoblocks,
+        * Get the target and target type for this particular block. Note that for autoblocks,
         * this returns the unredacted name; frontend functions need to call $block->getRedactedName()
         * in this situation.
-        * @return array [ User|String, Block::TYPE_ constant ]
-        * @todo FIXME: This should be an integral part of the Block member variables
+        * @return array [ User|String, AbstractBlock::TYPE_ constant ]
+        * @todo FIXME: This should be an integral part of the block member variables
         */
        public function getTargetAndType() {
                return [ $this->getTarget(), $this->getType() ];
        }
 
        /**
-        * Get the target for this particular Block.  Note that for autoblocks,
+        * Get the target for this particular block.  Note that for autoblocks,
         * this returns the unredacted name; frontend functions need to call $block->getRedactedName()
         * in this situation.
         * @return User|string
@@ -547,10 +547,11 @@ abstract class AbstractBlock {
 
        /**
         * Determine whether the block allows the user to edit their own
-        * user talk page. This is done separately from Block::appliesToRight
-        * because there is no right for editing one's own user talk page
-        * and because the user's talk page needs to be passed into the
-        * Block object, which is unaware of the user.
+        * user talk page. This is done separately from
+        * AbstractBlock::appliesToRight because there is no right for
+        * editing one's own user talk page and because the user's talk
+        * page needs to be passed into the block object, which is unaware
+        * of the user.
         *
         * The ipb_allow_usertalk flag (which corresponds to the property
         * allowUsertalk) is used on sitewide blocks and partial blocks
index 9823b3c..8d2fe0c 100644 (file)
@@ -20,7 +20,6 @@
 
 namespace MediaWiki\Block;
 
-use Block;
 use IP;
 use MediaWiki\User\UserIdentity;
 use User;
@@ -120,7 +119,7 @@ class BlockManager {
                // we should not look for XFF or cookie blocks.
                $request = $user->getRequest();
 
-               # We only need to worry about passing the IP address to the Block generator if the
+               # We only need to worry about passing the IP address to the block generator if the
                # user is not immune to autoblocks/hardblocks, and they are the current user so we
                # know which IP address they're actually coming from
                $ip = null;
@@ -135,8 +134,8 @@ class BlockManager {
                }
 
                // User/IP blocking
-               // TODO: remove dependency on Block
-               $block = Block::newFromTarget( $user, $ip, !$fromReplica );
+               // TODO: remove dependency on DatabaseBlock
+               $block = DatabaseBlock::newFromTarget( $user, $ip, !$fromReplica );
 
                // Cookie blocking
                if ( !$block instanceof AbstractBlock ) {
@@ -175,10 +174,10 @@ class BlockManager {
                        $xff = $request->getHeader( 'X-Forwarded-For' );
                        $xff = array_map( 'trim', explode( ',', $xff ) );
                        $xff = array_diff( $xff, [ $ip ] );
-                       // TODO: remove dependency on Block
-                       $xffblocks = Block::getBlocksForIPList( $xff, $isAnon, !$fromReplica );
-                       // TODO: remove dependency on Block
-                       $block = Block::chooseBlock( $xffblocks, $xff );
+                       // TODO: remove dependency on DatabaseBlock
+                       $xffblocks = DatabaseBlock::getBlocksForIPList( $xff, $isAnon, !$fromReplica );
+                       // TODO: remove dependency on DatabaseBlock
+                       $block = DatabaseBlock::chooseBlock( $xffblocks, $xff );
                        if ( $block instanceof AbstractBlock ) {
                                # Mangle the reason to alert the user that the block
                                # originated from matching the X-Forwarded-For header.
@@ -204,11 +203,11 @@ class BlockManager {
        }
 
        /**
-        * Try to load a Block from an ID given in a cookie value.
+        * Try to load a block from an ID given in a cookie value.
         *
         * @param UserIdentity $user
         * @param WebRequest $request
-        * @return Block|bool The Block object, or false if none could be loaded.
+        * @return DatabaseBlock|bool The block object, or false if none could be loaded.
         */
        private function getBlockFromCookieValue(
                UserIdentity $user,
@@ -221,21 +220,21 @@ class BlockManager {
                if ( strlen( $blockCookieVal ) < 1 || !is_numeric( substr( $blockCookieVal, 0, 1 ) ) ) {
                        return false;
                }
-               // Load the Block from the ID in the cookie.
-               // TODO: remove dependency on Block
-               $blockCookieId = Block::getIdFromCookieValue( $blockCookieVal );
+               // Load the block from the ID in the cookie.
+               // TODO: remove dependency on DatabaseBlock
+               $blockCookieId = DatabaseBlock::getIdFromCookieValue( $blockCookieVal );
                if ( $blockCookieId !== null ) {
                        // An ID was found in the cookie.
-                       // TODO: remove dependency on Block
-                       $tmpBlock = Block::newFromID( $blockCookieId );
-                       if ( $tmpBlock instanceof Block ) {
+                       // TODO: remove dependency on DatabaseBlock
+                       $tmpBlock = DatabaseBlock::newFromID( $blockCookieId );
+                       if ( $tmpBlock instanceof DatabaseBlock ) {
                                switch ( $tmpBlock->getType() ) {
-                                       case Block::TYPE_USER:
+                                       case DatabaseBlock::TYPE_USER:
                                                $blockIsValid = !$tmpBlock->isExpired() && $tmpBlock->isAutoblocking();
                                                $useBlockCookie = ( $this->cookieSetOnAutoblock === true );
                                                break;
-                                       case Block::TYPE_IP:
-                                       case Block::TYPE_RANGE:
+                                       case DatabaseBlock::TYPE_IP:
+                                       case DatabaseBlock::TYPE_RANGE:
                                                // If block is type IP or IP range, load only if user is not logged in (T152462)
                                                $blockIsValid = !$tmpBlock->isExpired() && $user->getId() === 0;
                                                $useBlockCookie = ( $this->cookieSetOnIpBlock === true );
@@ -251,12 +250,12 @@ class BlockManager {
                                }
 
                                // If the block is not valid, remove the cookie.
-                               // TODO: remove dependency on Block
-                               Block::clearCookie( $response );
+                               // TODO: remove dependency on DatabaseBlock
+                               DatabaseBlock::clearCookie( $response );
                        } else {
                                // If the block doesn't exist, remove the cookie.
-                               // TODO: remove dependency on Block
-                               Block::clearCookie( $response );
+                               // TODO: remove dependency on DatabaseBlock
+                               DatabaseBlock::clearCookie( $response );
                        }
                }
                return false;
index d242c87..88c0951 100644 (file)
@@ -251,7 +251,7 @@ class BlockRestrictionStore {
        }
 
        /**
-        * Delete the restrictions by Block ID.
+        * Delete the restrictions by block ID.
         *
         * @since 1.33
         * @param int|array $blockId
@@ -268,7 +268,7 @@ class BlockRestrictionStore {
        }
 
        /**
-        * Delete the restrictions by Parent Block ID.
+        * Delete the restrictions by parent block ID.
         *
         * @since 1.33
         * @param int|array $parentBlockId
diff --git a/includes/block/DatabaseBlock.php b/includes/block/DatabaseBlock.php
new file mode 100644 (file)
index 0000000..6d9dd13
--- /dev/null
@@ -0,0 +1,1596 @@
+<?php
+/**
+ * Class for blocks stored in the database.
+ *
+ * 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 ActorMigration;
+use AutoCommitUpdate;
+use BadMethodCallException;
+use CommentStore;
+use DateTime;
+use DeferredUpdates;
+use Hooks;
+use Html;
+use IContextSource;
+use IP;
+use MediaWiki\Block\Restriction\NamespaceRestriction;
+use MediaWiki\Block\Restriction\PageRestriction;
+use MediaWiki\Block\Restriction\Restriction;
+use MediaWiki\MediaWikiServices;
+use MWCryptHash;
+use MWException;
+use RequestContext;
+use stdClass;
+use Title;
+use User;
+use WebResponse;
+use Wikimedia\Rdbms\Database;
+use Wikimedia\Rdbms\IDatabase;
+
+/**
+ * A DatabaseBlock (unlike a SystemBlock) is stored in the database, may
+ * give rise to autoblocks and may be tracked with cookies. Such blocks
+ * are more customizable than system blocks: they may be hardblocks, and
+ * they may be sitewide or partial.
+ *
+ * @since 1.34 Renamed from Block.
+ */
+class DatabaseBlock extends AbstractBlock {
+       /** @var bool */
+       public $mAuto;
+
+       /** @var int */
+       public $mParentBlockId;
+
+       /** @var int */
+       private $mId;
+
+       /** @var bool */
+       private $mFromMaster;
+
+       /** @var int Hack for foreign blocking (CentralAuth) */
+       private $forcedTargetID;
+
+       /** @var bool */
+       private $isHardblock;
+
+       /** @var bool */
+       private $isAutoblocking;
+
+       /** @var Restriction[] */
+       private $restrictions;
+
+       /**
+        * Create a new block with specified option parameters on a user, IP or IP range.
+        *
+        * @param array $options Parameters of the block:
+        *     user int             Override target user ID (for foreign users)
+        *     auto bool            Is this an automatic block?
+        *     expiry string        Timestamp of expiration of the block or 'infinity'
+        *     anonOnly bool        Only disallow anonymous actions
+        *     createAccount bool   Disallow creation of new accounts
+        *     enableAutoblock bool Enable automatic blocking
+        *     hideName bool        Hide the target user name
+        *     blockEmail bool      Disallow sending emails
+        *     allowUsertalk bool   Allow the target to edit its own talk page
+        *     sitewide bool        Disallow editing all pages and all contribution
+        *                          actions, except those specifically allowed by
+        *                          other block flags
+        *
+        * @since 1.26 $options array
+        */
+       public function __construct( array $options = [] ) {
+               parent::__construct( $options );
+
+               $defaults = [
+                       'user'            => null,
+                       'auto'            => false,
+                       'expiry'          => '',
+                       'anonOnly'        => false,
+                       'createAccount'   => false,
+                       'enableAutoblock' => false,
+                       'hideName'        => false,
+                       'blockEmail'      => false,
+                       'allowUsertalk'   => false,
+                       'sitewide'        => true,
+               ];
+
+               $options += $defaults;
+
+               if ( $this->target instanceof User && $options['user'] ) {
+                       # Needed for foreign users
+                       $this->forcedTargetID = $options['user'];
+               }
+
+               $this->setExpiry( wfGetDB( DB_REPLICA )->decodeExpiry( $options['expiry'] ) );
+
+               # Boolean settings
+               $this->mAuto = (bool)$options['auto'];
+               $this->setHideName( (bool)$options['hideName'] );
+               $this->isHardblock( !$options['anonOnly'] );
+               $this->isAutoblocking( (bool)$options['enableAutoblock'] );
+               $this->isSitewide( (bool)$options['sitewide'] );
+               $this->isEmailBlocked( (bool)$options['blockEmail'] );
+               $this->isCreateAccountBlocked( (bool)$options['createAccount'] );
+               $this->isUsertalkEditAllowed( (bool)$options['allowUsertalk'] );
+
+               $this->mFromMaster = false;
+       }
+
+       /**
+        * Load a block from the block id.
+        *
+        * @param int $id id to search for
+        * @return DatabaseBlock|null
+        */
+       public static function newFromID( $id ) {
+               $dbr = wfGetDB( DB_REPLICA );
+               $blockQuery = self::getQueryInfo();
+               $res = $dbr->selectRow(
+                       $blockQuery['tables'],
+                       $blockQuery['fields'],
+                       [ 'ipb_id' => $id ],
+                       __METHOD__,
+                       [],
+                       $blockQuery['joins']
+               );
+               if ( $res ) {
+                       return self::newFromRow( $res );
+               } else {
+                       return null;
+               }
+       }
+
+       /**
+        * Return the list of ipblocks fields that should be selected to create
+        * a new block.
+        * @deprecated since 1.31, use self::getQueryInfo() instead.
+        * @return array
+        */
+       public static function selectFields() {
+               global $wgActorTableSchemaMigrationStage;
+
+               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) {
+                       // If code is using this instead of self::getQueryInfo(), there's a
+                       // decent chance it's going to try to directly access
+                       // $row->ipb_by or $row->ipb_by_text and we can't give it
+                       // useful values here once those aren't being used anymore.
+                       throw new BadMethodCallException(
+                               'Cannot use ' . __METHOD__
+                                       . ' when $wgActorTableSchemaMigrationStage has SCHEMA_COMPAT_READ_NEW'
+                       );
+               }
+
+               wfDeprecated( __METHOD__, '1.31' );
+               return [
+                       'ipb_id',
+                       'ipb_address',
+                       'ipb_by',
+                       'ipb_by_text',
+                       'ipb_by_actor' => 'NULL',
+                       'ipb_timestamp',
+                       'ipb_auto',
+                       'ipb_anon_only',
+                       'ipb_create_account',
+                       'ipb_enable_autoblock',
+                       'ipb_expiry',
+                       'ipb_deleted',
+                       'ipb_block_email',
+                       'ipb_allow_usertalk',
+                       'ipb_parent_block_id',
+                       'ipb_sitewide',
+               ] + CommentStore::getStore()->getFields( 'ipb_reason' );
+       }
+
+       /**
+        * Return the tables, fields, and join conditions to be selected to create
+        * a new block object.
+        * @since 1.31
+        * @return array With three keys:
+        *   - tables: (string[]) to include in the `$table` to `IDatabase->select()`
+        *   - fields: (string[]) to include in the `$vars` to `IDatabase->select()`
+        *   - joins: (array) to include in the `$join_conds` to `IDatabase->select()`
+        */
+       public static function getQueryInfo() {
+               $commentQuery = CommentStore::getStore()->getJoin( 'ipb_reason' );
+               $actorQuery = ActorMigration::newMigration()->getJoin( 'ipb_by' );
+               return [
+                       'tables' => [ 'ipblocks' ] + $commentQuery['tables'] + $actorQuery['tables'],
+                       'fields' => [
+                               'ipb_id',
+                               'ipb_address',
+                               'ipb_timestamp',
+                               'ipb_auto',
+                               'ipb_anon_only',
+                               'ipb_create_account',
+                               'ipb_enable_autoblock',
+                               'ipb_expiry',
+                               'ipb_deleted',
+                               'ipb_block_email',
+                               'ipb_allow_usertalk',
+                               'ipb_parent_block_id',
+                               'ipb_sitewide',
+                       ] + $commentQuery['fields'] + $actorQuery['fields'],
+                       'joins' => $commentQuery['joins'] + $actorQuery['joins'],
+               ];
+       }
+
+       /**
+        * Check if two blocks are effectively equal.  Doesn't check irrelevant things like
+        * the blocking user or the block timestamp, only things which affect the blocked user
+        *
+        * @param DatabaseBlock $block
+        * @return bool
+        */
+       public function equals( DatabaseBlock $block ) {
+               return (
+                       (string)$this->target == (string)$block->target
+                       && $this->type == $block->type
+                       && $this->mAuto == $block->mAuto
+                       && $this->isHardblock() == $block->isHardblock()
+                       && $this->isCreateAccountBlocked() == $block->isCreateAccountBlocked()
+                       && $this->getExpiry() == $block->getExpiry()
+                       && $this->isAutoblocking() == $block->isAutoblocking()
+                       && $this->getHideName() == $block->getHideName()
+                       && $this->isEmailBlocked() == $block->isEmailBlocked()
+                       && $this->isUsertalkEditAllowed() == $block->isUsertalkEditAllowed()
+                       && $this->getReason() == $block->getReason()
+                       && $this->isSitewide() == $block->isSitewide()
+                       // DatabaseBlock::getRestrictions() may perform a database query, so
+                       // keep it at the end.
+                       && $this->getBlockRestrictionStore()->equals(
+                               $this->getRestrictions(), $block->getRestrictions()
+                       )
+               );
+       }
+
+       /**
+        * Load a block from the database which affects the already-set $this->target:
+        *     1) A block directly on the given user or IP
+        *     2) A rangeblock encompassing the given IP (smallest first)
+        *     3) An autoblock on the given IP
+        * @param User|string|null $vagueTarget Also search for blocks affecting this target.  Doesn't
+        *     make any sense to use TYPE_AUTO / TYPE_ID here. Leave blank to skip IP lookups.
+        * @throws MWException
+        * @return bool Whether a relevant block was found
+        */
+       protected function newLoad( $vagueTarget = null ) {
+               $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_REPLICA );
+
+               if ( $this->type !== null ) {
+                       $conds = [
+                               'ipb_address' => [ (string)$this->target ],
+                       ];
+               } else {
+                       $conds = [ 'ipb_address' => [] ];
+               }
+
+               # Be aware that the != '' check is explicit, since empty values will be
+               # passed by some callers (T31116)
+               if ( $vagueTarget != '' ) {
+                       list( $target, $type ) = self::parseTarget( $vagueTarget );
+                       switch ( $type ) {
+                               case self::TYPE_USER:
+                                       # Slightly weird, but who are we to argue?
+                                       $conds['ipb_address'][] = (string)$target;
+                                       break;
+
+                               case self::TYPE_IP:
+                                       $conds['ipb_address'][] = (string)$target;
+                                       $conds[] = self::getRangeCond( IP::toHex( $target ) );
+                                       $conds = $db->makeList( $conds, LIST_OR );
+                                       break;
+
+                               case self::TYPE_RANGE:
+                                       list( $start, $end ) = IP::parseRange( $target );
+                                       $conds['ipb_address'][] = (string)$target;
+                                       $conds[] = self::getRangeCond( $start, $end );
+                                       $conds = $db->makeList( $conds, LIST_OR );
+                                       break;
+
+                               default:
+                                       throw new MWException( "Tried to load block with invalid type" );
+                       }
+               }
+
+               $blockQuery = self::getQueryInfo();
+               $res = $db->select(
+                       $blockQuery['tables'], $blockQuery['fields'], $conds, __METHOD__, [], $blockQuery['joins']
+               );
+
+               # This result could contain a block on the user, a block on the IP, and a russian-doll
+               # set of rangeblocks.  We want to choose the most specific one, so keep a leader board.
+               $bestRow = null;
+
+               # Lower will be better
+               $bestBlockScore = 100;
+
+               foreach ( $res as $row ) {
+                       $block = self::newFromRow( $row );
+
+                       # Don't use expired blocks
+                       if ( $block->isExpired() ) {
+                               continue;
+                       }
+
+                       # Don't use anon only blocks on users
+                       if ( $this->type == self::TYPE_USER && !$block->isHardblock() ) {
+                               continue;
+                       }
+
+                       if ( $block->getType() == self::TYPE_RANGE ) {
+                               # This is the number of bits that are allowed to vary in the block, give
+                               # or take some floating point errors
+                               $target = $block->getTarget();
+                               $max = IP::isIPv6( $target ) ? 128 : 32;
+                               list( $network, $bits ) = IP::parseCIDR( $target );
+                               $size = $max - $bits;
+
+                               # Rank a range block covering a single IP equally with a single-IP block
+                               $score = self::TYPE_RANGE - 1 + ( $size / $max );
+
+                       } else {
+                               $score = $block->getType();
+                       }
+
+                       if ( $score < $bestBlockScore ) {
+                               $bestBlockScore = $score;
+                               $bestRow = $row;
+                       }
+               }
+
+               if ( $bestRow !== null ) {
+                       $this->initFromRow( $bestRow );
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Get a set of SQL conditions which will select rangeblocks encompassing a given range
+        * @param string $start Hexadecimal IP representation
+        * @param string|null $end Hexadecimal IP representation, or null to use $start = $end
+        * @return string
+        */
+       public static function getRangeCond( $start, $end = null ) {
+               if ( $end === null ) {
+                       $end = $start;
+               }
+               # Per T16634, we want to include relevant active rangeblocks; for
+               # rangeblocks, we want to include larger ranges which enclose the given
+               # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
+               # so we can improve performance by filtering on a LIKE clause
+               $chunk = self::getIpFragment( $start );
+               $dbr = wfGetDB( DB_REPLICA );
+               $like = $dbr->buildLike( $chunk, $dbr->anyString() );
+
+               # Fairly hard to make a malicious SQL statement out of hex characters,
+               # but stranger things have happened...
+               $safeStart = $dbr->addQuotes( $start );
+               $safeEnd = $dbr->addQuotes( $end );
+
+               return $dbr->makeList(
+                       [
+                               "ipb_range_start $like",
+                               "ipb_range_start <= $safeStart",
+                               "ipb_range_end >= $safeEnd",
+                       ],
+                       LIST_AND
+               );
+       }
+
+       /**
+        * Get the component of an IP address which is certain to be the same between an IP
+        * address and a rangeblock containing that IP address.
+        * @param string $hex Hexadecimal IP representation
+        * @return string
+        */
+       protected static function getIpFragment( $hex ) {
+               global $wgBlockCIDRLimit;
+               if ( substr( $hex, 0, 3 ) == 'v6-' ) {
+                       return 'v6-' . substr( substr( $hex, 3 ), 0, floor( $wgBlockCIDRLimit['IPv6'] / 4 ) );
+               } else {
+                       return substr( $hex, 0, floor( $wgBlockCIDRLimit['IPv4'] / 4 ) );
+               }
+       }
+
+       /**
+        * Given a database row from the ipblocks table, initialize
+        * member variables
+        * @param stdClass $row A row from the ipblocks table
+        */
+       protected function initFromRow( $row ) {
+               $this->setTarget( $row->ipb_address );
+               $this->setBlocker( User::newFromAnyId(
+                       $row->ipb_by, $row->ipb_by_text, $row->ipb_by_actor ?? null
+               ) );
+
+               $this->setTimestamp( wfTimestamp( TS_MW, $row->ipb_timestamp ) );
+               $this->mAuto = $row->ipb_auto;
+               $this->setHideName( $row->ipb_deleted );
+               $this->mId = (int)$row->ipb_id;
+               $this->mParentBlockId = $row->ipb_parent_block_id;
+
+               // I wish I didn't have to do this
+               $db = wfGetDB( DB_REPLICA );
+               $this->setExpiry( $db->decodeExpiry( $row->ipb_expiry ) );
+               $this->setReason(
+                       CommentStore::getStore()
+                       // Legacy because $row may have come from self::selectFields()
+                       ->getCommentLegacy( $db, 'ipb_reason', $row )->text
+               );
+
+               $this->isHardblock( !$row->ipb_anon_only );
+               $this->isAutoblocking( $row->ipb_enable_autoblock );
+               $this->isSitewide( (bool)$row->ipb_sitewide );
+
+               $this->isCreateAccountBlocked( $row->ipb_create_account );
+               $this->isEmailBlocked( $row->ipb_block_email );
+               $this->isUsertalkEditAllowed( $row->ipb_allow_usertalk );
+       }
+
+       /**
+        * Create a new DatabaseBlock object from a database row
+        * @param stdClass $row Row from the ipblocks table
+        * @return DatabaseBlock
+        */
+       public static function newFromRow( $row ) {
+               $block = new DatabaseBlock;
+               $block->initFromRow( $row );
+               return $block;
+       }
+
+       /**
+        * Delete the row from the IP blocks table.
+        *
+        * @throws MWException
+        * @return bool
+        */
+       public function delete() {
+               if ( wfReadOnly() ) {
+                       return false;
+               }
+
+               if ( !$this->getId() ) {
+                       throw new MWException(
+                               __METHOD__ . " requires that the mId member be filled\n"
+                       );
+               }
+
+               $dbw = wfGetDB( DB_MASTER );
+
+               $this->getBlockRestrictionStore()->deleteByParentBlockId( $this->getId() );
+               $dbw->delete( 'ipblocks', [ 'ipb_parent_block_id' => $this->getId() ], __METHOD__ );
+
+               $this->getBlockRestrictionStore()->deleteByBlockId( $this->getId() );
+               $dbw->delete( 'ipblocks', [ 'ipb_id' => $this->getId() ], __METHOD__ );
+
+               return $dbw->affectedRows() > 0;
+       }
+
+       /**
+        * Insert a block into the block table. Will fail if there is a conflicting
+        * block (same name and options) already in the database.
+        *
+        * @param IDatabase|null $dbw If you have one available
+        * @return bool|array False on failure, assoc array on success:
+        *      ('id' => block ID, 'autoIds' => array of autoblock IDs)
+        */
+       public function insert( $dbw = null ) {
+               global $wgBlockDisablesLogin;
+
+               if ( !$this->getBlocker() || $this->getBlocker()->getName() === '' ) {
+                       throw new MWException( 'Cannot insert a block without a blocker set' );
+               }
+
+               wfDebug( __METHOD__ . "; timestamp {$this->mTimestamp}\n" );
+
+               if ( $dbw === null ) {
+                       $dbw = wfGetDB( DB_MASTER );
+               }
+
+               self::purgeExpired();
+
+               $row = $this->getDatabaseArray( $dbw );
+
+               $dbw->insert( 'ipblocks', $row, __METHOD__, [ 'IGNORE' ] );
+               $affected = $dbw->affectedRows();
+               if ( $affected ) {
+                       $this->setId( $dbw->insertId() );
+                       if ( $this->restrictions ) {
+                               $this->getBlockRestrictionStore()->insert( $this->restrictions );
+                       }
+               }
+
+               # Don't collide with expired blocks.
+               # Do this after trying to insert to avoid locking.
+               if ( !$affected ) {
+                       # T96428: The ipb_address index uses a prefix on a field, so
+                       # use a standard SELECT + DELETE to avoid annoying gap locks.
+                       $ids = $dbw->selectFieldValues( 'ipblocks',
+                               'ipb_id',
+                               [
+                                       'ipb_address' => $row['ipb_address'],
+                                       'ipb_user' => $row['ipb_user'],
+                                       'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() )
+                               ],
+                               __METHOD__
+                       );
+                       if ( $ids ) {
+                               $dbw->delete( 'ipblocks', [ 'ipb_id' => $ids ], __METHOD__ );
+                               $this->getBlockRestrictionStore()->deleteByBlockId( $ids );
+                               $dbw->insert( 'ipblocks', $row, __METHOD__, [ 'IGNORE' ] );
+                               $affected = $dbw->affectedRows();
+                               $this->setId( $dbw->insertId() );
+                               if ( $this->restrictions ) {
+                                       $this->getBlockRestrictionStore()->insert( $this->restrictions );
+                               }
+                       }
+               }
+
+               if ( $affected ) {
+                       $auto_ipd_ids = $this->doRetroactiveAutoblock();
+
+                       if ( $wgBlockDisablesLogin && $this->target instanceof User ) {
+                               // Change user login token to force them to be logged out.
+                               $this->target->setToken();
+                               $this->target->saveSettings();
+                       }
+
+                       return [ 'id' => $this->mId, 'autoIds' => $auto_ipd_ids ];
+               }
+
+               return false;
+       }
+
+       /**
+        * Update a block in the DB with new parameters.
+        * The ID field needs to be loaded first.
+        *
+        * @return bool|array False on failure, array on success:
+        *   ('id' => block ID, 'autoIds' => array of autoblock IDs)
+        */
+       public function update() {
+               wfDebug( __METHOD__ . "; timestamp {$this->mTimestamp}\n" );
+               $dbw = wfGetDB( DB_MASTER );
+
+               $dbw->startAtomic( __METHOD__ );
+
+               $result = $dbw->update(
+                       'ipblocks',
+                       $this->getDatabaseArray( $dbw ),
+                       [ 'ipb_id' => $this->getId() ],
+                       __METHOD__
+               );
+
+               // 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 = $this->getBlockRestrictionStore()->deleteByBlockId( $this->getId() );
+                       } else {
+                               $success = $this->getBlockRestrictionStore()->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)
+                       $dbw->update(
+                               'ipblocks',
+                               $this->getAutoblockUpdateArray( $dbw ),
+                               [ 'ipb_parent_block_id' => $this->getId() ],
+                               __METHOD__
+                       );
+
+                       // Only update the restrictions if they have been modified.
+                       if ( $this->restrictions !== null ) {
+                               $this->getBlockRestrictionStore()->updateByParentBlockId( $this->getId(), $this->restrictions );
+                       }
+               } else {
+                       // autoblock no longer required, delete corresponding autoblock(s)
+                       $this->getBlockRestrictionStore()->deleteByParentBlockId( $this->getId() );
+                       $dbw->delete(
+                               'ipblocks',
+                               [ 'ipb_parent_block_id' => $this->getId() ],
+                               __METHOD__
+                       );
+               }
+
+               $dbw->endAtomic( __METHOD__ );
+
+               if ( $result ) {
+                       $auto_ipd_ids = $this->doRetroactiveAutoblock();
+                       return [ 'id' => $this->mId, 'autoIds' => $auto_ipd_ids ];
+               }
+
+               return $result;
+       }
+
+       /**
+        * Get an array suitable for passing to $dbw->insert() or $dbw->update()
+        * @param IDatabase $dbw
+        * @return array
+        */
+       protected function getDatabaseArray( IDatabase $dbw ) {
+               $expiry = $dbw->encodeExpiry( $this->getExpiry() );
+
+               if ( $this->forcedTargetID ) {
+                       $uid = $this->forcedTargetID;
+               } else {
+                       $uid = $this->target instanceof User ? $this->target->getId() : 0;
+               }
+
+               $a = [
+                       'ipb_address'          => (string)$this->target,
+                       'ipb_user'             => $uid,
+                       'ipb_timestamp'        => $dbw->timestamp( $this->getTimestamp() ),
+                       'ipb_auto'             => $this->mAuto,
+                       'ipb_anon_only'        => !$this->isHardblock(),
+                       'ipb_create_account'   => $this->isCreateAccountBlocked(),
+                       'ipb_enable_autoblock' => $this->isAutoblocking(),
+                       'ipb_expiry'           => $expiry,
+                       'ipb_range_start'      => $this->getRangeStart(),
+                       'ipb_range_end'        => $this->getRangeEnd(),
+                       'ipb_deleted'          => intval( $this->getHideName() ), // typecast required for SQLite
+                       'ipb_block_email'      => $this->isEmailBlocked(),
+                       'ipb_allow_usertalk'   => $this->isUsertalkEditAllowed(),
+                       'ipb_parent_block_id'  => $this->mParentBlockId,
+                       'ipb_sitewide'         => $this->isSitewide(),
+               ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->getReason() )
+                       + ActorMigration::newMigration()->getInsertValues( $dbw, 'ipb_by', $this->getBlocker() );
+
+               return $a;
+       }
+
+       /**
+        * @param IDatabase $dbw
+        * @return array
+        */
+       protected function getAutoblockUpdateArray( IDatabase $dbw ) {
+               return [
+                       'ipb_create_account'   => $this->isCreateAccountBlocked(),
+                       'ipb_deleted'          => (int)$this->getHideName(), // typecast required for SQLite
+                       'ipb_allow_usertalk'   => $this->isUsertalkEditAllowed(),
+                       'ipb_sitewide'         => $this->isSitewide(),
+               ] + CommentStore::getStore()->insert( $dbw, 'ipb_reason', $this->getReason() )
+                       + ActorMigration::newMigration()->getInsertValues( $dbw, 'ipb_by', $this->getBlocker() );
+       }
+
+       /**
+        * Retroactively autoblocks the last IP used by the user (if it is a user)
+        * blocked by this block.
+        *
+        * @return array IDs of retroactive autoblocks made
+        */
+       protected function doRetroactiveAutoblock() {
+               $blockIds = [];
+               # If autoblock is enabled, autoblock the LAST IP(s) used
+               if ( $this->isAutoblocking() && $this->getType() == self::TYPE_USER ) {
+                       wfDebug( "Doing retroactive autoblocks for " . $this->getTarget() . "\n" );
+
+                       $continue = Hooks::run(
+                               'PerformRetroactiveAutoblock', [ $this, &$blockIds ] );
+
+                       if ( $continue ) {
+                               self::defaultRetroactiveAutoblock( $this, $blockIds );
+                       }
+               }
+               return $blockIds;
+       }
+
+       /**
+        * Retroactively autoblocks the last IP used by the user (if it is a user)
+        * blocked by this block. This will use the recentchanges table.
+        *
+        * @param DatabaseBlock $block
+        * @param array &$blockIds
+        */
+       protected static function defaultRetroactiveAutoblock( DatabaseBlock $block, array &$blockIds ) {
+               global $wgPutIPinRC;
+
+               // No IPs are in recentchanges table, so nothing to select
+               if ( !$wgPutIPinRC ) {
+                       return;
+               }
+
+               // Autoblocks only apply to TYPE_USER
+               if ( $block->getType() !== self::TYPE_USER ) {
+                       return;
+               }
+               $target = $block->getTarget(); // TYPE_USER => always a User object
+
+               $dbr = wfGetDB( DB_REPLICA );
+               $rcQuery = ActorMigration::newMigration()->getWhere( $dbr, 'rc_user', $target, false );
+
+               $options = [ 'ORDER BY' => 'rc_timestamp DESC' ];
+
+               // Just the last IP used.
+               $options['LIMIT'] = 1;
+
+               $res = $dbr->select(
+                       [ 'recentchanges' ] + $rcQuery['tables'],
+                       [ 'rc_ip' ],
+                       $rcQuery['conds'],
+                       __METHOD__,
+                       $options,
+                       $rcQuery['joins']
+               );
+
+               if ( !$res->numRows() ) {
+                       # No results, don't autoblock anything
+                       wfDebug( "No IP found to retroactively autoblock\n" );
+               } else {
+                       foreach ( $res as $row ) {
+                               if ( $row->rc_ip ) {
+                                       $id = $block->doAutoblock( $row->rc_ip );
+                                       if ( $id ) {
+                                               $blockIds[] = $id;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Checks whether a given IP is on the autoblock whitelist.
+        * TODO: this probably belongs somewhere else, but not sure where...
+        *
+        * @param string $ip The IP to check
+        * @return bool
+        */
+       public static function isWhitelistedFromAutoblocks( $ip ) {
+               // Try to get the autoblock_whitelist from the cache, as it's faster
+               // than getting the msg raw and explode()'ing it.
+               $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+               $lines = $cache->getWithSetCallback(
+                       $cache->makeKey( 'ip-autoblock', 'whitelist' ),
+                       $cache::TTL_DAY,
+                       function ( $curValue, &$ttl, array &$setOpts ) {
+                               $setOpts += Database::getCacheSetOptions( wfGetDB( DB_REPLICA ) );
+
+                               return explode( "\n",
+                                       wfMessage( 'autoblock_whitelist' )->inContentLanguage()->plain() );
+                       }
+               );
+
+               wfDebug( "Checking the autoblock whitelist..\n" );
+
+               foreach ( $lines as $line ) {
+                       # List items only
+                       if ( substr( $line, 0, 1 ) !== '*' ) {
+                               continue;
+                       }
+
+                       $wlEntry = substr( $line, 1 );
+                       $wlEntry = trim( $wlEntry );
+
+                       wfDebug( "Checking $ip against $wlEntry..." );
+
+                       # Is the IP in this range?
+                       if ( IP::isInRange( $ip, $wlEntry ) ) {
+                               wfDebug( " IP $ip matches $wlEntry, not autoblocking\n" );
+                               return true;
+                       } else {
+                               wfDebug( " No match\n" );
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Autoblocks the given IP, referring to this block.
+        *
+        * @param string $autoblockIP The IP to autoblock.
+        * @return int|bool ID if an autoblock was inserted, false if not.
+        */
+       public function doAutoblock( $autoblockIP ) {
+               # If autoblocks are disabled, go away.
+               if ( !$this->isAutoblocking() ) {
+                       return false;
+               }
+
+               # Check for presence on the autoblock whitelist.
+               if ( self::isWhitelistedFromAutoblocks( $autoblockIP ) ) {
+                       return false;
+               }
+
+               // Avoid PHP 7.1 warning of passing $this by reference
+               $block = $this;
+               # Allow hooks to cancel the autoblock.
+               if ( !Hooks::run( 'AbortAutoblock', [ $autoblockIP, &$block ] ) ) {
+                       wfDebug( "Autoblock aborted by hook.\n" );
+                       return false;
+               }
+
+               # It's okay to autoblock. Go ahead and insert/update the block...
+
+               # Do not add a *new* block if the IP is already blocked.
+               $ipblock = self::newFromTarget( $autoblockIP );
+               if ( $ipblock ) {
+                       # Check if the block is an autoblock and would exceed the user block
+                       # if renewed. If so, do nothing, otherwise prolong the block time...
+                       if ( $ipblock->mAuto && // @todo Why not compare $ipblock->mExpiry?
+                               $this->getExpiry() > self::getAutoblockExpiry( $ipblock->getTimestamp() )
+                       ) {
+                               # Reset block timestamp to now and its expiry to
+                               # $wgAutoblockExpiry in the future
+                               $ipblock->updateTimestamp();
+                       }
+                       return false;
+               }
+
+               # Make a new block object with the desired properties.
+               $autoblock = new DatabaseBlock;
+               wfDebug( "Autoblocking {$this->getTarget()}@" . $autoblockIP . "\n" );
+               $autoblock->setTarget( $autoblockIP );
+               $autoblock->setBlocker( $this->getBlocker() );
+               $autoblock->setReason(
+                       wfMessage( 'autoblocker', $this->getTarget(), $this->getReason() )
+                               ->inContentLanguage()->plain()
+               );
+               $timestamp = wfTimestampNow();
+               $autoblock->setTimestamp( $timestamp );
+               $autoblock->mAuto = 1;
+               $autoblock->isCreateAccountBlocked( $this->isCreateAccountBlocked() );
+               # Continue suppressing the name if needed
+               $autoblock->setHideName( $this->getHideName() );
+               $autoblock->isUsertalkEditAllowed( $this->isUsertalkEditAllowed() );
+               $autoblock->mParentBlockId = $this->mId;
+               $autoblock->isSitewide( $this->isSitewide() );
+               $autoblock->setRestrictions( $this->getRestrictions() );
+
+               if ( $this->getExpiry() == 'infinity' ) {
+                       # Original block was indefinite, start an autoblock now
+                       $autoblock->setExpiry( self::getAutoblockExpiry( $timestamp ) );
+               } else {
+                       # If the user is already blocked with an expiry date, we don't
+                       # want to pile on top of that.
+                       $autoblock->setExpiry( min( $this->getExpiry(), self::getAutoblockExpiry( $timestamp ) ) );
+               }
+
+               # Insert the block...
+               $status = $autoblock->insert();
+               return $status
+                       ? $status['id']
+                       : false;
+       }
+
+       /**
+        * Check if a block has expired. Delete it if it is.
+        * @return bool
+        */
+       public function deleteIfExpired() {
+               if ( $this->isExpired() ) {
+                       wfDebug( __METHOD__ . " -- deleting\n" );
+                       $this->delete();
+                       $retVal = true;
+               } else {
+                       wfDebug( __METHOD__ . " -- not expired\n" );
+                       $retVal = false;
+               }
+
+               return $retVal;
+       }
+
+       /**
+        * Has the block expired?
+        * @return bool
+        */
+       public function isExpired() {
+               $timestamp = wfTimestampNow();
+               wfDebug( __METHOD__ . " checking current " . $timestamp . " vs $this->mExpiry\n" );
+
+               if ( !$this->getExpiry() ) {
+                       return false;
+               } else {
+                       return $timestamp > $this->getExpiry();
+               }
+       }
+
+       /**
+        * Is the block address valid (i.e. not a null string?)
+        *
+        * @deprecated since 1.33 No longer needed in core.
+        * @return bool
+        */
+       public function isValid() {
+               wfDeprecated( __METHOD__, '1.33' );
+               return $this->getTarget() != null;
+       }
+
+       /**
+        * Update the timestamp on autoblocks.
+        */
+       public function updateTimestamp() {
+               if ( $this->mAuto ) {
+                       $this->setTimestamp( wfTimestamp() );
+                       $this->setExpiry( self::getAutoblockExpiry( $this->getTimestamp() ) );
+
+                       $dbw = wfGetDB( DB_MASTER );
+                       $dbw->update( 'ipblocks',
+                               [ /* SET */
+                                       'ipb_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
+                                       'ipb_expiry' => $dbw->timestamp( $this->getExpiry() ),
+                               ],
+                               [ /* WHERE */
+                                       'ipb_id' => $this->getId(),
+                               ],
+                               __METHOD__
+                       );
+               }
+       }
+
+       /**
+        * Get the IP address at the start of the range in Hex form
+        * @throws MWException
+        * @return string IP in Hex form
+        */
+       public function getRangeStart() {
+               switch ( $this->type ) {
+                       case self::TYPE_USER:
+                               return '';
+                       case self::TYPE_IP:
+                               return IP::toHex( $this->target );
+                       case self::TYPE_RANGE:
+                               list( $start, /*...*/ ) = IP::parseRange( $this->target );
+                               return $start;
+                       default:
+                               throw new MWException( "Block with invalid type" );
+               }
+       }
+
+       /**
+        * Get the IP address at the end of the range in Hex form
+        * @throws MWException
+        * @return string IP in Hex form
+        */
+       public function getRangeEnd() {
+               switch ( $this->type ) {
+                       case self::TYPE_USER:
+                               return '';
+                       case self::TYPE_IP:
+                               return IP::toHex( $this->target );
+                       case self::TYPE_RANGE:
+                               list( /*...*/, $end ) = IP::parseRange( $this->target );
+                               return $end;
+                       default:
+                               throw new MWException( "Block with invalid type" );
+               }
+       }
+
+       /**
+        * @inheritDoc
+        */
+       public function getId() {
+               return $this->mId;
+       }
+
+       /**
+        * Set the block ID
+        *
+        * @param int $blockId
+        * @return self
+        */
+       private function setId( $blockId ) {
+               $this->mId = (int)$blockId;
+
+               if ( is_array( $this->restrictions ) ) {
+                       $this->restrictions = $this->getBlockRestrictionStore()->setBlockId(
+                               $blockId, $this->restrictions
+                       );
+               }
+
+               return $this;
+       }
+
+       /**
+        * Get/set a flag determining whether the master is used for reads
+        *
+        * @param bool|null $x
+        * @return bool
+        */
+       public function fromMaster( $x = null ) {
+               return wfSetVar( $this->mFromMaster, $x );
+       }
+
+       /**
+        * Get/set whether the block is a hardblock (affects logged-in users on a given IP/range)
+        * @param bool|null $x
+        * @return bool
+        */
+       public function isHardblock( $x = null ) {
+               wfSetVar( $this->isHardblock, $x );
+
+               # You can't *not* hardblock a user
+               return $this->getType() == self::TYPE_USER
+                       ? true
+                       : $this->isHardblock;
+       }
+
+       /**
+        * @param null|bool $x
+        * @return bool
+        */
+       public function isAutoblocking( $x = null ) {
+               wfSetVar( $this->isAutoblocking, $x );
+
+               # You can't put an autoblock on an IP or range as we don't have any history to
+               # look over to get more IPs from
+               return $this->getType() == self::TYPE_USER
+                       ? $this->isAutoblocking
+                       : false;
+       }
+
+       /**
+        * Get the block name, but with autoblocked IPs hidden as per standard privacy policy
+        * @return string Text is escaped
+        */
+       public function getRedactedName() {
+               if ( $this->mAuto ) {
+                       return Html::element(
+                               'span',
+                               [ 'class' => 'mw-autoblockid' ],
+                               wfMessage( 'autoblockid', $this->mId )->text()
+                       );
+               } else {
+                       return htmlspecialchars( $this->getTarget() );
+               }
+       }
+
+       /**
+        * Get a timestamp of the expiry for autoblocks
+        *
+        * @param string|int $timestamp
+        * @return string
+        */
+       public static function getAutoblockExpiry( $timestamp ) {
+               global $wgAutoblockExpiry;
+
+               return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
+       }
+
+       /**
+        * Purge expired blocks from the ipblocks table
+        */
+       public static function purgeExpired() {
+               if ( wfReadOnly() ) {
+                       return;
+               }
+
+               DeferredUpdates::addUpdate( new AutoCommitUpdate(
+                       wfGetDB( DB_MASTER ),
+                       __METHOD__,
+                       function ( IDatabase $dbw, $fname ) {
+                               $ids = $dbw->selectFieldValues( 'ipblocks',
+                                       'ipb_id',
+                                       [ 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ],
+                                       $fname
+                               );
+                               if ( $ids ) {
+                                       $blockRestrictionStore = MediaWikiServices::getInstance()->getBlockRestrictionStore();
+                                       $blockRestrictionStore->deleteByBlockId( $ids );
+
+                                       $dbw->delete( 'ipblocks', [ 'ipb_id' => $ids ], $fname );
+                               }
+                       }
+               ) );
+       }
+
+       /**
+        * Given a target and the target's type, get an existing block object if possible.
+        * @param string|User|int $specificTarget A block target, which may be one of several types:
+        *     * A user to block, in which case $target will be a User
+        *     * An IP to block, in which case $target will be a User generated by using
+        *       User::newFromName( $ip, false ) to turn off name validation
+        *     * An IP range, in which case $target will be a String "123.123.123.123/18" etc
+        *     * The ID of an existing block, in the format "#12345" (since pure numbers are valid
+        *       usernames
+        *     Calling this with a user, IP address or range will not select autoblocks, and will
+        *     only select a block where the targets match exactly (so looking for blocks on
+        *     1.2.3.4 will not select 1.2.0.0/16 or even 1.2.3.4/32)
+        * @param string|User|int|null $vagueTarget As above, but we will search for *any* block which
+        *     affects that target (so for an IP address, get ranges containing that IP; and also
+        *     get any relevant autoblocks). Leave empty or blank to skip IP-based lookups.
+        * @param bool $fromMaster Whether to use the DB_MASTER database
+        * @return DatabaseBlock|null (null if no relevant block could be found). The target and type
+        *     of the returned block will refer to the actual block which was found, which might
+        *     not be the same as the target you gave if you used $vagueTarget!
+        */
+       public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromMaster = false ) {
+               list( $target, $type ) = self::parseTarget( $specificTarget );
+               if ( $type == self::TYPE_ID || $type == self::TYPE_AUTO ) {
+                       return self::newFromID( $target );
+
+               } elseif ( $target === null && $vagueTarget == '' ) {
+                       # We're not going to find anything useful here
+                       # Be aware that the == '' check is explicit, since empty values will be
+                       # passed by some callers (T31116)
+                       return null;
+
+               } elseif ( in_array(
+                       $type,
+                       [ self::TYPE_USER, self::TYPE_IP, self::TYPE_RANGE, null ] )
+               ) {
+                       $block = new DatabaseBlock();
+                       $block->fromMaster( $fromMaster );
+
+                       if ( $type !== null ) {
+                               $block->setTarget( $target );
+                       }
+
+                       if ( $block->newLoad( $vagueTarget ) ) {
+                               return $block;
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Get all blocks that match any IP from an array of IP addresses
+        *
+        * @param array $ipChain List of IPs (strings), usually retrieved from the
+        *     X-Forwarded-For header of the request
+        * @param bool $isAnon Exclude anonymous-only blocks if false
+        * @param bool $fromMaster Whether to query the master or replica DB
+        * @return array Array of Blocks
+        * @since 1.22
+        */
+       public static function getBlocksForIPList( array $ipChain, $isAnon, $fromMaster = false ) {
+               if ( $ipChain === [] ) {
+                       return [];
+               }
+
+               $conds = [];
+               $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
+               foreach ( array_unique( $ipChain ) as $ipaddr ) {
+                       # Discard invalid IP addresses. Since XFF can be spoofed and we do not
+                       # necessarily trust the header given to us, make sure that we are only
+                       # checking for blocks on well-formatted IP addresses (IPv4 and IPv6).
+                       # Do not treat private IP spaces as special as it may be desirable for wikis
+                       # to block those IP ranges in order to stop misbehaving proxies that spoof XFF.
+                       if ( !IP::isValid( $ipaddr ) ) {
+                               continue;
+                       }
+                       # Don't check trusted IPs (includes local CDNs which will be in every request)
+                       if ( $proxyLookup->isTrustedProxy( $ipaddr ) ) {
+                               continue;
+                       }
+                       # Check both the original IP (to check against single blocks), as well as build
+                       # the clause to check for rangeblocks for the given IP.
+                       $conds['ipb_address'][] = $ipaddr;
+                       $conds[] = self::getRangeCond( IP::toHex( $ipaddr ) );
+               }
+
+               if ( $conds === [] ) {
+                       return [];
+               }
+
+               if ( $fromMaster ) {
+                       $db = wfGetDB( DB_MASTER );
+               } else {
+                       $db = wfGetDB( DB_REPLICA );
+               }
+               $conds = $db->makeList( $conds, LIST_OR );
+               if ( !$isAnon ) {
+                       $conds = [ $conds, 'ipb_anon_only' => 0 ];
+               }
+               $blockQuery = self::getQueryInfo();
+               $rows = $db->select(
+                       $blockQuery['tables'],
+                       array_merge( [ 'ipb_range_start', 'ipb_range_end' ], $blockQuery['fields'] ),
+                       $conds,
+                       __METHOD__,
+                       [],
+                       $blockQuery['joins']
+               );
+
+               $blocks = [];
+               foreach ( $rows as $row ) {
+                       $block = self::newFromRow( $row );
+                       if ( !$block->isExpired() ) {
+                               $blocks[] = $block;
+                       }
+               }
+
+               return $blocks;
+       }
+
+       /**
+        * From a list of multiple blocks, find the most exact and strongest block.
+        *
+        * The logic for finding the "best" block is:
+        *  - Blocks that match the block's target IP are preferred over ones in a range
+        *  - Hardblocks are chosen over softblocks that prevent account creation
+        *  - Softblocks that prevent account creation are chosen over other softblocks
+        *  - Other softblocks are chosen over autoblocks
+        *  - If there are multiple exact or range blocks at the same level, the one chosen
+        *    is random
+        * This should be used when $blocks were retrieved from the user's IP address
+        * and $ipChain is populated from the same IP address information.
+        *
+        * @param array $blocks Array of DatabaseBlock objects
+        * @param array $ipChain List of IPs (strings). This is used to determine how "close"
+        *     a block is to the server, and if a block matches exactly, or is in a range.
+        *     The order is furthest from the server to nearest e.g., (Browser, proxy1, proxy2,
+        *     local-cdn, ...)
+        * @throws MWException
+        * @return DatabaseBlock|null The "best" block from the list
+        */
+       public static function chooseBlock( array $blocks, array $ipChain ) {
+               if ( $blocks === [] ) {
+                       return null;
+               } elseif ( count( $blocks ) == 1 ) {
+                       return $blocks[0];
+               }
+
+               // Sort hard blocks before soft ones and secondarily sort blocks
+               // that disable account creation before those that don't.
+               usort( $blocks, function ( DatabaseBlock $a, DatabaseBlock $b ) {
+                       $aWeight = (int)$a->isHardblock() . (int)$a->appliesToRight( 'createaccount' );
+                       $bWeight = (int)$b->isHardblock() . (int)$b->appliesToRight( 'createaccount' );
+                       return strcmp( $bWeight, $aWeight ); // highest weight first
+               } );
+
+               $blocksListExact = [
+                       'hard' => false,
+                       'disable_create' => false,
+                       'other' => false,
+                       'auto' => false
+               ];
+               $blocksListRange = [
+                       'hard' => false,
+                       'disable_create' => false,
+                       'other' => false,
+                       'auto' => false
+               ];
+               $ipChain = array_reverse( $ipChain );
+
+               foreach ( $blocks as $block ) {
+                       // Stop searching if we have already have a "better" block. This
+                       // is why the order of the blocks matters
+                       if ( !$block->isHardblock() && $blocksListExact['hard'] ) {
+                               break;
+                       } elseif ( !$block->appliesToRight( 'createaccount' ) && $blocksListExact['disable_create'] ) {
+                               break;
+                       }
+
+                       foreach ( $ipChain as $checkip ) {
+                               $checkipHex = IP::toHex( $checkip );
+                               if ( (string)$block->getTarget() === $checkip ) {
+                                       if ( $block->isHardblock() ) {
+                                               $blocksListExact['hard'] = $blocksListExact['hard'] ?: $block;
+                                       } elseif ( $block->appliesToRight( 'createaccount' ) ) {
+                                               $blocksListExact['disable_create'] = $blocksListExact['disable_create'] ?: $block;
+                                       } elseif ( $block->mAuto ) {
+                                               $blocksListExact['auto'] = $blocksListExact['auto'] ?: $block;
+                                       } else {
+                                               $blocksListExact['other'] = $blocksListExact['other'] ?: $block;
+                                       }
+                                       // We found closest exact match in the ip list, so go to the next block
+                                       break;
+                               } elseif ( array_filter( $blocksListExact ) == []
+                                       && $block->getRangeStart() <= $checkipHex
+                                       && $block->getRangeEnd() >= $checkipHex
+                               ) {
+                                       if ( $block->isHardblock() ) {
+                                               $blocksListRange['hard'] = $blocksListRange['hard'] ?: $block;
+                                       } elseif ( $block->appliesToRight( 'createaccount' ) ) {
+                                               $blocksListRange['disable_create'] = $blocksListRange['disable_create'] ?: $block;
+                                       } elseif ( $block->mAuto ) {
+                                               $blocksListRange['auto'] = $blocksListRange['auto'] ?: $block;
+                                       } else {
+                                               $blocksListRange['other'] = $blocksListRange['other'] ?: $block;
+                                       }
+                                       break;
+                               }
+                       }
+               }
+
+               if ( array_filter( $blocksListExact ) == [] ) {
+                       $blocksList = &$blocksListRange;
+               } else {
+                       $blocksList = &$blocksListExact;
+               }
+
+               $chosenBlock = null;
+               if ( $blocksList['hard'] ) {
+                       $chosenBlock = $blocksList['hard'];
+               } elseif ( $blocksList['disable_create'] ) {
+                       $chosenBlock = $blocksList['disable_create'];
+               } elseif ( $blocksList['other'] ) {
+                       $chosenBlock = $blocksList['other'];
+               } elseif ( $blocksList['auto'] ) {
+                       $chosenBlock = $blocksList['auto'];
+               } else {
+                       throw new MWException( "Proxy block found, but couldn't be classified." );
+               }
+
+               return $chosenBlock;
+       }
+
+       /**
+        * @inheritDoc
+        *
+        * Autoblocks have whichever type corresponds to their target, so to detect if a block is an
+        * autoblock, we have to check the mAuto property instead.
+        */
+       public function getType() {
+               return $this->mAuto
+                       ? self::TYPE_AUTO
+                       : parent::getType();
+       }
+
+       /**
+        * Set the 'BlockID' cookie to this block's ID and expiry time. The cookie's expiry will be
+        * the same as the block's, to a maximum of 24 hours.
+        *
+        * @since 1.29
+        *
+        * @param WebResponse $response The response on which to set the cookie.
+        */
+       public function setCookie( WebResponse $response ) {
+               // Calculate the default expiry time.
+               $maxExpiryTime = wfTimestamp( TS_MW, wfTimestamp() + ( 24 * 60 * 60 ) );
+
+               // Use the block's expiry time only if it's less than the default.
+               $expiryTime = $this->getExpiry();
+               if ( $expiryTime === 'infinity' || $expiryTime > $maxExpiryTime ) {
+                       $expiryTime = $maxExpiryTime;
+               }
+
+               // Set the cookie. Reformat the MediaWiki datetime as a Unix timestamp for the cookie.
+               $expiryValue = DateTime::createFromFormat( 'YmdHis', $expiryTime )->format( 'U' );
+               $cookieOptions = [ 'httpOnly' => false ];
+               $cookieValue = $this->getCookieValue();
+               $response->setCookie( 'BlockID', $cookieValue, $expiryValue, $cookieOptions );
+       }
+
+       /**
+        * Unset the 'BlockID' cookie.
+        *
+        * @since 1.29
+        *
+        * @param WebResponse $response The response on which to unset the cookie.
+        */
+       public static function clearCookie( WebResponse $response ) {
+               $response->clearCookie( 'BlockID', [ 'httpOnly' => false ] );
+       }
+
+       /**
+        * Get the BlockID cookie's value for this block. This is usually the block ID concatenated
+        * with an HMAC in order to avoid spoofing (T152951), but if wgSecretKey is not set will just
+        * be the block ID.
+        *
+        * @since 1.29
+        *
+        * @return string The block ID, probably concatenated with "!" and the HMAC.
+        */
+       public function getCookieValue() {
+               $config = RequestContext::getMain()->getConfig();
+               $id = $this->getId();
+               $secretKey = $config->get( 'SecretKey' );
+               if ( !$secretKey ) {
+                       // If there's no secret key, don't append a HMAC.
+                       return $id;
+               }
+               $hmac = MWCryptHash::hmac( $id, $secretKey, false );
+               $cookieValue = $id . '!' . $hmac;
+               return $cookieValue;
+       }
+
+       /**
+        * Get the stored ID from the 'BlockID' cookie. The cookie's value is usually a combination of
+        * the ID and a HMAC (see DatabaseBlock::setCookie), but will sometimes only be the ID.
+        *
+        * @since 1.29
+        *
+        * @param string $cookieValue The string in which to find the ID.
+        *
+        * @return int|null The block ID, or null if the HMAC is present and invalid.
+        */
+       public static function getIdFromCookieValue( $cookieValue ) {
+               // Extract the ID prefix from the cookie value (may be the whole value, if no bang found).
+               $bangPos = strpos( $cookieValue, '!' );
+               $id = ( $bangPos === false ) ? $cookieValue : substr( $cookieValue, 0, $bangPos );
+               // Get the site-wide secret key.
+               $config = RequestContext::getMain()->getConfig();
+               $secretKey = $config->get( 'SecretKey' );
+               if ( !$secretKey ) {
+                       // If there's no secret key, just use the ID as given.
+                       return $id;
+               }
+               $storedHmac = substr( $cookieValue, $bangPos + 1 );
+               $calculatedHmac = MWCryptHash::hmac( $id, $secretKey, false );
+               if ( $calculatedHmac === $storedHmac ) {
+                       return $id;
+               } else {
+                       return null;
+               }
+       }
+
+       /**
+        * @inheritDoc
+        *
+        * Build different messages for autoblocks and partial blocks.
+        */
+       public function getPermissionsError( IContextSource $context ) {
+               $params = $this->getBlockErrorParams( $context );
+
+               $msg = 'blockedtext';
+               if ( $this->mAuto ) {
+                       $msg = 'autoblockedtext';
+               } elseif ( !$this->isSitewide() ) {
+                       $msg = 'blockedtext-partial';
+               }
+
+               array_unshift( $params, $msg );
+
+               return $params;
+       }
+
+       /**
+        * Get Restrictions.
+        *
+        * Getting the restrictions will perform a database query if the restrictions
+        * are not already loaded.
+        *
+        * @since 1.33
+        * @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 = $this->getBlockRestrictionStore()->loadByBlockId( $this->mId );
+               }
+
+               return $this->restrictions;
+       }
+
+       /**
+        * Set Restrictions.
+        *
+        * @since 1.33
+        * @param Restriction[] $restrictions
+        * @return self
+        */
+       public function setRestrictions( array $restrictions ) {
+               $this->restrictions = array_filter( $restrictions, function ( $restriction ) {
+                       return $restriction instanceof Restriction;
+               } );
+
+               return $this;
+       }
+
+       /**
+        * @inheritDoc
+        */
+       public function appliesToTitle( Title $title ) {
+               if ( $this->isSitewide() ) {
+                       return true;
+               }
+
+               $restrictions = $this->getRestrictions();
+               foreach ( $restrictions as $restriction ) {
+                       if ( $restriction->matches( $title ) ) {
+                               return true;
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * @inheritDoc
+        */
+       public function appliesToNamespace( $ns ) {
+               if ( $this->isSitewide() ) {
+                       return true;
+               }
+
+               // Blocks do not apply to virtual namespaces.
+               if ( $ns < 0 ) {
+                       return false;
+               }
+
+               $restriction = $this->findRestriction( NamespaceRestriction::TYPE, $ns );
+
+               return (bool)$restriction;
+       }
+
+       /**
+        * @inheritDoc
+        */
+       public function appliesToPage( $pageId ) {
+               if ( $this->isSitewide() ) {
+                       return true;
+               }
+
+               // If the pageId is not over zero, the block cannot apply to it.
+               if ( $pageId <= 0 ) {
+                       return false;
+               }
+
+               $restriction = $this->findRestriction( PageRestriction::TYPE, $pageId );
+
+               return (bool)$restriction;
+       }
+
+       /**
+        * Find Restriction by type and value.
+        *
+        * @param string $type
+        * @param int $value
+        * @return Restriction|null
+        */
+       private function findRestriction( $type, $value ) {
+               $restrictions = $this->getRestrictions();
+               foreach ( $restrictions as $restriction ) {
+                       if ( $restriction->getType() !== $type ) {
+                               continue;
+                       }
+
+                       if ( $restriction->getValue() === $value ) {
+                               return $restriction;
+                       }
+               }
+
+               return null;
+       }
+
+       /**
+        * @inheritDoc
+        */
+       public function shouldTrackWithCookie( $isAnon ) {
+               $config = RequestContext::getMain()->getConfig();
+               switch ( $this->getType() ) {
+                       case self::TYPE_IP:
+                       case self::TYPE_RANGE:
+                               return $isAnon && $config->get( 'CookieSetOnIpBlock' );
+                       case self::TYPE_USER:
+                               return !$isAnon && $config->get( 'CookieSetOnAutoblock' ) && $this->isAutoblocking();
+                       default:
+                               return false;
+               }
+       }
+
+       /**
+        * Get a BlockRestrictionStore instance
+        *
+        * @return BlockRestrictionStore
+        */
+       private function getBlockRestrictionStore() : BlockRestrictionStore {
+               return MediaWikiServices::getInstance()->getBlockRestrictionStore();
+       }
+}
+
+/**
+ * @deprecated since 1.34
+ */
+class_alias( DatabaseBlock::class, 'Block' );
index 8d86853..a18185c 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * A Block restriction object of type 'Namespace'.
+ * A block restriction object of type 'Namespace'.
  *
  * 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
index ea73d19..45aab46 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * A Block restriction object of type 'Page'.
+ * 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
index b20c83e..aa38d1f 100644 (file)
@@ -19,6 +19,7 @@
  *
  * @file
  */
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Revision\MutableRevisionRecord;
 use MediaWiki\Revision\RevisionRecord;
@@ -959,7 +960,7 @@ class Article implements Page {
                        } else {
                                $specificTarget = $titleText;
                        }
-                       if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
+                       if ( DatabaseBlock::newFromTarget( $specificTarget, $vagueTarget ) instanceof DatabaseBlock ) {
                                return [
                                        'index' => 'noindex',
                                        'follow' => 'nofollow'
@@ -1361,14 +1362,14 @@ class Article implements Page {
                        $rootPart = explode( '/', $title->getText() )[0];
                        $user = User::newFromName( $rootPart, false /* allow IP users */ );
                        $ip = User::isIP( $rootPart );
-                       $block = Block::newFromTarget( $user, $user );
+                       $block = DatabaseBlock::newFromTarget( $user, $user );
 
                        if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
                                $outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
                                        [ 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ] );
                        } elseif (
                                !is_null( $block ) &&
-                               $block->getType() != Block::TYPE_AUTO &&
+                               $block->getType() != DatabaseBlock::TYPE_AUTO &&
                                ( $block->isSitewide() || $user->isBlockedFrom( $title ) )
                        ) {
                                // Show log extract if the user is sitewide blocked or is partially
index b79a482..810fbd2 100644 (file)
@@ -21,6 +21,7 @@
  * @ingroup SpecialPage
  */
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\Restriction\NamespaceRestriction;
 use MediaWiki\MediaWikiServices;
@@ -36,7 +37,7 @@ class SpecialBlock extends FormSpecialPage {
         * or as subpage (Special:Block/Foo) */
        protected $target;
 
-       /** @var int Block::TYPE_ constant */
+       /** @var int DatabaseBlock::TYPE_ constant */
        protected $type;
 
        /** @var User|string The previous block target */
@@ -101,7 +102,7 @@ class SpecialBlock extends FormSpecialPage {
                }
 
                list( $this->previousTarget, /*...*/ ) =
-                       Block::parseTarget( $request->getVal( 'wpPreviousTarget' ) );
+                       DatabaseBlock::parseTarget( $request->getVal( 'wpPreviousTarget' ) );
                $this->requestedHideUser = $request->getBool( 'wpHideUser' );
        }
 
@@ -335,12 +336,12 @@ class SpecialBlock extends FormSpecialPage {
                # This won't be
                $fields['PreviousTarget']['default'] = (string)$this->target;
 
-               $block = Block::newFromTarget( $this->target );
+               $block = DatabaseBlock::newFromTarget( $this->target );
 
                // Populate fields if there is a block that is not an autoblock; if it is a range
                // block, only populate the fields if the range is the same as $this->target
-               if ( $block instanceof Block && $block->getType() !== Block::TYPE_AUTO
-                       && ( $this->type != Block::TYPE_RANGE
+               if ( $block instanceof DatabaseBlock && $block->getType() !== DatabaseBlock::TYPE_AUTO
+                       && ( $this->type != DatabaseBlock::TYPE_RANGE
                                || $block->getTarget() == $this->target )
                ) {
                        $fields['HardBlock']['default'] = $block->isHardblock();
@@ -409,13 +410,13 @@ class SpecialBlock extends FormSpecialPage {
                }
 
                if ( $this->getConfig()->get( 'EnablePartialBlocks' ) ) {
-                       if ( $block instanceof Block && !$block->isSitewide() ) {
+                       if ( $block instanceof DatabaseBlock && !$block->isSitewide() ) {
                                $fields['EditingRestriction']['default'] = 'partial';
                        } else {
                                $fields['EditingRestriction']['default'] = 'sitewide';
                        }
 
-                       if ( $block instanceof Block ) {
+                       if ( $block instanceof DatabaseBlock ) {
                                $pageRestrictions = [];
                                $namespaceRestrictions = [];
                                foreach ( $block->getRestrictions() as $restriction ) {
@@ -614,11 +615,11 @@ class SpecialBlock extends FormSpecialPage {
 
        /**
         * Determine the target of the block, and the type of target
-        * @todo Should be in Block.php?
+        * @todo Should be in DatabaseBlock.php?
         * @param string $par Subpage parameter passed to setup, or data value from
         *     the HTMLForm
         * @param WebRequest|null $request Optionally try and get data from a request too
-        * @return array [ User|string|null, Block::TYPE_ constant|null ]
+        * @return array [ User|string|null, DatabaseBlock::TYPE_ constant|null ]
         * @phan-return array{0:User|string|null,1:int|null}
         */
        public static function getTargetAndType( $par, WebRequest $request = null ) {
@@ -654,7 +655,7 @@ class SpecialBlock extends FormSpecialPage {
                                        break 2;
                        }
 
-                       list( $target, $type ) = Block::parseTarget( $target );
+                       list( $target, $type ) = DatabaseBlock::parseTarget( $target );
 
                        if ( $type !== null ) {
                                return [ $target, $type ];
@@ -698,7 +699,7 @@ class SpecialBlock extends FormSpecialPage {
                list( $target, $type ) = self::getTargetAndType( $value );
                $status = Status::newGood( $target );
 
-               if ( $type == Block::TYPE_USER ) {
+               if ( $type == DatabaseBlock::TYPE_USER ) {
                        if ( $target->isAnon() ) {
                                $status->fatal(
                                        'nosuchusershort',
@@ -710,7 +711,7 @@ class SpecialBlock extends FormSpecialPage {
                        if ( $unblockStatus !== true ) {
                                $status->fatal( 'badaccess', $unblockStatus );
                        }
-               } elseif ( $type == Block::TYPE_RANGE ) {
+               } elseif ( $type == DatabaseBlock::TYPE_RANGE ) {
                        list( $ip, $range ) = explode( '/', $target, 2 );
 
                        if (
@@ -736,7 +737,7 @@ class SpecialBlock extends FormSpecialPage {
                        if ( IP::isIPv6( $ip ) && $range < $wgBlockCIDRLimit['IPv6'] ) {
                                $status->fatal( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv6'] );
                        }
-               } elseif ( $type == Block::TYPE_IP ) {
+               } elseif ( $type == DatabaseBlock::TYPE_IP ) {
                        # All is well
                } else {
                        $status->fatal( 'badipaddress' );
@@ -770,7 +771,7 @@ class SpecialBlock extends FormSpecialPage {
 
                /** @var User $target */
                list( $target, $type ) = self::getTargetAndType( $data['Target'] );
-               if ( $type == Block::TYPE_USER ) {
+               if ( $type == DatabaseBlock::TYPE_USER ) {
                        $user = $target;
                        $target = $user->getName();
                        $userId = $user->getId();
@@ -787,10 +788,10 @@ class SpecialBlock extends FormSpecialPage {
                        ) {
                                return [ 'ipb-blockingself', 'ipb-confirmaction' ];
                        }
-               } elseif ( $type == Block::TYPE_RANGE ) {
+               } elseif ( $type == DatabaseBlock::TYPE_RANGE ) {
                        $user = null;
                        $userId = 0;
-               } elseif ( $type == Block::TYPE_IP ) {
+               } elseif ( $type == DatabaseBlock::TYPE_IP ) {
                        $user = null;
                        $target = $target->getName();
                        $userId = 0;
@@ -842,7 +843,7 @@ class SpecialBlock extends FormSpecialPage {
                        }
 
                        # Recheck params here...
-                       if ( $type != Block::TYPE_USER ) {
+                       if ( $type != DatabaseBlock::TYPE_USER ) {
                                $data['HideUser'] = false; # IP users should not be hidden
                        } elseif ( !wfIsInfinity( $data['Expiry'] ) ) {
                                # Bad expiry.
@@ -860,7 +861,7 @@ class SpecialBlock extends FormSpecialPage {
                }
 
                # Create block object.
-               $block = new Block();
+               $block = new DatabaseBlock();
                $block->setTarget( $target );
                $block->setBlocker( $performer );
                $block->setReason( $data['Reason'][0] );
@@ -923,7 +924,7 @@ class SpecialBlock extends FormSpecialPage {
                        } else {
                                # This returns direct blocks before autoblocks/rangeblocks, since we should
                                # be sure the user is blocked by now it should work for our purposes
-                               $currentBlock = Block::newFromTarget( $target );
+                               $currentBlock = DatabaseBlock::newFromTarget( $target );
                                if ( $block->equals( $currentBlock ) ) {
                                        return [ [ 'ipb_already_blocked', $block->getTarget() ] ];
                                }
@@ -984,7 +985,7 @@ class SpecialBlock extends FormSpecialPage {
                }
 
                # Can't watch a rangeblock
-               if ( $type != Block::TYPE_RANGE && $data['Watch'] ) {
+               if ( $type != DatabaseBlock::TYPE_RANGE && $data['Watch'] ) {
                        WatchAction::doWatch(
                                Title::makeTitle( NS_USER, $target ),
                                $performer,
@@ -992,7 +993,7 @@ class SpecialBlock extends FormSpecialPage {
                        );
                }
 
-               # Block constructor sanitizes certain block options on insert
+               # DatabaseBlock constructor sanitizes certain block options on insert
                $data['BlockEmail'] = $block->isEmailBlocked();
                $data['AutoBlock'] = $block->isAutoblocking();
 
@@ -1141,7 +1142,7 @@ class SpecialBlock extends FormSpecialPage {
                                }
                        } elseif (
                                $target instanceof User &&
-                               $performer->getBlock() instanceof Block &&
+                               $performer->getBlock() instanceof DatabaseBlock &&
                                $performer->getBlock()->getBy() &&
                                $performer->getBlock()->getBy() === $target->getId()
                        ) {
@@ -1165,7 +1166,7 @@ class SpecialBlock extends FormSpecialPage {
         * Return a comma-delimited list of "flags" to be passed to the log
         * reader for this block, to provide more information in the logs
         * @param array $data From HTMLForm data
-        * @param int $type Block::TYPE_ constant (USER, RANGE, or IP)
+        * @param int $type DatabaseBlock::TYPE_ constant (USER, RANGE, or IP)
         * @return string
         */
        protected static function blockLogFlags( array $data, $type ) {
@@ -1177,7 +1178,7 @@ class SpecialBlock extends FormSpecialPage {
 
                # when blocking a user the option 'anononly' is not available/has no effect
                # -> do not write this into log
-               if ( !$data['HardBlock'] && $type != Block::TYPE_USER ) {
+               if ( !$data['HardBlock'] && $type != DatabaseBlock::TYPE_USER ) {
                        // For grepping: message block-log-flags-anononly
                        $flags[] = 'anononly';
                }
@@ -1188,7 +1189,7 @@ class SpecialBlock extends FormSpecialPage {
                }
 
                # Same as anononly, this is not displayed when blocking an IP address
-               if ( !$data['AutoBlock'] && $type == Block::TYPE_USER ) {
+               if ( !$data['AutoBlock'] && $type == DatabaseBlock::TYPE_USER ) {
                        // For grepping: message block-log-flags-noautoblock
                        $flags[] = 'noautoblock';
                }
index 8c6ad2d..9f7381c 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup SpecialPage
  */
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * A special page that lists existing blocks
  *
@@ -139,28 +141,28 @@ class SpecialBlockList extends SpecialPage {
                }
 
                if ( $this->target !== '' ) {
-                       list( $target, $type ) = Block::parseTarget( $this->target );
+                       list( $target, $type ) = DatabaseBlock::parseTarget( $this->target );
 
                        switch ( $type ) {
-                               case Block::TYPE_ID:
-                               case Block::TYPE_AUTO:
+                               case DatabaseBlock::TYPE_ID:
+                               case DatabaseBlock::TYPE_AUTO:
                                        $conds['ipb_id'] = $target;
                                        break;
 
-                               case Block::TYPE_IP:
-                               case Block::TYPE_RANGE:
+                               case DatabaseBlock::TYPE_IP:
+                               case DatabaseBlock::TYPE_RANGE:
                                        list( $start, $end ) = IP::parseRange( $target );
                                        $conds[] = wfGetDB( DB_REPLICA )->makeList(
                                                [
                                                        'ipb_address' => $target,
-                                                       Block::getRangeCond( $start, $end )
+                                                       DatabaseBlock::getRangeCond( $start, $end )
                                                ],
                                                LIST_OR
                                        );
                                        $conds['ipb_auto'] = 0;
                                        break;
 
-                               case Block::TYPE_USER:
+                               case DatabaseBlock::TYPE_USER:
                                        $conds['ipb_address'] = $target->getName();
                                        $conds['ipb_auto'] = 0;
                                        break;
index ce08392..d83853a 100644 (file)
@@ -21,6 +21,7 @@
  * @ingroup SpecialPage
  */
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Widget\DateInputWidget;
 
@@ -320,15 +321,16 @@ class SpecialContributions extends IncludableSpecialPage {
                        // Do not expose the autoblocks, since that may lead to a leak of accounts' IPs,
                        // and also this will display a totally irrelevant log entry as a current block.
                        if ( !$this->including() ) {
-                               // For IP ranges you must give Block::newFromTarget the CIDR string and not a user object.
+                               // For IP ranges you must give DatabaseBlock::newFromTarget the CIDR string
+                               // and not a user object.
                                if ( $userObj->isIPRange() ) {
-                                       $block = Block::newFromTarget( $userObj->getName(), $userObj->getName() );
+                                       $block = DatabaseBlock::newFromTarget( $userObj->getName(), $userObj->getName() );
                                } else {
-                                       $block = Block::newFromTarget( $userObj, $userObj );
+                                       $block = DatabaseBlock::newFromTarget( $userObj, $userObj );
                                }
 
-                               if ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
-                                       if ( $block->getType() == Block::TYPE_RANGE ) {
+                               if ( !is_null( $block ) && $block->getType() != DatabaseBlock::TYPE_AUTO ) {
+                                       if ( $block->getType() == DatabaseBlock::TYPE_RANGE ) {
                                                $nt = MediaWikiServices::getInstance()->getNamespaceInfo()->
                                                        getCanonicalName( NS_USER ) . ':' . $block->getTarget();
                                        }
@@ -388,7 +390,7 @@ class SpecialContributions extends IncludableSpecialPage {
                }
 
                if ( $sp->getUser()->isAllowed( 'block' ) ) { # Block / Change block / Unblock links
-                       if ( $target->getBlock() && $target->getBlock()->getType() != Block::TYPE_AUTO ) {
+                       if ( $target->getBlock() && $target->getBlock()->getType() != DatabaseBlock::TYPE_AUTO ) {
                                $tools['block'] = $linkRenderer->makeKnownLink( # Change block link
                                        SpecialPage::getTitleFor( 'Block', $username ),
                                        $sp->msg( 'change-blocklink' )->text()
index 73b438c..8817ba3 100644 (file)
@@ -21,6 +21,7 @@
  * @ingroup SpecialPage
  */
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\MediaWikiServices;
 
 /**
@@ -159,9 +160,9 @@ class DeletedContributionsPage extends SpecialPage {
                        $links = $this->getLanguage()->pipeList( $tools );
 
                        // Show a note if the user is blocked and display the last block log entry.
-                       $block = Block::newFromTarget( $userObj, $userObj );
-                       if ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
-                               if ( $block->getType() == Block::TYPE_RANGE ) {
+                       $block = DatabaseBlock::newFromTarget( $userObj, $userObj );
+                       if ( !is_null( $block ) && $block->getType() != DatabaseBlock::TYPE_AUTO ) {
+                               if ( $block->getType() == DatabaseBlock::TYPE_RANGE ) {
                                        $nt = MediaWikiServices::getInstance()->getNamespaceInfo()->
                                                getCanonicalName( NS_USER ) . ':' . $block->getTarget();
                                }
index a04fe4e..bedd2c5 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup SpecialPage
  */
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * A special page for unblocking users
  *
@@ -45,7 +47,7 @@ class SpecialUnblock extends SpecialPage {
                $this->checkReadOnly();
 
                list( $this->target, $this->type ) = SpecialBlock::getTargetAndType( $par, $this->getRequest() );
-               $this->block = Block::newFromTarget( $this->target );
+               $this->block = DatabaseBlock::newFromTarget( $this->target );
                if ( $this->target instanceof User ) {
                        # Set the 'relevant user' in the skin, so it displays links like Contributions,
                        # User logs, UserRights, etc.
@@ -67,17 +69,17 @@ class SpecialUnblock extends SpecialPage {
 
                if ( $form->show() ) {
                        switch ( $this->type ) {
-                               case Block::TYPE_IP:
+                               case DatabaseBlock::TYPE_IP:
                                        $out->addWikiMsg( 'unblocked-ip', wfEscapeWikiText( $this->target ) );
                                        break;
-                               case Block::TYPE_USER:
+                               case DatabaseBlock::TYPE_USER:
                                        $out->addWikiMsg( 'unblocked', wfEscapeWikiText( $this->target ) );
                                        break;
-                               case Block::TYPE_RANGE:
+                               case DatabaseBlock::TYPE_RANGE:
                                        $out->addWikiMsg( 'unblocked-range', wfEscapeWikiText( $this->target ) );
                                        break;
-                               case Block::TYPE_ID:
-                               case Block::TYPE_AUTO:
+                               case DatabaseBlock::TYPE_ID:
+                               case DatabaseBlock::TYPE_AUTO:
                                        $out->addWikiMsg( 'unblocked-id', wfEscapeWikiText( $this->target ) );
                                        break;
                        }
@@ -104,28 +106,28 @@ class SpecialUnblock extends SpecialPage {
                        ]
                ];
 
-               if ( $this->block instanceof Block ) {
+               if ( $this->block instanceof DatabaseBlock ) {
                        list( $target, $type ) = $this->block->getTargetAndType();
 
                        # Autoblocks are logged as "autoblock #123 because the IP was recently used by
                        # User:Foo, and we've just got any block, auto or not, that applies to a target
                        # the user has specified.  Someone could be fishing to connect IPs to autoblocks,
                        # so don't show any distinction between unblocked IPs and autoblocked IPs
-                       if ( $type == Block::TYPE_AUTO && $this->type == Block::TYPE_IP ) {
+                       if ( $type == DatabaseBlock::TYPE_AUTO && $this->type == DatabaseBlock::TYPE_IP ) {
                                $fields['Target']['default'] = $this->target;
                                unset( $fields['Name'] );
                        } else {
                                $fields['Target']['default'] = $target;
                                $fields['Target']['type'] = 'hidden';
                                switch ( $type ) {
-                                       case Block::TYPE_IP:
+                                       case DatabaseBlock::TYPE_IP:
                                                $fields['Name']['default'] = $this->getLinkRenderer()->makeKnownLink(
                                                        SpecialPage::getTitleFor( 'Contributions', $target->getName() ),
                                                        $target->getName()
                                                );
                                                $fields['Name']['raw'] = true;
                                                break;
-                                       case Block::TYPE_USER:
+                                       case DatabaseBlock::TYPE_USER:
                                                $fields['Name']['default'] = $this->getLinkRenderer()->makeLink(
                                                        $target->getUserPage(),
                                                        $target->getName()
@@ -133,11 +135,11 @@ class SpecialUnblock extends SpecialPage {
                                                $fields['Name']['raw'] = true;
                                                break;
 
-                                       case Block::TYPE_RANGE:
+                                       case DatabaseBlock::TYPE_RANGE:
                                                $fields['Name']['default'] = $target;
                                                break;
 
-                                       case Block::TYPE_AUTO:
+                                       case DatabaseBlock::TYPE_AUTO:
                                                $fields['Name']['default'] = $this->block->getRedactedName();
                                                $fields['Name']['raw'] = true;
                                                # Don't expose the real target of the autoblock
@@ -180,9 +182,9 @@ class SpecialUnblock extends SpecialPage {
        public static function processUnblock( array $data, IContextSource $context ) {
                $performer = $context->getUser();
                $target = $data['Target'];
-               $block = Block::newFromTarget( $data['Target'] );
+               $block = DatabaseBlock::newFromTarget( $data['Target'] );
 
-               if ( !$block instanceof Block ) {
+               if ( !$block instanceof DatabaseBlock ) {
                        return [ [ 'ipb_cant_unblock', $target ] ];
                }
 
@@ -197,7 +199,7 @@ class SpecialUnblock extends SpecialPage {
                # If the specified IP is a single address, and the block is a range block, don't
                # unblock the whole range.
                list( $target, $type ) = SpecialBlock::getTargetAndType( $target );
-               if ( $block->getType() == Block::TYPE_RANGE && $type == Block::TYPE_IP ) {
+               if ( $block->getType() == DatabaseBlock::TYPE_RANGE && $type == DatabaseBlock::TYPE_IP ) {
                        $range = $block->getTarget();
 
                        return [ [ 'ipb_blocked_as_range', $target, $range ] ];
@@ -232,7 +234,7 @@ class SpecialUnblock extends SpecialPage {
                }
 
                # Redact the name (IP address) for autoblocks
-               if ( $block->getType() == Block::TYPE_AUTO ) {
+               if ( $block->getType() == DatabaseBlock::TYPE_AUTO ) {
                        $page = Title::makeTitle( NS_USER, '#' . $block->getId() );
                } else {
                        $page = $block->getTarget() instanceof User
index a2dbb72..f7ad80c 100644 (file)
@@ -22,6 +22,7 @@
 /**
  * @ingroup Pager
  */
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\Restriction;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\Restriction\NamespaceRestriction;
@@ -107,10 +108,10 @@ class BlockListPager extends TablePager {
                                if ( $row->ipb_auto ) {
                                        $formatted = $this->msg( 'autoblockid', $row->ipb_id )->parse();
                                } else {
-                                       list( $target, $type ) = Block::parseTarget( $row->ipb_address );
+                                       list( $target, $type ) = DatabaseBlock::parseTarget( $row->ipb_address );
                                        switch ( $type ) {
-                                               case Block::TYPE_USER:
-                                               case Block::TYPE_IP:
+                                               case DatabaseBlock::TYPE_USER:
+                                               case DatabaseBlock::TYPE_IP:
                                                        $formatted = Linker::userLink( $target->getId(), $target );
                                                        $formatted .= Linker::userToolLinks(
                                                                $target->getId(),
@@ -119,7 +120,7 @@ class BlockListPager extends TablePager {
                                                                Linker::TOOL_LINKS_NOBLOCK
                                                        );
                                                        break;
-                                               case Block::TYPE_RANGE:
+                                               case DatabaseBlock::TYPE_RANGE:
                                                        $formatted = htmlspecialchars( $target );
                                        }
                                }
index 18d62f5..e8ca7ce 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use MediaWiki\Block\AbstractBlock;
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\SystemBlock;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Session\SessionManager;
@@ -1355,7 +1356,7 @@ class User implements IDBAccessObject, UserIdentity {
                if ( $user->isLoggedIn() ) {
                        $this->loadFromUserObject( $user );
                        if ( $user->getBlock() ) {
-                               // If this user is autoblocked, set a cookie to track the Block. This has to be done on
+                               // If this user is autoblocked, set a cookie to track the block. This has to be done on
                                // every session load, because an autoblocked editor might not edit again from the same
                                // IP address after being blocked.
                                $this->trackBlockWithCookie();
@@ -4355,7 +4356,7 @@ class User implements IDBAccessObject, UserIdentity {
                        return false;
                }
 
-               $userblock = Block::newFromTarget( $this->getName() );
+               $userblock = DatabaseBlock::newFromTarget( $this->getName() );
                if ( !$userblock ) {
                        return false;
                }
@@ -4377,7 +4378,9 @@ class User implements IDBAccessObject, UserIdentity {
                # blocked with createaccount disabled, prevent new account creation there even
                # when the user is logged in
                if ( $this->mBlockedFromCreateAccount === false && !$this->isAllowed( 'ipblock-exempt' ) ) {
-                       $this->mBlockedFromCreateAccount = Block::newFromTarget( null, $this->getRequest()->getIP() );
+                       $this->mBlockedFromCreateAccount = DatabaseBlock::newFromTarget(
+                               null, $this->getRequest()->getIP()
+                       );
                }
                return $this->mBlockedFromCreateAccount instanceof AbstractBlock
                        && $this->mBlockedFromCreateAccount->appliesToRight( 'createaccount' )
index 15bb4f3..db51e4b 100644 (file)
@@ -23,6 +23,8 @@
 
 require_once __DIR__ . '/Maintenance.php';
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * Maintenance script to clean up user blocks with user names not matching the
  * 'user' table.
@@ -39,7 +41,7 @@ class CleanupBlocks extends Maintenance {
 
        public function execute() {
                $db = $this->getDB( DB_MASTER );
-               $blockQuery = Block::getQueryInfo();
+               $blockQuery = DatabaseBlock::getQueryInfo();
 
                $max = $db->selectField( 'ipblocks', 'MAX(ipb_user)' );
 
@@ -77,14 +79,14 @@ class CleanupBlocks extends Maintenance {
                                        $blockQuery['joins']
                                );
                                foreach ( $res2 as $row2 ) {
-                                       $block = Block::newFromRow( $row2 );
+                                       $block = DatabaseBlock::newFromRow( $row2 );
                                        if ( !$bestBlock ) {
                                                $bestBlock = $block;
                                                continue;
                                        }
 
                                        // Find the most-restrictive block. Can't use
-                                       // Block::chooseBlock because that's for IP blocks, not
+                                       // DatabaseBlock::chooseBlock because that's for IP blocks, not
                                        // user blocks.
                                        $keep = null;
                                        if ( $keep === null && $block->getExpiry() !== $bestBlock->getExpiry() ) {
diff --git a/tests/phpunit/includes/BlockTest.php b/tests/phpunit/includes/BlockTest.php
deleted file mode 100644 (file)
index 3dc5e78..0000000
+++ /dev/null
@@ -1,765 +0,0 @@
-<?php
-
-use MediaWiki\Block\BlockRestrictionStore;
-use MediaWiki\Block\Restriction\PageRestriction;
-use MediaWiki\Block\Restriction\NamespaceRestriction;
-use MediaWiki\MediaWikiServices;
-
-/**
- * @group Database
- * @group Blocking
- */
-class BlockTest extends MediaWikiLangTestCase {
-
-       /**
-        * @return User
-        */
-       private function getUserForBlocking() {
-               $testUser = $this->getMutableTestUser();
-               $user = $testUser->getUser();
-               $user->addToDatabase();
-               TestUser::setPasswordForUser( $user, 'UTBlockeePassword' );
-               $user->saveSettings();
-               return $user;
-       }
-
-       /**
-        * @param User $user
-        *
-        * @return Block
-        * @throws MWException
-        */
-       private function addBlockForUser( User $user ) {
-               // Delete the last round's block if it's still there
-               $oldBlock = Block::newFromTarget( $user->getName() );
-               if ( $oldBlock ) {
-                       // An old block will prevent our new one from saving.
-                       $oldBlock->delete();
-               }
-
-               $blockOptions = [
-                       'address' => $user->getName(),
-                       'user' => $user->getId(),
-                       'by' => $this->getTestSysop()->getUser()->getId(),
-                       'reason' => 'Parce que',
-                       'expiry' => time() + 100500,
-               ];
-               $block = new Block( $blockOptions );
-
-               $block->insert();
-               // save up ID for use in assertion. Since ID is an autoincrement,
-               // its value might change depending on the order the tests are run.
-               // ApiBlockTest insert its own blocks!
-               if ( !$block->getId() ) {
-                       throw new MWException( "Failed to insert block for BlockTest; old leftover block remaining?" );
-               }
-
-               $this->addXffBlocks();
-
-               return $block;
-       }
-
-       /**
-        * @covers Block::newFromTarget
-        */
-       public function testINewFromTargetReturnsCorrectBlock() {
-               $user = $this->getUserForBlocking();
-               $block = $this->addBlockForUser( $user );
-
-               $this->assertTrue(
-                       $block->equals( Block::newFromTarget( $user->getName() ) ),
-                       "newFromTarget() returns the same block as the one that was made"
-               );
-       }
-
-       /**
-        * @covers Block::newFromID
-        */
-       public function testINewFromIDReturnsCorrectBlock() {
-               $user = $this->getUserForBlocking();
-               $block = $this->addBlockForUser( $user );
-
-               $this->assertTrue(
-                       $block->equals( Block::newFromID( $block->getId() ) ),
-                       "newFromID() returns the same block as the one that was made"
-               );
-       }
-
-       /**
-        * per T28425
-        * @covers Block::__construct
-        */
-       public function testT28425BlockTimestampDefaultsToTime() {
-               $user = $this->getUserForBlocking();
-               $block = $this->addBlockForUser( $user );
-               $madeAt = wfTimestamp( TS_MW );
-
-               // delta to stop one-off errors when things happen to go over a second mark.
-               $delta = abs( $madeAt - $block->getTimestamp() );
-               $this->assertLessThan(
-                       2,
-                       $delta,
-                       "If no timestamp is specified, the block is recorded as time()"
-               );
-       }
-
-       /**
-        * CheckUser since being changed to use Block::newFromTarget started failing
-        * because the new function didn't accept empty strings like Block::load()
-        * had. Regression T31116.
-        *
-        * @dataProvider provideT31116Data
-        * @covers Block::newFromTarget
-        */
-       public function testT31116NewFromTargetWithEmptyIp( $vagueTarget ) {
-               $user = $this->getUserForBlocking();
-               $initialBlock = $this->addBlockForUser( $user );
-               $block = Block::newFromTarget( $user->getName(), $vagueTarget );
-
-               $this->assertTrue(
-                       $initialBlock->equals( $block ),
-                       "newFromTarget() returns the same block as the one that was made when "
-                               . "given empty vagueTarget param " . var_export( $vagueTarget, true )
-               );
-       }
-
-       public static function provideT31116Data() {
-               return [
-                       [ null ],
-                       [ '' ],
-                       [ false ]
-               ];
-       }
-
-       /**
-        * @dataProvider provideNewFromTargetRangeBlocks
-        * @covers Block::newFromTarget
-        */
-       public function testNewFromTargetRangeBlocks( $targets, $ip, $expectedTarget ) {
-               $blocker = $this->getTestSysop()->getUser();
-
-               foreach ( $targets as $target ) {
-                       $block = new Block();
-                       $block->setTarget( $target );
-                       $block->setBlocker( $blocker );
-                       $block->insert();
-               }
-
-               // Should find the block with the narrowest range
-               $blockTarget = Block::newFromTarget( $this->getTestUser()->getUser(), $ip )->getTarget();
-               $this->assertSame(
-                       $blockTarget instanceof User ? $blockTarget->getName() : $blockTarget,
-                       $expectedTarget
-               );
-
-               foreach ( $targets as $target ) {
-                       $block = Block::newFromTarget( $target );
-                       $block->delete();
-               }
-       }
-
-       function provideNewFromTargetRangeBlocks() {
-               return [
-                       'Blocks to IPv4 ranges' => [
-                               [ '0.0.0.0/20', '0.0.0.0/30', '0.0.0.0/25' ],
-                               '0.0.0.0',
-                               '0.0.0.0/30'
-                       ],
-                       'Blocks to IPv6 ranges' => [
-                               [ '0:0:0:0:0:0:0:0/20', '0:0:0:0:0:0:0:0/30', '0:0:0:0:0:0:0:0/25' ],
-                               '0:0:0:0:0:0:0:0',
-                               '0:0:0:0:0:0:0:0/30'
-                       ],
-                       'Blocks to wide IPv4 range and IP' => [
-                               [ '0.0.0.0/16', '0.0.0.0' ],
-                               '0.0.0.0',
-                               '0.0.0.0'
-                       ],
-                       'Blocks to narrow IPv4 range and IP' => [
-                               [ '0.0.0.0/31', '0.0.0.0' ],
-                               '0.0.0.0',
-                               '0.0.0.0'
-                       ],
-                       'Blocks to wide IPv6 range and IP' => [
-                               [ '0:0:0:0:0:0:0:0/19', '0:0:0:0:0:0:0:0' ],
-                               '0:0:0:0:0:0:0:0',
-                               '0:0:0:0:0:0:0:0'
-                       ],
-                       'Blocks to narrow IPv6 range and IP' => [
-                               [ '0:0:0:0:0:0:0:0/127', '0:0:0:0:0:0:0:0' ],
-                               '0:0:0:0:0:0:0:0',
-                               '0:0:0:0:0:0:0:0'
-                       ],
-                       'Blocks to wide IPv6 range and IP, large numbers' => [
-                               [ '2000:DEAD:BEEF:A:0:0:0:0/19', '2000:DEAD:BEEF:A:0:0:0:0' ],
-                               '2000:DEAD:BEEF:A:0:0:0:0',
-                               '2000:DEAD:BEEF:A:0:0:0:0'
-                       ],
-                       'Blocks to narrow IPv6 range and IP, large numbers' => [
-                               [ '2000:DEAD:BEEF:A:0:0:0:0/127', '2000:DEAD:BEEF:A:0:0:0:0' ],
-                               '2000:DEAD:BEEF:A:0:0:0:0',
-                               '2000:DEAD:BEEF:A:0:0:0:0'
-                       ],
-               ];
-       }
-
-       /**
-        * @covers Block::appliesToRight
-        */
-       public function testBlockedUserCanNotCreateAccount() {
-               $username = 'BlockedUserToCreateAccountWith';
-               $u = User::newFromName( $username );
-               $u->addToDatabase();
-               $userId = $u->getId();
-               $this->assertNotEquals( 0, $userId, 'sanity' );
-               TestUser::setPasswordForUser( $u, 'NotRandomPass' );
-               unset( $u );
-
-               // Sanity check
-               $this->assertNull(
-                       Block::newFromTarget( $username ),
-                       "$username should not be blocked"
-               );
-
-               // Reload user
-               $u = User::newFromName( $username );
-               $this->assertFalse(
-                       $u->isBlockedFromCreateAccount(),
-                       "Our sandbox user should be able to create account before being blocked"
-               );
-
-               // Foreign perspective (blockee not on current wiki)...
-               $blockOptions = [
-                       'address' => $username,
-                       'user' => $userId,
-                       'reason' => 'crosswiki block...',
-                       'timestamp' => wfTimestampNow(),
-                       'expiry' => $this->db->getInfinity(),
-                       'createAccount' => true,
-                       'enableAutoblock' => true,
-                       'hideName' => true,
-                       'blockEmail' => true,
-                       'byText' => 'm>MetaWikiUser',
-               ];
-               $block = new Block( $blockOptions );
-               $block->insert();
-
-               // Reload block from DB
-               $userBlock = Block::newFromTarget( $username );
-               $this->assertTrue(
-                       (bool)$block->appliesToRight( 'createaccount' ),
-                       "Block object in DB should block right 'createaccount'"
-               );
-
-               $this->assertInstanceOf(
-                       Block::class,
-                       $userBlock,
-                       "'$username' block block object should be existent"
-               );
-
-               // Reload user
-               $u = User::newFromName( $username );
-               $this->assertTrue(
-                       (bool)$u->isBlockedFromCreateAccount(),
-                       "Our sandbox user '$username' should NOT be able to create account"
-               );
-       }
-
-       /**
-        * @covers Block::insert
-        */
-       public function testCrappyCrossWikiBlocks() {
-               // Delete the last round's block if it's still there
-               $oldBlock = Block::newFromTarget( 'UserOnForeignWiki' );
-               if ( $oldBlock ) {
-                       // An old block will prevent our new one from saving.
-                       $oldBlock->delete();
-               }
-
-               // Local perspective (blockee on current wiki)...
-               $user = User::newFromName( 'UserOnForeignWiki' );
-               $user->addToDatabase();
-               $userId = $user->getId();
-               $this->assertNotEquals( 0, $userId, 'sanity' );
-
-               // Foreign perspective (blockee not on current wiki)...
-               $blockOptions = [
-                       'address' => 'UserOnForeignWiki',
-                       'user' => $user->getId(),
-                       'reason' => 'crosswiki block...',
-                       'timestamp' => wfTimestampNow(),
-                       'expiry' => $this->db->getInfinity(),
-                       'createAccount' => true,
-                       'enableAutoblock' => true,
-                       'hideName' => true,
-                       'blockEmail' => true,
-                       'byText' => 'Meta>MetaWikiUser',
-               ];
-               $block = new Block( $blockOptions );
-
-               $res = $block->insert( $this->db );
-               $this->assertTrue( (bool)$res['id'], 'Block succeeded' );
-
-               $user = null; // clear
-
-               $block = Block::newFromID( $res['id'] );
-               $this->assertEquals(
-                       'UserOnForeignWiki',
-                       $block->getTarget()->getName(),
-                       'Correct blockee name'
-               );
-               $this->assertEquals( $userId, $block->getTarget()->getId(), 'Correct blockee id' );
-               $this->assertEquals( 'Meta>MetaWikiUser', $block->getBlocker()->getName(),
-                       'Correct blocker name' );
-               $this->assertEquals( 'Meta>MetaWikiUser', $block->getByName(), 'Correct blocker name' );
-               $this->assertEquals( 0, $block->getBy(), 'Correct blocker id' );
-       }
-
-       protected function addXffBlocks() {
-               static $inited = false;
-
-               if ( $inited ) {
-                       return;
-               }
-
-               $inited = true;
-
-               $blockList = [
-                       [ 'target' => '70.2.0.0/16',
-                               'type' => Block::TYPE_RANGE,
-                               'desc' => 'Range Hardblock',
-                               'ACDisable' => false,
-                               'isHardblock' => true,
-                               'isAutoBlocking' => false,
-                       ],
-                       [ 'target' => '2001:4860:4001::/48',
-                               'type' => Block::TYPE_RANGE,
-                               'desc' => 'Range6 Hardblock',
-                               'ACDisable' => false,
-                               'isHardblock' => true,
-                               'isAutoBlocking' => false,
-                       ],
-                       [ 'target' => '60.2.0.0/16',
-                               'type' => Block::TYPE_RANGE,
-                               'desc' => 'Range Softblock with AC Disabled',
-                               'ACDisable' => true,
-                               'isHardblock' => false,
-                               'isAutoBlocking' => false,
-                       ],
-                       [ 'target' => '50.2.0.0/16',
-                               'type' => Block::TYPE_RANGE,
-                               'desc' => 'Range Softblock',
-                               'ACDisable' => false,
-                               'isHardblock' => false,
-                               'isAutoBlocking' => false,
-                       ],
-                       [ 'target' => '50.1.1.1',
-                               'type' => Block::TYPE_IP,
-                               'desc' => 'Exact Softblock',
-                               'ACDisable' => false,
-                               'isHardblock' => false,
-                               'isAutoBlocking' => false,
-                       ],
-               ];
-
-               $blocker = $this->getTestUser()->getUser();
-               foreach ( $blockList as $insBlock ) {
-                       $target = $insBlock['target'];
-
-                       if ( $insBlock['type'] === Block::TYPE_IP ) {
-                               $target = User::newFromName( IP::sanitizeIP( $target ), false )->getName();
-                       } elseif ( $insBlock['type'] === Block::TYPE_RANGE ) {
-                               $target = IP::sanitizeRange( $target );
-                       }
-
-                       $block = new Block();
-                       $block->setTarget( $target );
-                       $block->setBlocker( $blocker );
-                       $block->setReason( $insBlock['desc'] );
-                       $block->setExpiry( 'infinity' );
-                       $block->isCreateAccountBlocked( $insBlock['ACDisable'] );
-                       $block->isHardblock( $insBlock['isHardblock'] );
-                       $block->isAutoblocking( $insBlock['isAutoBlocking'] );
-                       $block->insert();
-               }
-       }
-
-       public static function providerXff() {
-               return [
-                       [ 'xff' => '1.2.3.4, 70.2.1.1, 60.2.1.1, 2.3.4.5',
-                               'count' => 2,
-                               'result' => 'Range Hardblock'
-                       ],
-                       [ 'xff' => '1.2.3.4, 50.2.1.1, 60.2.1.1, 2.3.4.5',
-                               'count' => 2,
-                               'result' => 'Range Softblock with AC Disabled'
-                       ],
-                       [ 'xff' => '1.2.3.4, 70.2.1.1, 50.1.1.1, 2.3.4.5',
-                               'count' => 2,
-                               'result' => 'Exact Softblock'
-                       ],
-                       [ 'xff' => '1.2.3.4, 70.2.1.1, 50.2.1.1, 50.1.1.1, 2.3.4.5',
-                               'count' => 3,
-                               'result' => 'Exact Softblock'
-                       ],
-                       [ 'xff' => '1.2.3.4, 70.2.1.1, 50.2.1.1, 2.3.4.5',
-                               'count' => 2,
-                               'result' => 'Range Hardblock'
-                       ],
-                       [ 'xff' => '1.2.3.4, 70.2.1.1, 60.2.1.1, 2.3.4.5',
-                               'count' => 2,
-                               'result' => 'Range Hardblock'
-                       ],
-                       [ 'xff' => '50.2.1.1, 60.2.1.1, 2.3.4.5',
-                               'count' => 2,
-                               'result' => 'Range Softblock with AC Disabled'
-                       ],
-                       [ 'xff' => '1.2.3.4, 50.1.1.1, 60.2.1.1, 2.3.4.5',
-                               'count' => 2,
-                               'result' => 'Exact Softblock'
-                       ],
-                       [ 'xff' => '1.2.3.4, <$A_BUNCH-OF{INVALID}TEXT\>, 60.2.1.1, 2.3.4.5',
-                               'count' => 1,
-                               'result' => 'Range Softblock with AC Disabled'
-                       ],
-                       [ 'xff' => '1.2.3.4, 50.2.1.1, 2001:4860:4001:802::1003, 2.3.4.5',
-                               'count' => 2,
-                               'result' => 'Range6 Hardblock'
-                       ],
-               ];
-       }
-
-       /**
-        * @dataProvider providerXff
-        * @covers Block::getBlocksForIPList
-        * @covers Block::chooseBlock
-        */
-       public function testBlocksOnXff( $xff, $exCount, $exResult ) {
-               $user = $this->getUserForBlocking();
-               $this->addBlockForUser( $user );
-
-               $list = array_map( 'trim', explode( ',', $xff ) );
-               $xffblocks = Block::getBlocksForIPList( $list, true );
-               $this->assertEquals( $exCount, count( $xffblocks ), 'Number of blocks for ' . $xff );
-               $block = Block::chooseBlock( $xffblocks, $list );
-               $this->assertEquals(
-                       $exResult, $block->getReason(), 'Correct block type for XFF header ' . $xff
-               );
-       }
-
-       /**
-        * @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();
-       }
-
-       /**
-        * @covers Block::appliesToTitle
-        */
-       public function testAppliesToTitleReturnsTrueOnSitewideBlock() {
-               $this->setMwGlobals( [
-                       'wgBlockDisablesLogin' => false,
-               ] );
-               $user = $this->getTestUser()->getUser();
-               $block = new Block( [
-                       'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
-                       'allowUsertalk' => true,
-                       'sitewide' => true
-               ] );
-
-               $block->setTarget( $user );
-               $block->setBlocker( $this->getTestSysop()->getUser() );
-               $block->insert();
-
-               $title = $this->getExistingTestPage( 'Foo' )->getTitle();
-
-               $this->assertTrue( $block->appliesToTitle( $title ) );
-
-               // appliesToTitle() ignores allowUsertalk
-               $title = $user->getTalkPage();
-               $this->assertTrue( $block->appliesToTitle( $title ) );
-
-               $block->delete();
-       }
-
-       /**
-        * @covers Block::appliesToTitle
-        */
-       public function testAppliesToTitleOnPartialBlock() {
-               $this->setMwGlobals( [
-                       'wgBlockDisablesLogin' => false,
-               ] );
-               $user = $this->getTestUser()->getUser();
-               $block = new Block( [
-                       'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
-                       'allowUsertalk' => true,
-                       'sitewide' => false
-               ] );
-
-               $block->setTarget( $user );
-               $block->setBlocker( $this->getTestSysop()->getUser() );
-               $block->insert();
-
-               $pageFoo = $this->getExistingTestPage( 'Foo' );
-               $pageBar = $this->getExistingTestPage( 'Bar' );
-               $pageJohn = $this->getExistingTestPage( 'User:John' );
-
-               $pageRestriction = new PageRestriction( $block->getId(), $pageFoo->getId() );
-               $namespaceRestriction = new NamespaceRestriction( $block->getId(), NS_USER );
-               $this->getBlockRestrictionStore()->insert( [ $pageRestriction, $namespaceRestriction ] );
-
-               $this->assertTrue( $block->appliesToTitle( $pageFoo->getTitle() ) );
-               $this->assertFalse( $block->appliesToTitle( $pageBar->getTitle() ) );
-               $this->assertTrue( $block->appliesToTitle( $pageJohn->getTitle() ) );
-
-               $block->delete();
-       }
-
-       /**
-        * @covers Block::appliesToNamespace
-        * @covers Block::appliesToPage
-        */
-       public function testAppliesToReturnsTrueOnSitewideBlock() {
-               $this->setMwGlobals( [
-                       'wgBlockDisablesLogin' => false,
-               ] );
-               $user = $this->getTestUser()->getUser();
-               $block = new Block( [
-                       'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
-                       'allowUsertalk' => true,
-                       'sitewide' => true
-               ] );
-
-               $block->setTarget( $user );
-               $block->setBlocker( $this->getTestSysop()->getUser() );
-               $block->insert();
-
-               $title = $this->getExistingTestPage()->getTitle();
-
-               $this->assertTrue( $block->appliesToPage( $title->getArticleID() ) );
-               $this->assertTrue( $block->appliesToNamespace( NS_MAIN ) );
-               $this->assertTrue( $block->appliesToNamespace( NS_USER_TALK ) );
-
-               $block->delete();
-       }
-
-       /**
-        * @covers Block::appliesToPage
-        */
-       public function testAppliesToPageOnPartialPageBlock() {
-               $this->setMwGlobals( [
-                       'wgBlockDisablesLogin' => false,
-               ] );
-               $user = $this->getTestUser()->getUser();
-               $block = new Block( [
-                       'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
-                       'allowUsertalk' => true,
-                       'sitewide' => false
-               ] );
-
-               $block->setTarget( $user );
-               $block->setBlocker( $this->getTestSysop()->getUser() );
-               $block->insert();
-
-               $title = $this->getExistingTestPage()->getTitle();
-
-               $pageRestriction = new PageRestriction(
-                       $block->getId(),
-                       $title->getArticleID()
-               );
-               $this->getBlockRestrictionStore()->insert( [ $pageRestriction ] );
-
-               $this->assertTrue( $block->appliesToPage( $title->getArticleID() ) );
-
-               $block->delete();
-       }
-
-       /**
-        * @covers Block::appliesToNamespace
-        */
-       public function testAppliesToNamespaceOnPartialNamespaceBlock() {
-               $this->setMwGlobals( [
-                       'wgBlockDisablesLogin' => false,
-               ] );
-               $user = $this->getTestUser()->getUser();
-               $block = new Block( [
-                       'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
-                       'allowUsertalk' => true,
-                       'sitewide' => false
-               ] );
-
-               $block->setTarget( $user );
-               $block->setBlocker( $this->getTestSysop()->getUser() );
-               $block->insert();
-
-               $namespaceRestriction = new NamespaceRestriction( $block->getId(), NS_MAIN );
-               $this->getBlockRestrictionStore()->insert( [ $namespaceRestriction ] );
-
-               $this->assertTrue( $block->appliesToNamespace( NS_MAIN ) );
-               $this->assertFalse( $block->appliesToNamespace( NS_USER ) );
-
-               $block->delete();
-       }
-
-       /**
-        * @covers Block::appliesToRight
-        */
-       public function testBlockAllowsPurge() {
-               $this->setMwGlobals( [
-                       'wgBlockDisablesLogin' => false,
-               ] );
-               $block = new Block();
-               $this->assertFalse( $block->appliesToRight( 'purge' ) );
-       }
-
-       /**
-        * Get an instance of BlockRestrictionStore
-        *
-        * @return BlockRestrictionStore
-        */
-       protected function getBlockRestrictionStore() : BlockRestrictionStore {
-               return MediaWikiServices::getInstance()->getBlockRestrictionStore();
-       }
-}
index 4676fc5..2ce50b7 100644 (file)
@@ -3,11 +3,11 @@
 namespace MediaWiki\Tests\Permissions;
 
 use Action;
-use Block;
 use MediaWikiLangTestCase;
 use RequestContext;
 use Title;
 use User;
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\NamespaceRestriction;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\SystemBlock;
@@ -1036,7 +1036,7 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
                $prev = time();
                $now = time() + 120;
                $this->user->mBlockedby = $this->user->getId();
-               $this->user->mBlock = new Block( [
+               $this->user->mBlock = new DatabaseBlock( [
                        'address' => '127.0.8.1',
                        'by' => $this->user->getId(),
                        'reason' => 'no reason given',
@@ -1062,7 +1062,7 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
                global $wgLocalTZoffset;
                $wgLocalTZoffset = -60;
                $this->user->mBlockedby = $this->user->getName();
-               $this->user->mBlock = new Block( [
+               $this->user->mBlock = new DatabaseBlock( [
                        'address' => '127.0.8.1',
                        'by' => $this->user->getId(),
                        'reason' => 'no reason given',
@@ -1116,7 +1116,7 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
 
                // partial block message test
                $this->user->mBlockedby = $this->user->getName();
-               $this->user->mBlock = new Block( [
+               $this->user->mBlock = new DatabaseBlock( [
                        'address' => '127.0.8.1',
                        'by' => $this->user->getId(),
                        'reason' => 'no reason given',
@@ -1213,7 +1213,7 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
 
                $now = time();
                $this->user->mBlockedby = $this->user->getName();
-               $this->user->mBlock = new Block( [
+               $this->user->mBlock = new DatabaseBlock( [
                        'address' => '127.0.8.1',
                        'by' => $this->user->getId(),
                        'reason' => 'no reason given',
@@ -1248,7 +1248,7 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
 
                // Block the user
                $blocker = $this->getTestSysop()->getUser();
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'hideName' => true,
                        'allowUsertalk' => false,
                        'reason' => 'Because',
@@ -1260,7 +1260,7 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
 
                // Clear cache and confirm it loaded the block properly
                $user->clearInstanceCache();
-               $this->assertInstanceOf( Block::class, $user->getBlock( false ) );
+               $this->assertInstanceOf( DatabaseBlock::class, $user->getBlock( false ) );
                //$this->assertSame( $blocker->getName(), $user->blockedBy() );
                //$this->assertSame( 'Because', $user->blockedFor() );
                //$this->assertTrue( (bool)$user->isHidden() );
@@ -1285,7 +1285,7 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
         * @param bool $expect Expected result from User::isBlockedFrom()
         * @param array $options Additional test options:
         *  - 'blockAllowsUTEdit': (bool, default true) Value for $wgBlockAllowsUTEdit
-        *  - 'allowUsertalk': (bool, default false) Passed to Block::__construct()
+        *  - 'allowUsertalk': (bool, default false) Passed to DatabaseBlock::__construct()
         *  - 'pageRestrictions': (array|null) If non-empty, page restriction titles for the block.
         */
        public function testIsBlockedFrom( $title, $expect, array $options = [] ) {
@@ -1312,7 +1312,7 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
                        $restrictions[] = new NamespaceRestriction( 0, $ns );
                }
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
                        'allowUsertalk' => $options['allowUsertalk'] ?? false,
                        'sitewide' => !$restrictions,
index 1f8011d..e09546e 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\SystemBlock;
 use MediaWiki\MediaWikiServices;
@@ -920,7 +921,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                $prev = time();
                $now = time() + 120;
                $this->user->mBlockedby = $this->user->getId();
-               $this->user->mBlock = new Block( [
+               $this->user->mBlock = new DatabaseBlock( [
                        'address' => '127.0.8.1',
                        'by' => $this->user->getId(),
                        'reason' => 'no reason given',
@@ -943,7 +944,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                global $wgLocalTZoffset;
                $wgLocalTZoffset = -60;
                $this->user->mBlockedby = $this->user->getName();
-               $this->user->mBlock = new Block( [
+               $this->user->mBlock = new DatabaseBlock( [
                        'address' => '127.0.8.1',
                        'by' => $this->user->getId(),
                        'reason' => 'no reason given',
@@ -989,7 +990,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
 
                // partial block message test
                $this->user->mBlockedby = $this->user->getName();
-               $this->user->mBlock = new Block( [
+               $this->user->mBlock = new DatabaseBlock( [
                        'address' => '127.0.8.1',
                        'by' => $this->user->getId(),
                        'reason' => 'no reason given',
@@ -1073,7 +1074,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
 
                $now = time();
                $this->user->mBlockedby = $this->user->getName();
-               $this->user->mBlock = new Block( [
+               $this->user->mBlock = new DatabaseBlock( [
                        'address' => '127.0.8.1',
                        'by' => $this->user->getId(),
                        'reason' => 'no reason given',
index d80d627..5ad7736 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\PageRestriction;
 
 /**
@@ -213,7 +214,7 @@ class ActionTest extends MediaWikiTestCase {
                $page = $this->getExistingTestPage();
                $action = Action::factory( 'unblock', $page, $this->getContext() );
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $user,
                        'by' => $this->getTestSysop()->getUser()->getId(),
                        'expiry' => 'infinity',
index 64b2cd6..6a44ff3 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\MediaWikiServices;
 use Wikimedia\TestingAccessWrapper;
 
@@ -1325,7 +1326,7 @@ class ApiBaseTest extends ApiTestCase {
 
                // Has a blocked $user, so special block handling
                $user = $this->getMutableTestUser()->getUser();
-               $block = new \Block( [
+               $block = new DatabaseBlock( [
                        'address' => $user->getName(),
                        'user' => $user->getID(),
                        'by' => $this->getTestSysop()->getUser()->getId(),
@@ -1383,7 +1384,7 @@ class ApiBaseTest extends ApiTestCase {
 
                // Has a blocked $user, so special block handling
                $user = $this->getMutableTestUser()->getUser();
-               $block = new \Block( [
+               $block = new DatabaseBlock( [
                        'address' => $user->getName(),
                        'user' => $user->getID(),
                        'by' => $this->getTestSysop()->getUser()->getId(),
index a6e2d0d..ba5c003 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 use Wikimedia\TestingAccessWrapper;
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\SystemBlock;
 
 /**
@@ -26,11 +27,11 @@ class ApiBlockInfoTraitTest extends MediaWikiTestCase {
        public static function provideGetBlockDetails() {
                return [
                        'Sitewide block' => [
-                               new Block(),
+                               new DatabaseBlock(),
                                [ 'blockpartial' => false ],
                        ],
                        'Partial block' => [
-                               new Block( [ 'sitewide' => false ] ),
+                               new DatabaseBlock( [ 'sitewide' => false ] ),
                                [ 'blockpartial' => true ],
                        ],
                        'System block' => [
index 1c53147..060834f 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\Restriction\NamespaceRestriction;
 
@@ -57,7 +58,7 @@ class ApiBlockTest extends ApiTestCase {
                $ret = $this->doApiRequest( array_merge( $params, $extraParams ), null,
                        false, $blocker );
 
-               $block = Block::newFromTarget( $this->mUser->getName() );
+               $block = DatabaseBlock::newFromTarget( $this->mUser->getName() );
 
                $this->assertTrue( !is_null( $block ), 'Block is valid' );
 
@@ -89,7 +90,7 @@ class ApiBlockTest extends ApiTestCase {
                        'You cannot block or unblock other users because you are yourself blocked.' );
 
                $blocked = $this->getMutableTestUser( [ 'sysop' ] )->getUser();
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $blocked->getName(),
                        'by' => self::$users['sysop']->getUser()->getId(),
                        'reason' => 'Capriciousness',
@@ -237,7 +238,7 @@ class ApiBlockTest extends ApiTestCase {
 
                $this->doBlock();
 
-               $block = Block::newFromTarget( $this->mUser->getName() );
+               $block = DatabaseBlock::newFromTarget( $this->mUser->getName() );
 
                $this->assertTrue( $block->isSitewide() );
                $this->assertCount( 0, $block->getRestrictions() );
@@ -258,7 +259,7 @@ class ApiBlockTest extends ApiTestCase {
                        'namespacerestrictions' => $namespace,
                ] );
 
-               $block = Block::newFromTarget( $this->mUser->getName() );
+               $block = DatabaseBlock::newFromTarget( $this->mUser->getName() );
 
                $this->assertFalse( $block->isSitewide() );
                $this->assertCount( 2, $block->getRestrictions() );
index aeb829d..2045a13 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * Tests for MediaWiki api.php?action=edit.
  *
@@ -1474,9 +1476,9 @@ class ApiEditPageTest extends ApiTestCase {
        public function testEditWhileBlocked() {
                $name = 'Help:' . ucfirst( __FUNCTION__ );
 
-               $this->assertNull( Block::newFromTarget( '127.0.0.1' ), 'Sanity check' );
+               $this->assertNull( DatabaseBlock::newFromTarget( '127.0.0.1' ), 'Sanity check' );
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => self::$users['sysop']->getUser()->getName(),
                        'by' => self::$users['sysop']->getUser()->getId(),
                        'reason' => 'Capriciousness',
@@ -1495,7 +1497,7 @@ class ApiEditPageTest extends ApiTestCase {
                        $this->fail( 'Expected exception not thrown' );
                } catch ( ApiUsageException $ex ) {
                        $this->assertSame( 'You have been blocked from editing.', $ex->getMessage() );
-                       $this->assertNotNull( Block::newFromTarget( '127.0.0.1' ), 'Autoblock spread' );
+                       $this->assertNotNull( DatabaseBlock::newFromTarget( '127.0.0.1' ), 'Autoblock spread' );
                } finally {
                        $block->delete();
                        self::$users['sysop']->getUser()->clearInstanceCache();
index d437a52..d880923 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * @group API
  * @group Database
@@ -132,9 +134,9 @@ class ApiMoveTest extends ApiTestCase {
        }
 
        public function testMoveWhileBlocked() {
-               $this->assertNull( Block::newFromTarget( '127.0.0.1' ), 'Sanity check' );
+               $this->assertNull( DatabaseBlock::newFromTarget( '127.0.0.1' ), 'Sanity check' );
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => self::$users['sysop']->getUser()->getName(),
                        'by' => self::$users['sysop']->getUser()->getId(),
                        'reason' => 'Capriciousness',
@@ -156,7 +158,7 @@ class ApiMoveTest extends ApiTestCase {
                        $this->fail( 'Expected exception not thrown' );
                } catch ( ApiUsageException $ex ) {
                        $this->assertSame( 'You have been blocked from editing.', $ex->getMessage() );
-                       $this->assertNotNull( Block::newFromTarget( '127.0.0.1' ), 'Autoblock spread' );
+                       $this->assertNotNull( DatabaseBlock::newFromTarget( '127.0.0.1' ), 'Autoblock spread' );
                } finally {
                        $block->delete();
                        self::$users['sysop']->getUser()->clearInstanceCache();
index 6e00842..e281679 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\Restriction\NamespaceRestriction;
 
@@ -29,7 +30,7 @@ class ApiQueryBlocksTest extends ApiTestCase {
                $badActor = $this->getTestUser()->getUser();
                $sysop = $this->getTestSysop()->getUser();
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $badActor->getName(),
                        'user' => $badActor->getId(),
                        'by' => $sysop->getId(),
@@ -57,7 +58,7 @@ class ApiQueryBlocksTest extends ApiTestCase {
                $badActor = $this->getTestUser()->getUser();
                $sysop = $this->getTestSysop()->getUser();
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $badActor->getName(),
                        'user' => $badActor->getId(),
                        'by' => $sysop->getId(),
@@ -87,7 +88,7 @@ class ApiQueryBlocksTest extends ApiTestCase {
                $badActor = $this->getTestUser()->getUser();
                $sysop = $this->getTestSysop()->getUser();
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $badActor->getName(),
                        'user' => $badActor->getId(),
                        'by' => $sysop->getId(),
index 3aaad48..5da618e 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * @group API
  * @group medium
@@ -102,7 +104,7 @@ class ApiQueryInfoTest extends ApiTestCase {
                $badActor = $this->getTestUser()->getUser();
                $sysop = $this->getTestSysop()->getUser();
 
-               $block = new \Block( [
+               $block = new DatabaseBlock( [
                        'address' => $badActor->getName(),
                        'user' => $badActor->getId(),
                        'by' => $sysop->getId(),
index 703c150..556818e 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * @group medium
  * @covers ApiQueryUserInfo
@@ -13,7 +15,7 @@ class ApiQueryUserInfoTest extends ApiTestCase {
                        'userinfo'
                );
 
-               $block = new Block();
+               $block = new DatabaseBlock();
                $info = $apiQueryUserInfo->getBlockInfo( $block );
                $subset = [
                        'blockid' => null,
@@ -34,7 +36,7 @@ class ApiQueryUserInfoTest extends ApiTestCase {
                        'userinfo'
                );
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'sitewide' => false,
                ] );
                $info = $apiQueryUserInfo->getBlockInfo( $block );
index ea39da7..a1754fa 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * @group API
  * @group Database
@@ -26,24 +28,24 @@ class ApiUnblockTest extends ApiTestCase {
                $this->blockee = $this->getMutableTestUser()->getUser();
 
                // Initialize a blocked user (used by most tests, although not all)
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $this->blockee->getName(),
                        'by' => $this->blocker->getId(),
                ] );
                $result = $block->insert();
                $this->assertNotFalse( $result, 'Could not insert block' );
-               $blockFromDB = Block::newFromID( $result['id'] );
+               $blockFromDB = DatabaseBlock::newFromID( $result['id'] );
                $this->assertTrue( !is_null( $blockFromDB ), 'Could not retrieve block' );
        }
 
        private function getBlockFromParams( array $params ) {
                if ( array_key_exists( 'user', $params ) ) {
-                       return Block::newFromTarget( $params['user'] );
+                       return DatabaseBlock::newFromTarget( $params['user'] );
                }
                if ( array_key_exists( 'userid', $params ) ) {
-                       return Block::newFromTarget( User::newFromId( $params['userid'] ) );
+                       return DatabaseBlock::newFromTarget( User::newFromId( $params['userid'] ) );
                }
-               return Block::newFromId( $params['id'] );
+               return DatabaseBlock::newFromId( $params['id'] );
        }
 
        /**
@@ -64,7 +66,7 @@ class ApiUnblockTest extends ApiTestCase {
                // We only check later on whether the block existed to begin with, because maybe the caller
                // expects doApiRequestWithToken to throw, in which case the block might not be expected to
                // exist to begin with.
-               $this->assertInstanceOf( Block::class, $originalBlock, 'Block should initially exist' );
+               $this->assertInstanceOf( DatabaseBlock::class, $originalBlock, 'Block should initially exist' );
                $this->assertNull( $this->getBlockFromParams( $params ), 'Block should have been removed' );
        }
 
@@ -94,7 +96,7 @@ class ApiUnblockTest extends ApiTestCase {
        public function testUnblockWhenBlocked() {
                $this->setExpectedApiException( 'ipbblocked' );
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $this->blocker->getName(),
                        'by' => $this->getTestUser( 'sysop' )->getUser()->getId(),
                ] );
@@ -104,7 +106,7 @@ class ApiUnblockTest extends ApiTestCase {
        }
 
        public function testUnblockSelfWhenBlocked() {
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $this->blocker->getName(),
                        'by' => $this->getTestUser( 'sysop' )->getUser()->getId(),
                ] );
index 5889f82..a1bafed 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * @group API
  * @group Database
@@ -128,7 +130,7 @@ class ApiUserrightsTest extends ApiTestCase {
        public function testBlockedWithUserrights() {
                global $wgUser;
 
-               $block = new Block( [ 'address' => $wgUser, 'by' => $wgUser->getId(), ] );
+               $block = new DatabaseBlock( [ 'address' => $wgUser, 'by' => $wgUser->getId(), ] );
                $block->insert();
 
                try {
@@ -144,7 +146,7 @@ class ApiUserrightsTest extends ApiTestCase {
 
                $this->setPermissions( true, true );
 
-               $block = new Block( [ 'address' => $user, 'by' => $user->getId() ] );
+               $block = new DatabaseBlock( [ 'address' => $user, 'by' => $user->getId() ] );
                $block->insert();
 
                try {
index 5cf93c9..e6a1d38 100644 (file)
@@ -3,6 +3,7 @@
 namespace MediaWiki\Auth;
 
 use Config;
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Session\SessionInfo;
 use MediaWiki\Session\UserInfo;
 use Psr\Log\LoggerInterface;
@@ -1430,7 +1431,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
                        \TestUser::setPasswordForUser( $user, 'UTBlockeePassword' );
                        $user->saveSettings();
                }
-               $oldBlock = \Block::newFromTarget( 'UTBlockee' );
+               $oldBlock = DatabaseBlock::newFromTarget( 'UTBlockee' );
                if ( $oldBlock ) {
                        // An old block will prevent our new one from saving.
                        $oldBlock->delete();
@@ -1443,7 +1444,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
                        'expiry' => time() + 100500,
                        'createAccount' => true,
                ];
-               $block = new \Block( $blockOptions );
+               $block = new DatabaseBlock( $blockOptions );
                $block->insert();
                $status = $this->manager->checkAccountCreatePermissions( $user );
                $this->assertFalse( $status->isOK() );
@@ -1456,7 +1457,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
                        'expiry' => time() + 100500,
                        'createAccount' => true,
                ];
-               $block = new \Block( $blockOptions );
+               $block = new DatabaseBlock( $blockOptions );
                $block->insert();
                $scopeVariable = new ScopedCallback( [ $block, 'delete' ] );
                $status = $this->manager->checkAccountCreatePermissions( new \User );
index 14d7f09..c380036 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace MediaWiki\Auth;
 
+use MediaWiki\Block\DatabaseBlock;
 use Wikimedia\TestingAccessWrapper;
 
 /**
@@ -68,7 +69,7 @@ class CheckBlocksSecondaryAuthenticationProviderTest extends \MediaWikiTestCase
                        \TestUser::setPasswordForUser( $user, 'UTBlockeePassword' );
                        $user->saveSettings();
                }
-               $oldBlock = \Block::newFromTarget( 'UTBlockee' );
+               $oldBlock = DatabaseBlock::newFromTarget( 'UTBlockee' );
                if ( $oldBlock ) {
                        // An old block will prevent our new one from saving.
                        $oldBlock->delete();
@@ -81,7 +82,7 @@ class CheckBlocksSecondaryAuthenticationProviderTest extends \MediaWikiTestCase
                        'expiry' => time() + 100500,
                        'createAccount' => true,
                ];
-               $block = new \Block( $blockOptions );
+               $block = new DatabaseBlock( $blockOptions );
                $block->insert();
                return $user;
        }
@@ -154,7 +155,7 @@ class CheckBlocksSecondaryAuthenticationProviderTest extends \MediaWikiTestCase
                        'expiry' => time() + 100500,
                        'createAccount' => true,
                ];
-               $block = new \Block( $blockOptions );
+               $block = new DatabaseBlock( $blockOptions );
                $block->insert();
                $scopeVariable = new \Wikimedia\ScopedCallback( [ $block, 'delete' ] );
 
index ec6eea9..aec25c1 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 use MediaWiki\Block\BlockManager;
+use MediaWiki\Block\DatabaseBlock;
 
 /**
  * @group Blocking
@@ -51,7 +52,7 @@ class BlockManagerTest extends MediaWikiTestCase {
                        'wgCookieSetOnIpBlock' => true,
                ] );
 
-               $block = new Block( array_merge( [
+               $block = new DatabaseBlock( array_merge( [
                        'address' => $options[ 'target' ] ?: $this->user,
                        'by' => $this->sysopId,
                ], $options[ 'blockOptions' ] ) );
index 4eef457..ebbfde2 100644 (file)
@@ -3,6 +3,7 @@
 namespace MediaWiki\Tests\Block;
 
 use MediaWiki\Block\BlockRestrictionStore;
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\NamespaceRestriction;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\Restriction\Restriction;
@@ -583,7 +584,7 @@ class BlockRestrictionStoreTest extends \MediaWikiLangTestCase {
                $badActor = $this->getTestUser()->getUser();
                $sysop = $this->getTestSysop()->getUser();
 
-               $block = new \Block( [
+               $block = new DatabaseBlock( [
                        'address' => $badActor->getName(),
                        'user' => $badActor->getId(),
                        'by' => $sysop->getId(),
diff --git a/tests/phpunit/includes/block/DatabaseBlockTest.php b/tests/phpunit/includes/block/DatabaseBlockTest.php
new file mode 100644 (file)
index 0000000..0ef571d
--- /dev/null
@@ -0,0 +1,767 @@
+<?php
+
+use MediaWiki\Block\BlockRestrictionStore;
+use MediaWiki\Block\DatabaseBlock;
+use MediaWiki\Block\Restriction\PageRestriction;
+use MediaWiki\Block\Restriction\NamespaceRestriction;
+use MediaWiki\MediaWikiServices;
+
+/**
+ * @group Database
+ * @group Blocking
+ * @coversDefaultClass \MediaWiki\Block\DatabaseBlock
+ */
+class DatabaseBlockTest extends MediaWikiLangTestCase {
+
+       /**
+        * @return User
+        */
+       private function getUserForBlocking() {
+               $testUser = $this->getMutableTestUser();
+               $user = $testUser->getUser();
+               $user->addToDatabase();
+               TestUser::setPasswordForUser( $user, 'UTBlockeePassword' );
+               $user->saveSettings();
+               return $user;
+       }
+
+       /**
+        * @param User $user
+        *
+        * @return DatabaseBlock
+        * @throws MWException
+        */
+       private function addBlockForUser( User $user ) {
+               // Delete the last round's block if it's still there
+               $oldBlock = DatabaseBlock::newFromTarget( $user->getName() );
+               if ( $oldBlock ) {
+                       // An old block will prevent our new one from saving.
+                       $oldBlock->delete();
+               }
+
+               $blockOptions = [
+                       'address' => $user->getName(),
+                       'user' => $user->getId(),
+                       'by' => $this->getTestSysop()->getUser()->getId(),
+                       'reason' => 'Parce que',
+                       'expiry' => time() + 100500,
+               ];
+               $block = new DatabaseBlock( $blockOptions );
+
+               $block->insert();
+               // save up ID for use in assertion. Since ID is an autoincrement,
+               // its value might change depending on the order the tests are run.
+               // ApiBlockTest insert its own blocks!
+               if ( !$block->getId() ) {
+                       throw new MWException( "Failed to insert block for BlockTest; old leftover block remaining?" );
+               }
+
+               $this->addXffBlocks();
+
+               return $block;
+       }
+
+       /**
+        * @covers ::newFromTarget
+        */
+       public function testINewFromTargetReturnsCorrectBlock() {
+               $user = $this->getUserForBlocking();
+               $block = $this->addBlockForUser( $user );
+
+               $this->assertTrue(
+                       $block->equals( DatabaseBlock::newFromTarget( $user->getName() ) ),
+                       "newFromTarget() returns the same block as the one that was made"
+               );
+       }
+
+       /**
+        * @covers ::newFromID
+        */
+       public function testINewFromIDReturnsCorrectBlock() {
+               $user = $this->getUserForBlocking();
+               $block = $this->addBlockForUser( $user );
+
+               $this->assertTrue(
+                       $block->equals( DatabaseBlock::newFromID( $block->getId() ) ),
+                       "newFromID() returns the same block as the one that was made"
+               );
+       }
+
+       /**
+        * per T28425
+        * @covers ::__construct
+        */
+       public function testT28425BlockTimestampDefaultsToTime() {
+               $user = $this->getUserForBlocking();
+               $block = $this->addBlockForUser( $user );
+               $madeAt = wfTimestamp( TS_MW );
+
+               // delta to stop one-off errors when things happen to go over a second mark.
+               $delta = abs( $madeAt - $block->getTimestamp() );
+               $this->assertLessThan(
+                       2,
+                       $delta,
+                       "If no timestamp is specified, the block is recorded as time()"
+               );
+       }
+
+       /**
+        * CheckUser since being changed to use DatabaseBlock::newFromTarget started failing
+        * because the new function didn't accept empty strings like DatabaseBlock::load()
+        * had. Regression T31116.
+        *
+        * @dataProvider provideT31116Data
+        * @covers ::newFromTarget
+        */
+       public function testT31116NewFromTargetWithEmptyIp( $vagueTarget ) {
+               $user = $this->getUserForBlocking();
+               $initialBlock = $this->addBlockForUser( $user );
+               $block = DatabaseBlock::newFromTarget( $user->getName(), $vagueTarget );
+
+               $this->assertTrue(
+                       $initialBlock->equals( $block ),
+                       "newFromTarget() returns the same block as the one that was made when "
+                               . "given empty vagueTarget param " . var_export( $vagueTarget, true )
+               );
+       }
+
+       public static function provideT31116Data() {
+               return [
+                       [ null ],
+                       [ '' ],
+                       [ false ]
+               ];
+       }
+
+       /**
+        * @dataProvider provideNewFromTargetRangeBlocks
+        * @covers ::newFromTarget
+        */
+       public function testNewFromTargetRangeBlocks( $targets, $ip, $expectedTarget ) {
+               $blocker = $this->getTestSysop()->getUser();
+
+               foreach ( $targets as $target ) {
+                       $block = new DatabaseBlock();
+                       $block->setTarget( $target );
+                       $block->setBlocker( $blocker );
+                       $block->insert();
+               }
+
+               // Should find the block with the narrowest range
+               $blockTarget = DatabaseBlock::newFromTarget( $this->getTestUser()->getUser(), $ip )->getTarget();
+               $this->assertSame(
+                       $blockTarget instanceof User ? $blockTarget->getName() : $blockTarget,
+                       $expectedTarget
+               );
+
+               foreach ( $targets as $target ) {
+                       $block = DatabaseBlock::newFromTarget( $target );
+                       $block->delete();
+               }
+       }
+
+       function provideNewFromTargetRangeBlocks() {
+               return [
+                       'Blocks to IPv4 ranges' => [
+                               [ '0.0.0.0/20', '0.0.0.0/30', '0.0.0.0/25' ],
+                               '0.0.0.0',
+                               '0.0.0.0/30'
+                       ],
+                       'Blocks to IPv6 ranges' => [
+                               [ '0:0:0:0:0:0:0:0/20', '0:0:0:0:0:0:0:0/30', '0:0:0:0:0:0:0:0/25' ],
+                               '0:0:0:0:0:0:0:0',
+                               '0:0:0:0:0:0:0:0/30'
+                       ],
+                       'Blocks to wide IPv4 range and IP' => [
+                               [ '0.0.0.0/16', '0.0.0.0' ],
+                               '0.0.0.0',
+                               '0.0.0.0'
+                       ],
+                       'Blocks to narrow IPv4 range and IP' => [
+                               [ '0.0.0.0/31', '0.0.0.0' ],
+                               '0.0.0.0',
+                               '0.0.0.0'
+                       ],
+                       'Blocks to wide IPv6 range and IP' => [
+                               [ '0:0:0:0:0:0:0:0/19', '0:0:0:0:0:0:0:0' ],
+                               '0:0:0:0:0:0:0:0',
+                               '0:0:0:0:0:0:0:0'
+                       ],
+                       'Blocks to narrow IPv6 range and IP' => [
+                               [ '0:0:0:0:0:0:0:0/127', '0:0:0:0:0:0:0:0' ],
+                               '0:0:0:0:0:0:0:0',
+                               '0:0:0:0:0:0:0:0'
+                       ],
+                       'Blocks to wide IPv6 range and IP, large numbers' => [
+                               [ '2000:DEAD:BEEF:A:0:0:0:0/19', '2000:DEAD:BEEF:A:0:0:0:0' ],
+                               '2000:DEAD:BEEF:A:0:0:0:0',
+                               '2000:DEAD:BEEF:A:0:0:0:0'
+                       ],
+                       'Blocks to narrow IPv6 range and IP, large numbers' => [
+                               [ '2000:DEAD:BEEF:A:0:0:0:0/127', '2000:DEAD:BEEF:A:0:0:0:0' ],
+                               '2000:DEAD:BEEF:A:0:0:0:0',
+                               '2000:DEAD:BEEF:A:0:0:0:0'
+                       ],
+               ];
+       }
+
+       /**
+        * @covers ::appliesToRight
+        */
+       public function testBlockedUserCanNotCreateAccount() {
+               $username = 'BlockedUserToCreateAccountWith';
+               $u = User::newFromName( $username );
+               $u->addToDatabase();
+               $userId = $u->getId();
+               $this->assertNotEquals( 0, $userId, 'sanity' );
+               TestUser::setPasswordForUser( $u, 'NotRandomPass' );
+               unset( $u );
+
+               // Sanity check
+               $this->assertNull(
+                       DatabaseBlock::newFromTarget( $username ),
+                       "$username should not be blocked"
+               );
+
+               // Reload user
+               $u = User::newFromName( $username );
+               $this->assertFalse(
+                       $u->isBlockedFromCreateAccount(),
+                       "Our sandbox user should be able to create account before being blocked"
+               );
+
+               // Foreign perspective (blockee not on current wiki)...
+               $blockOptions = [
+                       'address' => $username,
+                       'user' => $userId,
+                       'reason' => 'crosswiki block...',
+                       'timestamp' => wfTimestampNow(),
+                       'expiry' => $this->db->getInfinity(),
+                       'createAccount' => true,
+                       'enableAutoblock' => true,
+                       'hideName' => true,
+                       'blockEmail' => true,
+                       'byText' => 'm>MetaWikiUser',
+               ];
+               $block = new DatabaseBlock( $blockOptions );
+               $block->insert();
+
+               // Reload block from DB
+               $userBlock = DatabaseBlock::newFromTarget( $username );
+               $this->assertTrue(
+                       (bool)$block->appliesToRight( 'createaccount' ),
+                       "Block object in DB should block right 'createaccount'"
+               );
+
+               $this->assertInstanceOf(
+                       DatabaseBlock::class,
+                       $userBlock,
+                       "'$username' block block object should be existent"
+               );
+
+               // Reload user
+               $u = User::newFromName( $username );
+               $this->assertTrue(
+                       (bool)$u->isBlockedFromCreateAccount(),
+                       "Our sandbox user '$username' should NOT be able to create account"
+               );
+       }
+
+       /**
+        * @covers ::insert
+        */
+       public function testCrappyCrossWikiBlocks() {
+               // Delete the last round's block if it's still there
+               $oldBlock = DatabaseBlock::newFromTarget( 'UserOnForeignWiki' );
+               if ( $oldBlock ) {
+                       // An old block will prevent our new one from saving.
+                       $oldBlock->delete();
+               }
+
+               // Local perspective (blockee on current wiki)...
+               $user = User::newFromName( 'UserOnForeignWiki' );
+               $user->addToDatabase();
+               $userId = $user->getId();
+               $this->assertNotEquals( 0, $userId, 'sanity' );
+
+               // Foreign perspective (blockee not on current wiki)...
+               $blockOptions = [
+                       'address' => 'UserOnForeignWiki',
+                       'user' => $user->getId(),
+                       'reason' => 'crosswiki block...',
+                       'timestamp' => wfTimestampNow(),
+                       'expiry' => $this->db->getInfinity(),
+                       'createAccount' => true,
+                       'enableAutoblock' => true,
+                       'hideName' => true,
+                       'blockEmail' => true,
+                       'byText' => 'Meta>MetaWikiUser',
+               ];
+               $block = new DatabaseBlock( $blockOptions );
+
+               $res = $block->insert( $this->db );
+               $this->assertTrue( (bool)$res['id'], 'Block succeeded' );
+
+               $user = null; // clear
+
+               $block = DatabaseBlock::newFromID( $res['id'] );
+               $this->assertEquals(
+                       'UserOnForeignWiki',
+                       $block->getTarget()->getName(),
+                       'Correct blockee name'
+               );
+               $this->assertEquals( $userId, $block->getTarget()->getId(), 'Correct blockee id' );
+               $this->assertEquals( 'Meta>MetaWikiUser', $block->getBlocker()->getName(),
+                       'Correct blocker name' );
+               $this->assertEquals( 'Meta>MetaWikiUser', $block->getByName(), 'Correct blocker name' );
+               $this->assertEquals( 0, $block->getBy(), 'Correct blocker id' );
+       }
+
+       protected function addXffBlocks() {
+               static $inited = false;
+
+               if ( $inited ) {
+                       return;
+               }
+
+               $inited = true;
+
+               $blockList = [
+                       [ 'target' => '70.2.0.0/16',
+                               'type' => DatabaseBlock::TYPE_RANGE,
+                               'desc' => 'Range Hardblock',
+                               'ACDisable' => false,
+                               'isHardblock' => true,
+                               'isAutoBlocking' => false,
+                       ],
+                       [ 'target' => '2001:4860:4001::/48',
+                               'type' => DatabaseBlock::TYPE_RANGE,
+                               'desc' => 'Range6 Hardblock',
+                               'ACDisable' => false,
+                               'isHardblock' => true,
+                               'isAutoBlocking' => false,
+                       ],
+                       [ 'target' => '60.2.0.0/16',
+                               'type' => DatabaseBlock::TYPE_RANGE,
+                               'desc' => 'Range Softblock with AC Disabled',
+                               'ACDisable' => true,
+                               'isHardblock' => false,
+                               'isAutoBlocking' => false,
+                       ],
+                       [ 'target' => '50.2.0.0/16',
+                               'type' => DatabaseBlock::TYPE_RANGE,
+                               'desc' => 'Range Softblock',
+                               'ACDisable' => false,
+                               'isHardblock' => false,
+                               'isAutoBlocking' => false,
+                       ],
+                       [ 'target' => '50.1.1.1',
+                               'type' => DatabaseBlock::TYPE_IP,
+                               'desc' => 'Exact Softblock',
+                               'ACDisable' => false,
+                               'isHardblock' => false,
+                               'isAutoBlocking' => false,
+                       ],
+               ];
+
+               $blocker = $this->getTestUser()->getUser();
+               foreach ( $blockList as $insBlock ) {
+                       $target = $insBlock['target'];
+
+                       if ( $insBlock['type'] === DatabaseBlock::TYPE_IP ) {
+                               $target = User::newFromName( IP::sanitizeIP( $target ), false )->getName();
+                       } elseif ( $insBlock['type'] === DatabaseBlock::TYPE_RANGE ) {
+                               $target = IP::sanitizeRange( $target );
+                       }
+
+                       $block = new DatabaseBlock();
+                       $block->setTarget( $target );
+                       $block->setBlocker( $blocker );
+                       $block->setReason( $insBlock['desc'] );
+                       $block->setExpiry( 'infinity' );
+                       $block->isCreateAccountBlocked( $insBlock['ACDisable'] );
+                       $block->isHardblock( $insBlock['isHardblock'] );
+                       $block->isAutoblocking( $insBlock['isAutoBlocking'] );
+                       $block->insert();
+               }
+       }
+
+       public static function providerXff() {
+               return [
+                       [ 'xff' => '1.2.3.4, 70.2.1.1, 60.2.1.1, 2.3.4.5',
+                               'count' => 2,
+                               'result' => 'Range Hardblock'
+                       ],
+                       [ 'xff' => '1.2.3.4, 50.2.1.1, 60.2.1.1, 2.3.4.5',
+                               'count' => 2,
+                               'result' => 'Range Softblock with AC Disabled'
+                       ],
+                       [ 'xff' => '1.2.3.4, 70.2.1.1, 50.1.1.1, 2.3.4.5',
+                               'count' => 2,
+                               'result' => 'Exact Softblock'
+                       ],
+                       [ 'xff' => '1.2.3.4, 70.2.1.1, 50.2.1.1, 50.1.1.1, 2.3.4.5',
+                               'count' => 3,
+                               'result' => 'Exact Softblock'
+                       ],
+                       [ 'xff' => '1.2.3.4, 70.2.1.1, 50.2.1.1, 2.3.4.5',
+                               'count' => 2,
+                               'result' => 'Range Hardblock'
+                       ],
+                       [ 'xff' => '1.2.3.4, 70.2.1.1, 60.2.1.1, 2.3.4.5',
+                               'count' => 2,
+                               'result' => 'Range Hardblock'
+                       ],
+                       [ 'xff' => '50.2.1.1, 60.2.1.1, 2.3.4.5',
+                               'count' => 2,
+                               'result' => 'Range Softblock with AC Disabled'
+                       ],
+                       [ 'xff' => '1.2.3.4, 50.1.1.1, 60.2.1.1, 2.3.4.5',
+                               'count' => 2,
+                               'result' => 'Exact Softblock'
+                       ],
+                       [ 'xff' => '1.2.3.4, <$A_BUNCH-OF{INVALID}TEXT\>, 60.2.1.1, 2.3.4.5',
+                               'count' => 1,
+                               'result' => 'Range Softblock with AC Disabled'
+                       ],
+                       [ 'xff' => '1.2.3.4, 50.2.1.1, 2001:4860:4001:802::1003, 2.3.4.5',
+                               'count' => 2,
+                               'result' => 'Range6 Hardblock'
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider providerXff
+        * @covers ::getBlocksForIPList
+        * @covers ::chooseBlock
+        */
+       public function testBlocksOnXff( $xff, $exCount, $exResult ) {
+               $user = $this->getUserForBlocking();
+               $this->addBlockForUser( $user );
+
+               $list = array_map( 'trim', explode( ',', $xff ) );
+               $xffblocks = DatabaseBlock::getBlocksForIPList( $list, true );
+               $this->assertEquals( $exCount, count( $xffblocks ), 'Number of blocks for ' . $xff );
+               $block = DatabaseBlock::chooseBlock( $xffblocks, $list );
+               $this->assertEquals(
+                       $exResult, $block->getReason(), 'Correct block type for XFF header ' . $xff
+               );
+       }
+
+       /**
+        * @covers ::newFromRow
+        */
+       public function testNewFromRow() {
+               $badActor = $this->getTestUser()->getUser();
+               $sysop = $this->getTestSysop()->getUser();
+
+               $block = new DatabaseBlock( [
+                       'address' => $badActor->getName(),
+                       'user' => $badActor->getId(),
+                       'by' => $sysop->getId(),
+                       'expiry' => 'infinity',
+               ] );
+               $block->insert();
+
+               $blockQuery = DatabaseBlock::getQueryInfo();
+               $row = $this->db->select(
+                       $blockQuery['tables'],
+                       $blockQuery['fields'],
+                       [
+                               'ipb_id' => $block->getId(),
+                       ],
+                       __METHOD__,
+                       [],
+                       $blockQuery['joins']
+               )->fetchObject();
+
+               $block = DatabaseBlock::newFromRow( $row );
+               $this->assertInstanceOf( DatabaseBlock::class, $block );
+               $this->assertEquals( $block->getBy(), $sysop->getId() );
+               $this->assertEquals( $block->getTarget()->getName(), $badActor->getName() );
+               $block->delete();
+       }
+
+       /**
+        * @covers ::equals
+        */
+       public function testEquals() {
+               $block = new DatabaseBlock();
+
+               $this->assertTrue( $block->equals( $block ) );
+
+               $partial = new DatabaseBlock( [
+                       'sitewide' => false,
+               ] );
+               $this->assertFalse( $block->equals( $partial ) );
+       }
+
+       /**
+        * @covers ::isSitewide
+        */
+       public function testIsSitewide() {
+               $block = new DatabaseBlock();
+               $this->assertTrue( $block->isSitewide() );
+
+               $block = new DatabaseBlock( [
+                       'sitewide' => true,
+               ] );
+               $this->assertTrue( $block->isSitewide() );
+
+               $block = new DatabaseBlock( [
+                       'sitewide' => false,
+               ] );
+               $this->assertFalse( $block->isSitewide() );
+
+               $block = new DatabaseBlock( [
+                       'sitewide' => false,
+               ] );
+               $block->isSitewide( true );
+               $this->assertTrue( $block->isSitewide() );
+       }
+
+       /**
+        * @covers ::getRestrictions
+        * @covers ::setRestrictions
+        */
+       public function testRestrictions() {
+               $block = new DatabaseBlock();
+               $restrictions = [
+                       new PageRestriction( 0, 1 )
+               ];
+               $block->setRestrictions( $restrictions );
+
+               $this->assertSame( $restrictions, $block->getRestrictions() );
+       }
+
+       /**
+        * @covers ::getRestrictions
+        * @covers ::insert
+        */
+       public function testRestrictionsFromDatabase() {
+               $badActor = $this->getTestUser()->getUser();
+               $sysop = $this->getTestSysop()->getUser();
+
+               $block = new DatabaseBlock( [
+                       '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 = DatabaseBlock::newFromID( $block->getId() );
+               $restrictions = $block->getRestrictions();
+               $this->assertCount( 1, $restrictions );
+               $this->assertTrue( $restriction->equals( $restrictions[0] ) );
+               $block->delete();
+       }
+
+       /**
+        * @covers ::insert
+        */
+       public function testInsertExistingBlock() {
+               $badActor = $this->getTestUser()->getUser();
+               $sysop = $this->getTestSysop()->getUser();
+
+               $block = new DatabaseBlock( [
+                       '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();
+       }
+
+       /**
+        * @covers ::appliesToTitle
+        */
+       public function testAppliesToTitleReturnsTrueOnSitewideBlock() {
+               $this->setMwGlobals( [
+                       'wgBlockDisablesLogin' => false,
+               ] );
+               $user = $this->getTestUser()->getUser();
+               $block = new DatabaseBlock( [
+                       'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
+                       'allowUsertalk' => true,
+                       'sitewide' => true
+               ] );
+
+               $block->setTarget( $user );
+               $block->setBlocker( $this->getTestSysop()->getUser() );
+               $block->insert();
+
+               $title = $this->getExistingTestPage( 'Foo' )->getTitle();
+
+               $this->assertTrue( $block->appliesToTitle( $title ) );
+
+               // appliesToTitle() ignores allowUsertalk
+               $title = $user->getTalkPage();
+               $this->assertTrue( $block->appliesToTitle( $title ) );
+
+               $block->delete();
+       }
+
+       /**
+        * @covers ::appliesToTitle
+        */
+       public function testAppliesToTitleOnPartialBlock() {
+               $this->setMwGlobals( [
+                       'wgBlockDisablesLogin' => false,
+               ] );
+               $user = $this->getTestUser()->getUser();
+               $block = new DatabaseBlock( [
+                       'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
+                       'allowUsertalk' => true,
+                       'sitewide' => false
+               ] );
+
+               $block->setTarget( $user );
+               $block->setBlocker( $this->getTestSysop()->getUser() );
+               $block->insert();
+
+               $pageFoo = $this->getExistingTestPage( 'Foo' );
+               $pageBar = $this->getExistingTestPage( 'Bar' );
+               $pageJohn = $this->getExistingTestPage( 'User:John' );
+
+               $pageRestriction = new PageRestriction( $block->getId(), $pageFoo->getId() );
+               $namespaceRestriction = new NamespaceRestriction( $block->getId(), NS_USER );
+               $this->getBlockRestrictionStore()->insert( [ $pageRestriction, $namespaceRestriction ] );
+
+               $this->assertTrue( $block->appliesToTitle( $pageFoo->getTitle() ) );
+               $this->assertFalse( $block->appliesToTitle( $pageBar->getTitle() ) );
+               $this->assertTrue( $block->appliesToTitle( $pageJohn->getTitle() ) );
+
+               $block->delete();
+       }
+
+       /**
+        * @covers ::appliesToNamespace
+        * @covers ::appliesToPage
+        */
+       public function testAppliesToReturnsTrueOnSitewideBlock() {
+               $this->setMwGlobals( [
+                       'wgBlockDisablesLogin' => false,
+               ] );
+               $user = $this->getTestUser()->getUser();
+               $block = new DatabaseBlock( [
+                       'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
+                       'allowUsertalk' => true,
+                       'sitewide' => true
+               ] );
+
+               $block->setTarget( $user );
+               $block->setBlocker( $this->getTestSysop()->getUser() );
+               $block->insert();
+
+               $title = $this->getExistingTestPage()->getTitle();
+
+               $this->assertTrue( $block->appliesToPage( $title->getArticleID() ) );
+               $this->assertTrue( $block->appliesToNamespace( NS_MAIN ) );
+               $this->assertTrue( $block->appliesToNamespace( NS_USER_TALK ) );
+
+               $block->delete();
+       }
+
+       /**
+        * @covers ::appliesToPage
+        */
+       public function testAppliesToPageOnPartialPageBlock() {
+               $this->setMwGlobals( [
+                       'wgBlockDisablesLogin' => false,
+               ] );
+               $user = $this->getTestUser()->getUser();
+               $block = new DatabaseBlock( [
+                       'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
+                       'allowUsertalk' => true,
+                       'sitewide' => false
+               ] );
+
+               $block->setTarget( $user );
+               $block->setBlocker( $this->getTestSysop()->getUser() );
+               $block->insert();
+
+               $title = $this->getExistingTestPage()->getTitle();
+
+               $pageRestriction = new PageRestriction(
+                       $block->getId(),
+                       $title->getArticleID()
+               );
+               $this->getBlockRestrictionStore()->insert( [ $pageRestriction ] );
+
+               $this->assertTrue( $block->appliesToPage( $title->getArticleID() ) );
+
+               $block->delete();
+       }
+
+       /**
+        * @covers ::appliesToNamespace
+        */
+       public function testAppliesToNamespaceOnPartialNamespaceBlock() {
+               $this->setMwGlobals( [
+                       'wgBlockDisablesLogin' => false,
+               ] );
+               $user = $this->getTestUser()->getUser();
+               $block = new DatabaseBlock( [
+                       'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
+                       'allowUsertalk' => true,
+                       'sitewide' => false
+               ] );
+
+               $block->setTarget( $user );
+               $block->setBlocker( $this->getTestSysop()->getUser() );
+               $block->insert();
+
+               $namespaceRestriction = new NamespaceRestriction( $block->getId(), NS_MAIN );
+               $this->getBlockRestrictionStore()->insert( [ $namespaceRestriction ] );
+
+               $this->assertTrue( $block->appliesToNamespace( NS_MAIN ) );
+               $this->assertFalse( $block->appliesToNamespace( NS_USER ) );
+
+               $block->delete();
+       }
+
+       /**
+        * @covers ::appliesToRight
+        */
+       public function testBlockAllowsPurge() {
+               $this->setMwGlobals( [
+                       'wgBlockDisablesLogin' => false,
+               ] );
+               $block = new DatabaseBlock();
+               $this->assertFalse( $block->appliesToRight( 'purge' ) );
+       }
+
+       /**
+        * Get an instance of BlockRestrictionStore
+        *
+        * @return BlockRestrictionStore
+        */
+       protected function getBlockRestrictionStore() : BlockRestrictionStore {
+               return MediaWikiServices::getInstance()->getBlockRestrictionStore();
+       }
+}
index a3b5adb..a09fd7c 100644 (file)
@@ -1,4 +1,7 @@
 <?php
+
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * Factory for handling the special page list and generating SpecialPage objects.
  *
@@ -30,7 +33,7 @@ abstract class FormSpecialPageTestCase extends SpecialPageTestBase {
 
                $user = clone $this->getTestUser()->getUser();
                $user->mBlockedby = $user->getName();
-               $user->mBlock = new Block( [
+               $user->mBlock = new DatabaseBlock( [
                        'address' => '127.0.8.1',
                        'by' => $user->getId(),
                        'reason' => 'sitewide block',
@@ -52,7 +55,7 @@ abstract class FormSpecialPageTestCase extends SpecialPageTestBase {
 
                $user = clone $this->getTestUser()->getUser();
                $user->mBlockedby = $user->getName();
-               $user->mBlock = new Block( [
+               $user->mBlock = new DatabaseBlock( [
                        'address' => '127.0.8.1',
                        'by' => $user->getId(),
                        'reason' => 'partial block',
index c1f2e42..86e3295 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 use MediaWiki\Block\BlockRestrictionStore;
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\Restriction\NamespaceRestriction;
 use Wikimedia\TestingAccessWrapper;
@@ -79,7 +80,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
                $block = $this->insertBlock();
 
                // Refresh the block from the database.
-               $block = Block::newFromTarget( $block->getTarget() );
+               $block = DatabaseBlock::newFromTarget( $block->getTarget() );
 
                $page = $this->newSpecialPage();
 
@@ -109,7 +110,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
                $pageSaturn = $this->getExistingTestPage( 'Saturn' );
                $pageMars = $this->getExistingTestPage( 'Mars' );
 
-               $block = new \Block( [
+               $block = new DatabaseBlock( [
                        'address' => $badActor->getName(),
                        'user' => $badActor->getId(),
                        'by' => $sysop->getId(),
@@ -129,7 +130,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
                $block->insert();
 
                // Refresh the block from the database.
-               $block = Block::newFromTarget( $block->getTarget() );
+               $block = DatabaseBlock::newFromTarget( $block->getTarget() );
 
                $page = $this->newSpecialPage();
 
@@ -179,7 +180,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
 
                $this->assertTrue( $result );
 
-               $block = Block::newFromTarget( $badActor );
+               $block = DatabaseBlock::newFromTarget( $badActor );
                $this->assertSame( $reason, $block->getReason() );
                $this->assertSame( $expiry, $block->getExpiry() );
        }
@@ -196,7 +197,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
                $context = RequestContext::getMain();
 
                // Create a block that will be updated.
-               $block = new \Block( [
+               $block = new DatabaseBlock( [
                        'address' => $badActor->getName(),
                        'user' => $badActor->getId(),
                        'by' => $sysop->getId(),
@@ -228,7 +229,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
 
                $this->assertTrue( $result );
 
-               $block = Block::newFromTarget( $badActor );
+               $block = DatabaseBlock::newFromTarget( $badActor );
                $this->assertSame( $reason, $block->getReason() );
                $this->assertSame( $expiry, $block->getExpiry() );
                $this->assertSame( '1', $block->isAutoblocking() );
@@ -277,7 +278,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
 
                $this->assertTrue( $result );
 
-               $block = Block::newFromTarget( $badActor );
+               $block = DatabaseBlock::newFromTarget( $badActor );
                $this->assertSame( $reason, $block->getReason() );
                $this->assertSame( $expiry, $block->getExpiry() );
                $this->assertCount( 2, $block->getRestrictions() );
@@ -331,7 +332,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
 
                $this->assertTrue( $result );
 
-               $block = Block::newFromTarget( $badActor );
+               $block = DatabaseBlock::newFromTarget( $badActor );
                $this->assertSame( $reason, $block->getReason() );
                $this->assertSame( $expiry, $block->getExpiry() );
                $this->assertFalse( $block->isSitewide() );
@@ -347,7 +348,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
 
                $this->assertTrue( $result );
 
-               $block = Block::newFromTarget( $badActor );
+               $block = DatabaseBlock::newFromTarget( $badActor );
                $this->assertSame( $reason, $block->getReason() );
                $this->assertSame( $expiry, $block->getExpiry() );
                $this->assertFalse( $block->isSitewide() );
@@ -362,7 +363,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
 
                $this->assertTrue( $result );
 
-               $block = Block::newFromTarget( $badActor );
+               $block = DatabaseBlock::newFromTarget( $badActor );
                $this->assertSame( $reason, $block->getReason() );
                $this->assertSame( $expiry, $block->getExpiry() );
                $this->assertFalse( $block->isSitewide() );
@@ -374,7 +375,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
 
                $this->assertTrue( $result );
 
-               $block = Block::newFromTarget( $badActor );
+               $block = DatabaseBlock::newFromTarget( $badActor );
                $this->assertSame( $reason, $block->getReason() );
                $this->assertSame( $expiry, $block->getExpiry() );
                $this->assertTrue( $block->isSitewide() );
@@ -420,7 +421,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
                        $$var = $users[$$var];
                }
 
-               $block = new \Block( [
+               $block = new DatabaseBlock( [
                        'address' => $blockedUser->getName(),
                        'user' => $blockedUser->getId(),
                        'by' => $blockPerformer->getId(),
@@ -454,7 +455,7 @@ class SpecialBlockTest extends SpecialPageTestBase {
                $badActor = $this->getTestUser()->getUser();
                $sysop = $this->getTestSysop()->getUser();
 
-               $block = new \Block( [
+               $block = new DatabaseBlock( [
                        'address' => $badActor->getName(),
                        'user' => $badActor->getId(),
                        'by' => $sysop->getId(),
index bd37c04..c0eadac 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\Restriction\NamespaceRestriction;
 use MediaWiki\MediaWikiServices;
@@ -222,7 +223,7 @@ class BlockListPagerTest extends MediaWikiTestCase {
                $target = '127.0.0.1';
 
                // Test Partial Blocks Blocks.
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $target,
                        'by' => $this->getTestSysop()->getUser()->getId(),
                        'reason' => 'Parce que',
index 58441f0..e38d77b 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use MediaWiki\Block\DatabaseBlock;
+
 /**
  * @covers LocalIdLookup
  * @group Database
@@ -20,7 +22,7 @@ class LocalIdLookupTest extends MediaWikiTestCase {
 
                $sysop = static::getTestSysop()->getUser();
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $this->localUsers[2]->getName(),
                        'by' => $sysop->getId(),
                        'reason' => __METHOD__,
@@ -29,7 +31,7 @@ class LocalIdLookupTest extends MediaWikiTestCase {
                ] );
                $block->insert();
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'address' => $this->localUsers[3]->getName(),
                        'by' => $sysop->getId(),
                        'reason' => __METHOD__,
index ca57c10..55a29e3 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 use MediaWiki\Auth\AuthManager;
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\SystemBlock;
 
 /**
@@ -85,7 +86,7 @@ class PasswordResetTest extends MediaWikiTestCase {
                                'enableEmail' => true,
                                'allowsAuthenticationDataChange' => true,
                                'canEditPrivate' => true,
-                               'block' => new Block( [ 'createAccount' => true ] ),
+                               'block' => new DatabaseBlock( [ 'createAccount' => true ] ),
                                'globalBlock' => null,
                                'isAllowed' => false,
                        ],
@@ -94,7 +95,7 @@ class PasswordResetTest extends MediaWikiTestCase {
                                'enableEmail' => true,
                                'allowsAuthenticationDataChange' => true,
                                'canEditPrivate' => true,
-                               'block' => new Block( [] ),
+                               'block' => new DatabaseBlock( [] ),
                                'globalBlock' => null,
                                'isAllowed' => true,
                        ],
index c90e988..aa6ae48 100644 (file)
@@ -3,6 +3,7 @@
 define( 'NS_UNITTEST', 5600 );
 define( 'NS_UNITTEST_TALK', 5601 );
 
+use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\Restriction\NamespaceRestriction;
 use MediaWiki\Block\SystemBlock;
@@ -602,7 +603,7 @@ class UserTest extends MediaWikiTestCase {
                $request1 = new FauxRequest();
                $request1->getSession()->setUser( $user1tmp );
                $expiryFiveHours = wfTimestamp() + ( 5 * 60 * 60 );
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'enableAutoblock' => true,
                        'expiry' => wfTimestamp( TS_MW, $expiryFiveHours ),
                ] );
@@ -616,8 +617,8 @@ class UserTest extends MediaWikiTestCase {
 
                // Confirm that the block has been applied as required.
                $this->assertTrue( $user1->isLoggedIn() );
-               $this->assertInstanceOf( Block::class, $user1->getBlock() );
-               $this->assertEquals( Block::TYPE_USER, $block->getType() );
+               $this->assertInstanceOf( DatabaseBlock::class, $user1->getBlock() );
+               $this->assertEquals( DatabaseBlock::TYPE_USER, $block->getType() );
                $this->assertTrue( $block->isAutoblocking() );
                $this->assertGreaterThanOrEqual( 1, $block->getId() );
 
@@ -625,7 +626,7 @@ class UserTest extends MediaWikiTestCase {
                $cookies = $request1->response()->getCookies();
                $this->assertArrayHasKey( 'wmsitetitleBlockID', $cookies );
                $this->assertEquals( $expiryFiveHours, $cookies['wmsitetitleBlockID']['expire'] );
-               $cookieValue = Block::getIdFromCookieValue( $cookies['wmsitetitleBlockID']['value'] );
+               $cookieValue = DatabaseBlock::getIdFromCookieValue( $cookies['wmsitetitleBlockID']['value'] );
                $this->assertEquals( $block->getId(), $cookieValue );
 
                // 2. Create a new request, set the cookies, and see if the (anon) user is blocked.
@@ -637,7 +638,7 @@ class UserTest extends MediaWikiTestCase {
                $this->assertNotEquals( $user1->getToken(), $user2->getToken() );
                $this->assertTrue( $user2->isAnon() );
                $this->assertFalse( $user2->isLoggedIn() );
-               $this->assertInstanceOf( Block::class, $user2->getBlock() );
+               $this->assertInstanceOf( DatabaseBlock::class, $user2->getBlock() );
                // Non-strict type-check.
                $this->assertEquals( true, $user2->getBlock()->isAutoblocking(), 'Autoblock does not work' );
                // Can't directly compare the objects because of member type differences.
@@ -653,7 +654,7 @@ class UserTest extends MediaWikiTestCase {
                $user3 = User::newFromSession( $request3 );
                $user3->load();
                $this->assertTrue( $user3->isLoggedIn() );
-               $this->assertInstanceOf( Block::class, $user3->getBlock() );
+               $this->assertInstanceOf( DatabaseBlock::class, $user3->getBlock() );
                $this->assertEquals( true, $user3->getBlock()->isAutoblocking() ); // Non-strict type-check.
 
                // Clean up.
@@ -682,7 +683,7 @@ class UserTest extends MediaWikiTestCase {
                $testUser = $this->getTestUser()->getUser();
                $request1 = new FauxRequest();
                $request1->getSession()->setUser( $testUser );
-               $block = new Block( [ 'enableAutoblock' => true ] );
+               $block = new DatabaseBlock( [ 'enableAutoblock' => true ] );
                $block->setBlocker( $this->getTestSysop()->getUser() );
                $block->setTarget( $testUser );
                $res = $block->insert();
@@ -693,8 +694,8 @@ class UserTest extends MediaWikiTestCase {
 
                // 2. Test that the cookie IS NOT present.
                $this->assertTrue( $user->isLoggedIn() );
-               $this->assertInstanceOf( Block::class, $user->getBlock() );
-               $this->assertEquals( Block::TYPE_USER, $block->getType() );
+               $this->assertInstanceOf( DatabaseBlock::class, $user->getBlock() );
+               $this->assertEquals( DatabaseBlock::TYPE_USER, $block->getType() );
                $this->assertTrue( $block->isAutoblocking() );
                $this->assertGreaterThanOrEqual( 1, $user->getBlockId() );
                $this->assertGreaterThanOrEqual( $block->getId(), $user->getBlockId() );
@@ -727,7 +728,7 @@ class UserTest extends MediaWikiTestCase {
                $user1Tmp = $this->getTestUser()->getUser();
                $request1 = new FauxRequest();
                $request1->getSession()->setUser( $user1Tmp );
-               $block = new Block( [ 'enableAutoblock' => true, 'expiry' => 'infinity' ] );
+               $block = new DatabaseBlock( [ 'enableAutoblock' => true, 'expiry' => 'infinity' ] );
                $block->setBlocker( $this->getTestSysop()->getUser() );
                $block->setTarget( $user1Tmp );
                $res = $block->insert();
@@ -738,8 +739,8 @@ class UserTest extends MediaWikiTestCase {
 
                // 2. Test the cookie's expiry timestamp.
                $this->assertTrue( $user1->isLoggedIn() );
-               $this->assertInstanceOf( Block::class, $user1->getBlock() );
-               $this->assertEquals( Block::TYPE_USER, $block->getType() );
+               $this->assertInstanceOf( DatabaseBlock::class, $user1->getBlock() );
+               $this->assertEquals( DatabaseBlock::TYPE_USER, $block->getType() );
                $this->assertTrue( $block->isAutoblocking() );
                $this->assertGreaterThanOrEqual( 1, $user1->getBlockId() );
                $cookies = $request1->response()->getCookies();
@@ -832,7 +833,7 @@ class UserTest extends MediaWikiTestCase {
                $user1tmp = $this->getTestUser()->getUser();
                $request1 = new FauxRequest();
                $request1->getSession()->setUser( $user1tmp );
-               $block = new Block( [ 'enableAutoblock' => true ] );
+               $block = new DatabaseBlock( [ 'enableAutoblock' => true ] );
                $block->setBlocker( $this->getTestSysop()->getUser() );
                $block->setTarget( $user1tmp );
                $res = $block->insert();
@@ -877,7 +878,7 @@ class UserTest extends MediaWikiTestCase {
                $user1tmp = $this->getTestUser()->getUser();
                $request1 = new FauxRequest();
                $request1->getSession()->setUser( $user1tmp );
-               $block = new Block( [ 'enableAutoblock' => true ] );
+               $block = new DatabaseBlock( [ 'enableAutoblock' => true ] );
                $block->setBlocker( $this->getTestSysop()->getUser() );
                $block->setTarget( $user1tmp );
                $res = $block->insert();
@@ -885,7 +886,7 @@ class UserTest extends MediaWikiTestCase {
                $user1 = User::newFromSession( $request1 );
                $user1->mBlock = $block;
                $user1->load();
-               $this->assertInstanceOf( Block::class, $user1->getBlock() );
+               $this->assertInstanceOf( DatabaseBlock::class, $user1->getBlock() );
 
                // 2. Create a new request, set the cookie to just the block ID, and the user should
                // still get blocked when they log in again.
@@ -897,7 +898,7 @@ class UserTest extends MediaWikiTestCase {
                $this->assertNotEquals( $user1->getToken(), $user2->getToken() );
                $this->assertTrue( $user2->isAnon() );
                $this->assertFalse( $user2->isLoggedIn() );
-               $this->assertInstanceOf( Block::class, $user2->getBlock() );
+               $this->assertInstanceOf( DatabaseBlock::class, $user2->getBlock() );
                $this->assertEquals( true, $user2->getBlock()->isAutoblocking() ); // Non-strict type-check.
 
                // Clean up.
@@ -1283,7 +1284,7 @@ class UserTest extends MediaWikiTestCase {
 
                // Block the user
                $blocker = $this->getTestSysop()->getUser();
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'hideName' => true,
                        'allowUsertalk' => false,
                        'reason' => 'Because',
@@ -1295,7 +1296,7 @@ class UserTest extends MediaWikiTestCase {
 
                // Clear cache and confirm it loaded the block properly
                $user->clearInstanceCache();
-               $this->assertInstanceOf( Block::class, $user->getBlock( false ) );
+               $this->assertInstanceOf( DatabaseBlock::class, $user->getBlock( false ) );
                $this->assertSame( $blocker->getName(), $user->blockedBy() );
                $this->assertSame( 'Because', $user->blockedFor() );
                $this->assertTrue( (bool)$user->isHidden() );
@@ -1320,7 +1321,7 @@ class UserTest extends MediaWikiTestCase {
         * @param bool $expect Expected result from User::isBlockedFrom()
         * @param array $options Additional test options:
         *  - 'blockAllowsUTEdit': (bool, default true) Value for $wgBlockAllowsUTEdit
-        *  - 'allowUsertalk': (bool, default false) Passed to Block::__construct()
+        *  - 'allowUsertalk': (bool, default false) Passed to DatabaseBlock::__construct()
         *  - 'pageRestrictions': (array|null) If non-empty, page restriction titles for the block.
         */
        public function testIsBlockedFrom( $title, $expect, array $options = [] ) {
@@ -1347,7 +1348,7 @@ class UserTest extends MediaWikiTestCase {
                        $restrictions[] = new NamespaceRestriction( 0, $ns );
                }
 
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
                        'allowUsertalk' => $options['allowUsertalk'] ?? false,
                        'sitewide' => !$restrictions,
@@ -1456,7 +1457,7 @@ class UserTest extends MediaWikiTestCase {
                ] );
 
                // setup block
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 5 * 60 * 60 ) ),
                ] );
                $block->setTarget( '1.2.3.4' );
@@ -1492,7 +1493,7 @@ class UserTest extends MediaWikiTestCase {
                ] );
 
                // setup block
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 5 * 60 * 60 ) ),
                ] );
                $block->setTarget( '1.2.3.4' );
@@ -1529,7 +1530,7 @@ class UserTest extends MediaWikiTestCase {
                ] );
 
                // setup block
-               $block = new Block( [
+               $block = new DatabaseBlock( [
                        'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
                ] );
                $block->setTarget( '1.2.3.4' );