/**
* If set, any SquidPurge call on a URL or URLs will send a second purge no less than
* this many seconds later via the job queue. This requires delayed job support.
- * This should be safely higher than the 'max lag' value in $wgLBFactoryConf.
+ * This should be safely higher than the 'max lag' value in $wgLBFactoryConf, so that
+ * slave lag does not cause page to be stuck in stales states in CDN.
+ *
+ * This also fixes race conditions in two-tiered CDN setups (e.g. cdn2 => cdn1 => MediaWiki).
+ * If a purge for a URL reaches cdn2 before cdn1 and a request reaches cdn2 for that URL,
+ * it will populate the response from the stale cdn1 value. When cdn1 gets the purge, cdn2
+ * will still be stale. If the rebound purge delay is safely higher than the time to relay
+ * a purge to all nodes, then the rebound puge will clear cdn2 after cdn1 was cleared.
*
* @since 1.27
*/
* @return bool|mixed
*/
private function processRevision( $pageInfo, $revisionInfo ) {
+ global $wgMaxArticleSize;
+
+ // Make sure revisions won't violate $wgMaxArticleSize, which could lead to
+ // database errors and instability. Testing for revisions with only listed
+ // content models, as other content models might use serialization formats
+ // which aren't checked against $wgMaxArticleSize.
+ if ( ( !isset( $revisionInfo['model'] ) ||
+ in_array( $revisionInfo['model'], array(
+ 'wikitext',
+ 'css',
+ 'json',
+ 'javascript',
+ 'text',
+ ''
+ ) ) ) &&
+ (int)( strlen( $revisionInfo['text'] ) / 1024 ) > $wgMaxArticleSize
+ ) {
+ throw new MWException( 'The text of ' .
+ ( isset( $revisionInfo['id'] ) ?
+ "the revision with ID $revisionInfo[id]" :
+ 'a revision'
+ ) . " exceeds the maximum allowable size ($wgMaxArticleSize KB)" );
+ }
+
$revision = new WikiRevision( $this->config );
if ( isset( $revisionInfo['id'] ) ) {
);
}
+ /**
+ * Throw a UsageException, which will (if uncaught) call the main module's
+ * error handler and die with an error message including block info.
+ *
+ * @since 1.27
+ * @param Block $block The block used to generate the UsageException
+ * @throws UsageException always
+ */
+ public function dieBlocked( Block $block ) {
+ // Die using the appropriate message depending on block type
+ if ( $block->getType() == Block::TYPE_AUTO ) {
+ $this->dieUsage(
+ 'Your IP address has been blocked automatically, because it was used by a blocked user',
+ 'autoblocked',
+ 0,
+ array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
+ );
+ } else {
+ $this->dieUsage(
+ 'You have been blocked from editing',
+ 'blocked',
+ 0,
+ array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
+ );
+ }
+ }
+
/**
* Get error (as code, string) from a Status object.
*
$params = $this->extractRequestParams();
$user = $this->getUser();
-
if ( !$user->isAllowed( RevisionDeleter::getRestriction( $params['type'] ) ) ) {
$this->dieUsageMsg( 'badaccess-group0' );
}
if ( $user->isBlocked() ) {
- $block = $user->getBlock();
-
- // Die using the appropriate message depending on block type
- if ( $block->getType() == TYPE_AUTO ) {
- $this->dieUsage(
- 'Your IP address has been blocked automatically, because it was used by a blocked user',
- 'autoblocked',
- 0,
- array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
- );
- } else {
- $this->dieUsage(
- 'You have been blocked from editing',
- 'blocked',
- 0,
- array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
- );
- }
+ $this->dieBlocked( $user->getBlock() );
}
if ( !$params['ids'] ) {
}
if ( $user->isBlocked() ) {
- $block = $user->getBlock();
-
- // Die using the appropriate message depending on block type
- if ( $block->getType() == TYPE_AUTO ) {
- $this->dieUsage(
- 'Your IP address has been blocked automatically, because it was used by a blocked user',
- 'autoblocked',
- 0,
- array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
- );
- } else {
- $this->dieUsage(
- 'You have been blocked from editing',
- 'blocked',
- 0,
- array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
- );
- }
+ $this->dieBlocked( $user->getBlock() );
}
// validate and process each revid, rcid and logid
$this->useTransactionalTimeLimit();
$params = $this->extractRequestParams();
-
- if ( !$this->getUser()->isAllowed( 'undelete' ) ) {
+ $user = $this->getUser();
+ if ( !$user->isAllowed( 'undelete' ) ) {
$this->dieUsageMsg( 'permdenied-undelete' );
}
- if ( $this->getUser()->isBlocked() ) {
- $this->dieUsage(
- 'You have been blocked from editing',
- 'blocked',
- 0,
- array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $this->getUser()->getBlock() ) )
- );
+ if ( $user->isBlocked() ) {
+ $this->dieBlocked( $user->getBlock() );
}
$titleObj = Title::newFromText( $params['title'] );
}
public function execute() {
+ $pUser = $this->getUser();
+
+ // Deny if the user is blocked and doesn't have the full 'userrights' permission.
+ // This matches what Special:UserRights does for the web UI.
+ if ( $pUser->isBlocked() && !$pUser->isAllowed( 'userrights' ) ) {
+ $this->dieBlocked( $pUser->getBlock() );
+ }
+
$params = $this->extractRequestParams();
$user = $this->getUrUser( $params );
// Build CSS rules
$rules = array();
- // Underline: 2 = browser default, 1 = always, 0 = never
+ // Underline: 2 = skin default, 1 = always, 0 = never
if ( $options['underline'] < 2 ) {
$rules[] = "a { text-decoration: " .
( $options['underline'] ? 'underline' : 'none' ) . "; }";
- } else {
- # The scripts of these languages are very hard to read with underlines
- $rules[] = 'a:lang(ar), a:lang(kk-arab), a:lang(mzn), ' .
- 'a:lang(ps), a:lang(ur) { text-decoration: none; }';
}
if ( $options['editfont'] !== 'default' ) {
// Double-check that $options['editfont'] consists of safe characters only
return array( 'all' => $style );
}
+ /**
+ * @param ResourceLoaderContext $context
+ * @return bool
+ */
+ public function isKnownEmpty( ResourceLoaderContext $context ) {
+ $styles = $this->getStyles( $context );
+ return isset( $styles['all'] ) && $styles['all'] === '';
+ }
+
/**
* @return string
*/
public function alterForm( HTMLForm $form ) {
Hooks::run( 'LanguageSelector', array( $this->getOutput(), 'mw-languageselector' ) );
+ $form->setSubmitTextMsg( 'pagelang-submit' );
}
/**
"pagelang-language": "Language",
"pagelang-use-default": "Use default language",
"pagelang-select-lang": "Select language",
+ "pagelang-submit": "Submit",
"right-pagelang": "Change page language",
"action-pagelang": "change the page language",
"log-name-pagelang": "Change language log",
"pagelang-language": "Language selector label for Special:PageLanguage\n{{Identical|Language}}",
"pagelang-use-default": "Radio label for selector on Special:PageLanguage for default language",
"pagelang-select-lang": "Radio label for selector on Special:PageLanguage for language selection\n{{Identical|Select language}}",
+ "pagelang-submit": "Submit button label for Special:PageLanguage form\n{{Identical|Submit}}",
"right-pagelang": "{{Doc-right|pagelang}}\nRight to change page language on Special:PageLanguage",
"action-pagelang": "{{Doc-action|pagelang}}",
"log-name-pagelang": "Display entry for log name for changes in page language in Special:Log.",
text-decoration: underline;
}
+a:lang(ar),
+a:lang(kk-arab),
+a:lang(mzn),
+a:lang(ps),
+a:lang(ur) {
+ text-decoration: none;
+}
+
a.stub {
color: #772233;
}
} );
};
+ /**
+ * @inheritdoc
+ */
+ CSP.getItemFromData = function ( data ) {
+ // This is a bit of a hack... We have to canonicalize the data in the same way that
+ // #createItemWidget and CategoryCapsuleItemWidget will do, otherwise we won't find duplicates.
+ data = mw.Title.newFromText( data, NS_CATEGORY ).getMainText();
+ return OO.ui.mixin.GroupElement.prototype.getItemFromData.call( this, data );
+ };
+
/**
* Validates the values in `this.searchType`.
*