Merge "Split HttpFunctions.php into separate files"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 4 Oct 2016 02:54:50 +0000 (02:54 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 4 Oct 2016 02:54:50 +0000 (02:54 +0000)
54 files changed:
RELEASE-NOTES-1.28
composer.json
includes/actions/HistoryAction.php
includes/cache/HTMLFileCache.php
includes/installer/MssqlUpdater.php
includes/installer/MysqlUpdater.php
includes/installer/OracleUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/IMaintainableDatabase.php
includes/libs/rdbms/lbfactory/LBFactoryMulti.php
includes/libs/rdbms/lbfactory/LBFactorySimple.php
includes/libs/rdbms/loadbalancer/LoadBalancer.php
includes/libs/rdbms/loadmonitor/ILoadMonitor.php
includes/libs/rdbms/loadmonitor/LoadMonitor.php
includes/libs/rdbms/loadmonitor/LoadMonitorMySQL.php
includes/libs/rdbms/loadmonitor/LoadMonitorNull.php
includes/page/WikiPage.php
includes/skins/SkinTemplate.php
includes/specials/SpecialNewpages.php [changed mode: 0644->0755]
languages/i18n/en.json
languages/i18n/qqq.json
maintenance/archives/patch-change_tag-ct_id.sql [new file with mode: 0644]
maintenance/archives/patch-tag_summary-ts_id.sql [new file with mode: 0644]
maintenance/interwiki.list
maintenance/interwiki.sql
maintenance/mssql/archives/patch-change_tag-ct_id.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-tag_summary-ts_id.sql [new file with mode: 0644]
maintenance/mssql/tables.sql
maintenance/oracle/archives/patch-change_tag-ct_id.sql [new file with mode: 0644]
maintenance/oracle/archives/patch-tag_summary-ts_id.sql [new file with mode: 0644]
maintenance/oracle/tables.sql
maintenance/postgres/tables.sql
maintenance/sql.php
maintenance/sqlite/archives/patch-change_tag-ct_id.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-tag_summary-ts_id.sql [new file with mode: 0644]
maintenance/tables.sql
resources/Resources.php
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-core-apex.css
resources/lib/oojs-ui/oojs-ui-core-mediawiki.css
resources/lib/oojs-ui/oojs-ui-core.js
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui-toolbars-apex.css
resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css
resources/lib/oojs-ui/oojs-ui-toolbars.js
resources/lib/oojs-ui/oojs-ui-widgets-apex.css
resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css
resources/lib/oojs-ui/oojs-ui-widgets.js
resources/lib/oojs-ui/oojs-ui-windows-apex.css
resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css
resources/lib/oojs-ui/oojs-ui-windows.js
resources/src/mediawiki/mediawiki.feedback.js

index 42d639a..8b7dced 100644 (file)
@@ -6,6 +6,7 @@ MediaWiki 1.28 is an alpha-quality branch and is not recommended for use in
 production.
 
 === Configuration changes in 1.28 ===
+* $wgSend404Code now affects status code of action=history if the page is not there.
 * BREAKING CHANGE: $wgHTTPProxy is now *required* for all external requests
   made by MediaWiki via a proxy. Relying on the http_proxy environment
   variable is no longer supported.
@@ -66,6 +67,7 @@ production.
 ==== Upgraded external libraries ====
 * Updated es5-shim from v4.1.5 to v4.5.8
 * Updated composer/semver from v1.4.1 to v1.4.2
+* Updated wikimedia/php-session-serializer from v1.0.3 to v1.0.4
 
 ==== New external libraries ====
 * Added wikimedia/scoped-callback v1.0.0
@@ -74,6 +76,7 @@ production.
 ==== Removed and replaced external libraries ====
 
 === Bug fixes in 1.28 ===
+* (T146496) action=history pages should return 404 HTTP error code if the page does not exist
 * (T137264) SECURITY: XSS in unclosed internal links
 * (T133147) SECURITY: Escape '<' and ']]>' in inline <style> blocks
 * (T133147) SECURITY: Require login to preview user CSS pages
index f809d45..884b64f 100644 (file)
@@ -25,7 +25,7 @@
                "ext-xml": "*",
                "liuggio/statsd-php-client": "1.0.18",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.17.9",
+               "oojs/oojs-ui": "0.17.10",
                "oyejorge/less.php": "1.7.0.10",
                "php": ">=5.5.9",
                "psr/log": "1.0.0",
@@ -36,7 +36,7 @@
                "wikimedia/composer-merge-plugin": "1.3.1",
                "wikimedia/html-formatter": "1.0.1",
                "wikimedia/ip-set": "1.1.0",
-               "wikimedia/php-session-serializer": "1.0.3",
+               "wikimedia/php-session-serializer": "1.0.4",
                "wikimedia/relpath": "1.0.3",
                "wikimedia/running-stat": "1.1.0",
                "wikimedia/scoped-callback": "1.0.0",
index f3ef3b3..1e1bb39 100644 (file)
@@ -139,6 +139,10 @@ class HistoryAction extends FormlessAction {
 
                // Fail nicely if article doesn't exist.
                if ( !$this->page->exists() ) {
+                       global $wgSend404Code;
+                       if ( $wgSend404Code ) {
+                               $out->setStatusCode( 404 );
+                       }
                        $out->addWikiMsg( 'nohistory' );
                        # show deletion/move log if there is an entry
                        LogEventsList::showLogExtract(
index a85639f..060837e 100644 (file)
@@ -167,9 +167,10 @@ class HTMLFileCache extends FileCacheBase {
                        $context->getTitle()->resetArticleID( 0 );
                }
 
+               $contLang = $config->get( 'ContLang' );
                $context->getOutput()->sendCacheControl();
                header( "Content-Type: {$config->get( 'MimeType' )}; charset=UTF-8" );
-               header( "Content-Language: {$config->get( 'LanguageCode' )}" );
+               header( "Content-Language: {$contLang->getHtmlCode()}" );
                if ( $this->useGzip() ) {
                        if ( wfClientAcceptsGzip() ) {
                                header( 'Content-Encoding: gzip' );
index 770d3bf..1175e9e 100644 (file)
@@ -92,6 +92,8 @@ class MssqlUpdater extends DatabaseUpdater {
                        // 1.28
                        [ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
                                'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
+                       [ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
+                       [ 'addField', 'tag_summary', 'ts_id', 'patch-tag_summary-ts_id.sql' ],
                ];
        }
 
index 693b6ff..497f273 100644 (file)
@@ -288,6 +288,8 @@ class MysqlUpdater extends DatabaseUpdater {
                                'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
                        [ 'doRevisionPageRevIndexNonUnique' ],
                        [ 'doNonUniquePlTlIl' ],
+                       [ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
+                       [ 'addField', 'tag_summary', 'ts_id', 'patch-tag_summary-ts_id.sql' ],
                ];
        }
 
index 8075aac..e1e0d0f 100644 (file)
@@ -116,6 +116,8 @@ class OracleUpdater extends DatabaseUpdater {
                        // 1.28
                        [ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
                                'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
+                       [ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
+                       [ 'addField', 'tag_summary', 'ts_id', 'patch-tag_summary-ts_id.sql' ],
 
                        // KEEP THIS AT THE BOTTOM!!
                        [ 'doRebuildDuplicateFunction' ],
index be94d91..f3d2860 100644 (file)
@@ -68,6 +68,8 @@ class PostgresUpdater extends DatabaseUpdater {
                        [ 'addSequence', 'archive', false, 'archive_ar_id_seq' ],
                        [ 'addSequence', 'externallinks', false, 'externallinks_el_id_seq' ],
                        [ 'addSequence', 'watchlist', false, 'watchlist_wl_id_seq' ],
+                       [ 'addSequence', 'change_tag', false, 'change_tag_ct_id_seq' ],
+                       [ 'addSequence', 'tag_summary', false, 'tag_summary_ts_id_seq' ],
 
                        # new tables
                        [ 'addTable', 'category', 'patch-category.sql' ],
@@ -437,6 +439,10 @@ class PostgresUpdater extends DatabaseUpdater {
                        // 1.28
                        [ 'addPgIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
                                '( rc_namespace, rc_type, rc_patrolled, rc_timestamp )' ],
+                       [ 'addPgField', 'change_tag', 'ct_id',
+                               "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('change_tag_ct_id_seq')" ],
+                       [ 'addPgField', 'tag_summary', 'ts_id',
+                               "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('tag_summary_ts_id_seq')" ],
                ];
        }
 
index 1c6e6eb..388c034 100644 (file)
@@ -156,6 +156,8 @@ class SqliteUpdater extends DatabaseUpdater {
                        // 1.28
                        [ 'addIndex', 'recentchanges', 'rc_name_type_patrolled_timestamp',
                                'patch-add-rc_name_type_patrolled_timestamp_index.sql' ],
+                       [ 'addField', 'change_tag', 'ct_id', 'patch-change_tag-ct_id.sql' ],
+                       [ 'addField', 'tag_summary', 'ts_id', 'patch-tag_summary-ts_id.sql' ],
                ];
        }
 
index 4f1f38e..16d30d7 100644 (file)
@@ -3037,10 +3037,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        public function sourceFile(
                $filename,
-               $lineCallback = false,
-               $resultCallback = false,
+               callable $lineCallback = null,
+               callable $resultCallback = null,
                $fname = false,
-               $inputCallback = false
+               callable $inputCallback = null
        ) {
                MediaWiki\suppressWarnings();
                $fp = fopen( $filename, 'r' );
@@ -3073,10 +3073,10 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
 
        public function sourceStream(
                $fp,
-               $lineCallback = false,
-               $resultCallback = false,
+               callable $lineCallback = null,
+               callable $resultCallback = null,
                $fname = __METHOD__,
-               $inputCallback = false
+               callable $inputCallback = null
        ) {
                $cmd = '';
 
index f65e0dd..8395359 100644 (file)
@@ -96,21 +96,21 @@ interface IMaintainableDatabase extends IDatabase {
         * on object's error ignore settings).
         *
         * @param string $filename File name to open
-        * @param bool|callable $lineCallback Optional function called before reading each line
-        * @param bool|callable $resultCallback Optional function called for each MySQL result
+        * @param callable|null $lineCallback Optional function called before reading each line
+        * @param callable|null $resultCallback Optional function called for each MySQL result
         * @param bool|string $fname Calling function name or false if name should be
         *   generated dynamically using $filename
-        * @param bool|callable $inputCallback Optional function called for each
+        * @param callable|null $inputCallback Optional function called for each
         *   complete line sent
         * @return bool|string
         * @throws Exception
         */
        public function sourceFile(
                $filename,
-               $lineCallback = false,
-               $resultCallback = false,
+               callable $lineCallback = null,
+               callable $resultCallback = null,
                $fname = false,
-               $inputCallback = false
+               callable $inputCallback = null
        );
 
        /**
@@ -120,18 +120,18 @@ interface IMaintainableDatabase extends IDatabase {
         * on object's error ignore settings).
         *
         * @param resource $fp File handle
-        * @param bool|callable $lineCallback Optional function called before reading each query
-        * @param bool|callable $resultCallback Optional function called for each MySQL result
+        * @param callable|null $lineCallback Optional function called before reading each query
+        * @param callable|null $resultCallback Optional function called for each MySQL result
         * @param string $fname Calling function name
-        * @param bool|callable $inputCallback Optional function called for each complete query sent
+        * @param callable|null $inputCallback Optional function called for each complete query sent
         * @return bool|string
         */
        public function sourceStream(
                $fp,
-               $lineCallback = false,
-               $resultCallback = false,
+               callable $lineCallback = null,
+               callable $resultCallback = null,
                $fname = __METHOD__,
-               $inputCallback = false
+               callable $inputCallback = null
        );
 
        /**
index 2fb8c4b..bee32dc 100644 (file)
@@ -309,7 +309,7 @@ class LBFactoryMulti extends LBFactory {
                        $this->baseLoadBalancerParams(),
                        [
                                'servers' => $this->makeServerArray( $template, $loads, $groupLoads ),
-                               'loadMonitor' => $this->loadMonitorClass,
+                               'loadMonitor' => [ 'class' => $this->loadMonitorClass ],
                                'readOnlyReason' => $readOnlyReason
                        ]
                ) );
index 610052f..674bafd 100644 (file)
@@ -67,7 +67,7 @@ class LBFactorySimple extends LBFactory {
                        : [];
                $this->loadMonitorClass = isset( $conf['loadMonitorClass'] )
                        ? $conf['loadMonitorClass']
-                       : null;
+                       : 'LoadMonitor';
        }
 
        /**
@@ -124,7 +124,7 @@ class LBFactorySimple extends LBFactory {
                        $this->baseLoadBalancerParams(),
                        [
                                'servers' => $servers,
-                               'loadMonitor' => $this->loadMonitorClass,
+                               'loadMonitor' => [ 'class' => $this->loadMonitorClass ],
                        ]
                ) );
                $this->initLoadBalancer( $lb );
index 37c028b..31c022c 100644 (file)
@@ -32,7 +32,7 @@ class LoadBalancer implements ILoadBalancer {
        private $mServers;
        /** @var array[] Map of (local/foreignUsed/foreignFree => server index => IDatabase array) */
        private $mConns;
-       /** @var array Map of (server index => weight) */
+       /** @var float[] Map of (server index => weight) */
        private $mLoads;
        /** @var array[] Map of (group => server index => weight) */
        private $mGroupLoads;
@@ -40,13 +40,13 @@ class LoadBalancer implements ILoadBalancer {
        private $mAllowLagged;
        /** @var integer Seconds to spend waiting on replica DB lag to resolve */
        private $mWaitTimeout;
-       /** @var string The LoadMonitor subclass name */
-       private $mLoadMonitorClass;
+       /** @var array The LoadMonitor configuration */
+       private $loadMonitorConfig;
        /** @var array[] $aliases Map of (table => (dbname, schema, prefix) map) */
        private $tableAliases = [];
 
        /** @var ILoadMonitor */
-       private $mLoadMonitor;
+       private $loadMonitor;
        /** @var BagOStuff */
        private $srvCache;
        /** @var BagOStuff */
@@ -150,14 +150,9 @@ class LoadBalancer implements ILoadBalancer {
                }
 
                if ( isset( $params['loadMonitor'] ) ) {
-                       $this->mLoadMonitorClass = $params['loadMonitor'];
+                       $this->loadMonitorConfig = $params['loadMonitor'];
                } else {
-                       $master = reset( $params['servers'] );
-                       if ( isset( $master['type'] ) && $master['type'] === 'mysql' ) {
-                               $this->mLoadMonitorClass = 'LoadMonitorMySQL';
-                       } else {
-                               $this->mLoadMonitorClass = 'LoadMonitorNull';
-                       }
+                       $this->loadMonitorConfig = [ 'class' => 'LoadMonitorNull' ];
                }
 
                foreach ( $params['servers'] as $i => $server ) {
@@ -217,13 +212,14 @@ class LoadBalancer implements ILoadBalancer {
         * @return ILoadMonitor
         */
        private function getLoadMonitor() {
-               if ( !isset( $this->mLoadMonitor ) ) {
-                       $class = $this->mLoadMonitorClass;
-                       $this->mLoadMonitor = new $class( $this, $this->srvCache, $this->memCache );
-                       $this->mLoadMonitor->setLogger( $this->replLogger );
+               if ( !isset( $this->loadMonitor ) ) {
+                       $class = $this->loadMonitorConfig['class'];
+                       $this->loadMonitor = new $class(
+                               $this, $this->srvCache, $this->memCache, $this->loadMonitorConfig );
+                       $this->loadMonitor->setLogger( $this->replLogger );
                }
 
-               return $this->mLoadMonitor;
+               return $this->loadMonitor;
        }
 
        /**
index e355c03..72a8785 100644 (file)
@@ -34,16 +34,19 @@ interface ILoadMonitor extends LoggerAwareInterface {
         * @param ILoadBalancer $lb LoadBalancer this instance serves
         * @param BagOStuff $sCache Local server memory cache
         * @param BagOStuff $cCache Local cluster memory cache
+        * @param array $options Options map
         */
-       public function __construct( ILoadBalancer $lb, BagOStuff $sCache, BagOStuff $cCache );
+       public function __construct(
+               ILoadBalancer $lb, BagOStuff $sCache, BagOStuff $cCache, array $options = []
+       );
 
        /**
         * Perform pre-connection load ratio adjustment.
-        * @param int[] &$loads
+        * @param int[] &$weightByServer Map of (server index => integer weight)
         * @param string|bool $group The selected query group. Default: false
         * @param string|bool $domain Default: false
         */
-       public function scaleLoads( &$loads, $group = false, $domain = false );
+       public function scaleLoads( array &$weightByServer, $group = false, $domain = false );
 
        /**
         * Get an estimate of replication lag (in seconds) for each server
@@ -55,7 +58,7 @@ interface ILoadMonitor extends LoggerAwareInterface {
         *
         * @return array Map of (server index => float|int|bool)
         */
-       public function getLagTimes( $serverIndexes, $domain );
+       public function getLagTimes( array $serverIndexes, $domain );
 
        /**
         * Clear any process and persistent cache of lag times
index 1da8f4e..dddc7b0 100644 (file)
@@ -37,27 +37,50 @@ class LoadMonitor implements ILoadMonitor {
        /** @var LoggerInterface */
        protected $replLogger;
 
-       public function __construct( ILoadBalancer $lb, BagOStuff $srvCache, BagOStuff $cache ) {
+       /** @var float Moving average ratio (e.g. 0.1 for 10% weight to new weight) */
+       private $movingAveRatio;
+
+       public function __construct(
+               ILoadBalancer $lb, BagOStuff $srvCache, BagOStuff $cache, array $options = []
+       ) {
                $this->parent = $lb;
                $this->srvCache = $srvCache;
                $this->mainCache = $cache;
                $this->replLogger = new \Psr\Log\NullLogger();
+
+               $this->movingAveRatio = isset( $options['movingAveRatio'] )
+                       ? $options['movingAveRatio']
+                       : 0.1;
        }
 
        public function setLogger( LoggerInterface $logger ) {
                $this->replLogger = $logger;
        }
 
-       public function scaleLoads( &$loads, $group = false, $domain = false ) {
+       public function scaleLoads( array &$weightByServer, $group = false, $domain = false ) {
+               $states = $this->getServerStates( $weightByServer, $domain );
+               $coefficientsByServer = $states['weightScales'];
+               foreach ( $weightByServer as $i => $weight ) {
+                       $weightByServer[$i] = $weight * $coefficientsByServer[$i];
+               }
+       }
+
+       public function getLagTimes( array $serverIndexes, $domain ) {
+               $states = $this->getServerStates( $serverIndexes, $domain );
+
+               return $states['lagTimes'];
        }
 
-       public function getLagTimes( $serverIndexes, $domain ) {
+       protected function getServerStates( array $serverIndexes, $domain ) {
                if ( count( $serverIndexes ) == 1 && reset( $serverIndexes ) == 0 ) {
                        # Single server only, just return zero without caching
-                       return [ 0 => 0 ];
+                       return [
+                               'lagTimes' => [ $this->parent->getWriterIndex() => 0 ],
+                               'weightScales' => [ $this->parent->getWriterIndex() => 1 ]
+                       ];
                }
 
-               $key = $this->getLagTimeCacheKey();
+               $key = $this->getCacheKey();
                # Randomize TTLs to reduce stampedes (4.0 - 5.0 sec)
                $ttl = mt_rand( 4e6, 5e6 ) / 1e6;
                # Keep keys around longer as fallbacks
@@ -67,7 +90,7 @@ class LoadMonitor implements ILoadMonitor {
                $value = $this->srvCache->get( $key );
                if ( $value && $value['timestamp'] > ( microtime( true ) - $ttl ) ) {
                        $this->replLogger->debug( __METHOD__ . ": got lag times ($key) from local cache" );
-                       return $value['lagTimes']; // cache hit
+                       return $value; // cache hit
                }
                $staleValue = $value ?: false;
 
@@ -77,7 +100,7 @@ class LoadMonitor implements ILoadMonitor {
                        $this->srvCache->set( $key, $value, $staleTTL );
                        $this->replLogger->debug( __METHOD__ . ": got lag times ($key) from main cache" );
 
-                       return $value['lagTimes']; // cache hit
+                       return $value; // cache hit
                }
                $staleValue = $value ?: $staleValue;
 
@@ -91,13 +114,16 @@ class LoadMonitor implements ILoadMonitor {
                        } );
                } elseif ( $staleValue ) {
                        # Could not acquire lock but an old cache exists, so use it
-                       return $staleValue['lagTimes'];
+                       return $staleValue;
                }
 
                $lagTimes = [];
+               $weightScales = [];
+               $movAveRatio = $this->movingAveRatio;
                foreach ( $serverIndexes as $i ) {
                        if ( $i == $this->parent->getWriterIndex() ) {
                                $lagTimes[$i] = 0; // master always has no lag
+                               $weightScales[$i] = 1.0; // nominal weight
                                continue;
                        }
 
@@ -109,17 +135,26 @@ class LoadMonitor implements ILoadMonitor {
                                $close = true; // new connection
                        }
 
+                       $lastWeight = isset( $staleValue['weightScales'][$i] )
+                               ? $staleValue['weightScales'][$i]
+                               : 1.0;
+                       $coefficient = $this->getWeightScale( $i, $conn ?: null );
+                       $newWeight = $movAveRatio * $coefficient + ( 1 - $movAveRatio ) * $lastWeight;
+
+                       // Scale from 10% to 100% of nominal weight
+                       $weightScales[$i] = max( $newWeight, .10 );
+
                        if ( !$conn ) {
                                $lagTimes[$i] = false;
                                $host = $this->parent->getServerName( $i );
-                               $this->replLogger->error( __METHOD__ . ": host $host (#$i) is unreachable" );
+                               $this->replLogger->error( __METHOD__ . ": host $host is unreachable" );
                                continue;
                        }
 
                        $lagTimes[$i] = $conn->getLag();
                        if ( $lagTimes[$i] === false ) {
                                $host = $this->parent->getServerName( $i );
-                               $this->replLogger->error( __METHOD__ . ": host $host (#$i) is not replicating?" );
+                               $this->replLogger->error( __METHOD__ . ": host $host is not replicating?" );
                        }
 
                        if ( $close ) {
@@ -132,26 +167,38 @@ class LoadMonitor implements ILoadMonitor {
                }
 
                # Add a timestamp key so we know when it was cached
-               $value = [ 'lagTimes' => $lagTimes, 'timestamp' => microtime( true ) ];
+               $value = [
+                       'lagTimes' => $lagTimes,
+                       'weightScales' => $weightScales,
+                       'timestamp' => microtime( true )
+               ];
                $this->mainCache->set( $key, $value, $staleTTL );
                $this->srvCache->set( $key, $value, $staleTTL );
                $this->replLogger->info( __METHOD__ . ": re-calculated lag times ($key)" );
 
-               return $value['lagTimes'];
+               return $value;
+       }
+
+       /**
+        * @param integer $index Server index
+        * @param IDatabase|null $conn Connection handle or null on connection failure
+        * @return float
+        */
+       protected function getWeightScale( $index, IDatabase $conn = null ) {
+               return $conn ? 1.0 : 0.0;
        }
 
        public function clearCaches() {
-               $key = $this->getLagTimeCacheKey();
+               $key = $this->getCacheKey();
                $this->srvCache->delete( $key );
                $this->mainCache->delete( $key );
        }
 
-       private function getLagTimeCacheKey() {
-               $writerIndex = $this->parent->getWriterIndex();
+       private function getCacheKey() {
                // Lag is per-server, not per-DB, so key on the master DB name
                return $this->srvCache->makeGlobalKey(
                        'lag-times',
-                       $this->parent->getServerName( $writerIndex )
+                       $this->parent->getServerName( $this->parent->getWriterIndex() )
                );
        }
 }
index 7286417..babd609 100644 (file)
  * @ingroup Database
  */
 class LoadMonitorMySQL extends LoadMonitor {
-       public function scaleLoads( &$loads, $group = false, $domain = false ) {
-               // @TODO: maybe use Threads_running/Threads_created ratio to guess load
-               // and Queries/Uptime to guess if a server is warming up the buffer pool
+       /** @var float What buffer pool use ratio counts as "warm" (e.g. 0.5 for 50% usage) */
+       private $warmCacheRatio;
+
+       public function __construct(
+               ILoadBalancer $lb, BagOStuff $srvCache, BagOStuff $cache, array $options = []
+       ) {
+               parent::__construct( $lb, $srvCache, $cache, $options );
+
+               $this->warmCacheRatio = isset( $options['warmCacheRatio'] )
+                       ? $options['warmCacheRatio']
+                       : 0.0;
+       }
+
+       protected function getWeightScale( $index, IDatabase $conn = null ) {
+               if ( !$conn ) {
+                       return 0.0;
+               }
+
+               $weight = 1.0;
+               if ( $this->warmCacheRatio > 0 ) {
+                       $res = $conn->query( 'SHOW STATUS', false );
+                       $s = $res ? $conn->fetchObject( $res ) : false;
+                       if ( $s === false ) {
+                               $host = $this->parent->getServerName( $index );
+                               $this->replLogger->error( __METHOD__ . ": could not get status for $host" );
+                       } else {
+                               // http://dev.mysql.com/doc/refman/5.7/en/server-status-variables.html
+                               if ( $s->Innodb_buffer_pool_pages_total > 0 ) {
+                                       $ratio = $s->Innodb_buffer_pool_pages_data / $s->Innodb_buffer_pool_pages_total;
+                               } elseif ( $s->Qcache_total_blocks > 0 ) {
+                                       $ratio = 1.0 - $s->Qcache_free_blocks / $s->Qcache_total_blocks;
+                               } else {
+                                       $ratio = 1.0;
+                               }
+                               // Stop caring once $ratio >= $this->warmCacheRatio
+                               $weight *= min( $ratio / $this->warmCacheRatio, 1.0 );
+                       }
+               }
+
+               return $weight;
        }
 }
index 8062001..67bac2b 100644 (file)
 use Psr\Log\LoggerInterface;
 
 class LoadMonitorNull implements ILoadMonitor {
-       public function __construct( ILoadBalancer $lb, BagOStuff $sCache, BagOStuff $cCache ) {
+       public function __construct(
+               ILoadBalancer $lb, BagOStuff $sCache, BagOStuff $cCache, array $options = []
+       ) {
 
        }
 
        public function setLogger( LoggerInterface $logger ) {
        }
 
-       public function scaleLoads( &$loads, $group = false, $domain = false ) {
+       public function scaleLoads( array &$loads, $group = false, $domain = false ) {
 
        }
 
-       public function getLagTimes( $serverIndexes, $domain ) {
+       public function getLagTimes( array $serverIndexes, $domain ) {
                return array_fill_keys( $serverIndexes, 0 );
        }
 
index 928fc14..4fa042e 100644 (file)
@@ -693,7 +693,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @deprecated since 1.21, getContent() should be used instead.
         */
        public function getText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
-               ContentHandler::deprecated( __METHOD__, '1.21' );
+               wfDeprecated( __METHOD__, '1.21' );
 
                $this->loadLastEdit();
                if ( $this->mLastRevision ) {
index ed7c6df..3efbd3b 100644 (file)
@@ -180,6 +180,7 @@ class SkinTemplate extends Skin {
                                        'text' => $ilLangName,
                                        'title' => $ilTitle,
                                        'class' => $class,
+                                       'link-class' => 'interlanguage-link-target',
                                        'lang' => $ilInterwikiCodeBCP47,
                                        'hreflang' => $ilInterwikiCodeBCP47,
                                ];
old mode 100644 (file)
new mode 100755 (executable)
index 718a6dc..d719e53
@@ -376,7 +376,11 @@ class SpecialNewpages extends IncludableSpecialPage {
 
                if ( !$title->equals( $oldTitle ) ) {
                        $oldTitleText = $oldTitle->getPrefixedText();
-                       $oldTitleText = $this->msg( 'rc-old-title' )->params( $oldTitleText )->escaped();
+                       $oldTitleText = Html::rawElement(
+                               'span',
+                               [ 'class' => 'mw-newpages-oldtitle' ],
+                               $this->msg( 'rc-old-title' )->params( $oldTitleText )->escaped()
+                       );
                }
 
                return "<li{$css}>{$time} {$dm}{$plink} {$hist} {$dm}{$length} "
index cbe755d..fc8ba1f 100644 (file)
        "searchprofile-advanced-tooltip": "Search in custom namespaces",
        "search-result-size": "$1 ({{PLURAL:$2|1 word|$2 words}})",
        "search-result-category-size": "{{PLURAL:$1|1 member|$1 members}} ({{PLURAL:$2|1 subcategory|$2 subcategories}}, {{PLURAL:$3|1 file|$3 files}})",
-       "search-redirect": "(redirect $1)",
+       "search-redirect": "(redirect from $1)",
        "search-section": "(section $1)",
        "search-category": "(category $1)",
        "search-file-match": "(matches file content)",
        "feedback-external-bug-report-button": "File a technical task",
        "feedback-dialog-title": "Submit feedback",
        "feedback-dialog-intro": "You can use the easy form below to submit your feedback. Your comment will be added to the page \"$1\", along with your username.",
-       "feedback-error-title": "Error",
        "feedback-error1": "Error: Unrecognized result from API",
        "feedback-error2": "Error: Edit failed",
        "feedback-error3": "Error: No response from API",
index 915f629..610ebea 100644 (file)
        "feedback-external-bug-report-button": "A button for submitting an external technical bug report.",
        "feedback-dialog-title": "Title of the feedback dialog",
        "feedback-dialog-intro": "An introduction at the top of the feedback dialog. $1 - Feedback page link",
-       "feedback-error-title": "{{Identical|Error}}",
        "feedback-error1": "Error message, appears when an unknown error occurs submitting feedback",
        "feedback-error2": "Error message, appears when we could not add feedback",
        "feedback-error3": "Error message, appears when we lose our connection to the wiki",
diff --git a/maintenance/archives/patch-change_tag-ct_id.sql b/maintenance/archives/patch-change_tag-ct_id.sql
new file mode 100644 (file)
index 0000000..7b986d6
--- /dev/null
@@ -0,0 +1,5 @@
+-- Primary key in change_tag table
+
+ALTER TABLE /*$wgDBprefix*/change_tag
+       ADD COLUMN ct_id INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
+       ADD PRIMARY KEY (ct_id);
diff --git a/maintenance/archives/patch-tag_summary-ts_id.sql b/maintenance/archives/patch-tag_summary-ts_id.sql
new file mode 100644 (file)
index 0000000..66fa72e
--- /dev/null
@@ -0,0 +1,5 @@
+-- Primary key in tag_summary table
+
+ALTER TABLE /*$wgDBprefix*/tag_summary
+       ADD COLUMN ts_id INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
+       ADD PRIMARY KEY (ts_id);
index b1e0fa4..3cc009e 100644 (file)
@@ -7,7 +7,6 @@ c2find|http://c2.com/cgi/wiki?FindPage&value=$1|0|
 cache|http://www.google.com/search?q=cache:$1|0|
 commons|https://commons.wikimedia.org/wiki/$1|0|https://commons.wikimedia.org/w/api.php
 dictionary|http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1|0|
-docbook|http://wiki.docbook.org/$1|0|
 doi|http://dx.doi.org/$1|0|
 drumcorpswiki|http://www.drumcorpswiki.com/$1|0|http://drumcorpswiki.com/api.php
 dwjwiki|http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1|0|
@@ -16,22 +15,18 @@ emacswiki|http://www.emacswiki.org/cgi-bin/wiki.pl?$1|0|
 foldoc|http://foldoc.org/?$1|0|
 foxwiki|http://fox.wikis.com/wc.dll?Wiki~$1|0|
 freebsdman|http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1|0|
-gej|http://www.esperanto.de/dej.malnova/aktivikio.pl?$1|0|
 gentoo-wiki|http://gentoo-wiki.com/$1|0|
 google|http://www.google.com/search?q=$1|0|
 googlegroups|http://groups.google.com/groups?q=$1|0|
 hammondwiki|http://www.dairiki.org/HammondWiki/$1|0|
 hrwiki|http://www.hrwiki.org/wiki/$1|0|http://www.hrwiki.org/w/api.php
 imdb|http://www.imdb.com/find?q=$1&tt=on|0|
-jargonfile|http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1|0|
 kmwiki|http://kmwiki.wikispaces.com/$1|0|
 linuxwiki|http://linuxwiki.de/$1|0|
 lojban|http://mw.lojban.org/papri/$1|0|
 lqwiki|http://wiki.linuxquestions.org/wiki/$1|0|
-lugkr|http://www.lug-kr.de/wiki/$1|0|
 meatball|http://www.usemod.com/cgi-bin/mb.pl?$1|0|
 mediawikiwiki|https://www.mediawiki.org/wiki/$1|0|https://www.mediawiki.org/w/api.php
-mediazilla|https://bugzilla.wikimedia.org/$1|0|
 memoryalpha|http://en.memory-alpha.org/wiki/$1|0|http://en.memory-alpha.org/api.php
 metawiki|http://sunir.org/apps/meta.pl?$1|0|
 metawikimedia|https://meta.wikimedia.org/wiki/$1|0|https://meta.wikimedia.org/w/api.php
@@ -39,25 +34,20 @@ mozillawiki|http://wiki.mozilla.org/$1|0|https://wiki.mozilla.org/api.php
 mw|https://www.mediawiki.org/wiki/$1|0|https://www.mediawiki.org/w/api.php
 oeis|http://oeis.org/$1|0|
 openwiki|http://openwiki.com/ow.asp?$1|0|
-ppr|http://c2.com/cgi/wiki?$1|0|
 pythoninfo|http://wiki.python.org/moin/$1|0|
 rfc|http://www.rfc-editor.org/rfc/rfc$1.txt|0|
 s23wiki|http://s23.org/wiki/$1|0|http://s23.org/w/api.php
 seattlewireless|http://seattlewireless.net/$1|0|
 senseislibrary|http://senseis.xmp.net/?$1|0|
 shoutwiki|http://www.shoutwiki.com/wiki/$1|0|http://www.shoutwiki.com/w/api.php
-sourcewatch|http://www.sourcewatch.org/index.php?title=$1|0|http://www.sourcewatch.org/api.php
 squeak|http://wiki.squeak.org/squeak/$1|0|
-tejo|http://www.tejo.org/vikio/$1|0|
 tmbw|http://www.tmbw.net/wiki/$1|0|http://tmbw.net/wiki/api.php
 tmnet|http://www.technomanifestos.net/?$1|0|
 theopedia|http://www.theopedia.com/$1|0|
 twiki|http://twiki.org/cgi-bin/view/$1|0|
-uea|http://uea.org/vikio/index.php/$1|0|http://uea.org/vikio/api.php
 uncyclopedia|http://en.uncyclopedia.co/wiki/$1|0|http://en.uncyclopedia.co/w/api.php
 unreal|http://wiki.beyondunreal.com/$1|0|http://wiki.beyondunreal.com/w/api.php
 usemod|http://www.usemod.com/cgi-bin/wiki.pl?$1|0|
-webseitzwiki|http://webseitz.fluxent.com/wiki/$1|0|
 wiki|http://c2.com/cgi/wiki?$1|0|
 wikia|http://www.wikia.com/wiki/$1|0|
 wikibooks|https://en.wikibooks.org/wiki/$1|0|https://en.wikibooks.org/w/api.php
index b7d1a84..0e0bb5c 100644 (file)
@@ -9,7 +9,6 @@ REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local,iw_api) VALUES
 ('cache','http://www.google.com/search?q=cache:$1',0,''),
 ('commons','https://commons.wikimedia.org/wiki/$1',0,'https://commons.wikimedia.org/w/api.php'),
 ('dictionary','http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1',0,''),
-('docbook','http://wiki.docbook.org/$1',0,''),
 ('doi','http://dx.doi.org/$1',0,''),
 ('drumcorpswiki','http://www.drumcorpswiki.com/$1',0,'http://drumcorpswiki.com/api.php'),
 ('dwjwiki','http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1',0,''),
@@ -18,22 +17,18 @@ REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local,iw_api) VALUES
 ('foldoc','http://foldoc.org/?$1',0,''),
 ('foxwiki','http://fox.wikis.com/wc.dll?Wiki~$1',0,''),
 ('freebsdman','http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1',0,''),
-('gej','http://www.esperanto.de/dej.malnova/aktivikio.pl?$1',0,''),
 ('gentoo-wiki','http://gentoo-wiki.com/$1',0,''),
 ('google','http://www.google.com/search?q=$1',0,''),
 ('googlegroups','http://groups.google.com/groups?q=$1',0,''),
 ('hammondwiki','http://www.dairiki.org/HammondWiki/$1',0,''),
 ('hrwiki','http://www.hrwiki.org/wiki/$1',0,'http://www.hrwiki.org/w/api.php'),
 ('imdb','http://www.imdb.com/find?q=$1&tt=on',0,''),
-('jargonfile','http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1',0,''),
 ('kmwiki','http://kmwiki.wikispaces.com/$1',0,''),
 ('linuxwiki','http://linuxwiki.de/$1',0,''),
 ('lojban','http://www.lojban.org/tiki/tiki-index.php?page=$1',0,''),
 ('lqwiki','http://wiki.linuxquestions.org/wiki/$1',0,''),
-('lugkr','http://www.lug-kr.de/wiki/$1',0,''),
 ('meatball','http://www.usemod.com/cgi-bin/mb.pl?$1',0,''),
 ('mediawikiwiki','https://www.mediawiki.org/wiki/$1',0,'https://www.mediawiki.org/w/api.php'),
-('mediazilla','https://bugzilla.wikimedia.org/$1',0,''),
 ('memoryalpha','http://en.memory-alpha.org/wiki/$1',0,'http://en.memory-alpha.org/api.php'),
 ('metawiki','http://sunir.org/apps/meta.pl?$1',0,''),
 ('metawikimedia','https://meta.wikimedia.org/wiki/$1',0,'https://meta.wikimedia.org/w/api.php'),
@@ -41,25 +36,20 @@ REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local,iw_api) VALUES
 ('mw','https://www.mediawiki.org/wiki/$1',0,'https://www.mediawiki.org/w/api.php'),
 ('oeis','http://oeis.org/$1',0,''),
 ('openwiki','http://openwiki.com/ow.asp?$1',0,''),
-('ppr','http://c2.com/cgi/wiki?$1',0,''),
 ('pythoninfo','http://wiki.python.org/moin/$1',0,''),
 ('rfc','http://www.rfc-editor.org/rfc/rfc$1.txt',0,''),
 ('s23wiki','http://s23.org/wiki/$1',0,'http://s23.org/w/api.php'),
 ('seattlewireless','http://seattlewireless.net/$1',0,''),
 ('senseislibrary','http://senseis.xmp.net/?$1',0,''),
 ('shoutwiki','http://www.shoutwiki.com/wiki/$1',0,'http://www.shoutwiki.com/w/api.php'),
-('sourcewatch','http://www.sourcewatch.org/index.php?title=$1',0,'http://www.sourcewatch.org/api.php'),
 ('squeak','http://wiki.squeak.org/squeak/$1',0,''),
-('tejo','http://www.tejo.org/vikio/$1',0,''),
 ('tmbw','http://www.tmbw.net/wiki/$1',0,'http://tmbw.net/wiki/api.php'),
 ('tmnet','http://www.technomanifestos.net/?$1',0,''),
 ('theopedia','http://www.theopedia.com/$1',0,''),
 ('twiki','http://twiki.org/cgi-bin/view/$1',0,''),
-('uea','http://uea.org/vikio/index.php/$1',0,'http://uea.org/vikio/api.php'),
 ('uncyclopedia','http://en.uncyclopedia.co/wiki/$1',0,'http://en.uncyclopedia.co/w/api.php'),
 ('unreal','http://wiki.beyondunreal.com/$1',0,'http://wiki.beyondunreal.com/w/api.php'),
 ('usemod','http://www.usemod.com/cgi-bin/wiki.pl?$1',0,''),
-('webseitzwiki','http://webseitz.fluxent.com/wiki/$1',0,''),
 ('wiki','http://c2.com/cgi/wiki?$1',0,''),
 ('wikia','http://www.wikia.com/wiki/$1',0,''),
 ('wikibooks','https://en.wikibooks.org/wiki/$1',0,'https://en.wikibooks.org/w/api.php'),
diff --git a/maintenance/mssql/archives/patch-change_tag-ct_id.sql b/maintenance/mssql/archives/patch-change_tag-ct_id.sql
new file mode 100644 (file)
index 0000000..94cb9d1
--- /dev/null
@@ -0,0 +1,4 @@
+-- Primary key in change_tag table
+
+ALTER TABLE /*_*/change_tag ADD ct_id INT IDENTITY;
+ALTER TABLE /*_*/change_tag ADD CONSTRAINT pk_change_tag PRIMARY KEY(ct_id)
diff --git a/maintenance/mssql/archives/patch-tag_summary-ts_id.sql b/maintenance/mssql/archives/patch-tag_summary-ts_id.sql
new file mode 100644 (file)
index 0000000..d62bd35
--- /dev/null
@@ -0,0 +1,4 @@
+-- Primary key in tag_summary table
+
+ALTER TABLE /*_*/tag_summary ADD ts_id INT IDENTITY;
+ALTER TABLE /*_*/tag_summary ADD CONSTRAINT pk_tag_summary PRIMARY KEY(ts_id)
index ea087a6..beb9727 100644 (file)
@@ -1193,6 +1193,7 @@ CREATE TABLE /*_*/updatelog (
 
 -- A table to track tags for revisions, logs and recent changes.
 CREATE TABLE /*_*/change_tag (
+  ct_id int NOT NULL PRIMARY KEY IDENTITY,
   -- RCID for the change
   ct_rc_id int NULL REFERENCES /*_*/recentchanges(rc_id),
   -- LOGID for the change
@@ -1215,6 +1216,7 @@ CREATE INDEX /*i*/change_tag_tag_id ON /*_*/change_tag (ct_tag,ct_rc_id,ct_rev_i
 -- Rollup table to pull a LIST of tags simply without ugly GROUP_CONCAT
 -- that only works on MySQL 4.1+
 CREATE TABLE /*_*/tag_summary (
+  ts_id int NOT NULL PRIMARY KEY IDENTITY,
   -- RCID for the change
   ts_rc_id int NULL REFERENCES /*_*/recentchanges(rc_id),
   -- LOGID for the change
diff --git a/maintenance/oracle/archives/patch-change_tag-ct_id.sql b/maintenance/oracle/archives/patch-change_tag-ct_id.sql
new file mode 100644 (file)
index 0000000..6672872
--- /dev/null
@@ -0,0 +1,6 @@
+define mw_prefix='{$wgDBprefix}';
+
+ALTER TABLE &mw_prefix.change_tag ADD (
+ct_id NUMBER NOT NULL,
+);
+ALTER TABLE &mw_prefix.change_tag ADD CONSTRAINT &mw_prefix.change_tag_pk PRIMARY KEY (ct_id);
diff --git a/maintenance/oracle/archives/patch-tag_summary-ts_id.sql b/maintenance/oracle/archives/patch-tag_summary-ts_id.sql
new file mode 100644 (file)
index 0000000..91c3338
--- /dev/null
@@ -0,0 +1,6 @@
+define mw_prefix='{$wgDBprefix}';
+
+ALTER TABLE &mw_prefix.tag_summary ADD (
+ts_id NUMBER NOT NULL,
+);
+ALTER TABLE &mw_prefix.tag_summary ADD CONSTRAINT &mw_prefix.tag_summary_pk PRIMARY KEY (ts_id);
index d9369c9..616b401 100644 (file)
@@ -616,23 +616,27 @@ CREATE TABLE &mw_prefix.updatelog (
 ALTER TABLE &mw_prefix.updatelog ADD CONSTRAINT &mw_prefix.updatelog_pk PRIMARY KEY (ul_key);
 
 CREATE TABLE &mw_prefix.change_tag (
+  ct_id NUMBER NOT NULL,
   ct_rc_id NUMBER NULL,
   ct_log_id NUMBER NULL,
   ct_rev_id NUMBER NULL,
   ct_tag VARCHAR2(255) NOT NULL,
   ct_params BLOB NULL
 );
+ALTER TABLE &mw_prefix.change_tag ADD CONSTRAINT &mw_prefix.change_tag_pk PRIMARY KEY (ct_id);
 CREATE UNIQUE INDEX &mw_prefix.change_tag_u01 ON &mw_prefix.change_tag (ct_rc_id,ct_tag);
 CREATE UNIQUE INDEX &mw_prefix.change_tag_u02 ON &mw_prefix.change_tag (ct_log_id,ct_tag);
 CREATE UNIQUE INDEX &mw_prefix.change_tag_u03 ON &mw_prefix.change_tag (ct_rev_id,ct_tag);
 CREATE INDEX &mw_prefix.change_tag_i01 ON &mw_prefix.change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
 
 CREATE TABLE &mw_prefix.tag_summary (
+  ts_id NUMBER NOT NULL,
   ts_rc_id NUMBER NULL,
   ts_log_id NUMBER NULL,
   ts_rev_id NUMBER NULL,
   ts_tags BLOB NOT NULL
 );
+ALTER TABLE &mw_prefix.tag_summary ADD CONSTRAINT &mw_prefix.tag_summary_pk PRIMARY KEY (ts_id);
 CREATE UNIQUE INDEX &mw_prefix.tag_summary_u01 ON &mw_prefix.tag_summary (ts_rc_id);
 CREATE UNIQUE INDEX &mw_prefix.tag_summary_u02 ON &mw_prefix.tag_summary (ts_log_id);
 CREATE UNIQUE INDEX &mw_prefix.tag_summary_u03 ON &mw_prefix.tag_summary (ts_rev_id);
index 95c87c0..2273761 100644 (file)
@@ -25,6 +25,8 @@ DROP SEQUENCE IF EXISTS category_cat_id_seq CASCADE;
 DROP SEQUENCE IF EXISTS archive_ar_id_seq CASCADE;
 DROP SEQUENCE IF EXISTS externallinks_el_id_seq CASCADE;
 DROP SEQUENCE IF EXISTS sites_site_id_seq CASCADE;
+DROP SEQUENCE IF EXISTS change_tag_ct_id_seq CASCADE;
+DROP SEQUENCE IF EXISTS tag_summary_ts_id_seq CASCADE;
 DROP FUNCTION IF EXISTS page_deleted() CASCADE;
 DROP FUNCTION IF EXISTS ts2_page_title() CASCADE;
 DROP FUNCTION IF EXISTS ts2_page_text() CASCADE;
@@ -653,7 +655,9 @@ CREATE TABLE category (
 CREATE UNIQUE INDEX category_title ON category(cat_title);
 CREATE INDEX category_pages ON category(cat_pages);
 
+CREATE SEQUENCE change_tag_ct_id_seq;
 CREATE TABLE change_tag (
+  ct_id      INTEGER  NOT NULL  PRIMARY KEY DEFAULT nextval('change_tag_ct_id_seq'),
   ct_rc_id   INTEGER      NULL,
   ct_log_id  INTEGER      NULL,
   ct_rev_id  INTEGER      NULL,
@@ -665,11 +669,13 @@ CREATE UNIQUE INDEX change_tag_log_tag ON change_tag(ct_log_id,ct_tag);
 CREATE UNIQUE INDEX change_tag_rev_tag ON change_tag(ct_rev_id,ct_tag);
 CREATE INDEX change_tag_tag_id ON change_tag(ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
 
+CREATE SEQUENCE tag_summary_ts_id_seq;
 CREATE TABLE tag_summary (
-  ts_rc_id   INTEGER     NULL,
-  ts_log_id  INTEGER     NULL,
-  ts_rev_id  INTEGER     NULL,
-  ts_tags    TEXT    NOT NULL
+  ts_id      INTEGER  NOT NULL  PRIMARY KEY DEFAULT nextval('tag_summary_ts_id_seq'),
+  ts_rc_id   INTEGER      NULL,
+  ts_log_id  INTEGER      NULL,
+  ts_rev_id  INTEGER      NULL,
+  ts_tags    TEXT     NOT NULL
 );
 CREATE UNIQUE INDEX tag_summary_rc_id ON tag_summary(ts_rc_id);
 CREATE UNIQUE INDEX tag_summary_log_id ON tag_summary(ts_log_id);
index a9a982c..cc976ed 100644 (file)
@@ -91,7 +91,7 @@ class MwSql extends Maintenance {
                                $this->error( "Unable to open input file", true );
                        }
 
-                       $error = $db->sourceStream( $file, false, [ $this, 'sqlPrintResult' ] );
+                       $error = $db->sourceStream( $file, null, [ $this, 'sqlPrintResult' ] );
                        if ( $error !== true ) {
                                $this->error( $error, true );
                        } else {
diff --git a/maintenance/sqlite/archives/patch-change_tag-ct_id.sql b/maintenance/sqlite/archives/patch-change_tag-ct_id.sql
new file mode 100644 (file)
index 0000000..1c01094
--- /dev/null
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS /*_*/change_tag_tmp;
+
+CREATE TABLE /*$wgDBprefix*/change_tag_tmp (
+  ct_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  ct_rc_id int NULL,
+  ct_log_id int NULL,
+  ct_rev_id int NULL,
+  ct_tag varchar(255) NOT NULL,
+  ct_params blob NULL
+);
+
+INSERT OR IGNORE INTO /*_*/change_tag_tmp (
+    ct_rc_id, ct_log_id, ct_rev_id, ct_tag, ct_params )
+    SELECT
+    ct_rc_id, ct_log_id, ct_rev_id, ct_tag, ct_params
+    FROM /*_*/change_tag;
+
+DROP TABLE /*_*/change_tag;
+
+ALTER TABLE /*_*/change_tag_tmp RENAME TO /*_*/change_tag;
+
+CREATE UNIQUE INDEX /*i*/change_tag_rc_tag ON /*_*/change_tag (ct_rc_id,ct_tag);
+CREATE UNIQUE INDEX /*i*/change_tag_log_tag ON /*_*/change_tag (ct_log_id,ct_tag);
+CREATE UNIQUE INDEX /*i*/change_tag_rev_tag ON /*_*/change_tag (ct_rev_id,ct_tag);
+CREATE INDEX /*i*/change_tag_tag_id ON /*_*/change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
diff --git a/maintenance/sqlite/archives/patch-tag_summary-ts_id.sql b/maintenance/sqlite/archives/patch-tag_summary-ts_id.sql
new file mode 100644 (file)
index 0000000..b6a1202
--- /dev/null
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS /*_*/tag_summary_tmp;
+
+CREATE TABLE /*$wgDBprefix*/tag_summary_tmp (
+  ts_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  ts_rc_id int NULL,
+  ts_log_id int NULL,
+  ts_rev_id int NULL,
+  ts_tags blob NOT NULL
+);
+
+INSERT OR IGNORE INTO /*_*/tag_summary_tmp (
+    ts_rc_id, ts_log_id, ts_rev_id, ts_tags )
+    SELECT
+    ts_rc_id, ts_log_id, ts_rev_id, ts_tags
+    FROM /*_*/tag_summary;
+
+DROP TABLE /*_*/tag_summary;
+
+ALTER TABLE /*_*/tag_summary_tmp RENAME TO /*_*/tag_summary;
+
+CREATE UNIQUE INDEX /*i*/tag_summary_rc_id ON /*_*/tag_summary (ts_rc_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_log_id ON /*_*/tag_summary (ts_log_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_rev_id ON /*_*/tag_summary (ts_rev_id);
index b5c14e3..03ce508 100644 (file)
@@ -1472,6 +1472,7 @@ CREATE TABLE /*_*/updatelog (
 
 -- A table to track tags for revisions, logs and recent changes.
 CREATE TABLE /*_*/change_tag (
+  ct_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   -- RCID for the change
   ct_rc_id int NULL,
   -- LOGID for the change
@@ -1494,6 +1495,7 @@ CREATE INDEX /*i*/change_tag_tag_id ON /*_*/change_tag (ct_tag,ct_rc_id,ct_rev_i
 -- Rollup table to pull a LIST of tags simply without ugly GROUP_CONCAT
 -- that only works on MySQL 4.1+
 CREATE TABLE /*_*/tag_summary (
+  ts_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   -- RCID for the change
   ts_rc_id int NULL,
   -- LOGID for the change
index b81fbde..32a754f 100644 (file)
@@ -1020,7 +1020,6 @@ return [
                        'feedback-cancel',
                        'feedback-close',
                        'feedback-dialog-title',
-                       'feedback-error-title',
                        'feedback-error1',
                        'feedback-error2',
                        'feedback-error3',
index 594cea2..d72957d 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:02Z
+ * Date: 2016-10-03T18:59:01Z
  */
 ( function ( OO ) {
 
index 6437ca8..2f811da 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:06Z
+ * Date: 2016-10-03T18:59:06Z
  */
 .oo-ui-element-hidden {
        display: none !important;
@@ -406,6 +406,10 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout {
        max-width: 100%;
        padding: 0;
        white-space: normal;
+       float: left;
+}
+.oo-ui-fieldsetLayout-group {
+       clear: both;
 }
 .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help {
        float: right;
index 08d91b4..9a3d7eb 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:06Z
+ * Date: 2016-10-03T18:59:06Z
  */
 .oo-ui-element-hidden {
        display: none !important;
@@ -529,6 +529,10 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout {
        max-width: 100%;
        padding: 0;
        white-space: normal;
+       float: left;
+}
+.oo-ui-fieldsetLayout-group {
+       clear: both;
 }
 .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-help {
        float: right;
index c982010..109645b 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:02Z
+ * Date: 2016-10-03T18:59:01Z
  */
 ( function ( OO ) {
 
@@ -10121,6 +10121,7 @@ OO.ui.FieldsetLayout = function OoUiFieldsetLayout( config ) {
        }
 
        // Initialization
+       this.$group.addClass( 'oo-ui-fieldsetLayout-group' );
        this.$element
                .addClass( 'oo-ui-fieldsetLayout' )
                .prepend( this.$label, this.$help, this.$icon, this.$group );
index 343508c..616f78e 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:02Z
+ * Date: 2016-10-03T18:59:01Z
  */
 ( function ( OO ) {
 
index 9c9954e..d6ed767 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:06Z
+ * Date: 2016-10-03T18:59:06Z
  */
 .oo-ui-popupTool .oo-ui-popupWidget-popup,
 .oo-ui-popupTool .oo-ui-popupWidget-anchor {
index a413005..411c6bb 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:06Z
+ * Date: 2016-10-03T18:59:06Z
  */
 .oo-ui-tool.oo-ui-widget-enabled {
        -webkit-transition: background-color 100ms;
index ba959cf..822b2d9 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:02Z
+ * Date: 2016-10-03T18:59:01Z
  */
 ( function ( OO ) {
 
index bd8034d..2d2b200 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:06Z
+ * Date: 2016-10-03T18:59:06Z
  */
 .oo-ui-draggableElement-handle,
 .oo-ui-draggableElement-handle.oo-ui-widget {
index 126b591..d1b4225 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:06Z
+ * Date: 2016-10-03T18:59:06Z
  */
 .oo-ui-draggableElement-handle,
 .oo-ui-draggableElement-handle.oo-ui-widget {
index 62195df..636e3f5 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:02Z
+ * Date: 2016-10-03T18:59:01Z
  */
 ( function ( OO ) {
 
index 3cff8f7..1cceac5 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:06Z
+ * Date: 2016-10-03T18:59:06Z
  */
 .oo-ui-actionWidget.oo-ui-pendingElement-pending {
        background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
index 2c115f9..38e40b9 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:06Z
+ * Date: 2016-10-03T18:59:06Z
  */
 .oo-ui-window {
        background: transparent;
index 8ef5ea5..0a29b8b 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.9
+ * OOjs UI v0.17.10
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-09-13T18:30:02Z
+ * Date: 2016-10-03T18:59:01Z
  */
 ( function ( OO ) {
 
index 46b6aec..0b3ea04 100644 (file)
                                        ]
                                };
                                break;
-                       case 'error1':
-                       case 'error2':
-                       case 'error3':
-                       case 'error4':
-                               dialogConfig = {
-                                       title: mw.msg( 'feedback-error-title' ),
-                                       message: mw.msg( 'feedback-' + status ),
-                                       actions: [
-                                               {
-                                                       action: 'accept',
-                                                       label: mw.msg( 'feedback-close' ),
-                                                       flags: 'primary'
-                                               }
-                                       ]
-                               };
-                               break;
                }
 
                // Show the message dialog
                                }, function () {
                                        fb.status = 'error4';
                                        mw.log.warn( 'Feedback report failed because MessagePoster could not be fetched' );
-                               } ).always( function () {
+                               } ).then( function () {
                                        fb.close();
+                               }, function () {
+                                       return fb.getErrorMessage();
                                } );
                        }, this );
                }
                return mw.Feedback.Dialog.parent.prototype.getActionProcess.call( this, action );
        };
 
+       /**
+        * Returns an error message for the current status.
+        *
+        * @private
+        *
+        * @return {OO.ui.Error}
+        */
+       mw.Feedback.Dialog.prototype.getErrorMessage = function () {
+               switch ( this.status ) {
+                       case 'error1':
+                       case 'error2':
+                       case 'error3':
+                       case 'error4':
+                               // Messages: feedback-error1, feedback-error2, feedback-error3, feedback-error4
+                               return new OO.ui.Error( mw.msg( 'feedback-' + this.status ) );
+               }
+       };
+
        /**
         * Posts the message
         *