* @return bool|null Null for unrecognized rights.
*/
public function prevents( $action, $x = null ) {
- global $wgBlockDisablesLogin;
+ $config = RequestContext::getMain()->getConfig();
+ $blockDisablesLogin = $config->get( 'BlockDisablesLogin' );
+ $blockAllowsUTEdit = $config->get( 'BlockAllowsUTEdit' );
+
$res = null;
switch ( $action ) {
case 'edit':
case 'sendemail':
$res = wfSetVar( $this->mBlockEmail, $x );
break;
+ case 'upload':
+ // Until T6995 is completed
+ $res = $this->isSitewide();
+ break;
case 'editownusertalk':
$res = wfSetVar( $this->mDisableUsertalk, $x );
+ // edit own user talk can be disabled by config
+ if ( !$blockAllowsUTEdit ) {
+ $res = true;
+ }
break;
case 'read':
$res = false;
break;
}
- if ( !$res && $wgBlockDisablesLogin ) {
+ if ( !$res && $blockDisablesLogin ) {
// If a block would disable login, then it should
// prevent any action that all users cannot do
$anon = new User;
* @return array
*/
public function getPermissionsError( IContextSource $context ) {
+ $params = $this->getBlockErrorParams( $context );
+
+ $msg = 'blockedtext';
+ if ( $this->getSystemBlockType() !== null ) {
+ $msg = 'systemblockedtext';
+ } elseif ( $this->mAuto ) {
+ $msg = 'autoblockedtext';
+ } elseif ( !$this->isSitewide() ) {
+ $msg = 'blockedtext-partial';
+ }
+
+ array_unshift( $params, $msg );
+
+ return $params;
+ }
+
+ /**
+ * Get block information used in different block error messages
+ *
+ * @param IContextSource $context
+ * @return array
+ */
+ public function getBlockErrorParams( IContextSource $context ) {
$blocker = $this->getBlocker();
if ( $blocker instanceof User ) { // local user
$blockerUserpage = $blocker->getUserPage();
/* $ip returns who *is* being blocked, $intended contains who was meant to be blocked.
* This could be a username, an IP range, or a single IP. */
$intended = $this->getTarget();
-
$systemBlockType = $this->getSystemBlockType();
-
$lang = $context->getLanguage();
+
return [
- $systemBlockType !== null
- ? 'systemblockedtext'
- : ( $this->mAuto ? 'autoblockedtext' : 'blockedtext' ),
$link,
$reason,
$context->getRequest()->getIP(),
return $this;
}
+
+ /**
+ * Checks if a block prevents an edit on a given article
+ *
+ * @param \Title $title
+ * @return bool
+ */
+ public function preventsEdit( \Title $title ) {
+ $blocked = $this->isSitewide();
+
+ // user talk page has it's 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();
+ if ( $title->equals( $user->getTalkPage() ) ) {
+ $blocked = $this->prevents( 'editownusertalk' );
+ }
+
+ if ( !$this->isSitewide() ) {
+ $restrictions = $this->getRestrictions();
+ foreach ( $restrictions as $restriction ) {
+ if ( $restriction->matches( $title ) ) {
+ $blocked = true;
+ }
+ }
+ }
+
+ return $blocked;
+ }
}
'blocked',
[ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
) );
+ } elseif ( is_array( $error ) && $error[0] === 'blockedtext-partial' && $user->getBlock() ) {
+ $status->fatal( ApiMessage::create(
+ 'apierror-blocked-partial',
+ 'blocked',
+ [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
+ ) );
} elseif ( is_array( $error ) && $error[0] === 'autoblockedtext' && $user->getBlock() ) {
$status->fatal( ApiMessage::create(
'apierror-autoblocked',
'autoblocked',
[ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
);
+ } elseif ( !$block->isSitewide() ) {
+ $this->dieWithError(
+ 'apierror-blocked-partial',
+ 'blocked',
+ [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
+ );
} else {
$this->dieWithError(
'apierror-blocked',
// obvious that this is even possible.
// @codeCoverageIgnoreStart
case EditPage::AS_BLOCKED_PAGE_FOR_USER:
- $this->dieWithError(
- 'apierror-blocked',
- 'blocked',
- [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
- );
+ $this->dieBlocked( $user->getBlock() );
case EditPage::AS_READ_ONLY_PAGE:
$this->dieReadOnly();
"apierror-bad-watchlist-token": "Incorrect watchlist token provided. Please set a correct token in [[Special:Preferences]].",
"apierror-blockedfrommail": "You have been blocked from sending email.",
"apierror-blocked": "You have been blocked from editing.",
+ "apierror-blocked-partial": "You have been blocked from editing this page.",
"apierror-botsnotsupported": "This interface is not supported for bots.",
"apierror-cannot-async-upload-file": "The parameters <var>async</var> and <var>file</var> cannot be combined. If you want asynchronous processing of your uploaded file, first upload it to stash (using the <var>stash</var> parameter) and then publish the stashed file asynchronously (using <var>filekey</var> and <var>async</var>).",
"apierror-cannotreauthenticate": "This action is not available as your identity cannot be verified.",
"apierror-bad-watchlist-token": "{{doc-apierror}}",
"apierror-blockedfrommail": "{{doc-apierror}}",
"apierror-blocked": "{{doc-apierror}}",
+ "apierror-blocked-partial": "{{doc-apierror}}",
"apierror-botsnotsupported": "{{doc-apierror}}",
"apierror-cannot-async-upload-file": "{{doc-apierror}}",
"apierror-cannotreauthenticate": "{{doc-apierror}}",
case 'badaccess':
throw new PermissionsError( 'sendemail' );
case 'blockedemailuser':
- throw new UserBlockedError( $this->getUser()->mBlock );
+ throw $this->getBlockedEmailError();
case 'actionthrottledtext':
throw new ThrottledError;
case 'mailnologin':
protected function getGroupName() {
return 'users';
}
+
+ /**
+ * Builds an error message based on the block params
+ *
+ * @return ErrorPageError
+ */
+ private function getBlockedEmailError() {
+ $block = $this->getUser()->mBlock;
+ $params = $block->getBlockErrorParams( $this->getContext() );
+
+ $msg = $block->isSitewide() ? 'blockedtext' : 'blocked-email-user';
+ return new ErrorPageError( 'blockedtitle', $msg, $params );
+ }
}
}
# Check blocks
- if ( $user->isBlocked() ) {
+ if ( $user->isBlockedFromUpload() ) {
throw new UserBlockedError( $user->getBlock() );
}
* Check if user is blocked from editing a particular article
*
* @param Title $title Title to check
- * @param bool $bFromSlave Whether to check the replica DB instead of the master
+ * @param bool $fromSlave Whether to check the replica DB instead of the master
* @return bool
*/
- public function isBlockedFrom( $title, $bFromSlave = false ) {
- global $wgBlockAllowsUTEdit;
+ public function isBlockedFrom( $title, $fromSlave = false ) {
+ $blocked = $this->isHidden();
- $blocked = $this->isBlocked( $bFromSlave );
- $allowUsertalk = ( $wgBlockAllowsUTEdit ? $this->mAllowUsertalk : false );
- // If a user's name is suppressed, they cannot make edits anywhere
- if ( !$this->mHideName && $allowUsertalk && $title->getText() === $this->getName()
- && $title->getNamespace() == NS_USER_TALK ) {
- $blocked = false;
- wfDebug( __METHOD__ . ": self-talk page, ignoring any blocks\n" );
+ if ( !$blocked ) {
+ $block = $this->getBlock( $fromSlave );
+ if ( $block ) {
+ $blocked = $block->preventsEdit( $title );
+ }
}
+ // only for the purpose of the hook. We really don't need this here.
+ $allowUsertalk = $this->mAllowUsertalk;
+
Hooks::run( 'UserIsBlockedFrom', [ $this, $title, &$blocked, &$allowUsertalk ] );
return $blocked;
*/
public function isHidden() {
if ( $this->mHideName !== null ) {
- return $this->mHideName;
+ return (bool)$this->mHideName;
}
$this->getBlockedStatus();
if ( !$this->mHideName ) {
$this->mHideName = $authUser && $authUser->isHidden();
Hooks::run( 'UserIsHidden', [ $this, &$this->mHideName ] );
}
- return $this->mHideName;
+ return (bool)$this->mHideName;
}
/**
return $this->mBlock && $this->mBlock->prevents( 'sendemail' );
}
+ /**
+ * Get whether the user is blocked from using Special:Upload
+ *
+ * @return bool
+ */
+ public function isBlockedFromUpload() {
+ $this->getBlockedStatus();
+ return $this->mBlock && $this->mBlock->prevents( 'upload' );
+ }
+
/**
* Get whether the user is allowed to create an account.
* @return bool
"subject-preview": "Preview of subject:",
"previewerrortext": "An error occurred while attempting to preview your changes.",
"blockedtitle": "User is blocked",
+ "blocked-email-user": "<strong>Your username has been blocked from sending email. You can still edit other pages on this wiki.</strong> You can view the full block details at [[Special:MyContributions|account contributions]].\n\nThe block was made by $1.\n\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n* Block ID #$5",
+ "blockedtext-partial": "<strong>Your username or IP address has been blocked from making changes to this page. You can still edit other pages on this wiki.</strong> You can view the full block details at [[Special:MyContributions|account contributions]].\n\nThe block was made by $1.\n\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n* Block ID #$5",
"blockedtext": "<strong>Your username or IP address has been blocked.</strong>\n\nThe block was made by $1.\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou can contact $1 or another [[{{MediaWiki:Grouppage-sysop}}|administrator]] to discuss the block.\nYou cannot use the \"{{int:emailuser}}\" feature unless a valid email address is specified in your [[Special:Preferences|account preferences]] and you have not been blocked from using it.\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
"autoblockedtext": "Your IP address has been automatically blocked because it was used by another user, who was blocked by $1.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou may contact $1 or one of the other [[{{MediaWiki:Grouppage-sysop}}|administrators]] to discuss the block.\n\nNote that you may not use the \"{{int:emailuser}}\" feature unless you have a valid email address registered in your [[Special:Preferences|user preferences]] and you have not been blocked from using it.\n\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
"systemblockedtext": "Your username or IP address has been automatically blocked by MediaWiki.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYour current IP address is $3.\nPlease include all above details in any queries you make.",
"subject-preview": "Used as label for preview of the section title when adding a new section on a talk page.\n\nShould match {{msg-mw|subject}}.\n\nSee also:\n* {{msg-mw|Summary-preview}}\n\n{{Identical|Subject}}",
"previewerrortext": "When a user has the editing preference LivePreview enabled, clicked the Preview or Show Changes button in the edit page and the action did not succeed.",
"blockedtitle": "Used as title displayed for blocked users. The corresponding message body is one of the following messages:\n* {{msg-mw|Blockedtext|notext=1}}\n* {{msg-mw|Autoblockedtext|notext=1}}\n* {{msg-mw|Systemblockedtext}}",
+ "blocked-email-user": "Text displayed to partially blocked users that are blocked from sending email.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link)\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Autoblockedtext}}\n* {{msg-mw|Systemblockedtext}}",
+ "blockedtext-partial": "Text displayed to partially blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link)\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Autoblockedtext}}\n* {{msg-mw|Systemblockedtext}}",
"blockedtext": "Text displayed to blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link)\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Autoblockedtext}}\n* {{msg-mw|Systemblockedtext}}",
"autoblockedtext": "Text displayed to automatically blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block (in case of autoblocks: {{msg-mw|autoblocker}})\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link). Use it for GENDER.\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Blockedtext}}\n* {{msg-mw|Systemblockedtext}}",
"systemblockedtext": "Text displayed to requests blocked by MediaWiki configuration.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - (Unused) A dummy user attributed as the blocker, possibly as a link to a user page.\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the dummy blocking user's username (plain text, without the link).\n* $5 - A short string indicating the type of system block.\n* $6 - the expiry of the block\n* $7 - the intended target of the block\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Blockedtext}}\n* {{msg-mw|Autoblockedtext}}",
<?php
+use MediaWiki\Block\BlockRestriction;
use MediaWiki\Block\Restriction\PageRestriction;
/**
$block->delete();
}
+ /**
+ * @covers Block::preventsEdit
+ */
+ public function testPreventsEditReturnsTrueOnSitewideBlock() {
+ $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->preventsEdit( $title ) );
+
+ $block->delete();
+ }
+
+ /**
+ * @covers Block::preventsEdit
+ */
+ public function testPreventsEditOnPartialBlock() {
+ $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' );
+
+ $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->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
+ ],
+ ];
+ }
}
'Useruser', 'test', '23:00, 31 December 1969', '127.0.8.1',
$wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ] ],
$this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
+
+ // partial block message test
+ $this->user->mBlockedby = $this->user->getName();
+ $this->user->mBlock = new Block( [
+ 'address' => '127.0.8.1',
+ 'by' => $this->user->getId(),
+ 'reason' => 'no reason given',
+ 'timestamp' => $now,
+ 'sitewide' => false,
+ 'expiry' => 10,
+ ] );
+
+ $this->assertEquals( [ [ 'blockedtext-partial',
+ '[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1',
+ 'Useruser', null, '23:00, 31 December 1969', '127.0.8.1',
+ $wgLang->timeanddate( wfTimestamp( TS_MW, $now ), true ) ] ],
+ $this->title->getUserPermissionsErrors( 'move-target', $this->user ) );
}
}
'user' => $badActor->getId(),
'by' => $sysop->getId(),
'expiry' => 'infinity',
- 'sitewide' => 0,
+ 'sitewide' => 1,
'enableAutoblock' => true,
] );