Merge "registration: Fix merging of $wgExtensionCredits"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 16 Mar 2015 17:37:51 +0000 (17:37 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 16 Mar 2015 17:37:51 +0000 (17:37 +0000)
13 files changed:
autoload.php
includes/api/ApiParse.php
includes/api/i18n/en.json
includes/api/i18n/qqq.json
includes/db/DatabaseSqlite.php
includes/htmlform/HTMLRadioField.php
includes/installer/SqliteInstaller.php
includes/specialpage/SpecialPage.php
includes/specials/SpecialUploadStash.php
maintenance/sqlite.inc
maintenance/sqlite.php
maintenance/update.php
tests/phpunit/includes/db/DatabaseSqliteTest.php

index 3e003f1..2b7614d 100644 (file)
@@ -290,7 +290,6 @@ $wgAutoloadLocalClasses = array(
        'DatabaseOracle' => __DIR__ . '/includes/db/DatabaseOracle.php',
        'DatabasePostgres' => __DIR__ . '/includes/db/DatabasePostgres.php',
        'DatabaseSqlite' => __DIR__ . '/includes/db/DatabaseSqlite.php',
-       'DatabaseSqliteStandalone' => __DIR__ . '/includes/db/DatabaseSqlite.php',
        'DatabaseUpdater' => __DIR__ . '/includes/installer/DatabaseUpdater.php',
        'DateFormats' => __DIR__ . '/maintenance/language/date-formats.php',
        'DateFormatter' => __DIR__ . '/includes/parser/DateFormatter.php',
index 83e105b..20592ca 100644 (file)
@@ -70,6 +70,9 @@ class ApiParse extends ApiBase {
 
                if ( isset( $params['section'] ) ) {
                        $this->section = $params['section'];
+                       if ( !preg_match( '/^((T-)?\d+|new)$/', $this->section ) ) {
+                               $this->dieUsage( "The section parameter must be a valid section id or 'new'", "invalidsection" );
+                       }
                } else {
                        $this->section = false;
                }
@@ -203,7 +206,14 @@ class ApiParse extends ApiBase {
                        }
 
                        if ( $this->section !== false ) {
-                               $this->content = $this->getSectionContent( $this->content, $titleObj->getPrefixedText() );
+                               if ( $this->section === 'new' ) {
+                                       // Insert the section title above the content.
+                                       if ( !is_null( $params['sectiontitle'] ) && $params['sectiontitle'] !== '' ) {
+                                               $this->content = $this->content->addSectionHeader( $params['sectiontitle'] );
+                                       }
+                               } else {
+                                       $this->content = $this->getSectionContent( $this->content, $titleObj->getPrefixedText() );
+                               }
                        }
 
                        if ( $params['pst'] || $params['onlypst'] ) {
@@ -706,6 +716,9 @@ class ApiParse extends ApiBase {
                        'onlypst' => false,
                        'effectivelanglinks' => false,
                        'section' => null,
+                       'sectiontitle' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                       ),
                        'disablepp' => false,
                        'disableeditsection' => false,
                        'generatexml' => array(
index 4237ff8..1c6af66 100644 (file)
        "apihelp-parse-param-pst": "Do a pre-save transform on the input before parsing it. Only valid when used with text.",
        "apihelp-parse-param-onlypst": "Do a pre-save transform (PST) on the input, but don't parse it. Returns the same wikitext, after a PST has been applied. Only valid when used with <var>$1text</var>.",
        "apihelp-parse-param-effectivelanglinks": "Includes language links supplied by extensions (for use with <kbd>$1prop=langlinks</kbd>).",
-       "apihelp-parse-param-section": "Only retrieve the content of this section number.",
+       "apihelp-parse-param-section": "Only retrieve the content of this section number or when <kbd>new</kbd> generate a new section.\n\n<kbd>new</kbd> section is only honored when specifying <var>text</var>.",
+       "apihelp-parse-param-sectiontitle": "New section title when <var>section</var> is <kbd>new</kbd>.\n\nUnlike page editing, this does not fall back to <var>summary</var> when omitted or empty.",
        "apihelp-parse-param-disablepp": "Disable the PP Report from the parser output.",
        "apihelp-parse-param-disableeditsection": "Disable edit section links from the parser output.",
        "apihelp-parse-param-generatexml": "Generate XML parse tree (requires content model <code>$1</code>).",
index 3f5084d..3ecb7a5 100644 (file)
        "apihelp-parse-param-onlypst": "{{doc-apihelp-param|parse|onlypst}}",
        "apihelp-parse-param-effectivelanglinks": "{{doc-apihelp-param|parse|effectivelanglinks}}",
        "apihelp-parse-param-section": "{{doc-apihelp-param|parse|section}}",
+       "apihelp-parse-param-sectiontitle": "{{doc-apihelp-param|parse|sectiontitle}}",
        "apihelp-parse-param-disablepp": "{{doc-apihelp-param|parse|disablepp}}",
        "apihelp-parse-param-disableeditsection": "{{doc-apihelp-param|parse|disableeditsection}}",
        "apihelp-parse-param-generatexml": "{{doc-apihelp-param|parse|generatexml|params=* $1 - Value of the constant CONTENT_MODEL_WIKITEXT|paramstart=2}}",
index 0b51972..ed86bab 100644 (file)
@@ -29,8 +29,11 @@ class DatabaseSqlite extends DatabaseBase {
        /** @var bool Whether full text is enabled */
        private static $fulltextEnabled = null;
 
+       /** @var string Directory */
+       protected $dbDir;
+
        /** @var string File name for SQLite database file */
-       public $mDatabaseFile;
+       protected $dbPath;
 
        /** @var string Transaction mode */
        protected $trxMode;
@@ -49,30 +52,61 @@ class DatabaseSqlite extends DatabaseBase {
 
        /**
         * Additional params include:
-        *   - trxMode : one of (deferred, immediate, exclusive)
+        *   - dbDirectory : directory containing the DB and the lock file directory
+        *                   [defaults to $wgSQLiteDataDir]
+        *   - dbFilePath  : use this to force the path of the DB file
+        *   - trxMode     : one of (deferred, immediate, exclusive)
         * @param array $p
         */
        function __construct( array $p ) {
                global $wgSharedDB, $wgSQLiteDataDir;
 
-               $this->mDBname = $p['dbname'];
-               parent::__construct( $p );
-               // parent doesn't open when $user is false, but we can work with $dbName
-               if ( $p['dbname'] && !$this->isOpen() ) {
-                       if ( $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] ) ) {
-                               if ( $wgSharedDB ) {
-                                       $this->attachDatabase( $wgSharedDB );
+               $this->dbDir = isset( $p['dbDirectory'] ) ? $p['dbDirectory'] : $wgSQLiteDataDir;
+
+               if ( isset( $p['dbFilePath'] ) ) {
+                       $this->mFlags = isset( $p['flags'] ) ? $p['flags'] : 0;
+                       // Standalone .sqlite file mode
+                       $this->openFile( $p['dbFilePath'] );
+                       // @FIXME: clean up base constructor so this can call super instead
+                       $this->mTrxAtomicLevels = new SplStack;
+               } else {
+                       $this->mDBname = $p['dbname'];
+                       // Stock wiki mode using standard file names per DB
+                       parent::__construct( $p );
+                       // parent doesn't open when $user is false, but we can work with $dbName
+                       if ( $p['dbname'] && !$this->isOpen() ) {
+                               if ( $this->open( $p['host'], $p['user'], $p['password'], $p['dbname'] ) ) {
+                                       if ( $wgSharedDB ) {
+                                               $this->attachDatabase( $wgSharedDB );
+                                       }
                                }
                        }
                }
 
                $this->trxMode = isset( $p['trxMode'] ) ? strtoupper( $p['trxMode'] ) : null;
-               if ( $this->trxMode && !in_array( $this->trxMode, array( 'IMMEDIATE', 'EXCLUSIVE' ) ) ) {
+               if ( $this->trxMode &&
+                       !in_array( $this->trxMode, array( 'DEFERRED', 'IMMEDIATE', 'EXCLUSIVE' ) )
+               ) {
                        $this->trxMode = null;
                        wfWarn( "Invalid SQLite transaction mode provided." );
                }
 
-               $this->lockMgr = new FSLockManager( array( 'lockDirectory' => "$wgSQLiteDataDir/locks" ) );
+               $this->lockMgr = new FSLockManager( array( 'lockDirectory' => "{$this->dbDir}/locks" ) );
+       }
+
+       /**
+        * @param string $filename
+        * @param array $p Options map; supports:
+        *   - flags       : (same as __construct counterpart)
+        *   - trxMode     : (same as __construct counterpart)
+        *   - dbDirectory : (same as __construct counterpart)
+        * @return DatabaseSqlite
+        * @since 1.25
+        */
+       public static function newStandaloneInstance( $filename, array $p = array() ) {
+               $p['dbFilePath'] = $filename;
+
+               return new self( $p );
        }
 
        /**
@@ -103,10 +137,8 @@ class DatabaseSqlite extends DatabaseBase {
         * @return PDO
         */
        function open( $server, $user, $pass, $dbName ) {
-               global $wgSQLiteDataDir;
-
                $this->close();
-               $fileName = self::generateFileName( $wgSQLiteDataDir, $dbName );
+               $fileName = self::generateFileName( $this->dbDir, $dbName );
                if ( !is_readable( $fileName ) ) {
                        $this->mConn = false;
                        throw new DBConnectionError( $this, "SQLite database not accessible" );
@@ -123,10 +155,10 @@ class DatabaseSqlite extends DatabaseBase {
         * @throws DBConnectionError
         * @return PDO|bool SQL connection or false if failed
         */
-       function openFile( $fileName ) {
+       protected function openFile( $fileName ) {
                $err = false;
 
-               $this->mDatabaseFile = $fileName;
+               $this->dbPath = $fileName;
                try {
                        if ( $this->mFlags & DBO_PERSISTENT ) {
                                $this->mConn = new PDO( "sqlite:$fileName", '', '',
@@ -156,6 +188,14 @@ class DatabaseSqlite extends DatabaseBase {
                return false;
        }
 
+       /**
+        * @return string SQLite DB file path
+        * @since 1.25
+        */
+       public function getDbFilePath() {
+               return $this->dbPath;
+       }
+
        /**
         * Does not actually close the connection, just destroys the reference for GC to do its work
         * @return bool
@@ -206,8 +246,7 @@ class DatabaseSqlite extends DatabaseBase {
                $cachedResult = false;
                $table = 'dummy_search_test';
 
-               $db = new DatabaseSqliteStandalone( ':memory:' );
-
+               $db = self::newStandaloneInstance( ':memory:' );
                if ( $db->query( "CREATE VIRTUAL TABLE $table USING FTS3(dummy_field)", __METHOD__, true ) ) {
                        $cachedResult = 'FTS3';
                }
@@ -223,14 +262,13 @@ class DatabaseSqlite extends DatabaseBase {
         * @param string $name Database name to be used in queries like
         *   SELECT foo FROM dbname.table
         * @param bool|string $file Database file name. If omitted, will be generated
-        *   using $name and $wgSQLiteDataDir
+        *   using $name and configured data directory
         * @param string $fname Calling function name
         * @return ResultWrapper
         */
        function attachDatabase( $name, $file = false, $fname = __METHOD__ ) {
-               global $wgSQLiteDataDir;
                if ( !$file ) {
-                       $file = self::generateFileName( $wgSQLiteDataDir, $name );
+                       $file = self::generateFileName( $this->dbDir, $name );
                }
                $file = $this->addQuotes( $file );
 
@@ -863,11 +901,9 @@ class DatabaseSqlite extends DatabaseBase {
        }
 
        public function lock( $lockName, $method, $timeout = 5 ) {
-               global $wgSQLiteDataDir;
-
-               if ( !is_dir( "$wgSQLiteDataDir/locks" ) ) { // create dir as needed
-                       if ( !is_writable( $wgSQLiteDataDir ) || !mkdir( "$wgSQLiteDataDir/locks" ) ) {
-                               throw new DBError( "Cannot create directory \"$wgSQLiteDataDir/locks\"." );
+               if ( !is_dir( "{$this->dbDir}/locks" ) ) { // create dir as needed
+                       if ( !is_writable( $this->dbDir ) || !mkdir( "{$this->dbDir}/locks" ) ) {
+                               throw new DBError( "Cannot create directory \"{$this->dbDir}/locks\"." );
                        }
                }
 
@@ -961,23 +997,6 @@ class DatabaseSqlite extends DatabaseBase {
        }
 } // end DatabaseSqlite class
 
-/**
- * This class allows simple acccess to a SQLite database independently from main database settings
- * @ingroup Database
- */
-class DatabaseSqliteStandalone extends DatabaseSqlite {
-       public function __construct( $fileName, $flags = 0 ) {
-               global $wgSQLiteDataDir;
-
-               $this->mTrxAtomicLevels = new SplStack;
-               $this->lockMgr = new FSLockManager( array( 'lockDirectory' => "$wgSQLiteDataDir/locks" ) );
-
-               $this->mFlags = $flags;
-               $this->tablePrefix( null );
-               $this->openFile( $fileName );
-       }
-}
-
 /**
  * @ingroup Database
  */
index 8765407..0f00540 100644 (file)
@@ -56,7 +56,7 @@ class HTMLRadioField extends HTMLFormField {
 
                                $html .= ' ' . Html::rawElement(
                                        'div',
-                                       array( 'class' => 'mw-htmlform-flatlist-item' ),
+                                       array( 'class' => 'mw-htmlform-flatlist-item mw-ui-radio' ),
                                        $radio
                                );
                        }
index 1e7e969..f990ddf 100644 (file)
@@ -55,7 +55,7 @@ class SqliteInstaller extends DatabaseInstaller {
        public function checkPrerequisites() {
                $result = Status::newGood();
                // Bail out if SQLite is too old
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
                if ( version_compare( $db->getServerVersion(), self::MINIMUM_VERSION, '<' ) ) {
                        $result->fatal( 'config-outdated-sqlite', $db->getServerVersion(), self::MINIMUM_VERSION );
                }
index 31d679a..f9d1e8a 100644 (file)
@@ -356,6 +356,7 @@ class SpecialPage {
                if ( $this->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) {
                        $out->addModuleStyles( array(
                                'mediawiki.ui.input',
+                               'mediawiki.ui.radio',
                                'mediawiki.ui.checkbox',
                        ) );
                }
index 4a92bb9..87e7063 100644 (file)
@@ -250,7 +250,7 @@ class SpecialUploadStash extends UnlistedSpecialPage {
                // make a curl call to the scaler to create a thumbnail
                $httpOptions = array(
                        'method' => 'GET',
-                       'timeout' => 'default'
+                       'timeout' => 5 // T90599 attempt to time out cleanly
                );
                $req = MWHttpRequest::factory( $scalerThumbUrl, $httpOptions, __METHOD__ );
                $status = $req->execute();
index 5c0fd07..e173190 100644 (file)
@@ -59,7 +59,7 @@ class Sqlite {
                        'blob', // NULL type is omitted intentionally
                ) );
 
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
                try {
                        foreach ( $files as $file ) {
                                $err = $db->sourceFile( $file );
index edc9e14..7e02a4b 100644 (file)
@@ -81,7 +81,7 @@ class SqliteMaintenance extends Maintenance {
        }
 
        private function vacuum() {
-               $prevSize = filesize( $this->db->mDatabaseFile );
+               $prevSize = filesize( $this->db->getDbFilePath() );
                if ( $prevSize == 0 ) {
                        $this->error( "Can't vacuum an empty database.\n", true );
                }
@@ -89,7 +89,7 @@ class SqliteMaintenance extends Maintenance {
                $this->output( 'VACUUM: ' );
                if ( $this->db->query( 'VACUUM' ) ) {
                        clearstatcache();
-                       $newSize = filesize( $this->db->mDatabaseFile );
+                       $newSize = filesize( $this->db->getDbFilePath() );
                        $this->output( sprintf( "Database size was %d, now %d (%.1f%% reduction).\n",
                                $prevSize, $newSize, ( $prevSize - $newSize ) * 100.0 / $prevSize ) );
                } else {
@@ -115,7 +115,7 @@ class SqliteMaintenance extends Maintenance {
        private function backup( $fileName ) {
                $this->output( "Backing up database:\n   Locking..." );
                $this->db->query( 'BEGIN IMMEDIATE TRANSACTION', __METHOD__ );
-               $ourFile = $this->db->mDatabaseFile;
+               $ourFile = $this->db->getDbFilePath();
                $this->output( "   Copying database file $ourFile to $fileName... " );
                wfSuppressWarnings( false );
                if ( !copy( $ourFile, $fileName ) ) {
index 182a2c4..6e93011 100755 (executable)
@@ -151,7 +151,7 @@ class UpdateMediaWiki extends Maintenance {
 
                $this->output( "Going to run database updates for " . wfWikiID() . "\n" );
                if ( $db->getType() === 'sqlite' ) {
-                       $this->output( "Using SQLite file: '{$db->mDatabaseFile}'\n" );
+                       $this->output( "Using SQLite file: '{$db->getDbFilePath()}'\n" );
                }
                $this->output( "Depending on the size of your database this may take a while!\n" );
 
index d32c1a6..645baf1 100644 (file)
@@ -1,10 +1,12 @@
 <?php
 
-class MockDatabaseSqlite extends DatabaseSqliteStandalone {
+class MockDatabaseSqlite extends DatabaseSqlite {
        private $lastQuery;
 
-       function __construct() {
-               parent::__construct( ':memory:' );
+       public static function newInstance( array $p = array() ) {
+               $p['dbFilePath'] = ':memory:';
+
+               return new self( $p );
        }
 
        function query( $sql, $fname = '', $tempIgnore = false ) {
@@ -36,7 +38,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                if ( !Sqlite::isPresent() ) {
                        $this->markTestSkipped( 'No SQLite support detected' );
                }
-               $this->db = new MockDatabaseSqlite();
+               $this->db = MockDatabaseSqlite::newInstance();
                if ( version_compare( $this->db->getServerVersion(), '3.6.0', '<' ) ) {
                        $this->markTestSkipped( "SQLite at least 3.6 required, {$this->db->getServerVersion()} found" );
                }
@@ -89,7 +91,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
         */
        public function testAddQuotes( $value, $expected ) {
                // check quoting
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
                $this->assertEquals( $expected, $db->addQuotes( $value ), 'string not quoted as expected' );
 
                // ok, quoting works as expected, now try a round trip.
@@ -172,7 +174,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
         */
        public function testTableName() {
                // @todo Moar!
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
                $this->assertEquals( 'foo', $db->tableName( 'foo' ) );
                $this->assertEquals( 'sqlite_master', $db->tableName( 'sqlite_master' ) );
                $db->tablePrefix( 'foo' );
@@ -184,7 +186,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
         * @covers DatabaseSqlite::duplicateTableStructure
         */
        public function testDuplicateTableStructure() {
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
                $db->query( 'CREATE TABLE foo(foo, barfoo)' );
 
                $db->duplicateTableStructure( 'foo', 'bar' );
@@ -208,7 +210,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
         * @covers DatabaseSqlite::duplicateTableStructure
         */
        public function testDuplicateTableStructureVirtual() {
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
                if ( $db->getFulltextSearchModule() != 'FTS3' ) {
                        $this->markTestSkipped( 'FTS3 not supported, cannot create virtual tables' );
                }
@@ -231,7 +233,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
         * @covers DatabaseSqlite::deleteJoin
         */
        public function testDeleteJoin() {
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
                $db->query( 'CREATE TABLE a (a_1)', __METHOD__ );
                $db->query( 'CREATE TABLE b (b_1, b_2)', __METHOD__ );
                $db->insert( 'a', array(
@@ -289,7 +291,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                        'user_newtalk.user_last_timestamp', // r84185
                );
 
-               $currentDB = new DatabaseSqliteStandalone( ':memory:' );
+               $currentDB = DatabaseSqlite::newStandaloneInstance( ':memory:' );
                $currentDB->sourceFile( "$IP/maintenance/tables.sql" );
 
                $profileToDb = false;
@@ -357,7 +359,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
         * @covers DatabaseSqlite::insertId
         */
        public function testInsertIdType() {
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
 
                $databaseCreation = $db->query( 'CREATE TABLE a ( a_1 )', __METHOD__ );
                $this->assertInstanceOf( 'ResultWrapper', $databaseCreation, "Database creation" );
@@ -377,7 +379,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                }
 
                global $IP;
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
                $db->sourceFile( "$IP/tests/phpunit/data/db/sqlite/tables-$version.sql" );
                $updater = DatabaseUpdater::newForDB( $db, false, $maint );
                $updater->doUpdates( array( 'core' ) );
@@ -440,7 +442,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
 
        public function testCaseInsensitiveLike() {
                // TODO: Test this for all databases
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
                $res = $db->query( 'SELECT "a" LIKE "A" AS a' );
                $row = $res->fetchRow();
                $this->assertFalse( (bool)$row['a'] );
@@ -450,7 +452,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
         * @covers DatabaseSqlite::numFields
         */
        public function testNumFields() {
-               $db = new DatabaseSqliteStandalone( ':memory:' );
+               $db = DatabaseSqlite::newStandaloneInstance( ':memory:' );
 
                $databaseCreation = $db->query( 'CREATE TABLE a ( a_1 )', __METHOD__ );
                $this->assertInstanceOf( 'ResultWrapper', $databaseCreation, "Failed to create table a" );