they move to a new IP address. This is disabled by default.
* Added ILocalizedException interface to standardize the use of localized
exceptions, largely so the API can handle them more sensibly.
+* Blocks created automatically by MediaWiki, such as for configured proxies or
+ dnsbls, are now indicated as such and use a new i18n message when displayed.
=== External library changes in 1.29 ===
/** @var bool */
protected $isAutoblocking;
+ /** @var string|null */
+ protected $systemBlockType;
+
# TYPE constants
const TYPE_USER = 1;
const TYPE_IP = 2;
* blockEmail bool Disallow sending emails
* allowUsertalk bool Allow the target to edit its own talk page
* byText string Username of the blocker (for foreign users)
+ * systemBlock string Indicate that this block is automatically
+ * created by MediaWiki rather than being stored
+ * in the database. Value is a string to return
+ * from self::getSystemBlockType().
*
* @since 1.26 accepts $options array instead of individual parameters; order
* of parameters above reflects the original order
'blockEmail' => false,
'allowUsertalk' => false,
'byText' => '',
+ 'systemBlock' => null,
];
if ( func_num_args() > 1 || !is_array( $options ) ) {
$this->prevents( 'createaccount', (bool)$options['createAccount'] );
$this->mFromMaster = false;
+ $this->systemBlockType = $options['systemBlock'];
}
/**
*/
public function insert( $dbw = null ) {
global $wgBlockDisablesLogin;
+
+ if ( $this->getSystemBlockType() !== null ) {
+ throw new MWException( 'Cannot insert a system block into the database' );
+ }
+
wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
if ( $dbw === null ) {
return false;
}
+ # Don't autoblock for system blocks
+ if ( $this->getSystemBlockType() !== null ) {
+ throw new MWException( 'Cannot autoblock from a system block' );
+ }
+
# Check for presence on the autoblock whitelist.
if ( self::isWhitelistedFromAutoblocks( $autoblockIP ) ) {
return false;
return $this->mId;
}
+ /**
+ * Get the system block type, if any
+ * @return string|null
+ */
+ public function getSystemBlockType() {
+ return $this->systemBlockType;
+ }
+
/**
* Get/set a flag determining whether the master is used for reads
*
* This could be a username, an IP range, or a single IP. */
$intended = $this->getTarget();
+ $systemBlockType = $this->getSystemBlockType();
+
$lang = $context->getLanguage();
return [
- $this->mAuto ? 'autoblockedtext' : 'blockedtext',
+ $systemBlockType !== null
+ ? 'systemblockedtext'
+ : ( $this->mAuto ? 'autoblockedtext' : 'blockedtext' ),
$link,
$reason,
$context->getRequest()->getIP(),
$this->getByName(),
- $this->getId(),
+ $systemBlockType !== null ? $systemBlockType : $this->getId(),
$lang->formatExpiry( $this->mExpiry ),
(string)$intended,
$lang->userTimeAndDate( $this->mTimestamp, $context->getUser() ),
$remove = [];
foreach ( $permErrors as $error ) {
if ( ( $this->preview || $this->diff )
- && ( $error[0] == 'blockedtext' || $error[0] == 'autoblockedtext' )
+ && (
+ $error[0] == 'blockedtext' ||
+ $error[0] == 'autoblockedtext' ||
+ $error[0] == 'systemblockedtext'
+ )
) {
$remove[] = $error;
}
*/
protected function formatListParam( array $params, $listType, $format ) {
if ( !isset( self::$listTypeMap[$listType] ) ) {
- $warning = 'Invalid list type for message "' . $this->getKey() . '": ' .
- htmlspecialchars( serialize( $params )
- );
+ $warning = 'Invalid list type for message "' . $this->getKey() . '": '
+ . htmlspecialchars( $listType )
+ . ' (params are ' . htmlspecialchars( serialize( $params ) ) . ')';
trigger_error( $warning, E_USER_WARNING );
$e = new Exception;
wfDebugLog( 'Bug58676', $warning . "\n" . $e->getTraceAsString() );
*
* @since 1.19 (r105919)
* @param array|string $query
- * @param bool $query2
+ * @param string|string[]|bool $query2
* @return string
*/
private static function fixUrlQueryArgs( $query, $query2 = false ) {
*
* @see self::getLocalURL for the arguments.
* @see wfExpandUrl
- * @param array|string $query
- * @param bool $query2
+ * @param string|string[] $query
+ * @param string|string[]|bool $query2
* @param string $proto Protocol type to use in URL
* @return string The URL
*/
* valid to link, locally, to the current Title.
* @see self::newFromText to produce a Title object.
*
- * @param string|array $query An optional query string,
+ * @param string|string[] $query An optional query string,
* not used for interwiki links. Can be specified as an associative array as well,
* e.g., array( 'action' => 'edit' ) (keys and values will be URL-escaped).
* Some query patterns will trigger various shorturl path replacements.
- * @param array $query2 An optional secondary query array. This one MUST
+ * @param string|string[]|bool $query2 An optional secondary query array. This one MUST
* be an array. If a string is passed it will be interpreted as a deprecated
* variant argument and urlencoded into a variant= argument.
* This second query argument will be added to the $query
* The result obviously should not be URL-escaped, but does need to be
* HTML-escaped if it's being output in HTML.
*
- * @param array $query
+ * @param string|string[] $query
* @param bool $query2
* @param string|int|bool $proto A PROTO_* constant on how the URL should be expanded,
* or false (default) for no expansion
'autoblocked',
[ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
) );
+ } elseif ( is_array( $error ) && $error[0] === 'systemblockedtext' && $user->getBlock() ) {
+ $status->fatal( ApiMessage::create(
+ 'apierror-systemblocked',
+ 'blocked',
+ [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
+ ) );
} else {
call_user_func_array( [ $status, 'fatal' ], (array)$error );
}
'confirmedittext' => 'confirmedittext',
'blockedtext' => 'apierror-blocked',
'autoblockedtext' => 'apierror-autoblocked',
+ 'systemblockedtext' => 'apierror-systemblocked',
'actionthrottledtext' => 'apierror-ratelimited',
'alreadyrolled' => 'alreadyrolled',
'cantrollback' => 'cantrollback',
* @param IContextSource $context
* @param ApiBase[]|ApiBase $modules
* @param array $options Formatting options (described above)
- * @return string
*/
public static function getHelp( IContextSource $context, $modules, array $options ) {
global $wgContLang;
'rcpatroldisabled' => 'patroldisabled',
'readonlytext' => 'readonly',
'sessionfailure' => 'badtoken',
+ 'systemblockedtext' => 'blocked',
'titleprotected' => 'protectedtitle',
'undo-failure' => 'undofailure',
'userrights-nodatabase' => 'nosuchdatabase',
* - blockreason - reason provided for the block
* - blockedtimestamp - timestamp for when the block was placed/modified
* - blockexpiry - expiry time of the block
+ * - systemblocktype - system block type, if any
*/
public static function getBlockInfo( Block $block ) {
global $wgContLang;
$vals['blockexpiry'] = $wgContLang->formatExpiry(
$block->getExpiry(), TS_ISO_8601, 'infinite'
);
+ if ( $block->getSystemBlockType() !== null ) {
+ $vals['systemblocktype'] = $block->getSystemBlockType();
+ }
return $vals;
}
"apierror-stashpathinvalid": "File key of improper format or otherwise invalid: $1.",
"apierror-stashwrongowner": "Wrong owner: $1",
"apierror-stashzerolength": "File is of zero length, and could not be stored in the stash: $1.",
+ "apierror-systemblocked": "You have been blocked automatically by MediaWiki.",
"apierror-templateexpansion-notwikitext": "Template expansion is only supported for wikitext content. $1 uses content model $2.",
"apierror-toofewexpiries": "$1 expiry {{PLURAL:$1|timestamp was|timestamps were}} provided where $2 {{PLURAL:$2|was|were}} needed.",
"apierror-unknownaction": "The action specified, <kbd>$1</kbd>, is not recognized.",
"apierror-stashpathinvalid": "{{doc-apierror}}\n\nParameters:\n* $1 - Exception text. Currently this is probably English, hopefully we'll fix that in the future.",
"apierror-stashwrongowner": "{{doc-apierror}}\n\nParameters:\n* $1 - Exception text, which should already end with punctuation. Currently this is probably English, hopefully we'll fix that in the future.",
"apierror-stashzerolength": "{{doc-apierror}}\n\nParameters:\n* $1 - Exception text. Currently this is probably English, hopefully we'll fix that in the future.",
+ "apierror-systemblocked": "{{doc-apierror}}",
"apierror-templateexpansion-notwikitext": "{{doc-apierror}}\n\nParameters:\n* $1 - Page title.\n* $2 - Content model.",
"apierror-toofewexpiries": "{{doc-apierror}}\n\nParameters:\n* $1 - Number provided.\n* $2 - Number needed.",
"apierror-unknownaction": "{{doc-apierror}}\n\nParameters:\n* $1 - Action provided.",
* @param string $log 'production' will always trigger a php error, 'auto'
* will trigger an error if $wgDevelopmentWarnings is true, and 'debug'
* will only write to the debug log(s).
- *
- * @return mixed
*/
public static function warning( $msg, $callerOffset = 1, $level = E_USER_NOTICE, $log = 'auto' ) {
global $wgDevelopmentWarnings;
/**
* Get a "<select>" element which has options for each of the allowed limits
*
- * @param string $attribs Extra attributes to set
+ * @param string[] $attribs Extra attributes to set
* @return string HTML fragment
*/
public function getLimitSelect( $attribs = [] ) {
}
public function needsUpdate() {
- return parent::needsUpdate() || $this->params !== $this->getDefaultParams();
+ return $this->params !== $this->getDefaultParams();
}
public function toString() {
*
* @return bool True if needs update, false otherwise
*/
- public function needsUpdate() {
- }
+ abstract public function needsUpdate();
/**
* Compare one Password object to this object
* @since 1.18
* @param string $feature
* @param mixed $data
- * @return bool
*/
public function setFeatureData( $feature, $data ) {
$this->features[$feature] = $data;
* @param string $subPage Subpage of the special page.
* @return string an AuthManager::ACTION_* constant.
*/
- protected function getDefaultAction( $subPage ) {
- throw new BadMethodCallException( 'Subclass did not implement getDefaultAction' );
- }
+ abstract protected function getDefaultAction( $subPage );
/**
* Return custom message key.
if ( !$block instanceof Block && $ip !== null && !in_array( $ip, $wgProxyWhitelist ) ) {
// Local list
if ( self::isLocallyBlockedProxy( $ip ) ) {
- $block = new Block;
- $block->setBlocker( wfMessage( 'proxyblocker' )->text() );
- $block->mReason = wfMessage( 'proxyblockreason' )->text();
- $block->setTarget( $ip );
+ $block = new Block( [
+ 'byText' => wfMessage( 'proxyblocker' )->text(),
+ 'reason' => wfMessage( 'proxyblockreason' )->text(),
+ 'address' => $ip,
+ 'systemBlock' => 'proxy',
+ ] );
$this->blockTrigger = 'proxy-block';
} elseif ( $this->isAnon() && $this->isDnsBlacklisted( $ip ) ) {
- $block = new Block;
- $block->setBlocker( wfMessage( 'sorbs' )->text() );
- $block->mReason = wfMessage( 'sorbsreason' )->text();
- $block->setTarget( $ip );
+ $block = new Block( [
+ 'byText' => wfMessage( 'sorbs' )->text(),
+ 'reason' => wfMessage( 'sorbsreason' )->text(),
+ 'address' => $ip,
+ 'systemBlock' => 'dnsbl',
+ ] );
$this->blockTrigger = 'openproxy-block';
}
}
if ( $blocked && $block === null ) {
// back-compat: UserIsBlockedGlobally didn't have $block param first
- $block = new Block;
- $block->setTarget( $ip );
+ $block = new Block( [
+ 'address' => $ip,
+ 'systemBlock' => 'global-block'
+ ] );
}
$this->mGlobalBlock = $blocked ? $block : false;
"blockedtitle": "User is blocked",
"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 \"email this user\" 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 \"email this user\" 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.",
"blockednoreason": "no reason given",
"whitelistedittext": "Please $1 to edit pages.",
"confirmedittext": "You must confirm your email address before editing pages.\nPlease set and validate your email address through your [[Special:Preferences|user preferences]].",
"summary-preview": "Preview of the edit summary, shown under the edit summary itself.\nShould match: {{msg-mw|summary}}.",
"subject-preview": "Should match {{msg-mw|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}}",
- "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}}",
- "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}}",
+ "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}}",
+ "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}}",
"blockednoreason": "Substituted with <code>$2</code> in the following message if the reason is not given:\n* {{msg-mw|cantcreateaccount-text}}.\n{{Identical|No reason given}}",
"whitelistedittext": "Used as error message. Parameters:\n* $1 - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description\nSee also:\n* {{msg-mw|Nocreatetext}}\n* {{msg-mw|Uploadnologintext}}\n* {{msg-mw|Loginreqpagetext}}",
"confirmedittext": "Used as error message.",
/**
* Lock the appropriate tables for the script
* @param Database $db
- * @param string $extraTable The name of any extra tables to lock (eg: text)
+ * @param string[] $extraTable The name of any extra tables to lock (eg: text)
*/
private function lockTables( $db, $extraTable = [] ) {
$tbls = [ 'page', 'revision', 'redirect' ];
"PhanTypeMismatchArgument",
// approximate error count: 39
"PhanTypeMismatchArgumentInternal",
- // approximate error count: 4
- "PhanTypeMismatchDefault",
// approximate error count: 16
"PhanTypeMismatchForeach",
// approximate error count: 63
"PhanTypeMismatchProperty",
// approximate error count: 95
"PhanTypeMismatchReturn",
- // approximate error count: 16
+ // approximate error count: 11
"PhanTypeMissingReturn",
// approximate error count: 5
"PhanTypeNonVarPassByRef",
"Account creation should not be blocked by default"
);
}
+
+ public function testSystemBlocks() {
+ $blockOptions = [
+ 'address' => 'UTBlockee',
+ 'reason' => 'test system block',
+ 'timestamp' => wfTimestampNow(),
+ 'expiry' => $this->db->getInfinity(),
+ 'byText' => 'MetaWikiUser',
+ 'systemBlock' => 'test',
+ 'enableAutoblock' => true,
+ ];
+ $block = new Block( $blockOptions );
+
+ $this->assertSame( 'test', $block->getSystemBlockType() );
+
+ try {
+ $block->insert();
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( MWException $ex ) {
+ $this->assertSame( 'Cannot insert a system block into the database', $ex->getMessage() );
+ }
+
+ try {
+ $block->doAutoblock( '192.0.2.2' );
+ $this->fail( 'Expected exception not thrown' );
+ } catch ( MWException $ex ) {
+ $this->assertSame( 'Cannot autoblock from a system block', $ex->getMessage() );
+ }
+ }
}
# $action != 'read' && $action != 'createaccount' && $user->isBlockedFrom( $this )
# $user->blockedFor() == ''
# $user->mBlock->mExpiry == 'infinity'
+
+ $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,
+ 'auto' => false,
+ 'expiry' => 10,
+ 'systemBlock' => 'test',
+ ] );
+ $this->assertEquals( [ [ 'systemblockedtext',
+ '[[User:Useruser|Useruser]]', 'no reason given', '127.0.0.1',
+ '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 ) );
}
}
$expect = Status::newGood();
$expect->fatal( 'blockedtext' );
$expect->fatal( 'autoblockedtext' );
+ $expect->fatal( 'systemblockedtext' );
$expect->fatal( 'mainpage' );
$expect->fatal( 'parentheses', 'foobar' );
$this->assertEquals( $expect, $mock->errorArrayToStatus( [
[ 'blockedtext' ],
[ 'autoblockedtext' ],
+ [ 'systemblockedtext' ],
'mainpage',
[ 'parentheses', 'foobar' ],
] ) );
$expect = Status::newGood();
$expect->fatal( ApiMessage::create( 'apierror-blocked', 'blocked', $blockinfo ) );
$expect->fatal( ApiMessage::create( 'apierror-autoblocked', 'autoblocked', $blockinfo ) );
+ $expect->fatal( ApiMessage::create( 'apierror-systemblocked', 'blocked', $blockinfo ) );
$expect->fatal( 'mainpage' );
$expect->fatal( 'parentheses', 'foobar' );
$this->assertEquals( $expect, $mock->errorArrayToStatus( [
[ 'blockedtext' ],
[ 'autoblockedtext' ],
+ [ 'systemblockedtext' ],
'mainpage',
[ 'parentheses', 'foobar' ],
], $user ) );