* (T188472) The 'comma' value for $wgArticleCountMethod is no longer supported for
performance reasons, and installations with this setting will now work as if it
was configured with 'any'.
+* (T185753) MediaWiki now defaults to using RemexHtml to tidy up user input, rather than
+ being off by default. If you wish to disable HTML tidying entirely, set $wgTidyConfig
+ to null; if you wish to use the old, deprecated Tidy external binary, both
+ set $wgTidyConfig to null and also set $wgUseTidy to true.
* $wgLogAutopatrol now defaults to false instead of true.
* $wgValidateAllHtml was removed and will be ignored.
/**
* Configuration for HTML postprocessing tool. Set this to a configuration
- * array to enable an external tool. Dave Raggett's "HTML Tidy" is typically
- * used. See https://www.w3.org/People/Raggett/tidy/
+ * array to enable an external tool. By default, we now use the RemexHtml
+ * library; historically, Dave Raggett's "HTML Tidy" was typically used.
+ * See https://www.w3.org/People/Raggett/tidy/
*
* If this is null and $wgUseTidy is true, the deprecated configuration
* parameters will be used instead.
* - tidyBin: For RaggettExternal, the path to the tidy binary.
* - tidyCommandLine: For RaggettExternal, additional command line options.
*/
-$wgTidyConfig = null;
+$wgTidyConfig = [ 'driver' => 'RemexHtml' ];
/**
* Set this to true to use the deprecated tidy configuration parameters.
. "{$otherInfo['minSupported']}, you are using {$phpInfo['implementation']} "
. "{$phpInfo['version']}.";
- $longText = "Error: You might be using an older {$phpInfo['implementation']} version. \n"
+ $longText = "Error: You might be using an older {$phpInfo['implementation']} version "
+ . "({$phpInfo['implementation']} {$phpInfo['version']}). \n"
. "MediaWiki $this->mwVersion needs {$phpInfo['implementation']}"
. " $minimumVersion or higher or {$otherInfo['implementation']} version "
. "{$otherInfo['minSupported']}.\n\nCheck if you have a"
*/
protected $mCaption = false;
+ /**
+ * Length to truncate filename to in caption when using "showfilename".
+ * A value of 'true' will truncate the filename to one line using CSS
+ * and will be the behaviour after deprecation.
+ *
+ * @var bool|int
+ */
+ protected $mCaptionLength = true;
+
/**
* @var bool Hide blacklisted images?
*/
Linker::linkKnown(
$nt,
htmlspecialchars(
- $this->mCaptionLength !== true ?
- $lang->truncate( $nt->getText(), $this->mCaptionLength ) :
+ is_int( $this->getCaptionLength() ) ?
+ $lang->truncate( $nt->getText(), $this->getCaptionLength() ) :
$nt->getText()
),
[
'class' => 'galleryfilename' .
- ( $this->mCaptionLength === true ? ' galleryfilename-truncate' : '' )
+ ( $this->getCaptionLength() === true ? ' galleryfilename-truncate' : '' )
]
) . "\n" :
'';
$galleryText = $textlink . $text . $meta;
$galleryText = $this->wrapGalleryText( $galleryText, $thumb );
+ $gbWidth = $this->getGBWidth( $thumb ) . 'px';
+ if ( $this->getGBWidthOverwrite( $thumb ) ) {
+ $gbWidth = $this->getGBWidthOverwrite( $thumb );
+ }
# Weird double wrapping (the extra div inside the li) needed due to FF2 bug
# Can be safely removed if FF2 falls completely out of existence
$output .= "\n\t\t" . '<li class="gallerybox" style="width: '
- . $this->getGBWidth( $thumb ) . 'px">'
- . '<div style="width: ' . $this->getGBWidth( $thumb ) . 'px">'
+ . $gbWidth . '">'
+ . '<div style="width: ' . $gbWidth . '">'
. $thumbhtml
. $galleryText
. "\n\t\t</div></li>";
return 8;
}
+ /**
+ * Length to truncate filename to in caption when using "showfilename" (if int).
+ * A value of 'true' will truncate the filename to one line using CSS, while
+ * 'false' will disable truncating.
+ *
+ * @return int|bool
+ */
+ protected function getCaptionLength() {
+ return $this->mCaptionLength;
+ }
+
/**
* Get total padding.
*
}
/**
- * Width of gallerybox <li>.
+ * Computed width of gallerybox <li>.
*
* Generally is the width of the image, plus padding on image
* plus padding on gallerybox.
return $this->mWidths + $this->getThumbPadding() + $this->getGBPadding();
}
+ /**
+ * Allows overwriting the computed width of the gallerybox <li> with a string,
+ * like '100%'.
+ *
+ * Generally is the width of the image, plus padding on image
+ * plus padding on gallerybox.
+ *
+ * @note Important: parameter will be false if no thumb used.
+ * @param MediaTransformOutput|bool $thumb MediaTransformObject object or false.
+ * @return bool|string Ignored if false.
+ */
+ protected function getGBWidthOverwrite( $thumb ) {
+ return false;
+ }
+
/**
* Get a list of modules to include in the page.
*
/**
* Serialize a string (escape and quote) for use as a CSS string value.
- * http://www.w3.org/TR/2013/WD-cssom-20131205/#serialize-a-string
+ * https://www.w3.org/TR/2016/WD-cssom-1-20160317/#serialize-a-string
*
* @param string $value
* @return string
- * @throws Exception
*/
public static function serializeStringValue( $value ) {
- if ( strstr( $value, "\0" ) ) {
- throw new Exception( "Invalid character in CSS string" );
- }
- $value = strtr( $value, [ '\\' => '\\\\', '"' => '\\"' ] );
- $value = preg_replace_callback( '/[\x01-\x1f\x7f-\x9f]/', function ( $match ) {
+ $value = strtr( $value, [ "\0" => "\\fffd ", '\\' => '\\\\', '"' => '\\"' ] );
+ $value = preg_replace_callback( '/[\x01-\x1f\x7f]/', function ( $match ) {
return '\\' . base_convert( ord( $match[0] ), 10, 16 ) . ' ';
}, $value );
return '"' . $value . '"';
protected function doInitConnection() {
if ( $this->dbPath !== null ) {
// Standalone .sqlite file mode.
- $this->openFile( $this->dbPath );
+ $this->openFile( $this->dbPath, $this->connectionParams['dbname'] );
} elseif ( $this->dbDir !== null ) {
// Stock wiki mode using standard file names per DB
if ( strlen( $this->connectionParams['dbname'] ) ) {
$this->conn = false;
throw new DBConnectionError( $this, "SQLite database not accessible" );
}
- $this->openFile( $fileName );
-
- if ( $this->conn ) {
- $this->dbName = $dbName;
- }
+ $this->openFile( $fileName, $dbName );
return (bool)$this->conn;
}
* Opens a database file
*
* @param string $fileName
+ * @param string $dbName
* @throws DBConnectionError
* @return PDO|bool SQL connection or false if failed
*/
- protected function openFile( $fileName ) {
+ protected function openFile( $fileName, $dbName ) {
$err = false;
$this->dbPath = $fileName;
$this->opened = is_object( $this->conn );
if ( $this->opened ) {
+ $this->dbName = $dbName;
# Set error codes only, don't raise exceptions
$this->conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
# Enforce LIKE to be case sensitive, just like MySQL
}
}
+ public function resetSequenceForTable( $table, $fname = __METHOD__ ) {
+ $encTable = $this->addIdentifierQuotes( 'sqlite_sequence' );
+ $encName = $this->addQuotes( $this->tableName( $table, 'raw' ) );
+ $this->query( "DELETE FROM $encTable WHERE name = $encName", $fname );
+ }
+
+ public function databasesAreIndependent() {
+ return true;
+ }
+
/**
* @return string
*/
* call ILoadBalancer::reuseConnection() on the handle when finished using it.
* In all other cases, this is not necessary, though not harmful either.
*
- * @param int $i Server index or DB_MASTER/DB_REPLICA
+ * @param int $i Server index (overrides $groups) or DB_MASTER/DB_REPLICA
* @param array|string|bool $groups Query group(s), or false for the generic reader
* @param string|bool $domain Domain ID, or false for the current domain
* @param int $flags Bitfield of CONN_* class constants
if ( $i == self::DB_MASTER ) {
$i = $this->getWriterIndex();
- } else {
+ } elseif ( $i == self::DB_REPLICA ) {
# Try to find an available server in any the query groups (in order)
foreach ( $groups as $group ) {
$groupIndex = $this->getReaderIndex( $group, $domain );
'LessFileCompilationTest' => "$testDir/phpunit/LessFileCompilationTest.php",
'MediaWikiCoversValidator' => "$testDir/phpunit/MediaWikiCoversValidator.php",
'PHPUnit4And6Compat' => "$testDir/phpunit/PHPUnit4And6Compat.php",
+ 'HamcrestPHPUnitIntegration' => "$testDir/phpunit/HamcrestPHPUnitIntegration.php",
# tests/phpunit/includes
'RevisionDbTestBase' => "$testDir/phpunit/includes/RevisionDbTestBase.php",
--- /dev/null
+<?php
+/**
+ * Copyright (C) 2018 Kunal Mehta <legoktm@member.fsf.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+/**
+ * @since 1.31
+ */
+trait HamcrestPHPUnitIntegration {
+
+ /**
+ * Wrapper around Hamcrest's assertThat, which marks the assertion
+ * for PHPUnit so the test is not marked as risky
+ */
+ public function assertThatHamcrest( /* ... */ ) {
+ call_user_func_array( 'assertThat', func_get_args() );
+ $this->addToAssertionCount( 1 );
+ }
+}
$db->delete( $tbl, '*', __METHOD__ );
}
- if ( $db->getType() === 'postgres' ) {
+ if ( in_array( $db->getType(), [ 'postgres', 'sqlite' ], true ) ) {
// Reset the table's sequence too.
$db->resetSequenceForTable( $tbl, __METHOD__ );
}
}
public function testNiceDomains() {
- global $wgDBname, $wgDBtype;
-
- if ( $wgDBtype === 'sqlite' ) {
- $tmpDir = $this->getNewTempDirectory();
- $dbPath = "$tmpDir/unit_test_db.sqlite";
- file_put_contents( $dbPath, '' );
- $tempFsFile = new TempFSFile( $dbPath );
- $tempFsFile->autocollect();
- } else {
- $dbPath = null;
+ global $wgDBname;
+
+ if ( wfGetDB( DB_MASTER )->databasesAreIndependent() ) {
+ self::markTestSkipped( "Skipping tests about selecting DBs: not applicable" );
+ return;
}
$factory = $this->newLBFactoryMulti(
[],
- [ 'dbFilePath' => $dbPath ]
+ []
);
$lb = $factory->getMainLB();
- if ( $wgDBtype !== 'sqlite' ) {
- $db = $lb->getConnectionRef( DB_MASTER );
- $this->assertEquals(
- wfWikiID(),
- $db->getDomainID()
- );
- unset( $db );
- }
+ $db = $lb->getConnectionRef( DB_MASTER );
+ $this->assertEquals(
+ wfWikiID(),
+ $db->getDomainID()
+ );
+ unset( $db );
/** @var Database $db */
$db = $lb->getConnection( DB_MASTER, [], '' );
$db = $lb->getConnection( DB_MASTER ); // local domain connection
$factory->setDomainPrefix( 'my_' );
+ $this->assertEquals( $wgDBname, $db->getDBname() );
$this->assertEquals(
"$wgDBname-my_",
$db->getDomainID()
}
public function testTrickyDomain() {
- global $wgDBtype, $wgDBname;
-
- if ( $wgDBtype === 'sqlite' ) {
- $tmpDir = $this->getNewTempDirectory();
- $dbPath = "$tmpDir/unit_test_db.sqlite";
- file_put_contents( $dbPath, '' );
- $tempFsFile = new TempFSFile( $dbPath );
- $tempFsFile->autocollect();
- } else {
- $dbPath = null;
+ global $wgDBname;
+
+ if ( wfGetDB( DB_MASTER )->databasesAreIndependent() ) {
+ self::markTestSkipped( "Skipping tests about selecting DBs: not applicable" );
+ return;
}
$dbname = 'unittest-domain'; // explodes if DB is selected
$factory = $this->newLBFactoryMulti(
[ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ],
[
- 'dbFilePath' => $dbPath,
'dbName' => 'do_not_select_me' // explodes if DB is selected
]
);
"Correct full table name"
);
- if ( $db->databasesAreIndependent() ) {
+ $lb->reuseConnection( $db ); // don't care
+
+ $factory->closeAll();
+ $factory->destroy();
+ }
+
+ public function testInvalidSelectDB() {
+ $dbname = 'unittest-domain'; // explodes if DB is selected
+ $factory = $this->newLBFactoryMulti(
+ [ 'localDomain' => ( new DatabaseDomain( $dbname, null, '' ) )->getId() ],
+ [
+ 'dbName' => 'do_not_select_me' // explodes if DB is selected
+ ]
+ );
+ $lb = $factory->getMainLB();
+ /** @var Database $db */
+ $db = $lb->getConnection( DB_MASTER, [], '' );
+
+ if ( $db->getType() === 'sqlite' ) {
+ $this->assertFalse( $db->selectDB( 'garbage-db' ) );
+ } elseif ( $db->databasesAreIndependent() ) {
try {
$e = null;
$db->selectDB( 'garbage-db' );
$this->assertFalse( $db->selectDB( 'garbage-db' ) );
\Wikimedia\restoreWarnings();
}
-
- $lb->reuseConnection( $db ); // don't care
-
- $factory->closeAll();
- $factory->destroy();
}
private function quoteTable( Database $db, $table ) {
$this->assertTrue( $dbr->getLBInfo( 'master' ), 'DB_REPLICA also gets the master' );
$this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on replica" );
- $dbwAuto = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
- $this->assertFalse(
- $dbwAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
- $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on master" );
- $this->assertNotEquals( $dbw, $dbwAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
+ if ( !$lb->getServerAttributes( $lb->getWriterIndex() )[$dbw::ATTR_DB_LEVEL_LOCKING] ) {
+ $dbwAuto = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
+ $this->assertFalse(
+ $dbwAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
+ $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on master" );
+ $this->assertNotEquals(
+ $dbw, $dbwAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
- $dbrAuto = $lb->getConnection( DB_REPLICA, [], false, $lb::CONN_TRX_AUTOCOMMIT );
- $this->assertFalse(
- $dbrAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
- $this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on replica" );
- $this->assertNotEquals( $dbr, $dbrAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
+ $dbrAuto = $lb->getConnection( DB_REPLICA, [], false, $lb::CONN_TRX_AUTOCOMMIT );
+ $this->assertFalse(
+ $dbrAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
+ $this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on replica" );
+ $this->assertNotEquals(
+ $dbr, $dbrAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
- $dbwAuto2 = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
- $this->assertEquals( $dbwAuto2, $dbwAuto, "CONN_TRX_AUTOCOMMIT reuses connections" );
+ $dbwAuto2 = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
+ $this->assertEquals( $dbwAuto2, $dbwAuto, "CONN_TRX_AUTOCOMMIT reuses connections" );
+ }
$lb->closeAll();
}
$this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX set on replica" );
$this->assertWriteForbidden( $dbr );
- $dbwAuto = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
- $this->assertFalse(
- $dbwAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
- $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on master" );
- $this->assertNotEquals( $dbw, $dbwAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
+ if ( !$lb->getServerAttributes( $lb->getWriterIndex() )[$dbw::ATTR_DB_LEVEL_LOCKING] ) {
+ $dbwAuto = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
+ $this->assertFalse(
+ $dbwAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
+ $this->assertTrue( $dbw->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on master" );
+ $this->assertNotEquals(
+ $dbw, $dbwAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
- $dbrAuto = $lb->getConnection( DB_REPLICA, [], false, $lb::CONN_TRX_AUTOCOMMIT );
- $this->assertFalse(
- $dbrAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
- $this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on replica" );
- $this->assertNotEquals( $dbr, $dbrAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
+ $dbrAuto = $lb->getConnection( DB_REPLICA, [], false, $lb::CONN_TRX_AUTOCOMMIT );
+ $this->assertFalse(
+ $dbrAuto->getFlag( $dbw::DBO_TRX ), "No DBO_TRX with CONN_TRX_AUTOCOMMIT" );
+ $this->assertTrue( $dbr->getFlag( $dbw::DBO_TRX ), "DBO_TRX still set on replica" );
+ $this->assertNotEquals(
+ $dbr, $dbrAuto, "CONN_TRX_AUTOCOMMIT uses separate connection" );
- $dbwAuto2 = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
- $this->assertEquals( $dbwAuto2, $dbwAuto, "CONN_TRX_AUTOCOMMIT reuses connections" );
+ $dbwAuto2 = $lb->getConnection( DB_MASTER, [], false, $lb::CONN_TRX_AUTOCOMMIT );
+ $this->assertEquals( $dbwAuto2, $dbwAuto, "CONN_TRX_AUTOCOMMIT reuses connections" );
+ }
$lb->closeAll();
}
] );
}
+ /**
+ * @dataProvider serializeStringValueProvider
+ * @covers CSSMin::serializeStringValue
+ */
+ public function testSerializeStringValue( $input, $expected ) {
+ $output = CSSMin::serializeStringValue( $input );
+ $this->assertEquals(
+ $expected,
+ $output,
+ 'Serialized output must be in the expected form.'
+ );
+ }
+
+ public function serializeStringValueProvider() {
+ return [
+ [ 'Hello World!', '"Hello World!"' ],
+ [ "Null\0Null", "\"Null\\fffd Null\"" ],
+ [ '"', '"\\""' ],
+ [ "'", '"\'"' ],
+ [ "\\", '"\\\\"' ],
+ [ "Tab\tTab", '"Tab\\9 Tab"' ],
+ [ "Space tab \t space", '"Space tab \\9 space"' ],
+ [ "Line\nfeed", '"Line\\a feed"' ],
+ [ "Return\rreturn", '"Return\\d return"' ],
+ [ "Next\xc2\x85line", "\"Next\xc2\x85line\"" ],
+ [ "Del\x7fDel", '"Del\\7f Del"' ],
+ [ "nb\xc2\xa0sp", "\"nb\xc2\xa0sp\"" ],
+ [ "AMP&AMP", "\"AMP&AMP\"" ],
+ [ '!"#$%&\'()*+,-./0123456789:;<=>?', '"!\\"#$%&\'()*+,-./0123456789:;<=>?"' ],
+ [ '@[\\]^_`{|}~', '"@[\\\\]^_`{|}~"' ],
+ [ 'ä', '"ä"' ],
+ [ 'Ä', '"Ä"' ],
+ [ '€', '"€"' ],
+ [ '𝒞', '"𝒞"' ], // U+1D49E 'MATHEMATICAL SCRIPT CAPITAL C'
+ ];
+ }
+
/**
* @dataProvider mimeTypeProvider
* @covers CSSMin::getMimeType
'<editsection> should survive tidy'
],
[ '<mw:toc>foo</mw:toc>', '<mw:toc>foo</mw:toc>', '<mw:toc> should survive tidy' ],
- [ "<link foo=\"bar\" />\nfoo", '<link foo="bar"/>foo', '<link> should survive tidy' ],
- [ "<meta foo=\"bar\" />\nfoo", '<meta foo="bar"/>foo', '<meta> should survive tidy' ],
+ [ "<link foo=\"bar\" />foo", '<link foo="bar"/>foo', '<link> should survive tidy' ],
+ [ "<meta foo=\"bar\" />foo", '<meta foo="bar"/>foo', '<meta> should survive tidy' ],
[ $testMathML, $testMathML, '<math> should survive tidy' ],
];
}
'use strict';
const password = 'vagrant',
+ fs = require( 'fs' ),
path = require( 'path' ),
username = 'Admin';
chromeOptions: {
// Run headless when there is no DISPLAY
// --headless: since Chrome 59 https://chromium.googlesource.com/chromium/src/+/59.0.3030.0/headless/README.md
- args: process.env.DISPLAY ? [] : [ '--headless' ]
+ args: (
+ process.env.DISPLAY ? [] : [ '--headless' ]
+ ).concat(
+ // Disable Chrome sandbox when running in Docker
+ fs.existsSync( '/.dockerenv' ) ? [ '--no-sandbox' ] : []
+ )
}
} ],
//