"in-place", as long as you have the necessary prerequisites available.
Required software:
-* Web server with PHP 5.5.9 or higher.
+* Web server with PHP 7.0.0 or HHVM 3.18.5 or higher.
* A SQL server, the following types are supported
** MySQL 5.5.8 or higher
** PostgreSQL 9.2 or higher
* The mediawiki.widgets.visibleByteLimit module alias, deprecated in 1.32, was
removed. Use mediawiki.widgets.visibleLengthLimit instead.
* The jquery.farbtastic module, unused since 1.18, was removed.
+* (T181318) The $wgStyleVersion setting and its appendage to various script and
+ style URLs in OutputPage, deprecated in 1.31, was removed.
=== Deprecations in 1.32 ===
* Use of a StartProfiler.php file is deprecated in favour of placing
* …
== Compatibility ==
-MediaWiki 1.32 requires PHP 5.5.9 or later. Although HHVM 3.18.5 or later is
-supported, it is generally advised to use PHP 5.5.9 or later for long term
+MediaWiki 1.32 requires PHP 7.0.0 or later. Although HHVM 3.18.5 or later is
+supported, it is generally advised to use PHP 7.0.0 or later for long term
support.
MySQL/MariaDB is the recommended DBMS. PostgreSQL or SQLite can also be used,
"pear/mail": "1.4.1",
"pear/mail_mime": "1.10.2",
"pear/mail_mime-decode": "1.5.5.2",
- "php": ">=5.5.9",
+ "php": ">=5.6.99",
"psr/log": "1.0.2",
"wikimedia/assert": "0.2.2",
"wikimedia/at-ease": "1.2.0",
*/
$wgGitInfoCacheDirectory = false;
-/**
- * Bump this number when changing the global style sheets and JavaScript.
- *
- * It should be appended in the query string of static CSS and JS includes,
- * to ensure that client-side caches do not keep obsolete copies of global
- * styles.
- *
- * @deprecated since 1.31
- */
-$wgStyleVersion = '303';
-
/**
* This will cache static pages for non-logged-in users to reduce
* database traffic on public sites. ResourceLoader requests to default
use Wikimedia\ScopedCallback;
use Wikimedia\Rdbms\DBReplicationWaitError;
-// Hide compatibility functions from Doxygen
-/// @cond
-/**
- * Compatibility functions
- *
- * We support PHP 5.5.9 and up.
- * Re-implementations of newer functions or functions in non-standard
- * PHP extensions may be included here.
- */
-
-// hash_equals function only exists in PHP >= 5.6.0
-// https://secure.php.net/hash_equals
-if ( !function_exists( 'hash_equals' ) ) {
- /**
- * Check whether a user-provided string is equal to a fixed-length secret string
- * without revealing bytes of the secret string through timing differences.
- *
- * The usual way to compare strings (PHP's === operator or the underlying memcmp()
- * function in C) is to compare corresponding bytes and stop at the first difference,
- * which would take longer for a partial match than for a complete mismatch. This
- * is not secure when one of the strings (e.g. an HMAC or token) must remain secret
- * and the other may come from an attacker. Statistical analysis of timing measurements
- * over many requests may allow the attacker to guess the string's bytes one at a time
- * (and check his guesses) even if the timing differences are extremely small.
- *
- * When making such a security-sensitive comparison, it is essential that the sequence
- * in which instructions are executed and memory locations are accessed not depend on
- * the secret string's value. HOWEVER, for simplicity, we do not attempt to minimize
- * the inevitable leakage of the string's length. That is generally known anyway as
- * a chararacteristic of the hash function used to compute the secret value.
- *
- * Longer explanation: http://www.emerose.com/timing-attacks-explained
- *
- * @codeCoverageIgnore
- * @param string $known_string Fixed-length secret string to compare against
- * @param string $user_string User-provided string
- * @return bool True if the strings are the same, false otherwise
- */
- function hash_equals( $known_string, $user_string ) {
- // Strict type checking as in PHP's native implementation
- if ( !is_string( $known_string ) ) {
- trigger_error( 'hash_equals(): Expected known_string to be a string, ' .
- gettype( $known_string ) . ' given', E_USER_WARNING );
-
- return false;
- }
-
- if ( !is_string( $user_string ) ) {
- trigger_error( 'hash_equals(): Expected user_string to be a string, ' .
- gettype( $user_string ) . ' given', E_USER_WARNING );
-
- return false;
- }
-
- $known_string_len = strlen( $known_string );
- if ( $known_string_len !== strlen( $user_string ) ) {
- return false;
- }
-
- $result = 0;
- for ( $i = 0; $i < $known_string_len; $i++ ) {
- $result |= ord( $known_string[$i] ) ^ ord( $user_string[$i] );
- }
-
- return ( $result === 0 );
- }
-}
-/// @endcond
-
/**
* Load an extension
*
'version' => PHP_VERSION,
'vendor' => 'the PHP Group',
'upstreamSupported' => '5.6.0',
- 'minSupported' => '5.5.9',
+ 'minSupported' => '7.0.0',
'upgradeURL' => 'https://secure.php.net/downloads.php',
);
}
. "MediaWiki $this->mwVersion needs {$phpInfo['implementation']}"
. " $minimumVersion or higher or {$otherInfo['implementation']} version "
. "{$otherInfo['minSupported']}.\n\nCheck if you have a"
- . " newer php executable with a different name, such as php5.\n\n";
+ . " newer php executable with a different name.\n\n";
// phpcs:disable Generic.Files.LineLength
$longHtml = <<<HTML
}
}
+ $title = $params['title'];
+ if ( !is_null( $title ) ) {
+ $titleObj = Title::newFromText( $title );
+ if ( is_null( $titleObj ) ) {
+ $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $title ) ] );
+ }
+ $this->addWhereFld( 'rc_namespace', $titleObj->getNamespace() );
+ $this->addWhereFld( 'rc_title', $titleObj->getDBkey() );
+ }
+
if ( !is_null( $params['show'] ) ) {
$show = array_flip( $params['show'] );
ApiBase::PARAM_TYPE => RecentChange::getChangeTypes()
],
'toponly' => false,
+ 'title' => null,
'continue' => [
ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
],
"apihelp-query+recentchanges-param-limit": "How many total changes to return.",
"apihelp-query+recentchanges-param-type": "Which types of changes to show.",
"apihelp-query+recentchanges-param-toponly": "Only list changes which are the latest revision.",
+ "apihelp-query+recentchanges-param-title": "Filter entries to those related to a page.",
"apihelp-query+recentchanges-param-generaterevisions": "When being used as a generator, generate revision IDs rather than titles. Recent change entries without associated revision IDs (e.g. most log entries) will generate nothing.",
"apihelp-query+recentchanges-example-simple": "List recent changes.",
"apihelp-query+recentchanges-example-generator": "Get page info about recent unpatrolled changes.",
"apihelp-query+recentchanges-param-limit": "{{doc-apihelp-param|query+recentchanges|limit}}",
"apihelp-query+recentchanges-param-type": "{{doc-apihelp-param|query+recentchanges|type}}",
"apihelp-query+recentchanges-param-toponly": "{{doc-apihelp-param|query+recentchanges|toponly}}",
+ "apihelp-query+recentchanges-param-title": "{{doc-apihelp-param|query+recentchanges|title}}",
"apihelp-query+recentchanges-param-generaterevisions": "{{doc-apihelp-param|query+recentchanges|generaterevisions}}",
"apihelp-query+recentchanges-example-simple": "{{doc-apihelp-example|query+recentchanges}}",
"apihelp-query+recentchanges-example-generator": "{{doc-apihelp-example|query+recentchanges}}",
// Don't interpret POST parameters starting with '@' as file uploads, because this
// makes it impossible to POST plain values starting with '@' (and causes security
// issues potentially exposing the contents of local files).
- // The PHP manual says this option was introduced in PHP 5.5 defaults to true in PHP 5.6,
- // but we support lower versions, and the option doesn't exist in HHVM 5.6.99.
- if ( defined( 'CURLOPT_SAFE_UPLOAD' ) ) {
- $this->curlOptions[CURLOPT_SAFE_UPLOAD] = true;
- } elseif ( is_array( $postData ) ) {
- // In PHP 5.2 and later, '@' is interpreted as a file upload if POSTFIELDS
- // is an array, but not if it's a string. So convert $req['body'] to a string
- // for safety.
- $postData = wfArrayToCgi( $postData );
- }
+ $this->curlOptions[CURLOPT_SAFE_UPLOAD] = true;
$this->curlOptions[CURLOPT_POSTFIELDS] = $postData;
// Suppress 'Expect: 100-continue' header, as some servers
$certLocations = [];
if ( $this->caInfo ) {
$certLocations = [ 'manual' => $this->caInfo ];
- } elseif ( version_compare( PHP_VERSION, '5.6.0', '<' ) ) {
- // Default locations, based on
- // https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/
- // PHP 5.5 and older doesn't have any defaults, so we try to guess ourselves.
- // PHP 5.6+ gets the CA location from OpenSSL as long as it is not set manually,
- // so we should leave capath/cafile empty there.
- $certLocations = array_filter( [
- getenv( 'SSL_CERT_DIR' ),
- getenv( 'SSL_CERT_PATH' ),
- '/etc/pki/tls/certs/ca-bundle.crt', # Fedora et al
- '/etc/ssl/certs', # Debian et al
- '/etc/pki/tls/certs/ca-bundle.trust.crt',
- '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem',
- '/System/Library/OpenSSL', # OSX
- ] );
}
foreach ( $certLocations as $key => $cert ) {
$scriptTypes = [
'php' => [
"<?php echo 'ex' . 'ec';",
- "#!/var/env php5\n<?php echo 'ex' . 'ec';",
+ "#!/var/env php\n<?php echo 'ex' . 'ec';",
],
];
// Match these three variants separately to avoid broken urls when
// e.g. a double quoted url contains a parenthesis, or when a
// single quoted url contains a double quote, etc.
+ // FIXME: Simplify now we only support PHP 7.0.0+
// Note: PCRE doesn't support multiple capture groups with the same name by default.
// - PCRE 6.7 introduced the "J" modifier (PCRE_INFO_JCHANGED for PCRE_DUPNAMES).
// https://secure.php.net/manual/en/reference.pcre.pattern.modifiers.php
// Don't interpret POST parameters starting with '@' as file uploads, because this
// makes it impossible to POST plain values starting with '@' (and causes security
// issues potentially exposing the contents of local files).
- // The PHP manual says this option was introduced in PHP 5.5 defaults to true in PHP 5.6,
- // but we support lower versions, and the option doesn't exist in HHVM 5.6.99.
- if ( defined( 'CURLOPT_SAFE_UPLOAD' ) ) {
- curl_setopt( $ch, CURLOPT_SAFE_UPLOAD, true );
- } elseif ( is_array( $req['body'] ) ) {
- // In PHP 5.2 and later, '@' is interpreted as a file upload if POSTFIELDS
- // is an array, but not if it's a string. So convert $req['body'] to a string
- // for safety.
- $req['body'] = http_build_query( $req['body'] );
- }
+ curl_setopt( $ch, CURLOPT_SAFE_UPLOAD, true );
curl_setopt( $ch, CURLOPT_POSTFIELDS, $req['body'] );
} else {
if ( is_resource( $req['body'] ) || $req['body'] !== '' ) {
'general' => [],
],
// Deprecations for style-only modules
- 'styledeprecations' => [],
+ 'styleDeprecations' => [],
];
foreach ( $this->modules as $name ) {
}
$deprecation = $module->getDeprecationInformation();
if ( $deprecation ) {
- $data['styledeprecations'][] = $deprecation;
+ $data['styleDeprecations'][] = $deprecation;
}
}
);
}
- // Deprecations for only=styles modules
- if ( $data['styledeprecations'] ) {
- $chunks[] = ResourceLoader::makeInlineScript(
- implode( '', $data['styledeprecations'] ),
- $nonce
- );
- }
-
// External stylesheets (only=styles)
if ( $data['styles'] ) {
$chunks[] = $this->getLoad(
* @return string|WrappedStringList HTML
*/
public function getBodyHtml() {
- return '';
+ $data = $this->getData();
+ $chunks = [];
+
+ // Deprecations for only=styles modules
+ if ( $data['styleDeprecations'] ) {
+ $chunks[] = ResourceLoader::makeInlineScript(
+ implode( '', $data['styleDeprecations'] ),
+ $this->options['nonce']
+ );
+ }
+
+ return WrappedStringList::join( "\n", $chunks );
}
private function getContext( $group, $type ) {
}
/**
- * Return a fully resolved style path url to images or styles stored in the current skins's folder.
- * This method returns a url resolved using the configured skin style path
- * and includes the style version inside of the url.
+ * Return a fully resolved style path URL to images or styles stored in the
+ * current skin's folder. This method returns a URL resolved using the
+ * configured skin style path.
*
* Requires $stylename to be set, otherwise throws MWException.
*
* @param string $name The name or path of a skin resource file
- * @return string The fully resolved style path url including styleversion
+ * @return string The fully resolved style path URL
* @throws MWException
*/
function getSkinStylePath( $name ) {
- global $wgStylePath, $wgStyleVersion;
+ global $wgStylePath;
if ( $this->stylename === null ) {
$class = static::class;
throw new MWException( "$class::\$stylename must be set to use getSkinStylePath()" );
}
- return "$wgStylePath/{$this->stylename}/$name?$wgStyleVersion";
+ return "$wgStylePath/{$this->stylename}/$name";
}
/* these are used extensively in SkinTemplate, but also some other places */
}
private function getRecentChangeFieldsFromRow( stdClass $row ) {
- // This can be simplified to single array_filter call filtering by key value,
- // once we stop supporting PHP 5.5
+ // FIXME: This can be simplified to single array_filter call filtering by key value,
+ // now we have stopped supporting PHP 5.5
$allFields = get_object_vars( $row );
$rcKeys = array_filter(
array_keys( $allFields ),
} else {
// Note use of just `a`, not `a.image`, since we also want this to trigger if a link
// within the caption text receives focus.
- // This is based on code from the 'jquery.mw-jump' module.
$content.find( 'ul.mw-gallery-packed-hover li.gallerybox' ).on( 'focus blur', 'a', function ( e ) {
// Confusingly jQuery leaves e.type as focusout for delegated blur events
var gettingFocus = e.type !== 'blur' && e.type !== 'focusout';
$preferences = $( '#preferences' );
// Make sure the accessibility tip is selectable so that screen reader users take notice,
- // but hide it per default to reduce interface clutter. Also make sure it becomes visible
- // when selected. Similar to jquery.mw-jump
+ // but hide it by default to reduce visual clutter.
+ // Make sure it becomes visible when focused.
$( '<div>' ).addClass( 'mw-navigation-hint' )
.text( mw.msg( 'prefs-tabs-navigation-hint' ) )
.attr( 'tabIndex', 0 )
$fieldsets.children( 'legend' ).addClass( 'mainLegend' );
// Make sure the accessibility tip is selectable so that screen reader users take notice,
- // but hide it per default to reduce interface clutter. Also make sure it becomes visible
- // when selected. Similar to jquery.mw-jump
+ // but hide it by default to reduce visual clutter.
+ // Make sure it becomes visible when focused.
$( '<div>' ).addClass( 'mw-navigation-hint' )
.text( mw.msg( 'prefs-tabs-navigation-hint' ) )
.attr( 'tabIndex', 0 )
);
}
+ public function testTitleParams() {
+ $page1 = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' );
+ $page2 = new TitleValue( 1, 'ApiQueryRecentChangesIntegrationTestPage2' );
+ $page3 = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage3' );
+ $this->doPageEdits(
+ $this->getLoggedInTestUser(),
+ [
+ [
+ 'target' => $page1,
+ 'summary' => 'Create the page',
+ ],
+ [
+ 'target' => $page2,
+ 'summary' => 'Create the page',
+ ],
+ [
+ 'target' => $page3,
+ 'summary' => 'Create the page',
+ ],
+ ]
+ );
+
+ $result = $this->doListRecentChangesRequest(
+ [
+ 'rctitle' => 'ApiQueryRecentChangesIntegrationTestPage',
+ 'rcprop' => 'title'
+ ]
+ );
+
+ $result2 = $this->doListRecentChangesRequest(
+ [
+ 'rctitle' => 'Talk:ApiQueryRecentChangesIntegrationTestPage2',
+ 'rcprop' => 'title'
+ ]
+ );
+
+ $this->assertEquals(
+ [
+ [
+ 'type' => 'new',
+ 'ns' => $page1->getNamespace(),
+ 'title' => $this->getPrefixedText( $page1 )
+ ],
+ ],
+ $this->getItemsFromApiResponse( $result )
+ );
+
+ $this->assertEquals(
+ [
+ [
+ 'type' => 'new',
+ 'ns' => $page2->getNamespace(),
+ 'title' => $this->getPrefixedText( $page2 )
+ ],
+ ],
+ $this->getItemsFromApiResponse( $result2 )
+ );
+ }
+
public function testStartEndParams() {
$target = new TitleValue( 0, 'ApiQueryRecentChangesIntegrationTestPage' );
$this->doPageEdit( $this->getLoggedInTestUser(), $target, 'Create the page' );
* @covers IPTC::parse
*/
public function testIPTCParseForcedUTFButInvalid() {
- if ( version_compare( PHP_VERSION, '5.5.26', '<' )
- || ( version_compare( PHP_VERSION, '5.6.0', '>' )
- && version_compare( PHP_VERSION, '5.6.10', '<' )
- )
- ) {
- $this->markTestSkipped( 'Test fails on pre-PHP 5.5.25. See T124574/T39665 for details.' );
- }
$iptcData = "Photoshop 3.0\08BIM\4\4\0\0\0\0\0\x11\x1c\x02\x19\x00\x04\xC3\xC3\xC3\xB8"
. "\x1c\x01\x5A\x00\x03\x1B\x25\x47";
$res = IPTC::parse( $iptcData );
'test.user',
],
],
- 'styledeprecations' => [
+ 'styleDeprecations' => [
Xml::encodeJsCall(
'mw.log.warn',
[ 'This page is using the deprecated ResourceLoader module "test.styles.deprecated".
. 'mw.loader.implement("test.private@{blankVer}",function($,jQuery,require,module){},{"css":[]});'
. 'mw.loader.load(["test"]);'
. 'mw.loader.load("/w/load.php?debug=false\u0026lang=nl\u0026modules=test.scripts\u0026only=scripts\u0026skin=fallback");'
- . 'mw.log.warn("This page is using the deprecated ResourceLoader module \"test.styles.deprecated\".\nDeprecation message.");'
. '});</script>' . "\n"
. '<link rel="stylesheet" href="/w/load.php?debug=false&lang=nl&modules=test.styles.deprecated%2Cpure&only=styles&skin=fallback"/>' . "\n"
. '<style>.private{}</style>' . "\n"
$context = self::makeContext();
$context->getResourceLoader()->register( self::makeSampleModules() );
- $client = new ResourceLoaderClientHtml( $context );
+ $client = new ResourceLoaderClientHtml( $context, [ 'nonce' => false ] );
$client->setConfig( [ 'key' => 'value' ] );
$client->setModules( [
'test',
'test.private.bottom',
] );
+ $client->setModuleStyles( [
+ 'test.styles.deprecated',
+ ] );
$client->setModuleScripts( [
'test.scripts',
] );
-
- $expected = '';
- $expected = self::expandVariables( $expected );
+ // phpcs:disable Generic.Files.LineLength
+ $expected = '<script>(window.RLQ=window.RLQ||[]).push(function(){'
+ . 'mw.log.warn("This page is using the deprecated ResourceLoader module \"test.styles.deprecated\".\nDeprecation message.");'
+ . '});</script>';
+ // phpcs:enable
$this->assertEquals( $expected, $client->getBodyHtml() );
}