* SpecialEmailUser::validateTarget(), ::getTarget() without a sender/user
specified, deprecated in 1.30, have been removed.
* BufferingStatsdDataFactory::getBuffer(), deprecated in 1.30, has been removed.
+* The constant DB_SLAVE, deprecated in 1.28, has been removed. Use DB_REPLICA.
* …
=== Deprecations in 1.34 ===
* ResourceLoaderContext::getConfig and ResourceLoaderContext::getLogger have
been deprecated. Inside ResourceLoaderModule subclasses, use the local methods
instead. Elsewhere, use the methods from the ResourceLoader class.
+* The Preprocessor_DOM implementation has been deprecated. It will be
+ removed in a future release. Use the Preprocessor_Hash implementation
+ instead.
=== Other changes in 1.34 ===
* …
return;
}
+ $user = RequestContext::getMain()->getUser();
+
// Various extensions may have their own backends that need access.
// Check if there is a special backend and storage base path for this file.
foreach ( $wgImgAuthUrlPathMap as $prefix => $storageDir ) {
$be = FileBackendGroup::singleton()->backendFromPath( $storageDir );
$filename = $storageDir . substr( $path, strlen( $prefix ) ); // strip prefix
// Check basic user authorization
- if ( !RequestContext::getMain()->getUser()->isAllowed( 'read' ) ) {
+ if ( !$user->isAllowed( 'read' ) ) {
wfForbidden( 'img-auth-accessdenied', 'img-auth-noread', $path );
return;
}
// Check user authorization for this title
// Checks Whitelist too
- if ( !$title->userCan( 'read' ) ) {
+ $permissionManager = \MediaWiki\MediaWikiServices::getInstance()->getPermissionManager();
+
+ if ( !$permissionManager->userCan( 'read', $user, $title ) ) {
wfForbidden( 'img-auth-accessdenied', 'img-auth-noread', $name );
return;
}
* If this parameter is not given, it uses Preprocessor_DOM if the
* DOM module is available, otherwise it uses Preprocessor_Hash.
*
+ * The Preprocessor_DOM class is deprecated, and will be removed in a future
+ * release.
+ *
* The entire associative array will be passed through to the constructor as
* the first parameter. Note that only Setup.php can use this variable --
* the configuration will change at runtime via Parser member functions, so
*/
# Obsolete aliases
-/**
- * @deprecated since 1.28, use DB_REPLICA instead
- */
-define( 'DB_SLAVE', -1 );
/**@{
* Obsolete IDatabase::makeList() constants
$query['returntoquery'] = wfArrayToCgi( $returntoquery );
}
}
+
+ $services = MediaWikiServices::getInstance();
+
$title = SpecialPage::getTitleFor( 'Userlogin' );
- $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
+ $linkRenderer = $services->getLinkRenderer();
$loginUrl = $title->getLinkURL( $query, false, PROTO_RELATIVE );
$loginLink = $linkRenderer->makeKnownLink(
$title,
$this->prepareErrorPage( $this->msg( 'loginreqtitle' ) );
$this->addHTML( $this->msg( $msg )->rawParams( $loginLink )->params( $loginUrl )->parse() );
+ $permissionManager = $services->getPermissionManager();
+
# Don't return to a page the user can't read otherwise
# we'll end up in a pointless loop
- if ( $displayReturnto && $displayReturnto->userCan( 'read', $this->getUser() ) ) {
+ if ( $displayReturnto && $permissionManager->userCan(
+ 'read', $this->getUser(), $displayReturnto
+ ) ) {
$this->returnToMain( null, $displayReturnto );
}
} else {
use MediaWiki\MediaWikiServices;
use RequestContext;
use Title;
+use WebResponse;
class EntryPoint {
+ /** @var RequestInterface */
+ private $request;
+ /** @var WebResponse */
+ private $webResponse;
+ /** @var Router */
+ private $router;
+
public static function main() {
// URL safety checks
global $wgRequest;
RequestContext::getMain()->setTitle( $wgTitle );
$services = MediaWikiServices::getInstance();
-
$conf = $services->getMainConfig();
+
$request = new RequestFromGlobals( [
'cookiePrefix' => $conf->get( 'CookiePrefix' )
] );
new ResponseFactory
);
- $response = $router->execute( $request );
+ $entryPoint = new self(
+ $request,
+ $wgRequest->response(),
+ $router );
+ $entryPoint->execute();
+ }
+
+ public function __construct( RequestInterface $request, WebResponse $webResponse,
+ Router $router
+ ) {
+ $this->request = $request;
+ $this->webResponse = $webResponse;
+ $this->router = $router;
+ }
+
+ public function execute() {
+ $response = $this->router->execute( $this->request );
- $webResponse = $wgRequest->response();
- $webResponse->header(
+ $this->webResponse->header(
'HTTP/' . $response->getProtocolVersion() . ' ' .
$response->getStatusCode() . ' ' .
$response->getReasonPhrase() );
foreach ( $response->getRawHeaderLines() as $line ) {
- $webResponse->header( $line );
+ $this->webResponse->header( $line );
}
foreach ( $response->getCookies() as $cookie ) {
- $webResponse->setCookie(
+ $this->webResponse->setCookie(
$cookie['name'],
$cookie['value'],
$cookie['expiry'],
* Unlike PSR-7, the container is mutable.
*/
class HeaderContainer {
- private $headerLists;
- private $headerLines;
- private $headerNames;
+ private $headerLists = [];
+ private $headerLines = [];
+ private $headerNames = [];
/**
* Erase any existing headers and replace them with the specified
private $headerCollection;
/** @var array */
- private $attributes = [];
+ private $pathParams = [];
/** @var string */
private $cookiePrefix;
return $this->headerCollection->getHeaderLine( $name );
}
- public function setAttributes( $attributes ) {
- $this->attributes = $attributes;
+ public function setPathParams( $params ) {
+ $this->pathParams = $params;
}
- public function getAttributes() {
- return $this->attributes;
+ public function getPathParams() {
+ return $this->pathParams;
}
- public function getAttribute( $name, $default = null ) {
- if ( array_key_exists( $name, $this->attributes ) ) {
- return $this->attributes[$name];
- } else {
- return $default;
- }
+ public function getPathParam( $name ) {
+ return $this->pathParams[$name] ?? null;
}
public function getCookiePrefix() {
* - queryParams: Equivalent to $_GET
* - uploadedFiles: An array of objects implementing UploadedFileInterface
* - postParams: Equivalent to $_POST
- * - attributes: The attributes, usually from path template parameters
+ * - pathParams: The path template parameters
* - headers: An array with the the key being the header name
* - cookiePrefix: A prefix to add to cookie names in getCookie()
*/
$this->queryParams = $params['queryParams'] ?? [];
$this->uploadedFiles = $params['uploadedFiles'] ?? [];
$this->postParams = $params['postParams'] ?? [];
- $this->setAttributes( $params['attributes'] ?? [] );
+ $this->setPathParams( $params['pathParams'] ?? [] );
$this->setHeaders( $params['headers'] ?? [] );
parent::__construct( $params['cookiePrefix'] ?? '' );
}
*/
function getUploadedFiles();
+ // MediaWiki extensions to PSR-7
+
/**
- * Retrieve attributes derived from the request.
- *
- * The request "attributes" may be used to allow injection of any
- * parameters derived from the request: e.g., the results of path
- * match operations; the results of decrypting cookies; the results of
- * deserializing non-form-encoded message bodies; etc. Attributes
- * will be application and request specific, and CAN be mutable.
+ * Get the parameters derived from the path template match
*
- * @return array Attributes derived from the request.
+ * @return string[]
*/
- function getAttributes();
+ function getPathParams();
/**
- * Retrieve a single derived request attribute.
+ * Retrieve a single path parameter.
*
- * Retrieves a single derived request attribute as described in
- * getAttributes(). If the attribute has not been previously set, returns
- * the default value as provided.
+ * Retrieves a single path parameter as described in getPathParams(). If
+ * the attribute has not been previously set, returns null.
*
- * This method obviates the need for a hasAttribute() method, as it allows
- * specifying a default value to return if the attribute is not found.
- *
- * @see getAttributes()
- * @param string $name The attribute name.
- * @param mixed|null $default Default value to return if the attribute does not exist.
- * @return mixed
+ * @see getPathParams()
+ * @param string $name The parameter name.
+ * @return string|null
*/
- function getAttribute( $name, $default = null );
-
- // MediaWiki extensions to PSR-7
+ function getPathParam( $name );
/**
- * Erase all attributes from the object and set the attribute array to the
- * specified value
+ * Erase all path parameters from the object and set the parameter array
+ * to the one specified.
*
- * @param mixed[] $attributes
+ * @param string[] $params
*/
- function setAttributes( $attributes );
+ function setPathParams( $params );
/**
* Get the current cookie prefix
}
}
- $request->setAttributes( $match['params'] );
+ $request->setPathParams( $match['params'] );
$spec = $match['userData'];
$objectFactorySpec = array_intersect_key( $spec,
[ 'factory' => true, 'class' => true, 'args' => true ] );
namespace MediaWiki\Rest;
/**
- * A handler base class which unpacks attributes from the path template and
+ * A handler base class which unpacks parameters from the path template and
* passes them as formal parameters to run().
*
* run() must be declared in the subclass. It cannot be declared as abstract
*/
class SimpleHandler extends Handler {
public function execute() {
- $params = array_values( $this->getRequest()->getAttributes() );
+ $params = array_values( $this->getRequest()->getPathParams() );
return $this->run( ...$params );
}
}
}
public function copyToStream( $stream ) {
- if ( $this->offset !== 0 ) {
- $block = substr( $this->contents, $this->offset );
- } else {
- $block = $this->contents;
- }
- $this->offset = strlen( $this->contents );
- fwrite( $stream, $block );
+ fwrite( $stream, $this->getContents() );
}
public function __toString() {
public function read( $length ) {
if ( $this->offset === 0 && $length >= strlen( $this->contents ) ) {
$ret = $this->contents;
+ } elseif ( $this->offset >= strlen( $this->contents ) ) {
+ $ret = '';
} else {
$ret = substr( $this->contents, $this->offset, $length );
}
public function getContents() {
if ( $this->offset === 0 ) {
$ret = $this->contents;
+ } elseif ( $this->offset >= strlen( $this->contents ) ) {
+ $ret = '';
} else {
$ret = substr( $this->contents, $this->offset );
}
use InvalidArgumentException;
use LogicException;
use MediaWiki\Linker\LinkTarget;
+use MediaWiki\MediaWikiServices;
use MediaWiki\User\UserIdentity;
use MWException;
use Title;
} else {
$text = $title->getPrefixedText();
wfDebug( "Checking for $permissionlist on $text due to $field match on $bitfield\n" );
+
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+
foreach ( $permissions as $perm ) {
- if ( $title->userCan( $perm, $user ) ) {
+ if ( $permissionManager->userCan( $perm, $user, $title ) ) {
return true;
}
}
// null if mSlots is not empty.
// NOTE: getId() and getPageId() may return null before a revision is saved, so don't
- //check them.
+ // check them.
return $this->getTimestamp() !== null
&& $this->getComment( self::RAW ) !== null
// Language in which the page content is (supposed to be) written
$pageLang = $title->getPageLanguage()->getCode();
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+
$pageLangHtml = $pageLang . ' - ' .
Language::fetchLanguageName( $pageLang, $lang->getCode() );
// Link to Special:PageLanguage with pre-filled page title if user has permissions
if ( $config->get( 'PageLanguageUseDB' )
- && $title->userCan( 'pagelang', $user )
+ && $permissionManager->userCan( 'pagelang', $user, $title )
) {
$pageLangHtml .= ' ' . $this->msg( 'parentheses' )->rawParams( $linkRenderer->makeLink(
SpecialPage::getTitleValueFor( 'PageLanguage', $title->getPrefixedText() ),
$modelHtml = htmlspecialchars( ContentHandler::getLocalizedName( $title->getContentModel() ) );
// If the user can change it, add a link to Special:ChangeContentModel
if ( $config->get( 'ContentHandlerUseDB' )
- && $title->userCan( 'editcontentmodel', $user )
+ && $permissionManager->userCan( 'editcontentmodel', $user, $title )
) {
$modelHtml .= ' ' . $this->msg( 'parentheses' )->rawParams( $linkRenderer->makeLink(
SpecialPage::getTitleValueFor( 'ChangeContentModel', $title->getPrefixedText() ),
$updater->setOriginalRevisionId( false );
$updater->setUndidRevisionId( $this->undo );
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+
// TODO: Ugh.
- if ( $wgUseRCPatrol && $this->getTitle()->userCan( 'autopatrol', $this->getUser() ) ) {
+ if ( $wgUseRCPatrol && $permissionManager->userCan(
+ 'autopatrol',
+ $this->getUser(),
+ $this->getTitle() )
+ ) {
$updater->setRcPatrolStatus( RecentChange::PRC_AUTOPATROLLED );
}
} else {
$block = new CompositeBlock( [
'address' => $ip,
+ 'byText' => 'MediaWiki default',
+ 'reason' => wfMessage( 'blockedtext-composite-reason' )->plain(),
'originalBlocks' => $blocks,
] );
}
return $this->originalBlocks;
}
+ /**
+ * @inheritDoc
+ */
+ public function getExpiry() {
+ $maxExpiry = null;
+ foreach ( $this->originalBlocks as $block ) {
+ $expiry = $block->getExpiry();
+ if ( $maxExpiry === null || $expiry === '' || $expiry > $maxExpiry ) {
+ $maxExpiry = $expiry;
+ }
+ }
+ return $maxExpiry;
+ }
+
/**
* @inheritDoc
*/
public function getPermissionsError( IContextSource $context ) {
$params = $this->getBlockErrorParams( $context );
- $msg = $this->isSitewide() ? 'blockedtext' : 'blockedtext-partial';
+ $msg = 'blockedtext-composite';
array_unshift( $params, $msg );
*/
use MediaWiki\MediaWikiServices;
+use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\Revision\SlotRecord;
use MediaWiki\Storage\NameTableAccessException;
$samePage = false;
}
- if ( $samePage && $this->mNewPage && $this->mNewPage->quickUserCan( 'edit', $user ) ) {
- if ( $this->mNewRev->isCurrent() && $this->mNewPage->userCan( 'rollback', $user ) ) {
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+
+ if ( $samePage && $this->mNewPage && $permissionManager->userCan(
+ 'edit', $user, $this->mNewPage, PermissionManager::RIGOR_QUICK
+ ) ) {
+ if ( $this->mNewRev->isCurrent() && $permissionManager->userCan(
+ 'rollback', $user, $this->mNewPage
+ ) ) {
$rollbackLink = Linker::generateRollback( $this->mNewRev, $this->getContext() );
if ( $rollbackLink ) {
$out->preventClickjacking();
} elseif ( !$title->canExist() ) {
$this->notice( 'import-error-special', $title->getPrefixedText() );
return false;
- } elseif ( !$title->userCan( 'edit' ) && !$commandLineMode ) {
- # Do not import if the importing wiki user cannot edit this page
- $this->notice( 'import-error-edit', $title->getPrefixedText() );
- return false;
- } elseif ( !$title->exists() && !$title->userCan( 'create' ) && !$commandLineMode ) {
- # Do not import if the importing wiki user cannot create this page
- $this->notice( 'import-error-create', $title->getPrefixedText() );
- return false;
+ } elseif ( !$commandLineMode ) {
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+ $user = RequestContext::getMain()->getUser();
+
+ if ( !$permissionManager->userCan( 'edit', $user, $title ) ) {
+ # Do not import if the importing wiki user cannot edit this page
+ $this->notice( 'import-error-edit', $title->getPrefixedText() );
+
+ return false;
+ }
+
+ if ( !$title->exists() && !$permissionManager->userCan( 'create', $user, $title ) ) {
+ # Do not import if the importing wiki user cannot create this page
+ $this->notice( 'import-error-create', $title->getPrefixedText() );
+
+ return false;
+ }
}
return [ $title, $foreignTitle ];
] );
$styleUrl = $server . dirname( dirname( $this->parent->getUrl() ) ) .
'/mw-config/config-cc.css';
- $iframeUrl = '//creativecommons.org/license/?' .
+ $iframeUrl = 'https://creativecommons.org/license/?' .
wfArrayToCgi( [
'partner' => 'MediaWiki',
'exit_url' => $exitUrl,
<div id="mw-panel">
<div class="portal" id="p-logo">
- <a style="background-image: url(images/installer-logo.png);"
- href="https://www.mediawiki.org/"
- title="Main Page"></a>
+ <a href="https://www.mediawiki.org/" title="Main Page"></a>
</div>
<?php
$message = wfMessage( 'config-sidebar' )->plain();
// TODO: this logic should not be in the storage layer, it's here for compatibility
// with 1.31 behavior. Applying the 'autopatrol' right should be done in the same
// place the 'bot' right is handled, which is currently in EditPage::attemptSave.
- if ( $needsPatrol && $this->getTitle()->userCan( 'autopatrol', $user ) ) {
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+
+ if ( $needsPatrol && $permissionManager->userCan(
+ 'autopatrol', $user, $this->getTitle()
+ ) ) {
$updater->setRcPatrolStatus( RecentChange::PRC_AUTOPATROLLED );
}
// TODO: this logic should not be in the storage layer, it's here for compatibility
// with 1.31 behavior. Applying the 'autopatrol' right should be done in the same
// place the 'bot' right is handled, which is currently in EditPage::attemptSave.
- if ( $wgUseRCPatrol && $this->getTitle()->userCan( 'autopatrol', $guser ) ) {
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+
+ if ( $wgUseRCPatrol && $permissionManager->userCan(
+ 'autopatrol', $guser, $this->getTitle()
+ ) ) {
$updater->setRcPatrolStatus( RecentChange::PRC_AUTOPATROLLED );
}
/**
* Expansion frame with custom arguments
+ * @deprecated since 1.34, use PPCustomFrame_Hash
* @ingroup Parser
*/
// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
/**
* An expansion frame, used as a context to expand the result of preprocessToObj()
+ * @deprecated since 1.34, use PPFrame_Hash
* @ingroup Parser
*/
// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
*/
/**
+ * @deprecated since 1.34, use PPNode_Hash_{Tree,Text,Array,Attr}
* @ingroup Parser
*/
// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
/**
* Expansion frame with template arguments
+ * @deprecated since 1.34, use PPTemplateFrame_Hash
* @ingroup Parser
*/
// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
* Which class should we use for the preprocessor if not otherwise specified?
*
* @since 1.34
+ * @deprecated since 1.34, removing configurability of preprocessor
* @return string
*/
public static function getDefaultPreprocessorClass() {
- if ( wfIsHHVM() ) {
- # Under HHVM Preprocessor_Hash is much faster than Preprocessor_DOM
- return Preprocessor_Hash::class;
- }
- if ( extension_loaded( 'domxml' ) ) {
- # PECL extension that conflicts with the core DOM extension (T15770)
- wfDebug( "Warning: you have the obsolete domxml extension for PHP. Please remove it!\n" );
- return Preprocessor_Hash::class;
- }
- if ( extension_loaded( 'dom' ) ) {
- return Preprocessor_DOM::class;
- }
return Preprocessor_Hash::class;
}
*
* @file
* @ingroup Parser
+ * @deprecated since 1.34, use Preprocessor_Hash
*/
/**
const CACHE_PREFIX = 'preprocess-xml';
public function __construct( $parser ) {
+ wfDeprecated( __METHOD__, '1.34' ); // T204945
$this->parser = $parser;
$mem = ini_get( 'memory_limit' );
$this->memoryLimit = false;
*/
public function getDirection() {
if ( $this->direction === null ) {
- $this->direction = $this->getRequest()->getRawVal( 'dir' );
- if ( !$this->direction ) {
+ $direction = $this->getRequest()->getRawVal( 'dir' );
+ if ( $direction === 'ltr' || $direction === 'rtl' ) {
+ $this->direction = $direction;
+ } else {
// Determine directionality based on user language (T8100)
$this->direction = Language::factory( $this->getLanguage() )->getDir();
}
*/
use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
/**
* A special page that allows users to export pages in a XML file
if ( $exportall ) {
$exporter->allPages();
} else {
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+
foreach ( $pages as $page ) {
# T10824: Only export pages the user can read
$title = Title::newFromText( $page );
continue;
}
- if ( !$title->userCan( 'read', $this->getUser() ) ) {
+ if ( !$permissionManager->userCan( 'read', $this->getUser(), $title ) ) {
// @todo Perhaps output an <error> tag or something.
continue;
}
*/
protected function isAllowed( $permission, User $user = null ) {
$user = $user ?: $this->getUser();
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+
if ( $this->mTargetObj !== null ) {
- return $this->mTargetObj->userCan( $permission, $user );
+ return $permissionManager->userCan( $permission, $user, $this->mTargetObj );
} else {
return $user->isAllowed( $permission );
}
// Add delete links if allowed
// From https://github.com/Wikia/app/pull/3859
- if ( $filePage->userCan( 'delete', $this->getUser() ) ) {
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+
+ if ( $permissionManager->userCan( 'delete', $this->getUser(), $filePage ) ) {
$deleteMsg = $this->msg( 'listfiles-delete' )->text();
$delete = $linkRenderer->makeKnownLink(
$result = (int)$s->user_id;
}
- self::$idCacheByName[$name] = $result;
-
- if ( count( self::$idCacheByName ) > 1000 ) {
+ if ( count( self::$idCacheByName ) >= 1000 ) {
self::$idCacheByName = [];
}
+ self::$idCacheByName[$name] = $result;
+
return $result;
}
// This is not quite safe, but better than showing excerpts from
// non-readable pages. Note that hiding the entry entirely would
// screw up paging (really?).
- if ( !$result->getTitle()->userCan( 'read', $this->specialPage->getUser() ) ) {
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+ if ( !$permissionManager->userCan(
+ 'read', $this->specialPage->getUser(), $result->getTitle()
+ ) ) {
return "<li>{$link}</li>";
}
"autoblockedtext": "Your IP address has been automatically blocked because it was used by another user, who was blocked by $1.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou may contact $1 or one of the other [[{{MediaWiki:Grouppage-sysop}}|administrators]] to discuss the block.\n\nNote that you may not use the \"{{int:emailuser}}\" feature unless you have a valid email address registered in your [[Special:Preferences|user preferences]] and you have not been blocked from using it.\n\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
"systemblockedtext": "Your username or IP address has been automatically blocked by MediaWiki.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYour current IP address is $3.\nPlease include all above details in any queries you make.",
"blockednoreason": "no reason given",
+ "blockedtext-composite": "<strong>Your username or IP address has been blocked.</strong>\n\nThe reason given is:\n\n:<em>$2</em>.\n\n* Start of block: $8\n* Expiration of longest block: $6\n\nYour current IP address is $3.\nPlease include all above details in any queries you make.",
+ "blockedtext-composite-reason": "There are multiple blocks against your account and/or IP address",
"whitelistedittext": "Please $1 to edit pages.",
"confirmedittext": "You must confirm your email address before editing pages.\nPlease set and validate your email address through your [[Special:Preferences|user preferences]].",
"nosuchsectiontitle": "Cannot find section",
"exif-copyrighted": "Copyright status. This is a true or false field showing either Copyrighted or Public Domain. It should be noted that Copyrighted includes freely-licensed works.",
"exif-copyrightowner": "{{exif-qqq}}\n\nCopyright owner. Can have more than one person or entity.",
"exif-usageterms": "Terms under which you're allowed to use the image/media.",
- "exif-webstatement": "{{exif-qqq}}\n\nURL detailing the copyright status of the image, and how you're allowed to use the image. Often this is a link to a creative commons license, however the creative commons people recommend using a page that generally contains specific information about the image, and recommend using {{msg-mw|exif-licenseurl}} for linking to the license. See http://wiki.creativecommons.org/XMP",
+ "exif-webstatement": "{{exif-qqq}}\n\nURL detailing the copyright status of the image, and how you're allowed to use the image. Often this is a link to a creative commons license, however the creative commons people recommend using a page that generally contains specific information about the image, and recommend using {{msg-mw|exif-licenseurl}} for linking to the license. See https://wiki.creativecommons.org/wiki/XMP",
"exif-originaldocumentid": "A unique ID of the original document (image) that this document (image) is based on.",
"exif-licenseurl": "{{exif-qqq}}\n\nURL for copyright license. This is almost always a creative commons license since this information comes from the creative commons namespace of XMP (but could be a link to any type of license). See also {{msg-mw|exif-webstatement}}",
"exif-morepermissionsurl": "A URL where you can \"buy\" (or otherwise negotiate) to get more rights for the image.",
"blockedtext": "Text displayed to blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link)\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Autoblockedtext|notext=1}}\n* {{msg-mw|Systemblockedtext|notext=1}}",
"autoblockedtext": "Text displayed to automatically blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block (in case of autoblocks: {{msg-mw|autoblocker}})\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link). Use it for GENDER.\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Blockedtext|notext=1}}\n* {{msg-mw|Systemblockedtext|notext=1}}",
"systemblockedtext": "Text displayed to requests blocked by MediaWiki configuration.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - (Unused) A dummy user attributed as the blocker, possibly as a link to a user page.\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the dummy blocking user's username (plain text, without the link).\n* $5 - A short string indicating the type of system block.\n* $6 - the expiry of the block\n* $7 - the intended target of the block\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Blockedtext|notext=1}}\n* {{msg-mw|Autoblockedtext|notext=1}}",
+ "blockedtext-composite": "Text displayed to requests blocked by more than one block.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - (Unused) A dummy user attributed as the blocker, possibly as a link to a user page.\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the dummy blocking user's username (plain text, without the link).\n* $5 - (Unused) placeholder for the block ID.\n* $6 - the expiry of the block with the longest duration\n* $7 - (Unused) the intended target of the block\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Systemblockedtext|notext=1}}",
+ "blockedtext-composite-reason": "Reason given to blocked users who are affected by more than one block.\n\nSee also:\n* {{msg-mw|blockedtext-composite}}",
"blockednoreason": "Substituted with <code>$2</code> in the following message if the reason is not given:\n* {{msg-mw|cantcreateaccount-text}}.\n{{Identical|No reason given}}",
"whitelistedittext": "Used as error message. Parameters:\n* $1 - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description\n* $2 - an URL to the same\n\nSee also:\n* {{msg-mw|Nocreatetext}}\n* {{msg-mw|Uploadnologintext}}\n* {{msg-mw|Loginreqpagetext}}",
"confirmedittext": "Used as error message.",
border: 1px solid #5dc9f4;
margin-left: 20px;
}
+
+#p-logo a {
+ background-image: url( images/installer-logo.png );
+}
* familiarise yourself with that CSS before making any changes to this code.
*
* Dual licensed:
- * - CC BY 3.0 <http://creativecommons.org/licenses/by/3.0>
+ * - CC BY 3.0 <https://creativecommons.org/licenses/by/3.0>
* - GPL2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
*
* @class jQuery.plugin.makeCollapsible
q = {};
// using replace to iterate over a string
if ( uri.query ) {
- uri.query.replace( /(?:^|&)([^&=]*)(?:(=)([^&]*))?/g, function ( $0, $1, $2, $3 ) {
- var k, v;
- if ( $1 ) {
- k = Uri.decode( $1 );
- v = ( $2 === '' || $2 === undefined ) ? null : Uri.decode( $3 );
+ uri.query.replace( /(?:^|&)([^&=]*)(?:(=)([^&]*))?/g, function ( match, k, eq, v ) {
+ if ( k ) {
+ k = Uri.decode( k );
+ v = ( eq === '' || eq === undefined ) ? null : Uri.decode( v );
// If overrideKeys, always (re)set top level value.
// If not overrideKeys but this key wasn't set before, then we set it as well.
$class = $wgParserConf['class'];
$parser = new $class( [ 'preprocessorClass' => $preprocessor ] + $wgParserConf );
+ if ( $preprocessor ) {
+ # Suppress deprecation warning for Preprocessor_DOM while testing
+ Wikimedia\suppressWarnings();
+ wfDeprecated( 'Preprocessor_DOM::__construct' );
+ Wikimedia\restoreWarnings();
+ $parser->getPreprocessor();
+ }
ParserTestParserHook::setup( $parser );
return $parser;
--- /dev/null
+<?php
+
+namespace MediaWiki\Tests\Rest;
+
+use EmptyBagOStuff;
+use GuzzleHttp\Psr7\Uri;
+use GuzzleHttp\Psr7\Stream;
+use MediaWiki\Rest\Handler;
+use MediaWikiTestCase;
+use MediaWiki\Rest\EntryPoint;
+use MediaWiki\Rest\RequestData;
+use MediaWiki\Rest\ResponseFactory;
+use MediaWiki\Rest\Router;
+use WebResponse;
+
+/**
+ * @covers \MediaWiki\Rest\EntryPoint
+ * @covers \MediaWiki\Rest\Router
+ */
+class EntryPointTest extends MediaWikiTestCase {
+ private static $mockHandler;
+
+ private function createRouter() {
+ return new Router(
+ [ __DIR__ . '/testRoutes.json' ],
+ [],
+ '/rest',
+ new EmptyBagOStuff(),
+ new ResponseFactory() );
+ }
+
+ private function createWebResponse() {
+ return $this->getMockBuilder( WebResponse::class )
+ ->setMethods( [ 'header' ] )
+ ->getMock();
+ }
+
+ public static function mockHandlerHeader() {
+ return new class extends Handler {
+ public function execute() {
+ $response = $this->getResponseFactory()->create();
+ $response->setHeader( 'Foo', 'Bar' );
+ return $response;
+ }
+ };
+ }
+
+ public function testHeader() {
+ $webResponse = $this->createWebResponse();
+ $webResponse->expects( $this->any() )
+ ->method( 'header' )
+ ->withConsecutive(
+ [ 'HTTP/1.1 200 OK', true, null ],
+ [ 'Foo: Bar', true, null ]
+ );
+
+ $entryPoint = new EntryPoint(
+ new RequestData( [ 'uri' => new Uri( '/rest/mock/EntryPoint/header' ) ] ),
+ $webResponse,
+ $this->createRouter() );
+ $entryPoint->execute();
+ $this->assertTrue( true );
+ }
+
+ public static function mockHandlerBodyRewind() {
+ return new class extends Handler {
+ public function execute() {
+ $response = $this->getResponseFactory()->create();
+ $stream = new Stream( fopen( 'php://memory', 'w+' ) );
+ $stream->write( 'hello' );
+ $response->setBody( $stream );
+ return $response;
+ }
+ };
+ }
+
+ /**
+ * Make sure EntryPoint rewinds a seekable body stream before reading.
+ */
+ public function testBodyRewind() {
+ $entryPoint = new EntryPoint(
+ new RequestData( [ 'uri' => new Uri( '/rest/mock/EntryPoint/bodyRewind' ) ] ),
+ $this->createWebResponse(),
+ $this->createRouter() );
+ ob_start();
+ $entryPoint->execute();
+ $this->assertSame( 'hello', ob_get_clean() );
+ }
+
+}
/** @dataProvider provideTestViaRouter */
public function testViaRouter( $requestInfo, $responseInfo ) {
$router = new Router(
- [ __DIR__ . '/testRoutes.json' ],
+ [ __DIR__ . '/../testRoutes.json' ],
[],
'/rest',
new EmptyBagOStuff(),
+++ /dev/null
-[
- {
- "path": "/user/{name}/hello",
- "class": "MediaWiki\\Rest\\Handler\\HelloHandler"
- }
-]
[ 'tesT' => [ 'bar' ] ],
[ 'tesT' => 'bar' ]
],
+ 'empty' => [ [], [], [] ],
];
}
--- /dev/null
+<?php
+
+namespace MediaWiki\Tests\Rest\PathTemplateMatcher;
+
+use MediaWiki\Rest\PathTemplateMatcher\PathConflict;
+use MediaWiki\Rest\PathTemplateMatcher\PathMatcher;
+use MediaWikiTestCase;
+
+/**
+ * @covers \MediaWiki\Rest\PathTemplateMatcher\PathMatcher
+ * @covers \MediaWiki\Rest\PathTemplateMatcher\PathConflict
+ */
+class PathMatcherTest extends MediaWikiTestCase {
+ private static $normalRoutes = [
+ '/a/b',
+ '/b/{x}',
+ '/c/{x}/d',
+ '/c/{x}/e',
+ '/c/{x}/{y}/d',
+ ];
+
+ public static function provideConflictingRoutes() {
+ return [
+ [ '/a/b', 0, '/a/b' ],
+ [ '/a/{x}', 0, '/a/b' ],
+ [ '/{x}/c', 1, '/b/{x}' ],
+ [ '/b/a', 1, '/b/{x}' ],
+ [ '/b/{x}', 1, '/b/{x}' ],
+ [ '/{x}/{y}/d', 2, '/c/{x}/d' ],
+ ];
+ }
+
+ public static function provideMatch() {
+ return [
+ [ '', false ],
+ [ '/a/b', [ 'params' => [], 'userData' => 0 ] ],
+ [ '/b', false ],
+ [ '/b/1', [ 'params' => [ 'x' => '1' ], 'userData' => 1 ] ],
+ [ '/c/1/d', [ 'params' => [ 'x' => '1' ], 'userData' => 2 ] ],
+ [ '/c/1/e', [ 'params' => [ 'x' => '1' ], 'userData' => 3 ] ],
+ [ '/c/000/e', [ 'params' => [ 'x' => '000' ], 'userData' => 3 ] ],
+ [ '/c/1/f', false ],
+ [ '/c//e', [ 'params' => [ 'x' => '' ], 'userData' => 3 ] ],
+ [ '/c///e', false ],
+ ];
+ }
+
+ public function createNormalRouter() {
+ $pm = new PathMatcher;
+ foreach ( self::$normalRoutes as $i => $route ) {
+ $pm->add( $route, $i );
+ }
+ return $pm;
+ }
+
+ /** @dataProvider provideConflictingRoutes */
+ public function testAddConflict( $attempt, $expectedUserData, $expectedTemplate ) {
+ $pm = $this->createNormalRouter();
+ $actualTemplate = null;
+ $actualUserData = null;
+ try {
+ $pm->add( $attempt, 'conflict' );
+ } catch ( PathConflict $pc ) {
+ $actualTemplate = $pc->existingTemplate;
+ $actualUserData = $pc->existingUserData;
+ }
+ $this->assertSame( $expectedUserData, $actualUserData );
+ $this->assertSame( $expectedTemplate, $actualTemplate );
+ }
+
+ /** @dataProvider provideMatch */
+ public function testMatch( $path, $expectedResult ) {
+ $pm = $this->createNormalRouter();
+ $result = $pm->match( $path );
+ $this->assertSame( $expectedResult, $result );
+ }
+}
--- /dev/null
+<?php
+
+namespace MediaWiki\Tests\Rest;
+
+use MediaWiki\Rest\StringStream;
+use MediaWikiTestCase;
+
+/** @covers \MediaWiki\Rest\StringStream */
+class StringStreamTest extends MediaWikiTestCase {
+ public static function provideSeekGetContents() {
+ return [
+ [ 'abcde', 0, SEEK_SET, 'abcde' ],
+ [ 'abcde', 1, SEEK_SET, 'bcde' ],
+ [ 'abcde', 5, SEEK_SET, '' ],
+ [ 'abcde', 1, SEEK_CUR, 'cde' ],
+ [ 'abcde', 0, SEEK_END, '' ],
+ ];
+ }
+
+ /** @dataProvider provideSeekGetContents */
+ public function testCopyToStream( $input, $offset, $whence, $expected ) {
+ $ss = new StringStream;
+ $ss->write( $input );
+ $ss->seek( 1 );
+ $ss->seek( $offset, $whence );
+ $destStream = fopen( 'php://memory', 'w+' );
+ $ss->copyToStream( $destStream );
+ fseek( $destStream, 0 );
+ $result = stream_get_contents( $destStream );
+ $this->assertSame( $expected, $result );
+ }
+
+ public function testGetSize() {
+ $ss = new StringStream;
+ $this->assertSame( 0, $ss->getSize() );
+ $ss->write( "hello" );
+ $this->assertSame( 5, $ss->getSize() );
+ $ss->rewind();
+ $this->assertSame( 5, $ss->getSize() );
+ }
+
+ public function testTell() {
+ $ss = new StringStream;
+ $this->assertSame( $ss->tell(), 0 );
+ $ss->write( "abc" );
+ $this->assertSame( $ss->tell(), 3 );
+ $ss->seek( 0 );
+ $ss->read( 1 );
+ $this->assertSame( $ss->tell(), 1 );
+ }
+
+ public function testEof() {
+ $ss = new StringStream( 'abc' );
+ $this->assertFalse( $ss->eof() );
+ $ss->read( 1 );
+ $this->assertFalse( $ss->eof() );
+ $ss->read( 1 );
+ $this->assertFalse( $ss->eof() );
+ $ss->read( 1 );
+ $this->assertTrue( $ss->eof() );
+ $ss->rewind();
+ $this->assertFalse( $ss->eof() );
+ }
+
+ public function testIsSeekable() {
+ $ss = new StringStream;
+ $this->assertTrue( $ss->isSeekable() );
+ }
+
+ public function testIsReadable() {
+ $ss = new StringStream;
+ $this->assertTrue( $ss->isReadable() );
+ }
+
+ public function testIsWritable() {
+ $ss = new StringStream;
+ $this->assertTrue( $ss->isWritable() );
+ }
+
+ public function testSeekWrite() {
+ $ss = new StringStream;
+ $this->assertSame( '', (string)$ss );
+ $ss->write( 'a' );
+ $this->assertSame( 'a', (string)$ss );
+ $ss->write( 'b' );
+ $this->assertSame( 'ab', (string)$ss );
+ $ss->seek( 1 );
+ $ss->write( 'c' );
+ $this->assertSame( 'ac', (string)$ss );
+ }
+
+ /** @dataProvider provideSeekGetContents */
+ public function testSeekGetContents( $input, $offset, $whence, $expected ) {
+ $ss = new StringStream( $input );
+ $ss->seek( 1 );
+ $ss->seek( $offset, $whence );
+ $this->assertSame( $expected, $ss->getContents() );
+ }
+
+ public static function provideSeekRead() {
+ return [
+ [ 'abcde', 0, SEEK_SET, 1, 'a' ],
+ [ 'abcde', 0, SEEK_SET, 2, 'ab' ],
+ [ 'abcde', 4, SEEK_SET, 2, 'e' ],
+ [ 'abcde', 5, SEEK_SET, 1, '' ],
+ [ 'abcde', 1, SEEK_CUR, 1, 'c' ],
+ [ 'abcde', 0, SEEK_END, 1, '' ],
+ [ 'abcde', -1, SEEK_END, 1, 'e' ],
+ ];
+ }
+
+ /** @dataProvider provideSeekRead */
+ public function testSeekRead( $input, $offset, $whence, $length, $expected ) {
+ $ss = new StringStream( $input );
+ $ss->seek( 1 );
+ $ss->seek( $offset, $whence );
+ $this->assertSame( $expected, $ss->read( $length ) );
+ }
+
+ /** @expectedException \InvalidArgumentException */
+ public function testReadBeyondEnd() {
+ $ss = new StringStream( 'abc' );
+ $ss->seek( 1, SEEK_END );
+ }
+
+ /** @expectedException \InvalidArgumentException */
+ public function testReadBeforeStart() {
+ $ss = new StringStream( 'abc' );
+ $ss->seek( -1 );
+ }
+}
--- /dev/null
+[
+ {
+ "path": "/user/{name}/hello",
+ "class": "MediaWiki\\Rest\\Handler\\HelloHandler"
+ },
+ {
+ "path": "/mock/EntryPoint/header",
+ "factory": "MediaWiki\\Tests\\Rest\\EntryPointTest::mockHandlerHeader"
+ },
+ {
+ "path": "/mock/EntryPoint/bodyRewind",
+ "factory": "MediaWiki\\Tests\\Rest\\EntryPointTest::mockHandlerBodyRewind"
+ }
+]
use Content;
use Language;
use LogicException;
+use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Revision\MutableRevisionRecord;
use MediaWiki\Revision\MainSlotRoleHandler;
use MediaWiki\Revision\RevisionRecord;
*/
class RevisionRendererTest extends MediaWikiTestCase {
+ /** @var PermissionManager|\PHPUnit_Framework_MockObject_MockObject $permissionManagerMock */
+ private $permissionManagerMock;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->permissionManagerMock = $this->createMock( PermissionManager::class );
+ $this->overrideMwServices( null, [
+ 'PermissionManager' => function (): PermissionManager {
+ return $this->permissionManagerMock;
+ }
+ ] );
+ }
+
/**
* @param int $articleId
* @param int $revisionId
return $mock->getArticleID() === $other->getArticleID();
}
);
- $mock->expects( $this->any() )
+ $this->permissionManagerMock->expects( $this->any() )
->method( 'userCan' )
->willReturnCallback(
- function ( $perm, User $user ) use ( $mock ) {
+ function ( $perm, User $user ) {
return $user->isAllowed( $perm );
}
);
$this->mOptions = ParserOptions::newFromUserAndLang( new User,
MediaWikiServices::getInstance()->getContentLanguage() );
+ # Suppress deprecation warning for Preprocessor_DOM while testing
+ $this->hideDeprecated( 'Preprocessor_DOM::__construct' );
+
$this->mPreprocessors = [];
foreach ( self::$classNames as $className ) {
$this->mPreprocessors[$className] = new $className( $this );
$this->assertEquals( 'zh|fallback|||styles|||||', $ctx->getHash() );
}
+ public static function provideDirection() {
+ yield 'LTR language' => [
+ [ 'lang' => 'en' ],
+ 'ltr',
+ ];
+ yield 'RTL language' => [
+ [ 'lang' => 'he' ],
+ 'rtl',
+ ];
+ yield 'explicit LTR' => [
+ [ 'lang' => 'he', 'dir' => 'ltr' ],
+ 'ltr',
+ ];
+ yield 'explicit RTL' => [
+ [ 'lang' => 'en', 'dir' => 'rtl' ],
+ 'rtl',
+ ];
+ // Not supported, but tested to cover the case and detect change
+ yield 'invalid dir' => [
+ [ 'lang' => 'he', 'dir' => 'xyz' ],
+ 'rtl',
+ ];
+ }
+
+ /**
+ * @dataProvider provideDirection
+ */
+ public function testDirection( array $params, $expected ) {
+ $ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( $params ) );
+ $this->assertEquals( $expected, $ctx->getDirection() );
+ }
+
public function testShouldInclude() {
$ctx = new ResourceLoaderContext( $this->getResourceLoader(), new FauxRequest( [] ) );
$this->assertTrue( $ctx->shouldIncludeScripts(), 'Scripts in combined' );
'wgServer' => 'http://acme.test',
'wgCanonicalServer' => 'http://acme.test',
'wgArticlePath' => '/wiki/$1',
- 'wgRightsUrl' => '//creativecommons.org/licenses/by-sa/3.0/',
+ 'wgRightsUrl' => 'https://creativecommons.org/licenses/by-sa/3.0/',
] );
$dumpScript =
// Check permissions if there are read restrictions
$varyHeader = [];
if ( !in_array( 'read', User::getGroupPermissions( [ '*' ] ), true ) ) {
- if ( !$img->getTitle() || !$img->getTitle()->userCan( 'read' ) ) {
+ $user = RequestContext::getMain()->getUser();
+ $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
+ $imgTitle = $img->getTitle();
+
+ if ( !$imgTitle || !$permissionManager->userCan( 'read', $user, $imgTitle ) ) {
wfThumbError( 403, 'Access denied. You do not have permission to access ' .
'the source file.' );
return;