Merge "KafkaHandler: allow customizing timeouts"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 29 Jan 2016 19:16:03 +0000 (19:16 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 29 Jan 2016 19:16:03 +0000 (19:16 +0000)
23 files changed:
docs/hooks.txt
includes/MediaWiki.php
includes/Sanitizer.php
includes/Title.php
includes/api/ApiMain.php
includes/api/ApiStashEdit.php
includes/db/loadbalancer/LBFactory.php
includes/session/SessionBackend.php
includes/session/SessionManager.php
includes/session/UserInfo.php
includes/user/User.php
languages/classes/LanguageGan.php
languages/classes/LanguageIu.php
languages/classes/LanguageKk.php
languages/classes/LanguageKu.php
languages/classes/LanguageShi.php
languages/classes/LanguageSr.php
languages/classes/LanguageTg.php
languages/classes/LanguageUz.php
languages/classes/LanguageZh.php
resources/src/mediawiki/mediawiki.js
tests/phpunit/includes/session/UserInfoTest.php
tests/qunit/suites/resources/mediawiki/mediawiki.test.js

index b516122..bc03714 100644 (file)
@@ -2347,6 +2347,11 @@ run. Use when page save hooks require the presence of custom tables to ensure
 that tests continue to run properly.
 &$tables: array of table names
 
+'ParserOutputStashForEdit': Called when an edit stash parse finishes, before the output is cached.
+$page: the WikiPage of the candidate edit
+$content: the Content object of the candidate edit
+$output: the ParserOutput result of the candidate edit
+
 'PasswordPoliciesForUser': Alter the effective password policy for a user.
 $user: User object whose policy you are modifying
 &$effectivePolicy: Array of policy statements that apply to this user
index 6342d71..8385a06 100644 (file)
@@ -551,21 +551,12 @@ class MediaWiki {
                $config = $context->getConfig();
 
                $factory = wfGetLBFactory();
-               // Check if any transaction was too big
-               $limit = $config->get( 'MaxUserDBWriteDuration' );
-               $factory->forEachLB( function ( LoadBalancer $lb ) use ( $limit ) {
-                       $lb->forEachOpenConnection( function ( IDatabase $db ) use ( $limit ) {
-                               $time = $db->pendingWriteQueryDuration();
-                               if ( $limit > 0 && $time > $limit ) {
-                                       throw new DBTransactionError(
-                                               $db,
-                                               wfMessage( 'transaction-duration-limit-exceeded', $time, $limit )->text()
-                                       );
-                               }
-                       } );
-               } );
                // Commit all changes
-               $factory->commitMasterChanges( __METHOD__ );
+               $factory->commitMasterChanges(
+                       __METHOD__,
+                       // Abort if any transaction was too big
+                       array( 'maxWriteDuration' => $config->get( 'MaxUserDBWriteDuration' ) )
+               );
                // Record ChronologyProtector positions
                $factory->shutdown();
                wfDebug( __METHOD__ . ': all transactions committed' );
index e2564cd..60c9498 100644 (file)
@@ -1241,7 +1241,7 @@ class Sanitizer {
 
        /**
         * Return an associative array of attribute names and values from
-        * a partial tag string. Attribute names are forces to lowercase,
+        * a partial tag string. Attribute names are forced to lowercase,
         * character references are decoded to UTF-8 text.
         *
         * @param string $text
index e549037..882b7dd 100644 (file)
@@ -246,7 +246,7 @@ class Title {
         * Create a new Title from text, such as what one would find in a link. De-
         * codes any HTML entities in the text.
         *
-        * @param string|null $text The link text; spaces, prefixes, and an
+        * @param string|int|null $text The link text; spaces, prefixes, and an
         *   initial ':' indicating the main namespace are accepted.
         * @param int $defaultNamespace The namespace to use if none is specified
         *   by a prefix.  If you want to force a specific namespace even if
@@ -259,7 +259,8 @@ class Title {
                if ( is_object( $text ) ) {
                        throw new InvalidArgumentException( '$text must be a string.' );
                }
-               if ( $text !== null && !is_string( $text ) ) {
+               // DWIM: Integers can be passed in here when page titles are used as array keys.
+               if ( $text !== null && !is_string( $text ) && !is_int( $text ) ) {
                        wfDebugLog( 'T76305', wfGetAllCallers( 5 ) );
                        return null;
                }
@@ -268,7 +269,7 @@ class Title {
                }
 
                try {
-                       return Title::newFromTextThrow( $text, $defaultNamespace );
+                       return Title::newFromTextThrow( strval( $text ), $defaultNamespace );
                } catch ( MalformedTitleException $ex ) {
                        return null;
                }
index 6ddc28a..458fd18 100644 (file)
@@ -1231,7 +1231,8 @@ class ApiMain extends ApiBase {
         * @param array $params An array with the request parameters
         */
        protected function setupExternalResponse( $module, $params ) {
-               if ( !$this->getRequest()->wasPosted() && $module->mustBePosted() ) {
+               $request = $this->getRequest();
+               if ( !$request->wasPosted() && $module->mustBePosted() ) {
                        // Module requires POST. GET request might still be allowed
                        // if $wgDebugApi is true, otherwise fail.
                        $this->dieUsageMsgOrDebug( array( 'mustbeposted', $this->mAction ) );
@@ -1243,6 +1244,15 @@ class ApiMain extends ApiBase {
                        // Create an appropriate printer
                        $this->mPrinter = $this->createPrinterByName( $params['format'] );
                }
+
+               if ( $request->getProtocol() === 'http' && (
+                       $request->getSession()->shouldForceHTTPS() ||
+                       ( $this->getUser()->isLoggedIn() &&
+                               $this->getUser()->requiresHTTPS() )
+               ) ) {
+                       $this->logFeatureUsage( 'https-expected' );
+                       $this->setWarning( 'HTTP used when HTTPS was expected' );
+               }
        }
 
        /**
index 8822750..00675f4 100644 (file)
@@ -141,6 +141,9 @@ class ApiStashEdit extends ApiBase {
                if ( $editInfo && $editInfo->output ) {
                        $key = self::getStashKey( $page->getTitle(), $content, $user );
 
+                       // Let extensions add ParserOutput metadata or warm other caches
+                       Hooks::run( 'ParserOutputStashForEdit', array( $page, $content, $editInfo->output ) );
+
                        list( $stashInfo, $ttl ) = self::buildStashValue(
                                $editInfo->pstContent, $editInfo->output, $editInfo->timestamp
                        );
@@ -148,6 +151,7 @@ class ApiStashEdit extends ApiBase {
                        if ( $stashInfo ) {
                                $ok = $cache->set( $key, $stashInfo, $ttl );
                                if ( $ok ) {
+
                                        $logger->debug( "Cached parser output for key '$key'." );
                                        return self::ERROR_NONE;
                                } else {
index 25fdea9..606f4f4 100644 (file)
@@ -228,9 +228,24 @@ abstract class LBFactory {
        /**
         * Commit changes on all master connections
         * @param string $fname Caller name
+        * @param array $options Options map:
+        *   - maxWriteDuration: abort if more than this much time was spent in write queries
         */
-       public function commitMasterChanges( $fname = __METHOD__ ) {
+       public function commitMasterChanges( $fname = __METHOD__, array $options = array() ) {
+               $limit = isset( $options['maxWriteDuration'] ) ? $options['maxWriteDuration'] : 0;
+
                $this->logMultiDbTransaction();
+               $this->forEachLB( function ( LoadBalancer $lb ) use ( $limit ) {
+                       $lb->forEachOpenConnection( function ( IDatabase $db ) use ( $limit ) {
+                               $time = $db->pendingWriteQueryDuration();
+                               if ( $limit > 0 && $time > $limit ) {
+                                       throw new DBTransactionError(
+                                               $db,
+                                               wfMessage( 'transaction-duration-limit-exceeded', $time, $limit )->text()
+                                       );
+                               }
+                       } );
+               } );
 
                $start = microtime( true );
                $this->forEachLBCallMethod( 'commitMasterChanges', array( $fname ) );
index 67cbc6d..f86daaa 100644 (file)
@@ -544,7 +544,7 @@ final class SessionBackend {
                // Ensure the user has a token
                // @codeCoverageIgnoreStart
                $anon = $this->user->isAnon();
-               if ( !$anon && !$this->user->getToken() ) {
+               if ( !$anon && !$this->user->getToken( false ) ) {
                        $this->logger->debug(
                                "SessionBackend $this->id creating token for user {$this->user} on save"
                        );
@@ -596,7 +596,7 @@ final class SessionBackend {
                        'provider' => (string)$this->provider,
                        'providerMetadata' => $this->providerMetadata,
                        'userId' => $anon ? 0 : $this->user->getId(),
-                       'userName' => $anon ? null : $this->user->getName(),
+                       'userName' => User::isValidUserName( $this->user->getName() ) ? $this->user->getName() : null,
                        'userToken' => $anon ? null : $this->user->getToken(),
                        'remember' => !$anon && $this->remember,
                        'forceHTTPS' => $this->forceHTTPS,
index d84a2d6..06a765c 100644 (file)
@@ -520,7 +520,7 @@ final class SessionManager implements SessionManagerInterface {
 
                // Reset the user's token to kill existing sessions
                $user = User::newFromName( $username );
-               if ( $user && $user->getToken() ) {
+               if ( $user && $user->getToken( false ) ) {
                        $user->setToken( true );
                        $user->saveSettings();
                }
index e844bb6..c01b9ec 100644 (file)
@@ -152,10 +152,10 @@ final class UserInfo {
 
        /**
         * Return the user token
-        * @return string|null
+        * @return string
         */
        public function getToken() {
-               return $this->user === null || $this->user->getId() === 0 ? null : $this->user->getToken( true );
+               return $this->user === null || $this->user->getId() === 0 ? '' : $this->user->getToken( false );
        }
 
        /**
index a268117..7c29242 100644 (file)
@@ -1261,12 +1261,20 @@ class User implements IDBAccessObject {
                        $all = false;
                }
 
-               if ( isset( $row->user_email ) ) {
-                       $this->mEmail = $row->user_email;
-                       $this->mToken = $row->user_token;
-                       if ( $this->mToken == '' ) {
+               if ( isset( $row->user_token ) ) {
+                       // The definition for the column is binary(32), so trim the NULs
+                       // that appends. The previous definition was char(32), so trim
+                       // spaces too.
+                       $this->mToken = rtrim( $row->user_token, " \0" );
+                       if ( $this->mToken === '' ) {
                                $this->mToken = null;
                        }
+               } else {
+                       $all = false;
+               }
+
+               if ( isset( $row->user_email ) ) {
+                       $this->mEmail = $row->user_email;
                        $this->mEmailAuthenticated = wfTimestampOrNull( TS_MW, $row->user_email_authenticated );
                        $this->mEmailToken = $row->user_email_token;
                        $this->mEmailTokenExpires = wfTimestampOrNull( TS_MW, $row->user_email_token_expires );
index a012f59..8c8dfe0 100644 (file)
@@ -21,9 +21,6 @@
  * @ingroup Language
  */
 
-require_once __DIR__ . '/../LanguageConverter.php';
-require_once __DIR__ . '/LanguageZh.php';
-
 /**
  * @ingroup Language
  */
index db3a22c..af0431f 100644 (file)
@@ -21,8 +21,6 @@
  * @ingroup Language
  */
 
-require_once __DIR__ . '/../LanguageConverter.php';
-
 /**
  * Conversion script between Latin and Syllabics for Inuktitut.
  * - Syllabics -> lowercase Latin
index 0f60889..0357730 100644 (file)
@@ -21,9 +21,6 @@
  * @ingroup Language
  */
 
-require_once __DIR__ . '/../LanguageConverter.php';
-require_once __DIR__ . '/LanguageKk_cyrl.php';
-
 define( 'KK_C_UC', 'АӘБВГҒДЕЁЖЗИЙКҚЛМНҢОӨПРСТУҰҮФХҺЦЧШЩЪЫІЬЭЮЯ' ); # Kazakh Cyrillic uppercase
 define( 'KK_C_LC', 'аәбвгғдеёжзийкқлмнңоөпрстуұүфхһцчшщъыіьэюя' ); # Kazakh Cyrillic lowercase
 define( 'KK_L_UC', 'AÄBCÇDEÉFGĞHIİÏJKLMNÑOÖPQRSŞTUÜVWXYÝZ' ); # Kazakh Latin uppercase
index 1fdebc2..c14f468 100644 (file)
@@ -21,9 +21,6 @@
  * @ingroup Language
  */
 
-require_once __DIR__ . '/../LanguageConverter.php';
-require_once __DIR__ . '/LanguageKu_ku.php';
-
 /**
  * Kurdish converter routines
  *
index 105a3af..afd7283 100644 (file)
@@ -21,8 +21,6 @@
  * @ingroup Language
  */
 
-require_once __DIR__ . '/../LanguageConverter.php';
-
 /**
  * Conversion script between Latin and Tifinagh for Tachelhit.
  * - Tifinagh -> lowercase Latin
index bdf1ec4..ece50e8 100644 (file)
@@ -21,8 +21,6 @@
  * @ingroup Language
  */
 
-require_once __DIR__ . '/../LanguageConverter.php';
-
 /**
  * There are two levels of conversion for Serbian: the script level
  * (Cyrillics <-> Latin), and the variant level (ekavian
index 10755b4..6518e65 100644 (file)
@@ -21,8 +21,6 @@
  * @ingroup Language
  */
 
-require_once __DIR__ . '/../LanguageConverter.php';
-
 /**
  * Converts Tajiki to latin orthography
  *
index 985de68..6910d9c 100644 (file)
@@ -21,8 +21,6 @@
  * @ingroup Language
  */
 
-require_once __DIR__ . '/../LanguageConverter.php';
-
 /**
  * @ingroup Language
  */
index 0f47c38..6e8860f 100644 (file)
@@ -21,9 +21,6 @@
  * @ingroup Language
  */
 
-require_once __DIR__ . '/../LanguageConverter.php';
-require_once __DIR__ . '/LanguageZh_hans.php';
-
 /**
  * @ingroup Language
  */
index b04e01c..b8349d0 100644 (file)
                /**
                 * Get a message object.
                 *
-                * Shorcut for `new mw.Message( mw.messages, key, parameters )`.
+                * Shortcut for `new mw.Message( mw.messages, key, parameters )`.
                 *
                 * @see mw.Message
                 * @param {string} key Key of message to get
                         */
 
                        /**
-                        * Write a message the console's warning channel.
+                        * Write a message to the console's warning channel.
                         * Actions not supported by the browser console are silently ignored.
                         *
                         * @param {...string} msg Messages to output to console
                                $.noop;
 
                        /**
-                        * Write a message the console's error channel.
+                        * Write a message to the console's error channel.
                         *
                         * Most browsers provide a stacktrace by default if the argument
                         * is a caught Error object.
                        /**
                         * A module has entered state 'ready', 'error', or 'missing'. Automatically update
                         * pending jobs and modules that depend upon this module. If the given module failed,
-                        * propagate the 'error' state up the dependency tree. Otherwise, go ahead an execute
+                        * propagate the 'error' state up the dependency tree. Otherwise, go ahead and execute
                         * all jobs/modules now having their dependencies satisfied.
                         *
                         * Jobs that depend on a failed module, will have their error callback ran (if any).
                                                                script( $, $ );
                                                                markModuleReady();
                                                        } else if ( typeof script === 'string' ) {
-                                                               // Site and user modules are legacy scripts that run in the global scope.
+                                                               // Site and user modules are legacy scripts that run in the global scope.
                                                                // This is transported as a string instead of a function to avoid needing
                                                                // to use string manipulation to undo the function wrapper.
                                                                if ( module === 'user' ) {
                        }
 
                        /**
-                        * Adds a dependencies to the queue with optional callbacks to be run
+                        * Adds all dependencies to the queue with optional callbacks to be run
                         * when the dependencies are ready or fail
                         *
                         * @private
                                 * When #load or #using requests one or more modules, the server
                                 * response contain calls to this function.
                                 *
-                                * All arguments are required.
-                                *
                                 * @param {string} module Name of module
-                                * @param {Function|Array} script Function with module code or Array of URLs to
+                                * @param {Function|Array} [script] Function with module code or Array of URLs to
                                 *  be used as the src attribute of a new `<script>` tag.
                                 * @param {Object} [style] Should follow one of the following patterns:
                                 *
        /**
         * Log a message to window.console, if possible.
         *
-        * Useful to force logging of some  errors that are otherwise hard to detect (i.e., this logs
+        * Useful to force logging of some errors that are otherwise hard to detect (i.e., this logs
         * also in production mode). Gets console references in each invocation instead of caching the
         * reference, so that debugging tools loaded later are supported (e.g. Firebug Lite in IE).
         *
                        msg += ( e ? ':' : '.' );
                        console.log( msg );
 
-                       // If we have an exception object, log it to the error channel to trigger a
-                       // proper stacktraces in browsers that support it. No fallback as we have no browsers
-                       // that don't support error(), but do support log().
+                       // If we have an exception object, log it to the error channel to trigger
+                       // proper stacktraces in browsers that support it. No fallback as we have
+                       // no browsers that don't support error(), but do support log().
                        if ( e && console.error ) {
                                console.error( String( e ), e );
                        }
index 121bb72..c38edd6 100644 (file)
@@ -19,7 +19,7 @@ class UserInfoTest extends MediaWikiTestCase {
                $this->assertTrue( $userinfo->isVerified() );
                $this->assertSame( 0, $userinfo->getId() );
                $this->assertSame( null, $userinfo->getName() );
-               $this->assertSame( null, $userinfo->getToken() );
+               $this->assertSame( '', $userinfo->getToken() );
                $this->assertNotNull( $userinfo->getUser() );
                $this->assertSame( $userinfo, $userinfo->verified() );
                $this->assertSame( '<anon>', (string)$userinfo );
@@ -102,7 +102,7 @@ class UserInfoTest extends MediaWikiTestCase {
                $this->assertFalse( $userinfo->isVerified() );
                $this->assertSame( $user->getId(), $userinfo->getId() );
                $this->assertSame( $user->getName(), $userinfo->getName() );
-               $this->assertSame( null, $userinfo->getToken() );
+               $this->assertSame( '', $userinfo->getToken() );
                $this->assertInstanceOf( 'User', $userinfo->getUser() );
                $userinfo2 = $userinfo->verified();
                $this->assertNotSame( $userinfo2, $userinfo );
@@ -112,7 +112,7 @@ class UserInfoTest extends MediaWikiTestCase {
                $this->assertTrue( $userinfo2->isVerified() );
                $this->assertSame( $user->getId(), $userinfo2->getId() );
                $this->assertSame( $user->getName(), $userinfo2->getName() );
-               $this->assertSame( null, $userinfo2->getToken() );
+               $this->assertSame( '', $userinfo2->getToken() );
                $this->assertInstanceOf( 'User', $userinfo2->getUser() );
                $this->assertSame( $userinfo2, $userinfo2->verified() );
                $this->assertSame( "<+:{$user->getId()}:{$user->getName()}>", (string)$userinfo2 );
@@ -157,7 +157,7 @@ class UserInfoTest extends MediaWikiTestCase {
                $this->assertFalse( $userinfo->isVerified() );
                $this->assertSame( $user->getId(), $userinfo->getId() );
                $this->assertSame( $user->getName(), $userinfo->getName() );
-               $this->assertSame( null, $userinfo->getToken() );
+               $this->assertSame( '', $userinfo->getToken() );
                $this->assertSame( $user, $userinfo->getUser() );
                $userinfo2 = $userinfo->verified();
                $this->assertNotSame( $userinfo2, $userinfo );
@@ -167,7 +167,7 @@ class UserInfoTest extends MediaWikiTestCase {
                $this->assertTrue( $userinfo2->isVerified() );
                $this->assertSame( $user->getId(), $userinfo2->getId() );
                $this->assertSame( $user->getName(), $userinfo2->getName() );
-               $this->assertSame( null, $userinfo2->getToken() );
+               $this->assertSame( '', $userinfo2->getToken() );
                $this->assertSame( $user, $userinfo2->getUser() );
                $this->assertSame( $userinfo2, $userinfo2->verified() );
                $this->assertSame( "<+:{$user->getId()}:{$user->getName()}>", (string)$userinfo2 );
index ed3f2cd..fe5530b 100644 (file)
                } );
        } );
 
+       QUnit.test( 'mw.loader.implement( empty )', 1, function ( assert ) {
+               mw.loader.implement( 'test.empty' );
+               assert.strictEqual( mw.loader.getState( 'test.empty' ), 'ready' );
+       } );
+
        QUnit.test( 'mw.loader with broken indirect dependency', 4, function ( assert ) {
                // don't emit an error event
                this.sandbox.stub( mw, 'track' );