input methods provided by the UniversalLanguageSelector extension.
* When $wgPingback is true, MediaWiki will periodically ping
https://www.mediawiki.org/beacon with basic information about the local
- MediaWiki installation. This data includes, for example, the type of system,
+ MediaWiki installation. This data includes, for example, the type of system,
PHP version, and chosen database backend. This behavior is off by default.
+* When $wgEditButtonPublishNotSave is true, MediaWiki will label the button to
+ store-to-database-and-show-to-others as "Publish page"/"Publish changes"; if
+ false, the default, they will be "Save page"/"Save changes".
=== New features in 1.28 ===
* User::isBot() method for checking if an account is a bot role account.
=== Other changes in 1.28 ===
* (T128697) Improved handling of large diffs.
-* [BREAKING CHANGE] $wgExtendedLoginCookies has been removed. You can
+* [BREAKING CHANGE] $wgExtendedLoginCookies has been removed. You can
use or update a custom session provider if needed.
* Deprecated APIEditBeforeSave hook in favor of EditFilterMergedContent.
* The 'UploadVerification' hook is deprecated. Use 'UploadVerifyFile' instead.
*/
$wgPingback = false;
+/**
+ * List of urls which appear often to be triggering CSP reports
+ * but do not appear to be caused by actual content, but by client
+ * software inserting scripts (i.e. Ad-Ware).
+ * List based on results from Wikimedia logs.
+ *
+ * @since 1.28
+ */
+$wgCSPFalsePositiveUrls = [
+ 'https://3hub.co' => true,
+ 'https://morepro.info' => true,
+ 'https://p.ato.mx' => true,
+ 'https://s.ato.mx' => true,
+ 'https://adserver.adtech.de' => true,
+ 'https://ums.adtechus.com' => true,
+ 'https://cas.criteo.com' => true,
+ 'https://cat.nl.eu.criteo.com' => true,
+ 'https://atpixel.alephd.com' => true,
+ 'https://rtb.metrigo.com' => true,
+ 'https://d5p.de17a.com' => true,
+];
+
/**
* For really cool vim folding this needs to be at the end:
* vim: foldmarker=@{,@} foldmethod=marker
$buttons = [];
$labelAsPublish = $this->mArticle->getContext()->getConfig()->get( 'EditButtonPublishNotSave' );
+ // Can't use $this->isNew as that's also true if we're adding a new section to an extant page
if ( $labelAsPublish ) {
- $buttonLabelKey = $this->isNew ? 'publishpage' : 'publishchanges';
+ $buttonLabelKey = !$this->mTitle->exists() ? 'publishpage' : 'publishchanges';
} else {
- $buttonLabelKey = $this->isNew ? 'savearticle' : 'savechanges';
+ $buttonLabelKey = !$this->mTitle->exists() ? 'savearticle' : 'savechanges';
}
$buttonLabel = wfMessage( $buttonLabelKey )->text();
$attribs = [
*
* @code
* // old style:
- * wfMsgExt( 'key', array( 'parseinline' ), 'apple' );
+ * wfMsgExt( 'key', [ 'parseinline' ], 'apple' );
* // new style:
* wfMessage( 'key', 'apple' )->parse();
* @endcode
* Places where HTML cannot be used. {{-transformation is done.
* @code
* // old style:
- * wfMsgExt( 'key', array( 'parsemag' ), 'apple', 'pear' );
+ * wfMsgExt( 'key', [ 'parsemag' ], 'apple', 'pear' );
* // new style:
* wfMessage( 'key', 'apple', 'pear' )->text();
* @endcode
// Special case in buildExemptModules()
return false;
}
- if ( $name === 'site.styles' ) {
- // HACK: Technically, 'site.styles' isn't in a separate request group.
- // But, in order to ensure its styles are in the right position,
- // pretend it's in a group called 'site'.
- $group = 'site';
- }
if ( isset( $exemptGroups[$group] ) ) {
$exemptStates[$name] = 'ready';
if ( !$module->isKnownEmpty( $context ) ) {
$reportOnly = $this->getParameter( 'reportonly' );
$userAgent = $this->getRequest()->getHeader( 'user-agent' );
$source = $this->getParameter( 'source' );
+ $falsePositives = $this->getConfig()->get( 'CSPFalsePositiveUrls' );
$flags = [];
if ( $source !== 'internal' ) {
if ( $reportOnly ) {
$flags[] = 'report-only';
}
+
+ if (
+ ( isset( $report['blocked-uri'] ) &&
+ isset( $falsePositives[$report['blocked-uri']] ) )
+ || ( isset( $report['source-file'] ) &&
+ isset( $falsePositives[$report['source-file']] ) )
+ ) {
+ // Report caused by Ad-Ware
+ $flags[] = 'false-positive';
+ }
return $flags;
}
* global configuration variable used by LoggerFactory to construct its
* default SPI provider:
* @code
- * $wgMWLoggerDefaultSpi = array(
+ * $wgMWLoggerDefaultSpi = [
* 'class' => '\\MediaWiki\\Logger\\MonologSpi',
- * 'args' => array( array(
- * 'loggers' => array(
- * '@default' => array(
- * 'processors' => array( 'wiki', 'psr', 'pid', 'uid', 'web' ),
- * 'handlers' => array( 'stream' ),
- * ),
- * 'runJobs' => array(
- * 'processors' => array( 'wiki', 'psr', 'pid' ),
- * 'handlers' => array( 'stream' ),
- * )
- * ),
- * 'processors' => array(
- * 'wiki' => array(
+ * 'args' => [ [
+ * 'loggers' => [
+ * '@default' => [
+ * 'processors' => [ 'wiki', 'psr', 'pid', 'uid', 'web' ],
+ * 'handlers' => [ 'stream' ],
+ * ],
+ * 'runJobs' => [
+ * 'processors' => [ 'wiki', 'psr', 'pid' ],
+ * 'handlers' => [ 'stream' ],
+ * ]
+ * ],
+ * 'processors' => [
+ * 'wiki' => [
* 'class' => '\\MediaWiki\\Logger\\Monolog\\WikiProcessor',
- * ),
- * 'psr' => array(
+ * ],
+ * 'psr' => [
* 'class' => '\\Monolog\\Processor\\PsrLogMessageProcessor',
- * ),
- * 'pid' => array(
+ * ],
+ * 'pid' => [
* 'class' => '\\Monolog\\Processor\\ProcessIdProcessor',
- * ),
- * 'uid' => array(
+ * ],
+ * 'uid' => [
* 'class' => '\\Monolog\\Processor\\UidProcessor',
- * ),
- * 'web' => array(
+ * ],
+ * 'web' => [
* 'class' => '\\Monolog\\Processor\\WebProcessor',
- * ),
- * ),
- * 'handlers' => array(
- * 'stream' => array(
+ * ],
+ * ],
+ * 'handlers' => [
+ * 'stream' => [
* 'class' => '\\Monolog\\Handler\\StreamHandler',
- * 'args' => array( 'path/to/your.log' ),
+ * 'args' => [ 'path/to/your.log' ],
* 'formatter' => 'line',
- * ),
- * 'redis' => array(
+ * ],
+ * 'redis' => [
* 'class' => '\\Monolog\\Handler\\RedisHandler',
- * 'args' => array( function() {
+ * 'args' => [ function() {
* $redis = new Redis();
* $redis->connect( '127.0.0.1', 6379 );
* return $redis;
* },
* 'logstash'
- * ),
+ * ],
* 'formatter' => 'logstash',
* 'buffer' => true,
- * ),
- * 'udp2log' => array(
+ * ],
+ * 'udp2log' => [
* 'class' => '\\MediaWiki\\Logger\\Monolog\\LegacyHandler',
- * 'args' => array(
+ * 'args' => [
* 'udp://127.0.0.1:8420/mediawiki
- * ),
+ * ],
* 'formatter' => 'line',
- * ),
- * ),
- * 'formatters' => array(
- * 'line' => array(
+ * ],
+ * ],
+ * 'formatters' => [
+ * 'line' => [
* 'class' => '\\Monolog\\Formatter\\LineFormatter',
- * ),
- * 'logstash' => array(
+ * ],
+ * 'logstash' => [
* 'class' => '\\Monolog\\Formatter\\LogstashFormatter',
- * 'args' => array( 'mediawiki', php_uname( 'n' ), null, '', 1 ),
- * ),
- * ),
- * ) ),
- * );
+ * 'args' => [ 'mediawiki', php_uname( 'n' ), null, '', 1 ],
+ * ],
+ * ],
+ * ] ],
+ * ];
* @endcode
*
* @see https://github.com/Seldaek/monolog
/**
* Get an array of install steps. Should always be in the format of
- * array(
+ * [
* 'name' => 'someuniquename',
- * 'callback' => array( $obj, 'method' ),
- * )
+ * 'callback' => [ $obj, 'method' ],
+ * ]
* There must be a config-install-$name message defined per step, which will
* be shown on install.
*
* Add an installation step following the given step.
*
* @param callable $callback A valid installation callback array, in this form:
- * array( 'name' => 'some-unique-name', 'callback' => array( $obj, 'function' ) );
+ * [ 'name' => 'some-unique-name', 'callback' => [ $obj, 'function' ] ];
* @param string $findStep The step to find. Omit to put the step at the beginning
*/
public function addInstallStep( $callback, $findStep = 'BEGINNING' ) {
* For $wgGroupPermissions, set a given ['group']['permission'] value.
* @param string $group Group name
* @param array $rightsArr An array of permissions, in the form of:
- * array( 'right' => true, 'right2' => false )
+ * [ 'right' => true, 'right2' => false ]
*/
public function setGroupRights( $group, $rightsArr ) {
$this->groupPermissions[$group] = $rightsArr;
[ 'addField', 'ipblocks', 'ipb_allow_usertalk', 'patch-ipb_allow_usertalk.sql' ],
// 1.15
- [ 'doUniquePlTlIl' ],
[ 'addTable', 'change_tag', 'patch-change_tag.sql' ],
[ 'addTable', 'tag_summary', 'patch-tag_summary.sql' ],
[ 'addTable', 'valid_tag', 'patch-valid_tag.sql' ],
// 1.28
[ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
- [ 'doRevisionPageRevIndexNonUnique' ]
+ [ 'doRevisionPageRevIndexNonUnique' ],
+ [ 'doNonUniquePlTlIl' ],
];
}
return true;
}
- protected function doUniquePlTlIl() {
+ protected function doNonUniquePlTlIl() {
$info = $this->db->indexInfo( 'pagelinks', 'pl_namespace' );
- if ( is_array( $info ) && !$info[0]->Non_unique ) {
- $this->output( "...pl_namespace, tl_namespace, il_to indices are already UNIQUE.\n" );
+ if ( is_array( $info ) && $info[0]->Non_unique ) {
+ $this->output( "...pl_namespace, tl_namespace, il_to indices are already non-UNIQUE.\n" );
return true;
}
if ( $this->skipSchema ) {
$this->output( "...skipping schema change (making pl_namespace, tl_namespace " .
- "and il_to indices UNIQUE).\n" );
+ "and il_to indices non-UNIQUE).\n" );
return false;
}
return $this->applyPatch(
- 'patch-pl-tl-il-unique.sql',
+ 'patch-pl-tl-il-nonunique.sql',
false,
- 'Making pl_namespace, tl_namespace and il_to indices UNIQUE'
+ 'Making pl_namespace, tl_namespace and il_to indices non-UNIQUE'
);
}
* the form:
*
* @code
- * 'UNIQ-xxxxx' => array(
+ * 'UNIQ-xxxxx' => [
* 'element',
* 'tag content',
- * array( 'param' => 'x' ),
- * '<element param="x">tag content</element>' ) )
+ * [ 'param' => 'x' ],
+ * '<element param="x">tag content</element>' ]
* @endcode
*
* @param array $elements List of element names. Comments are always extracted.
*
* @param ResourceLoaderContext $context
* @return array List of CSS strings or array of CSS strings keyed by media type.
- * like array( 'screen' => '.foo { width: 0 }' );
- * or array( 'screen' => array( '.foo { width: 0 }' ) );
+ * like [ 'screen' => '.foo { width: 0 }' ];
+ * or [ 'screen' => [ '.foo { width: 0 }' ] ];
*/
public function getStyles( ResourceLoaderContext $context ) {
// Stub, override expected
* load the files directly. See also getScriptURLsForDebug()
*
* @param ResourceLoaderContext $context
- * @return array Array( mediaType => array( URL1, URL2, ... ), ... )
+ * @return array [ mediaType => [ URL1, URL2, ... ], ... ]
*/
public function getStyleURLsForDebug( ResourceLoaderContext $context ) {
$resourceLoader = $context->getResourceLoader();
// Styles
if ( $context->shouldIncludeStyles() ) {
$styles = [];
- // Don't create empty stylesheets like array( '' => '' ) for modules
+ // Don't create empty stylesheets like [ '' => '' ] for modules
// that don't *have* any stylesheets (bug 38024).
$stylePairs = $this->getStyles( $context );
if ( count( $stylePairs ) ) {
public function getType() {
return self::LOAD_STYLES;
}
+
+ /**
+ * @return string
+ */
+ public function getGroup() {
+ return 'site';
+ }
}
'class' => 'mw-ui-flush-right mw-secure',
], $this->msg( 'userlogin-signwithsecure' )->text() );
}
+ $usernameHelpLink = '';
+ if ( !$this->msg( 'createacct-helpusername' )->isDisabled() ) {
+ $usernameHelpLink = Html::rawElement( 'span', [
+ 'class' => 'mw-ui-flush-right',
+ ], $this->msg( 'createacct-helpusername' )->parse() );
+ }
if ( $this->isSignup() ) {
$fieldDefinitions = [
'weight' => -105,
],
'username' => [
- 'label-message' => 'userlogin-yourname',
- // FIXME help-message does not match old formatting
- 'help-message' => 'createacct-helpusername',
+ 'label-raw' => $this->msg( 'userlogin-yourname' )->escaped() . $usernameHelpLink,
'id' => 'wpName2',
'placeholder-message' => $isLoggedIn ? 'createacct-another-username-ph'
: 'userlogin-yourname-ph',
* @return array Containing the namespace URI and prefix
*/
private static function splitXmlNamespace( $element ) {
- // 'http://www.w3.org/2000/svg:script' -> array( 'http://www.w3.org/2000/svg', 'script' )
+ // 'http://www.w3.org/2000/svg:script' -> [ 'http://www.w3.org/2000/svg', 'script' ]
$parts = explode( ':', strtolower( $element ) );
$name = array_pop( $parts );
$ns = implode( ':', $parts );
--- /dev/null
+-- Make reorderings of UNIQUE indices non-UNIQUE
+-- Since 1.24, these indices have been non-UNIQUE in tables.sql.
+-- However, an earlier update from 1.15 that made the indices
+-- UNIQUE was not removed until 1.28 (T78513).
+
+DROP INDEX /*i*/pl_namespace ON /*_*/pagelinks;
+CREATE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace, pl_title, pl_from);
+DROP INDEX /*i*/tl_namespace ON /*_*/templatelinks;
+CREATE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace, tl_title, tl_from);
+DROP INDEX /*i*/il_to ON /*_*/imagelinks;
+CREATE INDEX /*i*/il_to ON /*_*/imagelinks (il_to, il_from);
+++ /dev/null
---
--- patch-pl-tl-il-unique-index.sql
---
--- Make reorderings of UNIQUE indices UNIQUE as well
-
-DROP INDEX /*i*/pl_namespace ON /*_*/pagelinks;
-CREATE UNIQUE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace, pl_title, pl_from);
-DROP INDEX /*i*/tl_namespace ON /*_*/templatelinks;
-CREATE UNIQUE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace, tl_title, tl_from);
-DROP INDEX /*i*/il_to ON /*_*/imagelinks;
-CREATE UNIQUE INDEX /*i*/il_to ON /*_*/imagelinks (il_to, il_from);
$this->error( "Error: SQLite support not found\n" );
}
$files = [ $this->getOption( 'check-syntax' ) ];
- $files += $this->mArgs;
+ $files = array_merge( $files, $this->mArgs );
$result = Sqlite::checkSqlSyntax( $files );
if ( $result === true ) {
$this->output( "SQL syntax check: no errors detected.\n" );
'group' => 'jquery.ui',
],
'jquery.ui.position' => [
+ 'deprecated' => true,
'scripts' => 'resources/lib/jquery.ui/jquery.ui.position.js',
'group' => 'jquery.ui',
],
if ( $this->regex != '' && !preg_match( '/' . $this->regex . '/', $desc ) ) {
$this->assertTrue( true ); // XXX: don't flood output with "test made no assertions"
// $this->markTestSkipped( 'Filtered out by the user' );
+ $this->teardownGlobals();
return;
}
// parser tests frequently assume that the main namespace contains wikitext.
// @todo When setting up pages, force the content model. Only skip if
// $wgtContentModelUseDB is false.
+ $this->teardownGlobals();
$this->markTestSkipped( "Main namespace does not support wikitext,"
. "skipping parser test: $desc" );
}
global $wgTexvc;
if ( !isset( $wgTexvc ) ) {
+ $this->teardownGlobals();
$this->markTestSkipped( "SKIPPED: \$wgTexvc is not set" );
} elseif ( !is_executable( $wgTexvc ) ) {
+ $this->teardownGlobals();
$this->markTestSkipped( "SKIPPED: texvc binary does not exist"
. " or is not executable.\n"
. "Current configuration is:\n\$wgTexvc = '$wgTexvc'" );
if ( isset( $opts['djvu'] ) ) {
if ( !$this->djVuSupport->isEnabled() ) {
+ $this->teardownGlobals();
$this->markTestSkipped( "SKIPPED: djvu binaries do not exist or are not executable.\n" );
}
}
if ( isset( $opts['tidy'] ) ) {
if ( !$this->tidySupport->isEnabled() ) {
+ $this->teardownGlobals();
$this->markTestSkipped( "SKIPPED: tidy extension is not installed.\n" );
} else {
$options->setTidy( true );
<?php
+/**
+ * @covers Preprocessor
+ *
+ * @covers Preprocessor_DOM
+ * @covers PPDStack
+ * @covers PPDStackElement
+ * @covers PPDPart
+ * @covers PPFrame_DOM
+ * @covers PPTemplateFrame_DOM
+ * @covers PPCustomFrame_DOM
+ * @covers PPNode_DOM
+ *
+ * @covers Preprocessor_Hash
+ * @covers PPDStack_Hash
+ * @covers PPDStackElement_Hash
+ * @covers PPDPart_Hash
+ * @covers PPFrame_Hash
+ * @covers PPTemplateFrame_Hash
+ * @covers PPCustomFrame_Hash
+ * @covers PPNode_Hash_Tree
+ * @covers PPNode_Hash_Text
+ * @covers PPNode_Hash_Array
+ * @covers PPNode_Hash_Attr
+ */
class PreprocessorTest extends MediaWikiTestCase {
protected $mTitle = 'Page title';
protected $mPPNodeCount = 0;
/**
* @dataProvider provideCases
- * @covers Preprocessor_DOM::preprocessToXml
*/
public function testPreprocessorOutput( $className, $wikiText, $expectedXml ) {
$this->assertEquals( $this->normalizeXml( $expectedXml ),
/**
* @dataProvider provideFiles
- * @covers Preprocessor_DOM::preprocessToXml
*/
public function testPreprocessorOutputFiles( $className, $filename ) {
$folder = __DIR__ . "/../../../parser/preprocess";
/**
* @dataProvider provideHeadings
- * @covers Preprocessor_DOM::preprocessToXml
*/
public function testHeadings( $className, $wikiText, $expectedXml ) {
$this->assertEquals( $this->normalizeXml( $expectedXml ),