Merge "Hard deprecate wfBaseConvert()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 13 Oct 2017 18:15:34 +0000 (18:15 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 13 Oct 2017 18:15:34 +0000 (18:15 +0000)
16 files changed:
RELEASE-NOTES-1.31
autoload.php
composer.json
includes/EditPage.php
includes/GlobalFunctions.php
maintenance/Maintenance.php
maintenance/checkSyntax.php [deleted file]
maintenance/resetUserTokens.php
maintenance/update.php
resources/Resources.php
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.SavedLinksListItemWidget.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ViewSwitchWidget.less
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.GroupWidget.js [new file with mode: 0644]
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SavedLinksListWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ViewSwitchWidget.js

index 57cbec4..efadf9a 100644 (file)
@@ -44,6 +44,8 @@ changes to languages because of Phabricator reports.
 * MessageBlobStore::insertMessageBlob() (deprecated in 1.27) was removed.
 * The global function wfBCP47 was renamed to LanguageCode::bcp47.
 * The global function wfBCP47 is now deprecated.
+* The global function wfCountDown() is now deprecated in favor of
+  Maintenance::countDown().
 
 == Compatibility ==
 MediaWiki 1.31 requires PHP 5.5.9 or later. There is experimental support for
index 0a2ecf0..abedffd 100644 (file)
@@ -255,7 +255,6 @@ $wgAutoloadLocalClasses = [
        'CheckLanguageCLI' => __DIR__ . '/maintenance/language/checkLanguage.inc',
        'CheckLess' => __DIR__ . '/maintenance/checkLess.php',
        'CheckStorage' => __DIR__ . '/maintenance/storage/checkStorage.php',
-       'CheckSyntax' => __DIR__ . '/maintenance/checkSyntax.php',
        'CheckUsernames' => __DIR__ . '/maintenance/checkUsernames.php',
        'ClassCollector' => __DIR__ . '/includes/utils/AutoloadGenerator.php',
        'CleanupAncientTables' => __DIR__ . '/maintenance/cleanupAncientTables.php',
index 634bace..2bd8d6c 100644 (file)
@@ -59,7 +59,7 @@
                "nikic/php-parser": "2.1.0",
                "nmred/kafka-php": "0.1.5",
                "phpunit/phpunit": "4.8.35",
-               "psy/psysh": "0.8.5",
+               "psy/psysh": "0.8.11",
                "wikimedia/avro": "1.7.7",
                "wikimedia/testing-access-wrapper": "~1.0",
                "wmde/hamcrest-html-matchers": "^0.1.0"
index 3fc12ce..bc91623 100644 (file)
@@ -3166,7 +3166,7 @@ class EditPage {
         */
        function getSummaryInputOOUI( $summary = "", $labelText = null, $inputAttrs = null ) {
                wfDeprecated( __METHOD__, '1.30' );
-               $this->getSummaryInputWidget( $summary, $labelText, $inputAttrs );
+               return $this->getSummaryInputWidget( $summary, $labelText, $inputAttrs );
        }
 
        /**
index 279fc95..ffdfc92 100644 (file)
@@ -3048,6 +3048,8 @@ function wfWaitForSlaves(
  * Count down from $seconds to zero on the terminal, with a one-second pause
  * between showing each number. For use in command-line scripts.
  *
+ * @deprecated since 1.31, use Maintenance::countDown()
+ *
  * @codeCoverageIgnore
  * @param int $seconds
  */
index ecbbb85..9a29055 100644 (file)
@@ -1416,6 +1416,32 @@ abstract class Maintenance {
                return $title;
        }
 
+       /**
+        * Count down from $seconds to zero on the terminal, with a one-second pause
+        * between showing each number. If the maintenance script is in quiet mode,
+        * this function does nothing.
+        *
+        * @since 1.31
+        *
+        * @codeCoverageIgnore
+        * @param int $seconds
+        */
+       protected function countDown( $seconds ) {
+               if ( $this->isQuiet() ) {
+                       return;
+               }
+               for ( $i = $seconds; $i >= 0; $i-- ) {
+                       if ( $i != $seconds ) {
+                               $this->output( str_repeat( "\x08", strlen( $i + 1 ) ) );
+                       }
+                       $this->output( $i );
+                       if ( $i ) {
+                               sleep( 1 );
+                       }
+               }
+               $this->output( "\n" );
+       }
+
        /**
         * Wrapper for posix_isatty()
         * We default as considering stdin a tty (for nice readline methods)
diff --git a/maintenance/checkSyntax.php b/maintenance/checkSyntax.php
deleted file mode 100644 (file)
index 3910f29..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-<?php
-/**
- * Check syntax of all PHP files in MediaWiki
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Maintenance
- */
-
-require_once __DIR__ . '/Maintenance.php';
-
-/**
- * Maintenance script to check syntax of all PHP files in MediaWiki.
- *
- * @ingroup Maintenance
- */
-class CheckSyntax extends Maintenance {
-
-       // List of files we're going to check
-       private $mFiles = [], $mFailures = [], $mWarnings = [];
-       private $mIgnorePaths = [], $mNoStyleCheckPaths = [];
-
-       public function __construct() {
-               parent::__construct();
-               $this->addDescription( 'Check syntax for all PHP files in MediaWiki' );
-               $this->addOption( 'with-extensions', 'Also recurse the extensions folder' );
-               $this->addOption(
-                       'path',
-                       'Specific path (file or directory) to check, either with absolute path or '
-                               . 'relative to the root of this MediaWiki installation',
-                       false,
-                       true
-               );
-               $this->addOption(
-                       'list-file',
-                       'Text file containing list of files or directories to check',
-                       false,
-                       true
-               );
-               $this->addOption(
-                       'modified',
-                       'Check only files that were modified (requires Git command-line client)'
-               );
-               $this->addOption( 'syntax-only', 'Check for syntax validity only, skip code style warnings' );
-       }
-
-       public function getDbType() {
-               return Maintenance::DB_NONE;
-       }
-
-       public function execute() {
-               $this->buildFileList();
-
-               $this->output( "Checking syntax (using php -l, this can take a long time)\n" );
-               foreach ( $this->mFiles as $f ) {
-                       $this->checkFileWithCli( $f );
-                       if ( !$this->hasOption( 'syntax-only' ) ) {
-                               $this->checkForMistakes( $f );
-                       }
-               }
-               $this->output( "\nDone! " . count( $this->mFiles ) . " files checked, " .
-                       count( $this->mFailures ) . " failures and " . count( $this->mWarnings ) .
-                       " warnings found\n" );
-       }
-
-       /**
-        * Build the list of files we'll check for syntax errors
-        */
-       private function buildFileList() {
-               global $IP;
-
-               $this->mIgnorePaths = [
-               ];
-
-               $this->mNoStyleCheckPaths = [
-                       // Third-party code we don't care about
-                       "/activemq_stomp/",
-                       "EmailPage/PHPMailer",
-                       "FCKeditor/fckeditor/",
-                       '\bphplot-',
-                       "/svggraph/",
-                       "\bjsmin.php$",
-                       "PEAR/File_Ogg/",
-                       "QPoll/Excel/",
-                       "/geshi/",
-                       "/smarty/",
-               ];
-
-               if ( $this->hasOption( 'path' ) ) {
-                       $path = $this->getOption( 'path' );
-                       if ( !$this->addPath( $path ) ) {
-                               $this->error( "Error: can't find file or directory $path\n", true );
-                       }
-
-                       return; // process only this path
-               } elseif ( $this->hasOption( 'list-file' ) ) {
-                       $file = $this->getOption( 'list-file' );
-                       MediaWiki\suppressWarnings();
-                       $f = fopen( $file, 'r' );
-                       MediaWiki\restoreWarnings();
-                       if ( !$f ) {
-                               $this->error( "Can't open file $file\n", true );
-                       }
-                       $path = trim( fgets( $f ) );
-                       while ( $path ) {
-                               $this->addPath( $path );
-                       }
-                       fclose( $f );
-
-                       return;
-               } elseif ( $this->hasOption( 'modified' ) ) {
-                       $this->output( "Retrieving list from Git... " );
-                       $files = $this->getGitModifiedFiles( $IP );
-                       $this->output( "done\n" );
-                       foreach ( $files as $file ) {
-                               if ( $this->isSuitableFile( $file ) && !is_dir( $file ) ) {
-                                       $this->mFiles[] = $file;
-                               }
-                       }
-
-                       return;
-               }
-
-               $this->output( 'Building file list...', 'listfiles' );
-
-               // Only check files in these directories.
-               // Don't just put $IP, because the recursive dir thingie goes into all subdirs
-               $dirs = [
-                       $IP . '/includes',
-                       $IP . '/mw-config',
-                       $IP . '/languages',
-                       $IP . '/maintenance',
-                       $IP . '/skins',
-               ];
-               if ( $this->hasOption( 'with-extensions' ) ) {
-                       $dirs[] = $IP . '/extensions';
-               }
-
-               foreach ( $dirs as $d ) {
-                       $this->addDirectoryContent( $d );
-               }
-
-               // Manually add two user-editable files that are usually sources of problems
-               if ( file_exists( "$IP/LocalSettings.php" ) ) {
-                       $this->mFiles[] = "$IP/LocalSettings.php";
-               }
-
-               $this->output( 'done.', 'listfiles' );
-       }
-
-       /**
-        * Returns a list of tracked files in a Git work tree differing from the master branch.
-        * @param string $path Path to the repository
-        * @return array Resulting list of changed files
-        */
-       private function getGitModifiedFiles( $path ) {
-               global $wgMaxShellMemory;
-
-               if ( !is_dir( "$path/.git" ) ) {
-                       $this->error( "Error: Not a Git repository!\n", true );
-               }
-
-               // git diff eats memory.
-               $oldMaxShellMemory = $wgMaxShellMemory;
-               if ( $wgMaxShellMemory < 1024000 ) {
-                       $wgMaxShellMemory = 1024000;
-               }
-
-               $ePath = wfEscapeShellArg( $path );
-
-               // Find an ancestor in common with master (rather than just using its HEAD)
-               // to prevent files only modified there from showing up in the list.
-               $cmd = "cd $ePath && git merge-base master HEAD";
-               $retval = 0;
-               $output = wfShellExec( $cmd, $retval );
-               if ( $retval !== 0 ) {
-                       $this->error( "Error retrieving base SHA1 from Git!\n", true );
-               }
-
-               // Find files in the working tree that changed since then.
-               $eBase = wfEscapeShellArg( rtrim( $output, "\n" ) );
-               $cmd = "cd $ePath && git diff --name-only --diff-filter AM $eBase";
-               $retval = 0;
-               $output = wfShellExec( $cmd, $retval );
-               if ( $retval !== 0 ) {
-                       $this->error( "Error retrieving list from Git!\n", true );
-               }
-
-               $wgMaxShellMemory = $oldMaxShellMemory;
-
-               $arr = [];
-               $filename = strtok( $output, "\n" );
-               while ( $filename !== false ) {
-                       if ( $filename !== '' ) {
-                               $arr[] = "$path/$filename";
-                       }
-                       $filename = strtok( "\n" );
-               }
-
-               return $arr;
-       }
-
-       /**
-        * Returns true if $file is of a type we can check
-        * @param string $file
-        * @return bool
-        */
-       private function isSuitableFile( $file ) {
-               $file = str_replace( '\\', '/', $file );
-               $ext = pathinfo( $file, PATHINFO_EXTENSION );
-               if ( $ext != 'php' && $ext != 'inc' && $ext != 'php5' ) {
-                       return false;
-               }
-               foreach ( $this->mIgnorePaths as $regex ) {
-                       $m = [];
-                       if ( preg_match( "~{$regex}~", $file, $m ) ) {
-                               return false;
-                       }
-               }
-
-               return true;
-       }
-
-       /**
-        * Add given path to file list, searching it in include path if needed
-        * @param string $path
-        * @return bool
-        */
-       private function addPath( $path ) {
-               global $IP;
-
-               return $this->addFileOrDir( $path ) || $this->addFileOrDir( "$IP/$path" );
-       }
-
-       /**
-        * Add given file to file list, or, if it's a directory, add its content
-        * @param string $path
-        * @return bool
-        */
-       private function addFileOrDir( $path ) {
-               if ( is_dir( $path ) ) {
-                       $this->addDirectoryContent( $path );
-               } elseif ( file_exists( $path ) ) {
-                       $this->mFiles[] = $path;
-               } else {
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * Add all suitable files in given directory or its subdirectories to the file list
-        *
-        * @param string $dir Directory to process
-        */
-       private function addDirectoryContent( $dir ) {
-               $iterator = new RecursiveIteratorIterator(
-                       new RecursiveDirectoryIterator( $dir ),
-                       RecursiveIteratorIterator::SELF_FIRST
-               );
-               foreach ( $iterator as $file ) {
-                       if ( $this->isSuitableFile( $file->getRealPath() ) ) {
-                               $this->mFiles[] = $file->getRealPath();
-                       }
-               }
-       }
-
-       /**
-        * Check a file for syntax errors using php -l
-        * @param string $file Path to a file to check for syntax errors
-        * @return bool
-        */
-       private function checkFileWithCli( $file ) {
-               $res = exec( 'php -l ' . wfEscapeShellArg( $file ) );
-               if ( strpos( $res, 'No syntax errors detected' ) === false ) {
-                       $this->mFailures[$file] = $res;
-                       $this->output( $res . "\n" );
-
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * Check a file for non-fatal coding errors, such as byte-order marks in the beginning
-        * or pointless ?> closing tags at the end.
-        *
-        * @param string $file String Path to a file to check for errors
-        */
-       private function checkForMistakes( $file ) {
-               foreach ( $this->mNoStyleCheckPaths as $regex ) {
-                       $m = [];
-                       if ( preg_match( "~{$regex}~", $file, $m ) ) {
-                               return;
-                       }
-               }
-
-               $text = file_get_contents( $file );
-               $tokens = token_get_all( $text );
-
-               $this->checkEvilToken( $file, $tokens, '@', 'Error supression operator (@)' );
-               $this->checkRegex( $file, $text, '/^[\s\r\n]+<\?/', 'leading whitespace' );
-               $this->checkRegex( $file, $text, '/\?>[\s\r\n]*$/', 'trailing ?>' );
-               $this->checkRegex( $file, $text, '/^[\xFF\xFE\xEF]/', 'byte-order mark' );
-       }
-
-       private function checkRegex( $file, $text, $regex, $desc ) {
-               if ( !preg_match( $regex, $text ) ) {
-                       return;
-               }
-
-               if ( !isset( $this->mWarnings[$file] ) ) {
-                       $this->mWarnings[$file] = [];
-               }
-               $this->mWarnings[$file][] = $desc;
-               $this->output( "Warning in file $file: $desc found.\n" );
-       }
-
-       private function checkEvilToken( $file, $tokens, $evilToken, $desc ) {
-               if ( !in_array( $evilToken, $tokens ) ) {
-                       return;
-               }
-
-               if ( !isset( $this->mWarnings[$file] ) ) {
-                       $this->mWarnings[$file] = [];
-               }
-               $this->mWarnings[$file][] = $desc;
-               $this->output( "Warning in file $file: $desc found.\n" );
-       }
-}
-
-$maintClass = "CheckSyntax";
-require_once RUN_MAINTENANCE_IF_MAIN;
index 481da98..1c8b4b9 100644 (file)
@@ -64,7 +64,7 @@ class ResetUserTokens extends Maintenance {
                        $this->output( "\n" );
                        $this->output( "Abort with control-c in the next five seconds "
                                . "(skip this countdown with --nowarn) ... " );
-                       wfCountDown( 5 );
+                       $this->countDown( 5 );
                }
 
                // We list user by user_id from one of the replica DBs
index 5f705ba..5e2947b 100755 (executable)
@@ -128,7 +128,7 @@ class UpdateMediaWiki extends Maintenance {
                        $this->compatChecks();
                } else {
                        $this->output( "Skipping compatibility checks, proceed at your own risk (Ctrl+C to abort)\n" );
-                       wfCountDown( 5 );
+                       $this->countDown( 5 );
                }
 
                // Check external dependencies are up to date
@@ -155,7 +155,7 @@ class UpdateMediaWiki extends Maintenance {
                if ( !$this->hasOption( 'quick' ) ) {
                        $this->output( "Abort with control-c in the next five seconds "
                                . "(skip this countdown with --quick) ... " );
-                       wfCountDown( 5 );
+                       $this->countDown( 5 );
                }
 
                $time1 = microtime( true );
index 4df8194..06012b8 100644 (file)
@@ -1786,6 +1786,7 @@ return [
        ],
        'mediawiki.rcfilters.filters.ui' => [
                'scripts' => [
+                       'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.GroupWidget.js',
                        'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.CheckboxInputWidget.js',
                        'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js',
                        'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ItemMenuOptionWidget.js',
index 4bc6183..5b12cf7 100644 (file)
         */
        mw.rcfilters.Controller.prototype.clearFilter = function ( filterName ) {
                var filterItem = this.filtersModel.getItemByName( filterName ),
-                       isHighlighted = filterItem.isHighlighted();
+                       isHighlighted = filterItem.isHighlighted(),
+                       isSelected = filterItem.isSelected();
 
-               if ( filterItem.isSelected() || isHighlighted ) {
+               if ( isSelected || isHighlighted ) {
                        this.filtersModel.clearHighlightColor( filterName );
                        this.filtersModel.toggleFilterSelected( filterName, false );
-                       this.updateChangesList();
+
+                       if ( isSelected ) {
+                               // Only update the changes list if the filter changed
+                               // its selection state. If it only changed its highlight
+                               // then don't reload
+                               this.updateChangesList();
+                       }
+
                        this.filtersModel.reassessFilterInteractions( filterItem );
 
                        // Log filter grouping
index 9dccc24..6a20f56 100644 (file)
@@ -22,6 +22,7 @@
                min-width: 16px;
                min-height: 16px;
                opacity: 0.5;
+               position: relative;
        }
 
        &-icon span {
index a6ce4cd..1896103 100644 (file)
@@ -6,9 +6,5 @@
 
        &-buttons {
                margin-top: 0.5em;
-
-               .oo-ui-buttonWidget:not( :first-child ) {
-                       margin-left: 0.5em;
-               }
        }
 }
diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.GroupWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.GroupWidget.js
new file mode 100644 (file)
index 0000000..167df09
--- /dev/null
@@ -0,0 +1,41 @@
+( function ( mw ) {
+       /**
+        * A group widget to allow for aggregation of events
+        *
+        * @extends OO.ui.Widget
+        *
+        * @constructor
+        * @param {Object} [config] Configuration object
+        * @param {Object} [events] Events to aggregate. The object represent the
+        *  event name to aggregate and the event value to emit on aggregate for items.
+        */
+       mw.rcfilters.ui.GroupWidget = function MwRcfiltersUiViewSwitchWidget( config ) {
+               var aggregate = {};
+
+               config = config || {};
+
+               // Parent constructor
+               mw.rcfilters.ui.GroupWidget.parent.call( this, config );
+
+               // Mixin constructors
+               OO.ui.mixin.GroupElement.call( this, $.extend( {}, config, { $group: this.$element } ) );
+
+               if ( config.events ) {
+                       // Aggregate events
+                       $.each( config.events, function ( eventName, eventEmit ) {
+                               aggregate[ eventName ] = eventEmit;
+                       } );
+
+                       this.aggregate( aggregate );
+               }
+
+               if ( Array.isArray( config.items ) ) {
+                       this.addItems( config.items );
+               }
+       };
+
+       /* Initialize */
+
+       OO.inheritClass( mw.rcfilters.ui.GroupWidget, OO.ui.Widget );
+       OO.mixinClass( mw.rcfilters.ui.GroupWidget, OO.ui.mixin.GroupWidget );
+}( mediaWiki ) );
index 7eda90a..8287ef9 100644 (file)
                        label: $labelNoEntries,
                        icon: 'unClip'
                } );
-               // The only reason we're using "ButtonGroupWidget" here is that
-               // straight-out "GroupWidget" is a mixin and cannot be initialized
-               // on its own, so we need something to be its widget.
-               this.menu = new OO.ui.ButtonGroupWidget( {
+
+               this.menu = new mw.rcfilters.ui.GroupWidget( {
+                       events: {
+                               click: 'menuItemClick',
+                               'delete': 'menuItemDelete',
+                               'default': 'menuItemDefault',
+                               edit: 'menuItemEdit'
+                       },
                        classes: [ 'mw-rcfilters-ui-savedLinksListWidget-menu' ],
                        items: [ this.placeholderItem ]
                } );
                        }
                } );
 
-               this.menu.aggregate( {
-                       click: 'menuItemClick',
-                       'delete': 'menuItemDelete',
-                       'default': 'menuItemDefault',
-                       edit: 'menuItemEdit'
-               } );
-
                // Events
                this.model.connect( this, {
                        add: 'onModelAddItem',
index fef8567..aadded1 100644 (file)
                this.controller = controller;
                this.model = model;
 
-               this.buttons = new OO.ui.ButtonGroupWidget( {
+               this.buttons = new mw.rcfilters.ui.GroupWidget( {
+                       events: {
+                               click: 'buttonClick'
+                       },
                        items: [
                                new OO.ui.ButtonWidget( {
                                        data: 'namespaces',
@@ -35,7 +38,6 @@
 
                // Events
                this.model.connect( this, { update: 'onModelUpdate' } );
-               this.buttons.aggregate( { click: 'buttonClick' } );
                this.buttons.connect( this, { buttonClick: 'onButtonClick' } );
 
                this.$element