"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()
);
},
$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 &&
'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,
*/
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
);
'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>
* @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, mode );
+ return escapeIdInternal( str, config.FragmentMode[ 0 ] );
},
/**
* @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();
}