return;
}
- $target = $block->getTarget();
- // FIXME: Push this into getTargetActor() or whatever to reuse
- if ( is_string( $target ) ) {
- $target = User::newFromName( $target, false );
+ // 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 );
* @param User|string $user Local User object or username string
*/
public function setBlocker( $user ) {
- // FIXME: Push this into getTargetActor() or whatever to reuse
if ( is_string( $user ) ) {
$user = User::newFromName( $user, false );
}
}
/**
- * Checks if a block prevents an edit on a given article
+ * Checks if a block applies to a particular title
+ *
+ * This check does not consider whether `$this->prevents( 'editownusertalk' )`
+ * returns false, as the identity of the user making the hypothetical edit
+ * isn't known here (particularly in the case of IP hardblocks, range
+ * blocks, and auto-blocks).
*
- * @param \Title $title
+ * @param Title $title
* @return bool
*/
- public function preventsEdit( \Title $title ) {
- $blocked = $this->isSitewide();
-
- // user talk page has its own rules
- // This check happens before partial blocks because the flag
- // to allow user to edit their user talk page could be
- // overwritten by a partial block restriction (E.g. user talk namespace)
- $user = $this->getTarget();
-
- // Not all blocked `$user`s are self::TYPE_USER
- // FIXME: Push this into getTargetActor() or whatever to reuse
- if ( is_string( $user ) ) {
- $user = User::newFromName( $user, false );
- }
-
- if ( $title->equals( $user->getTalkPage() ) ) {
- $blocked = $this->prevents( 'editownusertalk' );
+ public function appliesToTitle( Title $title ) {
+ if ( $this->isSitewide() ) {
+ return true;
}
- if ( !$this->isSitewide() ) {
- $restrictions = $this->getRestrictions();
- foreach ( $restrictions as $restriction ) {
- if ( $restriction->matches( $title ) ) {
- $blocked = true;
- }
+ $restrictions = $this->getRestrictions();
+ foreach ( $restrictions as $restriction ) {
+ if ( $restriction->matches( $title ) ) {
+ return true;
}
}
- return $blocked;
+ return false;
}
}
if ( !$blocked ) {
$block = $this->getBlock( $fromSlave );
if ( $block ) {
- $blocked = $block->preventsEdit( $title );
+ // A sitewide block covers everything except maybe the user's
+ // talk page. A partial block covering the user's talk page
+ // overrides the editownusertalk flag, however.
+ // TODO: Do we really want a partial block on the user talk
+ // namespace to ignore editownusertalk?
+ $blocked = $block->isSitewide();
+ if ( $blocked && $title->equals( $this->getTalkPage() ) ) {
+ $blocked = $block->prevents( 'editownusertalk' );
+ }
+ if ( !$block->isSitewide() ) {
+ $blocked = $block->appliesToTitle( $title );
+ }
}
}
}
/**
- * @covers Block::preventsEdit
+ * @covers Block::appliesToTitle
*/
- public function testPreventsEditReturnsTrueOnSitewideBlock() {
+ public function testAppliesToTitleReturnsTrueOnSitewideBlock() {
$user = $this->getTestUser()->getUser();
$block = new Block( [
'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
$title = $this->getExistingTestPage( 'Foo' )->getTitle();
- $this->assertTrue( $block->preventsEdit( $title ) );
+ $this->assertTrue( $block->appliesToTitle( $title ) );
+
+ // appliesToTitle() ignores allowUsertalk
+ $title = $user->getTalkPage();
+ $this->assertTrue( $block->appliesToTitle( $title ) );
$block->delete();
}
/**
- * @covers Block::preventsEdit
+ * @covers Block::appliesToTitle
*/
- public function testPreventsEditOnPartialBlock() {
+ public function testAppliesToTitleOnPartialBlock() {
$user = $this->getTestUser()->getUser();
$block = new Block( [
'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
$pageRestriction = new PageRestriction( $block->getId(), $pageFoo->getId() );
BlockRestriction::insert( [ $pageRestriction ] );
- $this->assertTrue( $block->preventsEdit( $pageFoo->getTitle() ) );
- $this->assertFalse( $block->preventsEdit( $pageBar->getTitle() ) );
-
- $block->delete();
- }
-
- /**
- * @covers Block::preventsEdit
- * @dataProvider preventsEditOnUserTalkProvider
- */
- public function testPreventsEditOnUserTalkPage(
- $allowUsertalk, $sitewide, $result, $blockAllowsUTEdit = true
- ) {
- $this->setMwGlobals( [
- 'wgBlockAllowsUTEdit' => $blockAllowsUTEdit,
- ] );
-
- $user = $this->getTestUser()->getUser();
- $block = new Block( [
- 'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
- 'allowUsertalk' => $allowUsertalk,
- 'sitewide' => $sitewide
- ] );
-
- $block->setTarget( $user );
- $block->setBlocker( $this->getTestSysop()->getUser() );
- $block->insert();
+ $this->assertTrue( $block->appliesToTitle( $pageFoo->getTitle() ) );
+ $this->assertFalse( $block->appliesToTitle( $pageBar->getTitle() ) );
- $this->assertEquals( $result, $block->preventsEdit( $user->getTalkPage() ) );
$block->delete();
}
- public function preventsEditOnUserTalkProvider() {
- return [
- [
- 'allowUsertalk' => false,
- 'sitewide' => true,
- 'result' => true,
- ],
- [
- 'allowUsertalk' => true,
- 'sitewide' => true,
- 'result' => false,
- ],
- [
- 'allowUsertalk' => true,
- 'sitewide' => false,
- 'result' => false,
- ],
- [
- 'allowUsertalk' => false,
- 'sitewide' => false,
- 'result' => true,
- ],
- [
- 'allowUsertalk' => true,
- 'sitewide' => true,
- 'result' => true,
- 'blockAllowsUTEdit' => false
- ],
- [
- 'allowUsertalk' => true,
- 'sitewide' => false,
- 'result' => true,
- 'blockAllowsUTEdit' => false
- ],
- ];
- }
}
define( 'NS_UNITTEST', 5600 );
define( 'NS_UNITTEST_TALK', 5601 );
+use MediaWiki\Block\Restriction\PageRestriction;
use MediaWiki\MediaWikiServices;
use MediaWiki\User\UserIdentityValue;
use Wikimedia\TestingAccessWrapper;
* @group Database
*/
class UserTest extends MediaWikiTestCase {
+
+ /** Constant for self::testIsBlockedFrom */
+ const USER_TALK_PAGE = '<user talk page>';
+
/**
* @var User
*/
$this->assertFalse( $user->isBlockedFrom( $ut ) );
}
+ /**
+ * @covers User::isBlockedFrom
+ * @dataProvider provideIsBlockedFrom
+ * @param string|null $title Title to test.
+ * @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()
+ * - 'pageRestrictions': (array|null) If non-empty, page restriction titles for the block.
+ */
+ public function testIsBlockedFrom( $title, $expect, array $options = [] ) {
+ $this->setMwGlobals( [
+ 'wgBlockAllowsUTEdit' => $options['blockAllowsUTEdit'] ?? true,
+ ] );
+
+ $user = $this->getTestUser()->getUser();
+
+ if ( $title === self::USER_TALK_PAGE ) {
+ $title = $user->getTalkPage();
+ } else {
+ $title = Title::newFromText( $title );
+ }
+
+ $restrictions = [];
+ foreach ( $options['pageRestrictions'] ?? [] as $pagestr ) {
+ $page = $this->getExistingTestPage(
+ $pagestr === self::USER_TALK_PAGE ? $user->getTalkPage() : $pagestr
+ );
+ $restrictions[] = new PageRestriction( 0, $page->getId() );
+ }
+
+ $block = new Block( [
+ 'expiry' => wfTimestamp( TS_MW, wfTimestamp() + ( 40 * 60 * 60 ) ),
+ 'allowUsertalk' => $options['allowUsertalk'] ?? false,
+ 'sitewide' => !$restrictions,
+ ] );
+ $block->setTarget( $user );
+ $block->setBlocker( $this->getTestSysop()->getUser() );
+ if ( $restrictions ) {
+ $block->setRestrictions( $restrictions );
+ }
+ $block->insert();
+
+ try {
+ $this->assertSame( $expect, $user->isBlockedFrom( $title ) );
+ } finally {
+ $block->delete();
+ }
+ }
+
+ public static function provideIsBlockedFrom() {
+ return [
+ 'Basic operation' => [ 'Test page', true ],
+ 'User talk page, not allowed' => [ self::USER_TALK_PAGE, true, [
+ 'allowUsertalk' => false,
+ ] ],
+ 'User talk page, allowed' => [ self::USER_TALK_PAGE, false, [
+ 'allowUsertalk' => true,
+ ] ],
+ 'User talk page, allowed but $wgBlockAllowsUTEdit is false' => [ self::USER_TALK_PAGE, true, [
+ 'allowUsertalk' => true,
+ 'blockAllowsUTEdit' => false,
+ ] ],
+
+ 'Partial block, blocking the page' => [ 'Test page', true, [
+ 'pageRestrictions' => [ 'Test page' ],
+ ] ],
+ 'Partial block, not blocking the page' => [ 'Test page 2', false, [
+ 'pageRestrictions' => [ 'Test page' ],
+ ] ],
+ 'Partial block, overriding allowUsertalk' => [ self::USER_TALK_PAGE, true, [
+ 'pageRestrictions' => [ self::USER_TALK_PAGE ],
+ ] ],
+ ];
+ }
+
/**
* Block cookie should be set for IP Blocks if
* wgCookieSetOnIpBlock is set to true