Use the mediawiki.String module instead.
* mw.language.specialCharacters, deprecated in 1.33, has been removed.
Use require( 'mediawiki.language.specialCharacters' ) instead.
+* The jquery.colorUtil module was removed. Use jquery.color instead.
* EditPage::submit(), deprecated in 1.29, has been removed. Use $this->edit()
directly.
* HTMLForm::getErrors(), deprecated in 1.28, has been removed. Use
"ext-xml": "*",
"guzzlehttp/guzzle": "6.3.3",
"liuggio/statsd-php-client": "1.0.18",
- "oojs/oojs-ui": "0.34.0",
+ "oojs/oojs-ui": "0.34.1",
"pear/mail": "1.4.1",
"pear/mail_mime": "1.10.2",
"pear/net_smtp": "1.8.1",
},
'BlockManager' => function ( MediaWikiServices $services ) : BlockManager {
- $context = RequestContext::getMain();
return new BlockManager(
new ServiceOptions(
BlockManager::$constructorOptions, $services->getMainConfig()
),
- $context->getUser(),
- $context->getRequest(),
$services->getPermissionManager()
);
},
$this->lazyFillContentModel( $this->loadFieldFromDB( 'page_content_model', $flags ) );
} elseif (
( !$this->mContentModel || $flags & self::GAID_FOR_UPDATE ) &&
- $this->getArticleId( $flags )
+ $this->getArticleID( $flags )
) {
$linkCache = MediaWikiServices::getInstance()->getLinkCache();
$linkCache->addLinkObj( $this ); # in case we already had an article ID
$user = $this->getUser();
$this->checkUserRightsAny( RevisionDeleter::getRestriction( $params['type'] ) );
- // @TODO Use PermissionManager::isBlockedFrom() instead.
- $block = $user->getBlock();
- if ( $block ) {
- $this->dieBlocked( $block );
- }
-
if ( !$params['ids'] ) {
$this->dieWithError( [ 'apierror-paramempty', 'ids' ], 'paramempty_ids' );
}
$this->dieWithError( [ 'apierror-revdel-needtarget' ], 'needtarget' );
}
+ if ( $this->getPermissionManager()->isBlockedFrom( $user, $targetObj ) ) {
+ $this->dieBlocked( $user->getBlock() );
+ }
+
$list = RevisionDeleter::createList(
$params['type'], $this->getContext(), $targetObj, $params['ids']
);
* @since 1.34 Refactored from User and Block.
*/
class BlockManager {
- // TODO: This should be UserIdentity instead of User
- /** @var User */
- private $currentUser;
-
- /** @var WebRequest */
- private $currentRequest;
-
- /** @var PermissionManager */
- private $permissionManager;
-
/**
* TODO Make this a const when HHVM support is dropped (T192166)
*
/**
* @param ServiceOptions $options
- * @param User $currentUser
- * @param WebRequest $currentRequest
* @param PermissionManager $permissionManager
*/
public function __construct(
ServiceOptions $options,
- User $currentUser,
- WebRequest $currentRequest,
PermissionManager $permissionManager
) {
$options->assertRequiredOptions( self::$constructorOptions );
$this->options = $options;
- $this->currentUser = $currentUser;
- $this->currentRequest = $currentRequest;
$this->permissionManager = $permissionManager;
}
* return a composite block that combines the strictest features of the applicable
* blocks.
*
- * TODO: $user should be UserIdentity instead of User
+ * Different blocks may be sought, depending on the user and their permissions. The
+ * user may be:
+ * (1) The global user (and can be affected by IP blocks). The global request object
+ * is needed for checking the IP address, the XFF header and the cookies.
+ * (2) The global user (and exempt from IP blocks). The global request object is
+ * needed for checking the cookies.
+ * (3) Another user (not the global user). No request object is available or needed;
+ * just look for a block against the user account.
+ *
+ * Cases #1 and #2 check whether the global user is blocked in practice; the block
+ * may due to their user account being blocked or to an IP address block or cookie
+ * block (or multiple of these). Case #3 simply checks whether a user's account is
+ * blocked, and does not determine whether the person using that account is affected
+ * in practice by any IP address or cookie blocks.
*
* @internal This should only be called by User::getBlockedStatus
* @param User $user
+ * @param WebRequest|null $request The global request object if the user is the
+ * global user (cases #1 and #2), otherwise null (case #3). The IP address and
+ * information from the request header are needed to find some types of blocks.
* @param bool $fromReplica Whether to check the replica DB first.
* To improve performance, non-critical checks are done against replica DBs.
* Check when actually saving should be done against master.
* @return AbstractBlock|null The most relevant block, or null if there is no block.
*/
- public function getUserBlock( User $user, $fromReplica ) {
- $isAnon = $user->getId() === 0;
+ public function getUserBlock( User $user, $request, $fromReplica ) {
$fromMaster = !$fromReplica;
+ $ip = null;
- // TODO: If $user is the current user, we should use the current request. Otherwise,
- // we should not look for XFF or cookie blocks.
- $request = $user->getRequest();
+ // If this is the global user, they may be affected by IP blocks (case #1),
+ // or they may be exempt (case #2). If affected, look for additional blocks
+ // against the IP address.
+ $checkIpBlocks = $request &&
+ !$this->permissionManager->userHasRight( $user, 'ipblock-exempt' );
- # 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;
- $sessionUser = $this->currentUser;
- // the session user is set up towards the end of Setup.php. Until then,
- // assume it's a logged-out user.
- $globalUserName = $sessionUser->isSafeToLoad()
- ? $sessionUser->getName()
- : IP::sanitizeIP( $this->currentRequest->getIP() );
- if ( $user->getName() === $globalUserName &&
- !$this->permissionManager->userHasRight( $user, 'ipblock-exempt' ) ) {
- $ip = $this->currentRequest->getIP();
+ if ( $request && $checkIpBlocks ) {
+
+ // Case #1: checking the global user, including IP blocks
+ $ip = $request->getIP();
+ // TODO: remove dependency on DatabaseBlock (T221075)
+ $blocks = DatabaseBlock::newListFromTarget( $user, $ip, $fromMaster );
+ $this->getAdditionalIpBlocks( $blocks, $request, !$user->isRegistered(), $fromMaster );
+ $this->getCookieBlock( $blocks, $user, $request );
+
+ } elseif ( $request ) {
+
+ // Case #2: checking the global user, but they are exempt from IP blocks
+ // TODO: remove dependency on DatabaseBlock (T221075)
+ $blocks = DatabaseBlock::newListFromTarget( $user, null, $fromMaster );
+ $this->getCookieBlock( $blocks, $user, $request );
+
+ } else {
+
+ // Case #3: checking whether a user's account is blocked
+ // TODO: remove dependency on DatabaseBlock (T221075)
+ $blocks = DatabaseBlock::newListFromTarget( $user, null, $fromMaster );
+
+ }
+
+ // Filter out any duplicated blocks, e.g. from the cookie
+ $blocks = $this->getUniqueBlocks( $blocks );
+
+ $block = null;
+ if ( count( $blocks ) > 0 ) {
+ if ( count( $blocks ) === 1 ) {
+ $block = $blocks[ 0 ];
+ } else {
+ $block = new CompositeBlock( [
+ 'address' => $ip,
+ 'byText' => 'MediaWiki default',
+ 'reason' => wfMessage( 'blockedtext-composite-reason' )->plain(),
+ 'originalBlocks' => $blocks,
+ ] );
+ }
}
- // User/IP blocking
- // After this, $blocks is an array of blocks or an empty array
- // TODO: remove dependency on DatabaseBlock
- $blocks = DatabaseBlock::newListFromTarget( $user, $ip, $fromMaster );
+ Hooks::run( 'GetUserBlock', [ clone $user, $ip, &$block ] );
+
+ return $block;
+ }
- // Cookie blocking
+ /**
+ * Get the cookie block, if there is one.
+ *
+ * @param AbstractBlock[] &$blocks
+ * @param UserIdentity $user
+ * @param WebRequest $request
+ * @return void
+ */
+ private function getCookieBlock( &$blocks, UserIdentity $user, WebRequest $request ) {
$cookieBlock = $this->getBlockFromCookieValue( $user, $request );
- if ( $cookieBlock instanceof AbstractBlock ) {
+ if ( $cookieBlock instanceof DatabaseBlock ) {
$blocks[] = $cookieBlock;
}
+ }
+
+ /**
+ * Check for any additional blocks against the IP address or any IPs in the XFF header.
+ *
+ * @param AbstractBlock[] &$blocks Blocks found so far
+ * @param WebRequest $request
+ * @param bool $isAnon The user is logged out
+ * @param bool $fromMaster
+ * @return void
+ */
+ private function getAdditionalIpBlocks( &$blocks, WebRequest $request, $isAnon, $fromMaster ) {
+ $ip = $request->getIP();
// Proxy blocking
- if ( $ip !== null && !in_array( $ip, $this->options->get( 'ProxyWhitelist' ) ) ) {
+ if ( !in_array( $ip, $this->options->get( 'ProxyWhitelist' ) ) ) {
// Local list
if ( $this->isLocallyBlockedProxy( $ip ) ) {
$blocks[] = new SystemBlock( [
}
}
- // (T25343) Apply IP blocks to the contents of XFF headers, if enabled
- if ( $this->options->get( 'ApplyIpBlocksToXff' )
- && $ip !== null
- && !in_array( $ip, $this->options->get( 'ProxyWhitelist' ) )
- ) {
- $xff = $request->getHeader( 'X-Forwarded-For' );
- $xff = array_map( 'trim', explode( ',', $xff ) );
- $xff = array_diff( $xff, [ $ip ] );
- // TODO: remove dependency on DatabaseBlock
- $xffblocks = DatabaseBlock::getBlocksForIPList( $xff, $isAnon, $fromMaster );
- $blocks = array_merge( $blocks, $xffblocks );
- }
-
// Soft blocking
- if ( $ip !== null
- && $isAnon
- && IP::isInRanges( $ip, $this->options->get( 'SoftBlockRanges' ) )
- ) {
+ if ( $isAnon && IP::isInRanges( $ip, $this->options->get( 'SoftBlockRanges' ) ) ) {
$blocks[] = new SystemBlock( [
'address' => $ip,
'byText' => 'MediaWiki default',
] );
}
- // Filter out any duplicated blocks, e.g. from the cookie
- $blocks = $this->getUniqueBlocks( $blocks );
-
- $block = null;
- if ( count( $blocks ) > 0 ) {
- if ( count( $blocks ) === 1 ) {
- $block = $blocks[ 0 ];
- } else {
- $block = new CompositeBlock( [
- 'address' => $ip,
- 'byText' => 'MediaWiki default',
- 'reason' => wfMessage( 'blockedtext-composite-reason' )->plain(),
- 'originalBlocks' => $blocks,
- ] );
- }
+ // (T25343) Apply IP blocks to the contents of XFF headers, if enabled
+ if ( $this->options->get( 'ApplyIpBlocksToXff' )
+ && !in_array( $ip, $this->options->get( 'ProxyWhitelist' ) )
+ ) {
+ $xff = $request->getHeader( 'X-Forwarded-For' );
+ $xff = array_map( 'trim', explode( ',', $xff ) );
+ $xff = array_diff( $xff, [ $ip ] );
+ // TODO: remove dependency on DatabaseBlock (T221075)
+ $xffblocks = DatabaseBlock::getBlocksForIPList( $xff, $isAnon, $fromMaster );
+ $blocks = array_merge( $blocks, $xffblocks );
}
-
- Hooks::run( 'GetUserBlock', [ clone $user, $ip, &$block ] );
-
- return $block;
}
/**
$blockCookieId = $this->getIdFromCookieValue( $cookieValue );
if ( !is_null( $blockCookieId ) ) {
- // TODO: remove dependency on DatabaseBlock
+ // TODO: remove dependency on DatabaseBlock (T221075)
$block = DatabaseBlock::newFromID( $blockCookieId );
if (
$block instanceof DatabaseBlock &&
*/
public function __construct( $id, $title, $c = null ) {
if ( is_string( $title ) ) {
- wfDeprecated( __METHOD__ . " with a string for the title", 1.34 );
+ wfDeprecated( __METHOD__ . " with a string for the title", '1.34' );
$this->title = Title::newFromText( $title );
if ( $this->title === null ) {
throw new InvalidArgumentException( "Cannot construct the title: $title" );
$this->id = $id;
// is_string() check is back-compat for ApprovedRevs
if ( is_string( $c ) ) {
- wfDeprecated( __METHOD__ . " with a string for the content", 1.34 );
+ wfDeprecated( __METHOD__ . " with a string for the content", '1.34' );
$c = new TextContent( $c );
} elseif ( is_bool( $c ) ) {
- wfDeprecated( __METHOD__ . " with a boolean for the content", 1.34 );
+ wfDeprecated( __METHOD__ . " with a boolean for the content", '1.34' );
$c = null;
}
$this->content = $c;
if ( !isset( $options['serializer'] ) || $options['serializer'] === 'php' ) {
$this->serializer = Redis::SERIALIZER_PHP;
} elseif ( $options['serializer'] === 'igbinary' ) {
+ if ( !defined( 'Redis::SERIALIZER_IGBINARY' ) ) {
+ throw new InvalidArgumentException(
+ __CLASS__ . ': configured serializer "igbinary" not available' );
+ }
$this->serializer = Redis::SERIALIZER_IGBINARY;
} elseif ( $options['serializer'] === 'none' ) {
$this->serializer = Redis::SERIALIZER_NONE;
$errorText = implode( "\n\n", $this->errors );
$errorResponse = self::makeComment( $errorText );
if ( $context->shouldIncludeScripts() ) {
- $errorResponse .= 'if (window.console && console.error) {'
- . Xml::encodeJsCall( 'console.error', [ $errorText ] )
- . "}\n";
+ $errorResponse .= 'if (window.console && console.error) { console.error('
+ . self::encodeJsonForScript( $errorText )
+ . "); }\n";
}
// Prepend error info to the response
* @internal
* @since 1.32
* @param mixed $data
- * @return string JSON
+ * @return string|false JSON string, false on error
*/
public static function encodeJsonForScript( $data ) {
// Keep output as small as possible by disabling needless escape modes
* @throws Exception
*/
public static function makeConfigSetScript( array $configuration ) {
- $js = Xml::encodeJsCall(
- 'mw.config.set',
- [ $configuration ],
- self::inDebugMode()
- );
- if ( $js === false ) {
+ $json = self::encodeJsonForScript( $configuration );
+ if ( $json === false ) {
$e = new Exception(
'JSON serialization of config data failed. ' .
'This usually means the config data is not valid UTF-8.'
);
MWExceptionHandler::logException( $e );
- $js = Xml::encodeJsCall( 'mw.log.error', [ $e->__toString() ] );
+ return 'mw.log.error(' . self::encodeJsonForScript( $e->__toString() ) . ');';
}
- return $js;
+ return "mw.config.set($json);";
}
/**
] );
} else {
$chunk = ResourceLoader::makeInlineScript(
- Xml::encodeJsCall( 'mw.loader.load', [ $url ] ),
+ 'mw.loader.load(' . ResourceLoader::encodeJsonForScript( $url ) . ');',
$nonce
);
}
* @return string JavaScript code
*/
public function getScript( ResourceLoaderContext $context ) {
- $fileScript = parent::getScript( $context );
- $langDataScript = Xml::encodeJsCall(
- 'mw.language.setData',
- [
- $context->getLanguage(),
- $this->getData( $context )
- ],
- ResourceLoader::inDebugMode()
- );
- return $fileScript . $langDataScript;
+ return parent::getScript( $context )
+ . 'mw.language.setData('
+ . ResourceLoader::encodeJsonForScript( $context->getLanguage() ) . ','
+ . ResourceLoader::encodeJsonForScript( $this->getData( $context ) )
+ . ');';
}
/**
* @return string JavaScript code
*/
public function getScript( ResourceLoaderContext $context ) {
- return Xml::encodeJsCall(
- 'mw.user.options.set',
- [ User::getDefaultOptions() ],
- ResourceLoader::inDebugMode()
- );
+ return 'mw.user.options.set('
+ . ResourceLoader::encodeJsonForScript( User::getDefaultOptions() )
+ . ');';
}
}
*/
public function getScript( ResourceLoaderContext $context ) {
// Use FILTER_NOMIN annotation to prevent needless minification and caching (T84960).
- return ResourceLoader::FILTER_NOMIN . Xml::encodeJsCall(
- 'mw.user.options.set',
- [ $context->getUserObj()->getOptions( User::GETOPTIONS_EXCLUDE_DEFAULTS ) ],
- ResourceLoader::inDebugMode()
- );
+ return ResourceLoader::FILTER_NOMIN
+ . 'mw.user.options.set('
+ . ResourceLoader::encodeJsonForScript(
+ $context->getUserObj()->getOptions( User::GETOPTIONS_EXCLUDE_DEFAULTS )
+ )
+ . ');';
}
/**
*/
public function getScript( ResourceLoaderContext $context ) {
// Use FILTER_NOMIN annotation to prevent needless minification and caching (T84960).
- return ResourceLoader::FILTER_NOMIN . Xml::encodeJsCall(
- 'mw.user.tokens.set',
- [ $this->contextUserTokens( $context ) ],
- ResourceLoader::inDebugMode()
- );
+ return ResourceLoader::FILTER_NOMIN
+ . 'mw.user.tokens.set('
+ . ResourceLoader::encodeJsonForScript( $this->contextUserTokens( $context ) )
+ . ');';
}
/**
'NewSection' => \SpecialNewSection::class,
'PermanentLink' => \SpecialPermanentLink::class,
'Redirect' => \SpecialRedirect::class,
- 'Revisiondelete' => \SpecialRevisionDelete::class,
+ 'Revisiondelete' => [
+ 'class' => \SpecialRevisionDelete::class,
+ 'services' => [
+ 'PermissionManager',
+ ],
+ ],
'RunJobs' => \SpecialRunJobs::class,
'Specialpages' => \SpecialSpecialpages::class,
'PageData' => \SpecialPageData::class,
}
if ( $error ) {
- $out->wrapWikiMsg( "<div class='previewnote'>\n$1\n</div>", $error );
+ $out->wrapWikiMsg( "<div class='previewnote errorbox'>\n$1\n</div>", $error );
return;
}
}
*/
private $existingPropNames = null;
+ /**
+ * @var string|null
+ */
+ private $ns;
+
/**
* @var bool
*/
'label-message' => 'pageswithprop-prop',
'required' => true,
],
+ 'namespace' => [
+ 'type' => 'namespaceselect',
+ 'name' => 'namespace',
+ 'label-message' => 'namespace',
+ 'all' => null,
+ 'default' => null,
+ ],
'reverse' => [
'type' => 'check',
'name' => 'reverse',
public function onSubmit( $data, $form ) {
$this->propName = $data['propname'];
+ $this->ns = $data['namespace'];
parent::execute( $data['propname'] );
}
}
public function getQueryInfo() {
- return [
+ $query = [
'tables' => [ 'page_props', 'page' ],
'fields' => [
'page_id' => 'pp_page',
],
'options' => []
];
+
+ if ( $this->ns && isset( $this->ns ) ) {
+ $query['conds']['page_namespace'] = $this->ns;
+ }
+
+ return $query;
}
function getOrderFields() {
*/
use MediaWiki\Storage\RevisionRecord;
+use MediaWiki\Permissions\PermissionManager;
/**
* Special page allowing users with the appropriate permissions to view
/** @var string */
private $otherReason;
+ /** @var PermissionManager */
+ private $permissionManager;
+
/**
* UI labels for each type.
*/
],
];
- public function __construct() {
+ /**
+ * @inheritDoc
+ *
+ * @param PermissionManager $permissionManager
+ */
+ public function __construct( PermissionManager $permissionManager ) {
parent::__construct( 'Revisiondelete', 'deleterevision' );
+
+ $this->permissionManager = $permissionManager;
}
public function doesWrites() {
$output = $this->getOutput();
$user = $this->getUser();
- // Check blocks
- // @TODO Use PermissionManager::isBlockedFrom() instead.
- $block = $user->getBlock();
- if ( $block ) {
- throw new UserBlockedError( $block );
- }
-
$this->setHeaders();
$this->outputHeader();
$request = $this->getRequest();
return;
}
+ // Check blocks
+ if ( $this->permissionManager->isBlockedFrom( $user, $this->targetObj ) ) {
+ throw new UserBlockedError( $user->getBlock() );
+ }
+
$this->typeLabels = self::$UILabels[$this->typeName];
$list = $this->getList();
$list->reset();
}
// Validate request parameters
- $optional = [ 'maxjobs' => 0, 'maxtime' => 30, 'type' => false, 'async' => true ];
+ $optional = [ 'maxjobs' => 0, 'maxtime' => 30, 'type' => false,
+ 'async' => true, 'stats' => false ];
$required = array_flip( [ 'title', 'tasks', 'signature', 'sigexpiry' ] );
$params = array_intersect_key( $this->getRequest()->getValues(), $required + $optional );
$missing = array_diff_key( $required, $params );
DeferredUpdates::POSTSEND
);
} else {
- $this->doRun( $params );
- print "Done\n";
+ $stats = $this->doRun( $params );
+
+ if ( $params['stats'] ) {
+ $this->getRequest()->response()->header( 'Content-Type: application/json' );
+ print FormatJson::encode( $stats );
+ } else {
+ print "Done\n";
+ }
}
}
protected function doRun( array $params ) {
$runner = new JobRunner( LoggerFactory::getInstance( 'runJobs' ) );
- $runner->run( [
+ return $runner->run( [
'type' => $params['type'],
'maxJobs' => $params['maxjobs'] ?: 1,
'maxTime' => $params['maxtime'] ?: 30
// overwriting mBlockedby, surely?
$this->load();
+ // TODO: Block checking shouldn't really be done from the User object. Block
+ // checking can involve checking for IP blocks, cookie blocks, and/or XFF blocks,
+ // which need more knowledge of the request context than the User should have.
+ // Since we do currently check blocks from the User, we have to do the following
+ // here:
+ // - Check if this is the user associated with the main request
+ // - If so, pass the relevant request information to the block manager
+ $request = null;
+
+ // The session user is set up towards the end of Setup.php. Until then,
+ // assume it's a logged-out user.
+ $sessionUser = RequestContext::getMain()->getUser();
+ $globalUserName = $sessionUser->isSafeToLoad()
+ ? $sessionUser->getName()
+ : IP::sanitizeIP( $sessionUser->getRequest()->getIP() );
+
+ if ( $this->getName() === $globalUserName ) {
+ // This is the global user, so we need to pass the request
+ $request = $this->getRequest();
+ }
+
// @phan-suppress-next-line PhanAccessMethodInternal It's the only allowed use
$block = MediaWikiServices::getInstance()->getBlockManager()->getUserBlock(
$this,
+ $request,
$fromReplica
);
"--exclude": [
"resources/src/jquery.tablesorter",
"resources/src/jquery.tipsy",
- "resources/src/jquery/jquery.color.js",
+ "resources/src/jquery.color/jquery.color.js",
"resources/src/jquery/jquery.highlightText.js",
"resources/src/jquery/jquery.mw-jump.js",
"resources/src/mediawiki.base/legacy.wikibits.js",
'targets' => [ 'desktop', 'mobile' ],
],
'jquery.color' => [
- 'scripts' => 'resources/src/jquery/jquery.color.js',
- 'dependencies' => 'jquery.colorUtil',
- ],
- 'jquery.colorUtil' => [
- 'scripts' => 'resources/src/jquery/jquery.colorUtil.js',
+ 'scripts' => [
+ 'resources/src/jquery.color/jquery.colorUtil.js',
+ 'resources/src/jquery.color/jquery.color.js',
+ ],
],
'jquery.confirmable' => [
'scripts' => [
'scripts' => [
'resources/src/mediawiki.htmlform.checker.js',
],
+ 'dependencies' => [
+ 'mediawiki.util',
+ ],
'targets' => [ 'desktop', 'mobile' ],
],
'mediawiki.htmlform.ooui' => [
'dependencies' => [
'mediawiki.api',
'mediawiki.jqueryMsg',
- 'jquery.throttle-debounce',
'mediawiki.htmlform.checker',
],
],
ooui:
type: tar
- src: https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.34.0.tgz
- integrity: sha384-DVG3XayKF02r0rqXSJUEWJImcK8XJeiIVC/Ii5R20cINYpTaAs+x4rdCU52VaKKw
+ src: https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.34.1.tgz
+ integrity: sha384-QXYp5vK60xpu4nkv/JStszI6U4TYGCNe7uXb5rYb7FYURLTR41mtNO74gl7HXgpz
dest:
# Main stuff
# OOUI Release History
+## v0.34.1 / 2019-09-10
+### Deprecating changes
+* [DEPRECATING CHANGE] icons: Rename 'beaker' to 'labFlask' (Volker E.)
+
+### Styles
+* icons: Add 'userGroup' (Volker E.)
+
+### Code
+* Wrap long strings in popups (Sam Wilson)
+* demos: Add missing file to PHP demo to fix infusion (Bartosz Dziewoński)
+
+
## v0.34.0 / 2019-09-04
### Breaking changes
* [BREAKING CHANGE] Use OOjs v3.0.0, up from v2.2.2 (James D. Forrester)
"ooui-item-remove": "Supprimer",
"ooui-dialog-message-accept": "OK",
"ooui-dialog-message-reject": "Annuler",
- "ooui-dialog-process-error": "Quelque chose s'est mal passé",
+ "ooui-dialog-process-error": "Quelque chose s’est mal passé",
"ooui-dialog-process-dismiss": "Fermer",
"ooui-dialog-process-retry": "Réessayer",
"ooui-dialog-process-continue": "Continuer",
- "ooui-combobox-button-label": "Liste déroulante de combobox",
+ "ooui-combobox-button-label": "Liste déroulante pour boîte de saisie",
"ooui-selectfile-button-select": "Sélectionner un fichier",
"ooui-selectfile-not-supported": "La sélection de fichier n’est pas prise en charge",
"ooui-selectfile-placeholder": "Aucun fichier sélectionné",
"Gloria sah",
"Andrzej aa",
"The Polish",
- "Railfail536"
+ "Railfail536",
+ "Rail"
]
},
"ooui-outline-control-move-down": "Przesuń w dół",
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:52Z
+ * Date: 2019-09-10T23:46:03Z
*/
( function ( OO ) {
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:59Z
+ * Date: 2019-09-10T23:46:11Z
*/
.oo-ui-element-hidden {
display: none !important;
.oo-ui-popupWidget-popup {
position: relative;
overflow: hidden;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
}
.oo-ui-popupWidget-anchor {
display: none;
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:59Z
+ * Date: 2019-09-10T23:46:11Z
*/
.oo-ui-element-hidden {
display: none !important;
.oo-ui-popupWidget-popup {
position: relative;
overflow: hidden;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
}
.oo-ui-popupWidget-anchor {
display: none;
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:52Z
+ * Date: 2019-09-10T23:46:03Z
*/
( function ( OO ) {
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:59Z
+ * Date: 2019-09-10T23:46:11Z
*/
.oo-ui-tool > .oo-ui-tool-link > .oo-ui-tool-checkIcon {
display: none;
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:59Z
+ * Date: 2019-09-10T23:46:11Z
*/
.oo-ui-tool {
-webkit-box-sizing: border-box;
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:52Z
+ * Date: 2019-09-10T23:46:03Z
*/
( function ( OO ) {
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:59Z
+ * Date: 2019-09-10T23:46:11Z
*/
.oo-ui-draggableElement-handle:not( .oo-ui-draggableElement-undraggable ).oo-ui-widget {
cursor: move;
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:59Z
+ * Date: 2019-09-10T23:46:11Z
*/
.oo-ui-draggableElement-handle:not( .oo-ui-draggableElement-undraggable ).oo-ui-widget {
cursor: move;
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:52Z
+ * Date: 2019-09-10T23:46:03Z
*/
( function ( OO ) {
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:52Z
+ * Date: 2019-09-10T23:46:03Z
*/
( function ( OO ) {
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:59Z
+ * Date: 2019-09-10T23:46:11Z
*/
.oo-ui-window {
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:59Z
+ * Date: 2019-09-10T23:46:11Z
*/
.oo-ui-window {
/*!
- * OOUI v0.34.0-pre (d5e74518ab)
+ * OOUI v0.34.1-pre (3913589098)
* https://www.mediawiki.org/wiki/OOUI
*
* Copyright 2011–2019 OOUI Team and other contributors.
* Released under the MIT license
* http://oojs.mit-license.org
*
- * Date: 2019-09-04T18:28:52Z
+ * Date: 2019-09-10T23:46:03Z
*/
( function ( OO ) {
"file": "../wikimediaui/images/icons/attachment.svg"
},
"beaker": {
- "file": "../wikimediaui/images/icons/beaker.svg"
+ "file": "../wikimediaui/images/icons/labFlask.svg",
+ "deprecated": "Renamed since v0.34.1, use 'labFlask' instead."
},
"calendar": {
"file": "../wikimediaui/images/icons/calendar.svg"
"imageLayoutThumbnail": {
"file": "../wikimediaui/images/icons/imageLayoutThumbnail.svg"
},
+ "labFlask": {
+ "file": "../wikimediaui/images/icons/labFlask.svg"
+ },
"language": {
"file": "../wikimediaui/images/icons/language.svg"
},
"userAvatarOutline": {
"file": "../wikimediaui/images/icons/userAvatarOutline.svg"
},
+ "userGroup": {
+ "file": {
+ "ltr": "../wikimediaui/images/icons/userGroup-ltr.svg",
+ "rtl": "../wikimediaui/images/icons/userGroup-rtl.svg"
+ }
+ },
"userTalk": {
"file": {
"ltr": "../wikimediaui/images/icons/userTalk-ltr.svg",
"file": "images/icons/attachment.svg"
},
"beaker": {
- "file": "images/icons/beaker.svg"
+ "file": "images/icons/labFlask.svg",
+ "deprecated": "Renamed since v0.34.1, use 'labFlask' instead."
},
"calendar": {
"file": "images/icons/calendar.svg"
"imageLayoutThumbnail": {
"file": "images/icons/imageLayoutThumbnail.svg"
},
+ "labFlask": {
+ "file": "images/icons/labFlask.svg"
+ },
"language": {
"file": "images/icons/language.svg"
},
"userAvatarOutline": {
"file": "images/icons/userAvatarOutline.svg"
},
+ "userGroup": {
+ "file": {
+ "ltr": "images/icons/userGroup-ltr.svg",
+ "rtl": "images/icons/userGroup-rtl.svg"
+ }
+ },
"userTalk": {
"file": {
"ltr": "images/icons/userTalk-ltr.svg",
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
- <title>
- beaker
- </title><g fill="#fff">
- <path d="M13 7.61V3h1V1H6v2h1v4.61l-5.86 9.88A1 1 0 0 0 2 19h16a1 1 0 0 0 .86-1.51zm-4.2.88a1 1 0 0 0 .2-.6V3h2v4.89a1 1 0 0 0 .14.51l2.14 3.6H6.72z"/>
-</g></svg>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
- <title>
- beaker
- </title><g fill="#36c">
- <path d="M13 7.61V3h1V1H6v2h1v4.61l-5.86 9.88A1 1 0 0 0 2 19h16a1 1 0 0 0 .86-1.51zm-4.2.88a1 1 0 0 0 .2-.6V3h2v4.89a1 1 0 0 0 .14.51l2.14 3.6H6.72z"/>
-</g></svg>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
- <title>
- beaker
- </title>
- <path d="M13 7.61V3h1V1H6v2h1v4.61l-5.86 9.88A1 1 0 0 0 2 19h16a1 1 0 0 0 .86-1.51zm-4.2.88a1 1 0 0 0 .2-.6V3h2v4.89a1 1 0 0 0 .14.51l2.14 3.6H6.72z"/>
-</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+ <title>
+ laboratory flask
+ </title><g fill="#fff">
+ <path d="M13 7.61V3h1V1H6v2h1v4.61l-5.86 9.88A1 1 0 0 0 2 19h16a1 1 0 0 0 .86-1.51zm-4.2.88a1 1 0 0 0 .2-.6V3h2v4.89a1 1 0 0 0 .14.51l2.14 3.6H6.72z"/>
+</g></svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+ <title>
+ laboratory flask
+ </title><g fill="#36c">
+ <path d="M13 7.61V3h1V1H6v2h1v4.61l-5.86 9.88A1 1 0 0 0 2 19h16a1 1 0 0 0 .86-1.51zm-4.2.88a1 1 0 0 0 .2-.6V3h2v4.89a1 1 0 0 0 .14.51l2.14 3.6H6.72z"/>
+</g></svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+ <title>
+ laboratory flask
+ </title>
+ <path d="M13 7.61V3h1V1H6v2h1v4.61l-5.86 9.88A1 1 0 0 0 2 19h16a1 1 0 0 0 .86-1.51zm-4.2.88a1 1 0 0 0 .2-.6V3h2v4.89a1 1 0 0 0 .14.51l2.14 3.6H6.72z"/>
+</svg>
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<title>
user avatar
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<title>
user avatar
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
<title>
user avatar
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+ <title>
+ user group
+ </title><g fill="#fff">
+ <path d="M14 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zM6 3c1.66 0 3 1.34 3 3S7.66 9 6 9 3 7.66 3 6s1.34-3 3-3zm8 7c3.31 0 6 1.79 6 4v2h-6v-2c0-1.48-1.21-2.77-3-3.46.88-.35 1.91-.54 3-.54zm-8 0c3.31 0 6 1.79 6 4v2H0v-2c0-2.21 2.69-4 6-4z"/>
+</g></svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+ <title>
+ user group
+ </title><g fill="#36c">
+ <path d="M14 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zM6 3c1.66 0 3 1.34 3 3S7.66 9 6 9 3 7.66 3 6s1.34-3 3-3zm8 7c3.31 0 6 1.79 6 4v2h-6v-2c0-1.48-1.21-2.77-3-3.46.88-.35 1.91-.54 3-.54zm-8 0c3.31 0 6 1.79 6 4v2H0v-2c0-2.21 2.69-4 6-4z"/>
+</g></svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+ <title>
+ user group
+ </title>
+ <path d="M14 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zM6 3c1.66 0 3 1.34 3 3S7.66 9 6 9 3 7.66 3 6s1.34-3 3-3zm8 7c3.31 0 6 1.79 6 4v2h-6v-2c0-1.48-1.21-2.77-3-3.46.88-.35 1.91-.54 3-.54zm-8 0c3.31 0 6 1.79 6 4v2H0v-2c0-2.21 2.69-4 6-4z"/>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+ <title>
+ user group
+ </title><g fill="#fff">
+ <path d="M6 3C4.34 3 3 4.34 3 6s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zm8 0c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zm-8 7c-3.31 0-6 1.79-6 4v2h6v-2c0-1.48 1.21-2.77 3-3.46-.88-.35-1.91-.54-3-.54zm8 0c-3.31 0-6 1.79-6 4v2h12v-2c0-2.21-2.69-4-6-4z"/>
+</g></svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+ <title>
+ user group
+ </title><g fill="#36c">
+ <path d="M6 3C4.34 3 3 4.34 3 6s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zm8 0c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zm-8 7c-3.31 0-6 1.79-6 4v2h6v-2c0-1.48 1.21-2.77 3-3.46-.88-.35-1.91-.54-3-.54zm8 0c-3.31 0-6 1.79-6 4v2h12v-2c0-2.21-2.69-4-6-4z"/>
+</g></svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
+ <title>
+ user group
+ </title>
+ <path d="M6 3C4.34 3 3 4.34 3 6s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zm8 0c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zm-8 7c-3.31 0-6 1.79-6 4v2h6v-2c0-1.48 1.21-2.77 3-3.46-.88-.35-1.91-.54-3-.54zm8 0c-3.31 0-6 1.79-6 4v2h12v-2c0-2.21-2.69-4-6-4z"/>
+</svg>
--- /dev/null
+/**
+ * jQuery Color Animations
+ *
+ * @author John Resig, 2007
+ * @author Krinkle, 2011
+ * Released under the MIT and GPL licenses.
+ *
+ * - 2011-01-05: Forked for MediaWiki. See also jQuery.colorUtil plugin
+ */
+( function () {
+
+ function getColor( elem, attr ) {
+ var color;
+
+ do {
+ color = $.css( elem, attr );
+
+ // Keep going until we find an element that has color, or we hit the body
+ if ( color !== '' && color !== 'transparent' || elem.nodeName.toLowerCase() === 'body' ) {
+ break;
+ }
+
+ attr = 'backgroundColor';
+ // eslint-disable-next-line no-cond-assign
+ } while ( elem = elem.parentNode );
+
+ return $.colorUtil.getRGB( color );
+ }
+
+ // We override the animation for all of these color styles
+ [
+ 'backgroundColor',
+ 'borderBottomColor',
+ 'borderLeftColor',
+ 'borderRightColor',
+ 'borderTopColor',
+ 'color',
+ 'outlineColor'
+ ].forEach( function ( attr ) {
+ $.fx.step[ attr ] = function ( fx ) {
+ if ( !fx.colorInit ) {
+ fx.start = getColor( fx.elem, attr );
+ fx.end = $.colorUtil.getRGB( fx.end );
+ fx.colorInit = true;
+ }
+
+ fx.elem.style[ attr ] = 'rgb(' + [
+ Math.max( Math.min( parseInt( ( fx.pos * ( fx.end[ 0 ] - fx.start[ 0 ] ) ) + fx.start[ 0 ], 10 ), 255 ), 0 ),
+ Math.max( Math.min( parseInt( ( fx.pos * ( fx.end[ 1 ] - fx.start[ 1 ] ) ) + fx.start[ 1 ], 10 ), 255 ), 0 ),
+ Math.max( Math.min( parseInt( ( fx.pos * ( fx.end[ 2 ] - fx.start[ 2 ] ) ) + fx.start[ 2 ], 10 ), 255 ), 0 )
+ ].join( ',' ) + ')';
+ };
+ } );
+
+}() );
--- /dev/null
+/*!
+ * jQuery Color Utilities
+ *
+ * Released under the MIT and GPL licenses.
+ *
+ * Mostly based on other plugins and functions (linted and optimized a little).
+ * Sources cited inline.
+ */
+( function () {
+ /**
+ * @class jQuery.colorUtil
+ * @singleton
+ */
+ $.colorUtil = {
+
+ /**
+ * Parse CSS color strings looking for color tuples
+ *
+ * Based on highlightFade by Blair Mitchelmore
+ * <http://jquery.offput.ca/highlightFade/>
+ *
+ * @param {Array|string} color
+ * @return {Array}
+ */
+ getRGB: function ( color ) {
+ var result;
+
+ // Check if we're already dealing with an array of colors
+ if ( color && Array.isArray( color ) && color.length === 3 ) {
+ return color;
+ }
+ if ( typeof color !== 'string' ) {
+ return undefined;
+ }
+
+ // Look for rgb(num,num,num)
+ // eslint-disable-next-line no-cond-assign
+ if ( result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec( color ) ) {
+ return [
+ parseInt( result[ 1 ], 10 ),
+ parseInt( result[ 2 ], 10 ),
+ parseInt( result[ 3 ], 10 )
+ ];
+ }
+
+ // Look for rgb(num%,num%,num%)
+ // eslint-disable-next-line no-cond-assign
+ if ( result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec( color ) ) {
+ return [
+ parseFloat( result[ 1 ] ) * 2.55,
+ parseFloat( result[ 2 ] ) * 2.55,
+ parseFloat( result[ 3 ] ) * 2.55
+ ];
+ }
+
+ // Look for #a0b1c2
+ // eslint-disable-next-line no-cond-assign
+ if ( result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec( color ) ) {
+ return [
+ parseInt( result[ 1 ], 16 ),
+ parseInt( result[ 2 ], 16 ),
+ parseInt( result[ 3 ], 16 )
+ ];
+ }
+
+ // Look for #fff
+ // eslint-disable-next-line no-cond-assign
+ if ( result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec( color ) ) {
+ return [
+ parseInt( result[ 1 ] + result[ 1 ], 16 ),
+ parseInt( result[ 2 ] + result[ 2 ], 16 ),
+ parseInt( result[ 3 ] + result[ 3 ], 16 )
+ ];
+ }
+
+ // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
+ // eslint-disable-next-line no-cond-assign
+ if ( result = /rgba\(0, 0, 0, 0\)/.exec( color ) ) {
+ return $.colorUtil.colors.transparent;
+ }
+
+ // Otherwise, we're most likely dealing with a named color
+ return $.colorUtil.colors[ color.trim().toLowerCase() ];
+ },
+
+ /**
+ * Named color map
+ *
+ * Based on Interface by Stefan Petre
+ * <http://interface.eyecon.ro/>
+ *
+ * @property {Object}
+ */
+ colors: {
+ aqua: [ 0, 255, 255 ],
+ azure: [ 240, 255, 255 ],
+ beige: [ 245, 245, 220 ],
+ black: [ 0, 0, 0 ],
+ blue: [ 0, 0, 255 ],
+ brown: [ 165, 42, 42 ],
+ cyan: [ 0, 255, 255 ],
+ darkblue: [ 0, 0, 139 ],
+ darkcyan: [ 0, 139, 139 ],
+ darkgrey: [ 169, 169, 169 ],
+ darkgreen: [ 0, 100, 0 ],
+ darkkhaki: [ 189, 183, 107 ],
+ darkmagenta: [ 139, 0, 139 ],
+ darkolivegreen: [ 85, 107, 47 ],
+ darkorange: [ 255, 140, 0 ],
+ darkorchid: [ 153, 50, 204 ],
+ darkred: [ 139, 0, 0 ],
+ darksalmon: [ 233, 150, 122 ],
+ darkviolet: [ 148, 0, 211 ],
+ fuchsia: [ 255, 0, 255 ],
+ gold: [ 255, 215, 0 ],
+ green: [ 0, 128, 0 ],
+ indigo: [ 75, 0, 130 ],
+ khaki: [ 240, 230, 140 ],
+ lightblue: [ 173, 216, 230 ],
+ lightcyan: [ 224, 255, 255 ],
+ lightgreen: [ 144, 238, 144 ],
+ lightgrey: [ 211, 211, 211 ],
+ lightpink: [ 255, 182, 193 ],
+ lightyellow: [ 255, 255, 224 ],
+ lime: [ 0, 255, 0 ],
+ magenta: [ 255, 0, 255 ],
+ maroon: [ 128, 0, 0 ],
+ navy: [ 0, 0, 128 ],
+ olive: [ 128, 128, 0 ],
+ orange: [ 255, 165, 0 ],
+ pink: [ 255, 192, 203 ],
+ purple: [ 128, 0, 128 ],
+ violet: [ 128, 0, 128 ],
+ red: [ 255, 0, 0 ],
+ silver: [ 192, 192, 192 ],
+ white: [ 255, 255, 255 ],
+ yellow: [ 255, 255, 0 ],
+ transparent: [ 255, 255, 255 ]
+ },
+
+ /**
+ * Convert an RGB color value to HSL.
+ *
+ * Conversion formula based on
+ * <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
+ *
+ * Adapted from <https://en.wikipedia.org/wiki/HSL_color_space>.
+ *
+ * Assumes `r`, `g`, and `b` are contained in the set `[0, 255]` and
+ * returns `h`, `s`, and `l` in the set `[0, 1]`.
+ *
+ * @param {number} r The red color value
+ * @param {number} g The green color value
+ * @param {number} b The blue color value
+ * @return {number[]} The HSL representation
+ */
+ rgbToHsl: function ( r, g, b ) {
+ var d, h, s, l, min, max;
+
+ r = r / 255;
+ g = g / 255;
+ b = b / 255;
+
+ max = Math.max( r, g, b );
+ min = Math.min( r, g, b );
+ l = ( max + min ) / 2;
+
+ if ( max === min ) {
+ // achromatic
+ h = s = 0;
+ } else {
+ d = max - min;
+ s = l > 0.5 ? d / ( 2 - max - min ) : d / ( max + min );
+ switch ( max ) {
+ case r:
+ h = ( g - b ) / d + ( g < b ? 6 : 0 );
+ break;
+ case g:
+ h = ( b - r ) / d + 2;
+ break;
+ case b:
+ h = ( r - g ) / d + 4;
+ break;
+ }
+ h /= 6;
+ }
+
+ return [ h, s, l ];
+ },
+
+ /**
+ * Convert an HSL color value to RGB.
+ *
+ * Conversion formula based on
+ * <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
+ *
+ * Adapted from <https://en.wikipedia.org/wiki/HSL_color_space>.
+ *
+ * Assumes `h`, `s`, and `l` are contained in the set `[0, 1]` and
+ * returns `r`, `g`, and `b` in the set `[0, 255]`.
+ *
+ * @param {number} h The hue
+ * @param {number} s The saturation
+ * @param {number} l The lightness
+ * @return {number[]} The RGB representation
+ */
+ hslToRgb: function ( h, s, l ) {
+ var r, g, b, hue2rgb, q, p;
+
+ if ( s === 0 ) {
+ r = g = b = l; // achromatic
+ } else {
+ hue2rgb = function ( p, q, t ) {
+ if ( t < 0 ) {
+ t += 1;
+ }
+ if ( t > 1 ) {
+ t -= 1;
+ }
+ if ( t < 1 / 6 ) {
+ return p + ( q - p ) * 6 * t;
+ }
+ if ( t < 1 / 2 ) {
+ return q;
+ }
+ if ( t < 2 / 3 ) {
+ return p + ( q - p ) * ( 2 / 3 - t ) * 6;
+ }
+ return p;
+ };
+
+ q = l < 0.5 ? l * ( 1 + s ) : l + s - l * s;
+ p = 2 * l - q;
+ r = hue2rgb( p, q, h + 1 / 3 );
+ g = hue2rgb( p, q, h );
+ b = hue2rgb( p, q, h - 1 / 3 );
+ }
+
+ return [ r * 255, g * 255, b * 255 ];
+ },
+
+ /**
+ * Get a brighter or darker rgb() value string.
+ *
+ * Usage:
+ *
+ * $.colorUtil.getColorBrightness( 'red', +0.1 );
+ * // > "rgb(255,50,50)"
+ * $.colorUtil.getColorBrightness( 'rgb(200,50,50)', -0.2 );
+ * // > "rgb(118,29,29)"
+ *
+ * @param {Mixed} currentColor Current value in css
+ * @param {number} mod Wanted brightness modification between -1 and 1
+ * @return {string} Like `'rgb(r,g,b)'`
+ */
+ getColorBrightness: function ( currentColor, mod ) {
+ var rgbArr = $.colorUtil.getRGB( currentColor ),
+ hslArr = $.colorUtil.rgbToHsl( rgbArr[ 0 ], rgbArr[ 1 ], rgbArr[ 2 ] );
+ rgbArr = $.colorUtil.hslToRgb( hslArr[ 0 ], hslArr[ 1 ], hslArr[ 2 ] + mod );
+
+ return 'rgb(' +
+ [ parseInt( rgbArr[ 0 ], 10 ), parseInt( rgbArr[ 1 ], 10 ), parseInt( rgbArr[ 2 ], 10 ) ].join( ',' ) +
+ ')';
+ }
+
+ };
+
+}() );
+++ /dev/null
-/**
- * jQuery Color Animations
- *
- * @author John Resig, 2007
- * @author Krinkle, 2011
- * Released under the MIT and GPL licenses.
- *
- * - 2011-01-05: Forked for MediaWiki. See also jQuery.colorUtil plugin
- */
-( function () {
-
- function getColor( elem, attr ) {
- var color;
-
- do {
- color = $.css( elem, attr );
-
- // Keep going until we find an element that has color, or we hit the body
- if ( color !== '' && color !== 'transparent' || elem.nodeName.toLowerCase() === 'body' ) {
- break;
- }
-
- attr = 'backgroundColor';
- // eslint-disable-next-line no-cond-assign
- } while ( elem = elem.parentNode );
-
- return $.colorUtil.getRGB( color );
- }
-
- // We override the animation for all of these color styles
- [
- 'backgroundColor',
- 'borderBottomColor',
- 'borderLeftColor',
- 'borderRightColor',
- 'borderTopColor',
- 'color',
- 'outlineColor'
- ].forEach( function ( attr ) {
- $.fx.step[ attr ] = function ( fx ) {
- if ( !fx.colorInit ) {
- fx.start = getColor( fx.elem, attr );
- fx.end = $.colorUtil.getRGB( fx.end );
- fx.colorInit = true;
- }
-
- fx.elem.style[ attr ] = 'rgb(' + [
- Math.max( Math.min( parseInt( ( fx.pos * ( fx.end[ 0 ] - fx.start[ 0 ] ) ) + fx.start[ 0 ], 10 ), 255 ), 0 ),
- Math.max( Math.min( parseInt( ( fx.pos * ( fx.end[ 1 ] - fx.start[ 1 ] ) ) + fx.start[ 1 ], 10 ), 255 ), 0 ),
- Math.max( Math.min( parseInt( ( fx.pos * ( fx.end[ 2 ] - fx.start[ 2 ] ) ) + fx.start[ 2 ], 10 ), 255 ), 0 )
- ].join( ',' ) + ')';
- };
- } );
-
-}() );
+++ /dev/null
-/*!
- * jQuery Color Utilities
- *
- * Released under the MIT and GPL licenses.
- *
- * Mostly based on other plugins and functions (linted and optimized a little).
- * Sources cited inline.
- */
-( function () {
- /**
- * @class jQuery.colorUtil
- * @singleton
- */
- $.colorUtil = {
-
- /**
- * Parse CSS color strings looking for color tuples
- *
- * Based on highlightFade by Blair Mitchelmore
- * <http://jquery.offput.ca/highlightFade/>
- *
- * @param {Array|string} color
- * @return {Array}
- */
- getRGB: function ( color ) {
- var result;
-
- // Check if we're already dealing with an array of colors
- if ( color && Array.isArray( color ) && color.length === 3 ) {
- return color;
- }
- if ( typeof color !== 'string' ) {
- return undefined;
- }
-
- // Look for rgb(num,num,num)
- // eslint-disable-next-line no-cond-assign
- if ( result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec( color ) ) {
- return [
- parseInt( result[ 1 ], 10 ),
- parseInt( result[ 2 ], 10 ),
- parseInt( result[ 3 ], 10 )
- ];
- }
-
- // Look for rgb(num%,num%,num%)
- // eslint-disable-next-line no-cond-assign
- if ( result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec( color ) ) {
- return [
- parseFloat( result[ 1 ] ) * 2.55,
- parseFloat( result[ 2 ] ) * 2.55,
- parseFloat( result[ 3 ] ) * 2.55
- ];
- }
-
- // Look for #a0b1c2
- // eslint-disable-next-line no-cond-assign
- if ( result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec( color ) ) {
- return [
- parseInt( result[ 1 ], 16 ),
- parseInt( result[ 2 ], 16 ),
- parseInt( result[ 3 ], 16 )
- ];
- }
-
- // Look for #fff
- // eslint-disable-next-line no-cond-assign
- if ( result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec( color ) ) {
- return [
- parseInt( result[ 1 ] + result[ 1 ], 16 ),
- parseInt( result[ 2 ] + result[ 2 ], 16 ),
- parseInt( result[ 3 ] + result[ 3 ], 16 )
- ];
- }
-
- // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
- // eslint-disable-next-line no-cond-assign
- if ( result = /rgba\(0, 0, 0, 0\)/.exec( color ) ) {
- return $.colorUtil.colors.transparent;
- }
-
- // Otherwise, we're most likely dealing with a named color
- return $.colorUtil.colors[ color.trim().toLowerCase() ];
- },
-
- /**
- * Named color map
- *
- * Based on Interface by Stefan Petre
- * <http://interface.eyecon.ro/>
- *
- * @property {Object}
- */
- colors: {
- aqua: [ 0, 255, 255 ],
- azure: [ 240, 255, 255 ],
- beige: [ 245, 245, 220 ],
- black: [ 0, 0, 0 ],
- blue: [ 0, 0, 255 ],
- brown: [ 165, 42, 42 ],
- cyan: [ 0, 255, 255 ],
- darkblue: [ 0, 0, 139 ],
- darkcyan: [ 0, 139, 139 ],
- darkgrey: [ 169, 169, 169 ],
- darkgreen: [ 0, 100, 0 ],
- darkkhaki: [ 189, 183, 107 ],
- darkmagenta: [ 139, 0, 139 ],
- darkolivegreen: [ 85, 107, 47 ],
- darkorange: [ 255, 140, 0 ],
- darkorchid: [ 153, 50, 204 ],
- darkred: [ 139, 0, 0 ],
- darksalmon: [ 233, 150, 122 ],
- darkviolet: [ 148, 0, 211 ],
- fuchsia: [ 255, 0, 255 ],
- gold: [ 255, 215, 0 ],
- green: [ 0, 128, 0 ],
- indigo: [ 75, 0, 130 ],
- khaki: [ 240, 230, 140 ],
- lightblue: [ 173, 216, 230 ],
- lightcyan: [ 224, 255, 255 ],
- lightgreen: [ 144, 238, 144 ],
- lightgrey: [ 211, 211, 211 ],
- lightpink: [ 255, 182, 193 ],
- lightyellow: [ 255, 255, 224 ],
- lime: [ 0, 255, 0 ],
- magenta: [ 255, 0, 255 ],
- maroon: [ 128, 0, 0 ],
- navy: [ 0, 0, 128 ],
- olive: [ 128, 128, 0 ],
- orange: [ 255, 165, 0 ],
- pink: [ 255, 192, 203 ],
- purple: [ 128, 0, 128 ],
- violet: [ 128, 0, 128 ],
- red: [ 255, 0, 0 ],
- silver: [ 192, 192, 192 ],
- white: [ 255, 255, 255 ],
- yellow: [ 255, 255, 0 ],
- transparent: [ 255, 255, 255 ]
- },
-
- /**
- * Convert an RGB color value to HSL.
- *
- * Conversion formula based on
- * <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
- *
- * Adapted from <https://en.wikipedia.org/wiki/HSL_color_space>.
- *
- * Assumes `r`, `g`, and `b` are contained in the set `[0, 255]` and
- * returns `h`, `s`, and `l` in the set `[0, 1]`.
- *
- * @param {number} r The red color value
- * @param {number} g The green color value
- * @param {number} b The blue color value
- * @return {number[]} The HSL representation
- */
- rgbToHsl: function ( r, g, b ) {
- var d, h, s, l, min, max;
-
- r = r / 255;
- g = g / 255;
- b = b / 255;
-
- max = Math.max( r, g, b );
- min = Math.min( r, g, b );
- l = ( max + min ) / 2;
-
- if ( max === min ) {
- // achromatic
- h = s = 0;
- } else {
- d = max - min;
- s = l > 0.5 ? d / ( 2 - max - min ) : d / ( max + min );
- switch ( max ) {
- case r:
- h = ( g - b ) / d + ( g < b ? 6 : 0 );
- break;
- case g:
- h = ( b - r ) / d + 2;
- break;
- case b:
- h = ( r - g ) / d + 4;
- break;
- }
- h /= 6;
- }
-
- return [ h, s, l ];
- },
-
- /**
- * Convert an HSL color value to RGB.
- *
- * Conversion formula based on
- * <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
- *
- * Adapted from <https://en.wikipedia.org/wiki/HSL_color_space>.
- *
- * Assumes `h`, `s`, and `l` are contained in the set `[0, 1]` and
- * returns `r`, `g`, and `b` in the set `[0, 255]`.
- *
- * @param {number} h The hue
- * @param {number} s The saturation
- * @param {number} l The lightness
- * @return {number[]} The RGB representation
- */
- hslToRgb: function ( h, s, l ) {
- var r, g, b, hue2rgb, q, p;
-
- if ( s === 0 ) {
- r = g = b = l; // achromatic
- } else {
- hue2rgb = function ( p, q, t ) {
- if ( t < 0 ) {
- t += 1;
- }
- if ( t > 1 ) {
- t -= 1;
- }
- if ( t < 1 / 6 ) {
- return p + ( q - p ) * 6 * t;
- }
- if ( t < 1 / 2 ) {
- return q;
- }
- if ( t < 2 / 3 ) {
- return p + ( q - p ) * ( 2 / 3 - t ) * 6;
- }
- return p;
- };
-
- q = l < 0.5 ? l * ( 1 + s ) : l + s - l * s;
- p = 2 * l - q;
- r = hue2rgb( p, q, h + 1 / 3 );
- g = hue2rgb( p, q, h );
- b = hue2rgb( p, q, h - 1 / 3 );
- }
-
- return [ r * 255, g * 255, b * 255 ];
- },
-
- /**
- * Get a brighter or darker rgb() value string.
- *
- * Usage:
- *
- * $.colorUtil.getColorBrightness( 'red', +0.1 );
- * // > "rgb(255,50,50)"
- * $.colorUtil.getColorBrightness( 'rgb(200,50,50)', -0.2 );
- * // > "rgb(118,29,29)"
- *
- * @param {Mixed} currentColor Current value in css
- * @param {number} mod Wanted brightness modification between -1 and 1
- * @return {string} Like `'rgb(r,g,b)'`
- */
- getColorBrightness: function ( currentColor, mod ) {
- var rgbArr = $.colorUtil.getRGB( currentColor ),
- hslArr = $.colorUtil.rgbToHsl( rgbArr[ 0 ], rgbArr[ 1 ], rgbArr[ 2 ] );
- rgbArr = $.colorUtil.hslToRgb( hslArr[ 0 ], hslArr[ 1 ], hslArr[ 2 ] + mod );
-
- return 'rgb(' +
- [ parseInt( rgbArr[ 0 ], 10 ), parseInt( rgbArr[ 1 ], 10 ), parseInt( rgbArr[ 2 ], 10 ) ].join( ',' ) +
- ')';
- }
-
- };
-
-}() );
// FIXME: mw.htmlform.Element also sets this to empty object
mw.htmlform = {};
- function debounce( delay, callback ) {
- var timeout;
- return function () {
- clearTimeout( timeout );
- timeout = setTimeout( Function.prototype.apply.bind( callback, this, arguments ), delay );
- };
- }
-
/**
* @class mw.htmlform.Checker
*/
if ( $extraElements ) {
$e = $e.add( $extraElements );
}
- $e.on( events, debounce( 1000, this.validate.bind( this ) ) );
+ $e.on( events, mw.util.debounce( 1000, this.validate.bind( this ) ) );
return this;
};
* @return {string} Encoded string
*/
function rawurlencode( str ) {
- str = String( str );
- return encodeURIComponent( str )
+ return encodeURIComponent( String( str ) )
.replace( /!/g, '%21' ).replace( /'/g, '%27' ).replace( /\(/g, '%28' )
.replace( /\)/g, '%29' ).replace( /\*/g, '%2A' ).replace( /~/g, '%7E' );
}
rawurlencode: rawurlencode,
/**
- * Encode string into HTML id compatible form suitable for use in HTML
- * Analog to PHP Sanitizer::escapeIdForAttribute()
+ * Encode a string as CSS id, for use as HTML id attribute value.
*
- * @since 1.30
+ * Analog to `Sanitizer::escapeIdForAttribute()` in PHP.
*
+ * @since 1.30
* @param {string} str String to encode
* @return {string} Encoded string
*/
escapeIdForAttribute: function ( str ) {
- var mode = config.FragmentMode[ 0 ];
-
- return escapeIdInternal( str, mode );
+ return escapeIdInternal( str, config.FragmentMode[ 0 ] );
},
/**
- * Encode string into HTML id compatible form suitable for use in links
- * Analog to PHP Sanitizer::escapeIdForLink()
+ * Encode a string as URL fragment, for use as HTML anchor link.
*
- * @since 1.30
+ * Analog to `Sanitizer::escapeIdForLink()` in PHP.
*
+ * @since 1.30
* @param {string} str String to encode
* @return {string} Encoded string
*/
escapeIdForLink: function ( str ) {
- var mode = config.FragmentMode[ 0 ];
+ return escapeIdInternal( str, config.FragmentMode[ 0 ] );
+ },
- return escapeIdInternal( str, mode );
+ /**
+ * Return a wrapper function that is debounced for the given duration.
+ *
+ * When it is first called, a timeout is scheduled. If before the timer
+ * is reached the wrapper is called again, it gets rescheduled for the
+ * same duration from now until it stops being called. The original function
+ * is called from the "tail" of such chain, with the last set of arguments.
+ *
+ * @since 1.34
+ * @param {number} delay Time in milliseconds
+ * @param {Function} callback
+ * @return {Function}
+ */
+ debounce: function ( delay, callback ) {
+ var timeout;
+ return function () {
+ clearTimeout( timeout );
+ timeout = setTimeout( Function.prototype.apply.bind( callback, this, arguments ), delay );
+ };
},
/**
* @return {string} Url of the page with name of `pageName`
*/
getUrl: function ( pageName, params ) {
- var titleFragmentStart, url, query,
- fragment = '',
+ var fragmentIdx, url, query, fragment,
title = typeof pageName === 'string' ? pageName : mw.config.get( 'wgPageName' );
// Find any fragment
- titleFragmentStart = title.indexOf( '#' );
- if ( titleFragmentStart !== -1 ) {
- fragment = title.slice( titleFragmentStart + 1 );
+ fragmentIdx = title.indexOf( '#' );
+ if ( fragmentIdx !== -1 ) {
+ fragment = title.slice( fragmentIdx + 1 );
// Exclude the fragment from the page name
- title = title.slice( 0, titleFragmentStart );
+ title = title.slice( 0, fragmentIdx );
}
// Produce query string
}
// Append the encoded fragment
- if ( fragment.length ) {
+ if ( fragment && fragment.length ) {
url += '#' + util.escapeIdForLink( fragment );
}
},
/**
- * Get address to a script in the wiki root.
- * For index.php use `mw.config.get( 'wgScript' )`.
+ * Get URL to a MediaWiki entry point.
*
* @since 1.18
- * @param {string} str Name of script (e.g. 'api'), defaults to 'index'
- * @return {string} Address to script (e.g. '/w/api.php' )
+ * @param {string} [str="index"] Name of MW entry point (e.g. 'index' or 'api')
+ * @return {string} URL to the script file (e.g. '/w/api.php' )
*/
wikiScript: function ( str ) {
- str = str || 'index';
- if ( str === 'index' ) {
+ if ( !str || str === 'index' ) {
return mw.config.get( 'wgScript' );
} else if ( str === 'load' ) {
return config.LoadScript;
*/
addCSS: function ( text ) {
var s = mw.loader.addStyleTag( text );
- return s.sheet || s.styleSheet || s;
+ return s.sheet;
},
/**
* @return {boolean}
*/
isIPv4Address: function ( address, allowBlock ) {
- var block, RE_IP_BYTE, RE_IP_ADD;
+ var block,
+ RE_IP_BYTE = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])',
+ RE_IP_ADD = '(?:' + RE_IP_BYTE + '\\.){3}' + RE_IP_BYTE;
if ( typeof address !== 'string' ) {
return false;
}
block = allowBlock ? '(?:\\/(?:3[0-2]|[12]?\\d))?' : '';
- RE_IP_BYTE = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])';
- RE_IP_ADD = '(?:' + RE_IP_BYTE + '\\.){3}' + RE_IP_BYTE;
return ( new RegExp( '^' + RE_IP_ADD + block + '$' ).test( address ) );
},
<?php
+use MediaWiki\Block\DatabaseBlock;
+use MediaWiki\Block\Restriction\PageRestriction;
+
/**
* Tests for action=revisiondelete
* @covers APIRevisionDelete
$this->assertTrue( $item['texthidden'], 'texthidden' );
$this->assertEquals( $item['id'], $revid );
}
+
+ public function testPartiallyBlockedPage() {
+ $this->setExpectedApiException( 'apierror-blocked-partial' );
+
+ $user = static::getTestSysop()->getUser();
+
+ $block = new DatabaseBlock( [
+ 'address' => $user,
+ 'by' => static::getTestSysop()->getUser()->getId(),
+ 'sitewide' => false,
+ ] );
+
+ $block->setRestrictions( [
+ new PageRestriction( 0, Title::newFromText( self::$page )->getArticleID() )
+ ] );
+ $block->insert();
+
+ $revid = array_shift( $this->revs );
+
+ $this->doApiRequest( [
+ 'action' => 'revisiondelete',
+ 'type' => 'revision',
+ 'target' => self::$page,
+ 'ids' => $revid,
+ 'hide' => 'content|user|comment',
+ 'token' => $user->getEditToken(),
+ ] );
+ }
}
BlockManager::$constructorOptions,
MediaWikiServices::getInstance()->getMainConfig()
),
- $this->user,
- $this->user->getRequest(),
MediaWikiServices::getInstance()->getPermissionManager()
];
}
// logged in users should be inmune to cookie block of type ip/range
$this->assertNull( $user->getBlock() );
- // cookie is being cleared
- $cookies = $request->response()->getCookies();
- $this->assertEquals( '', $cookies['wikiBlockID']['value'] );
-
// clean up
$block->delete();
}
],
'dependencies' => [
'jquery.color',
- 'jquery.colorUtil',
'jquery.getAttrs',
'jquery.highlightText',
'jquery.lengthLimit',
assert.strictEqual( mw.util.escapeRegExp( normal ), normal, 'Alphanumerals are left alone' );
} );
+
+ QUnit.test( 'debounce', function ( assert ) {
+ var fn,
+ q = [],
+ done = assert.async();
+
+ fn = mw.util.debounce( 0, function ( data ) {
+ q.push( data );
+ } );
+
+ fn( 1 );
+ fn( 2 );
+ fn( 3 );
+
+ setTimeout( function () {
+ assert.deepEqual(
+ q,
+ [ 3 ],
+ 'Last one ran'
+ );
+ done();
+ } );
+ } );
}() );