Merge "Stop doing $that = $this in includes/specials"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 18 Feb 2016 20:57:06 +0000 (20:57 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 18 Feb 2016 20:57:06 +0000 (20:57 +0000)
23 files changed:
includes/EditPage.php
includes/Title.php
includes/api/ApiQueryWatchlist.php
includes/changes/ChangesList.php
includes/db/Database.php
includes/libs/CSSMin.php
includes/rcfeed/IRCColourfulRCFeedFormatter.php
includes/registration/ExtensionProcessor.php
includes/session/SessionManager.php
includes/specials/SpecialBotPasswords.php
languages/i18n/en-gb.json
languages/i18n/en.json
resources/src/mediawiki.special/mediawiki.special.search.js
resources/src/mediawiki.ui/components/checkbox.less
resources/src/mediawiki.ui/components/radio.less
resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
resources/src/mediawiki/api/upload.js
resources/src/mediawiki/mediawiki.feedback.js
tests/phpunit/includes/libs/CSSMinTest.php
tests/phpunit/includes/logging/BlockLogFormatterTest.php
tests/phpunit/includes/registration/ExtensionProcessorTest.php
tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js

index 5ebb44a..82fcdcf 100644 (file)
@@ -1420,6 +1420,11 @@ class EditPage {
                        }
                }
 
+               // "wpExtraQueryRedirect" is a hidden input to modify
+               // after save URL and is not used by actual edit form
+               $request = RequestContext::getMain()->getRequest();
+               $extraQueryRedirect = $request->getVal( 'wpExtraQueryRedirect' );
+
                switch ( $status->value ) {
                        case self::AS_HOOK_ERROR_EXPECTED:
                        case self::AS_CONTENT_TOO_BIG:
@@ -1443,6 +1448,13 @@ class EditPage {
 
                        case self::AS_SUCCESS_NEW_ARTICLE:
                                $query = $resultDetails['redirect'] ? 'redirect=no' : '';
+                               if ( $extraQueryRedirect ) {
+                                       if ( $query === '' ) {
+                                               $query = $extraQueryRedirect;
+                                       } else {
+                                               $query = $query . '&' . $extraQueryRedirect;
+                                       }
+                               }
                                $anchor = isset( $resultDetails['sectionanchor'] ) ? $resultDetails['sectionanchor'] : '';
                                $wgOut->redirect( $this->mTitle->getFullURL( $query ) . $anchor );
                                return false;
@@ -1464,6 +1476,14 @@ class EditPage {
                                                $extraQuery = 'redirect=no&' . $extraQuery;
                                        }
                                }
+                               if ( $extraQueryRedirect ) {
+                                       if ( $extraQuery === '' ) {
+                                               $extraQuery = $extraQueryRedirect;
+                                       } else {
+                                               $extraQuery = $extraQuery . '&' . $extraQueryRedirect;
+                                       }
+                               }
+
                                $wgOut->redirect( $this->mTitle->getFullURL( $extraQuery ) . $sectionanchor );
                                return false;
 
index 38ee2ee..c0ec97f 100644 (file)
@@ -3434,7 +3434,7 @@ class Title implements LinkTarget {
         * WARNING: do not use this function on arbitrary user-supplied titles!
         * On heavily-used templates it will max out the memory.
         *
-        * @param array $options May be FOR UPDATE
+        * @param array $options Query option to Database::select()
         * @return Title[] Array of Title the Title objects linking here
         */
        public function getTemplateLinksTo( $options = [] ) {
@@ -3448,7 +3448,7 @@ class Title implements LinkTarget {
         * WARNING: do not use this function on arbitrary user-supplied titles!
         * On heavily-used templates it will max out the memory.
         *
-        * @param array $options May be FOR UPDATE
+        * @param array $options Query option to Database::select()
         * @param string $table Table name
         * @param string $prefix Fields prefix
         * @return array Array of Title objects linking here
@@ -3461,11 +3461,7 @@ class Title implements LinkTarget {
                        return [];
                }
 
-               if ( count( $options ) > 0 ) {
-                       $db = wfGetDB( DB_MASTER );
-               } else {
-                       $db = wfGetDB( DB_SLAVE );
-               }
+               $db = wfGetDB( DB_SLAVE );
 
                $blNamespace = "{$prefix}_namespace";
                $blTitle = "{$prefix}_title";
@@ -4703,6 +4699,18 @@ class Title implements LinkTarget {
                        return $wgLang;
                }
 
+               // Checking if DB language is set
+               $dbPageLanguage = $this->getDbPageLanguageCode();
+               if ( $dbPageLanguage ) {
+                       $pageLang = wfGetLangObj( $dbPageLanguage );
+                       $variant = $pageLang->getPreferredVariant();
+                       if ( $pageLang->getCode() !== $variant ) {
+                               $pageLang = Language::factory( $variant );
+                       }
+
+                       return $pageLang;
+               }
+
                // @note Can't be cached persistently, depends on user settings.
                // @note ContentHandler::getPageViewLanguage() may need to load the
                //   content to determine the page language!
index 7e561cb..db2cf86 100644 (file)
@@ -192,8 +192,11 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
                        $this->addWhereIf( 'rc_user != 0', isset( $show['!anon'] ) );
                        $this->addWhereIf( 'rc_patrolled = 0', isset( $show['!patrolled'] ) );
                        $this->addWhereIf( 'rc_patrolled != 0', isset( $show['patrolled'] ) );
-                       $this->addWhereIf( 'wl_notificationtimestamp IS NOT NULL', isset( $show['unread'] ) );
-                       $this->addWhereIf( 'wl_notificationtimestamp IS NULL', isset( $show['!unread'] ) );
+                       $this->addWhereIf( 'rc_timestamp >= wl_notificationtimestamp', isset( $show['unread'] ) );
+                       $this->addWhereIf(
+                               'wl_notificationtimestamp IS NULL OR rc_timestamp < wl_notificationtimestamp',
+                               isset( $show['!unread'] )
+                       );
                }
 
                if ( !is_null( $params['type'] ) ) {
index 2599cf2..485d523 100644 (file)
@@ -387,7 +387,7 @@ class ChangesList extends ContextSource {
                        $params = [ 'redirect' => 'no' ];
                }
 
-               $articlelink = Linker::linkKnown(
+               $articlelink = Linker::link(
                        $rc->getTitle(),
                        null,
                        [ 'class' => 'mw-changeslist-title' ],
index d741b2f..351d438 100644 (file)
@@ -1069,7 +1069,9 @@ abstract class DatabaseBase implements IDatabase {
                $table, $var, $cond = '', $fname = __METHOD__, $options = [], $join_conds = []
        ) {
                if ( $var === '*' ) { // sanity
-                       throw new DBUnexpectedError( $this, "Cannot use a * field: got '$var'" );
+                       throw new DBUnexpectedError( $this, "Cannot use a * field" );
+               } elseif ( !is_string( $var ) ) { // sanity
+                       throw new DBUnexpectedError( $this, "Cannot use an array of fields" );
                }
 
                if ( !is_array( $options ) ) {
index 5f4dda9..ece29e8 100644 (file)
@@ -349,7 +349,7 @@ class CSSMin {
                                                        $url = $match['file'] . $match['query'];
                                                        $file = $local . $match['file'];
                                                        if (
-                                                               !CSSMin::isRemoteUrl( $url ) && !CSSMin::isLocalUrl( $url )
+                                                               !self::isRemoteUrl( $url ) && !self::isLocalUrl( $url )
                                                                && file_exists( $file )
                                                        ) {
                                                                $mimeTypes[ CSSMin::getMimeType( $file ) ] = true;
@@ -391,11 +391,10 @@ class CSSMin {
        /**
         * Is this CSS rule referencing a remote URL?
         *
-        * @private Until we require PHP 5.5 and we can access self:: from closures.
         * @param string $maybeUrl
         * @return bool
         */
-       public static function isRemoteUrl( $maybeUrl ) {
+       protected static function isRemoteUrl( $maybeUrl ) {
                if ( substr( $maybeUrl, 0, 2 ) === '//' || parse_url( $maybeUrl, PHP_URL_SCHEME ) ) {
                        return true;
                }
@@ -405,11 +404,10 @@ class CSSMin {
        /**
         * Is this CSS rule referencing a local URL?
         *
-        * @private Until we require PHP 5.5 and we can access self:: from closures.
         * @param string $maybeUrl
         * @return bool
         */
-       public static function isLocalUrl( $maybeUrl ) {
+       protected static function isLocalUrl( $maybeUrl ) {
                if ( $maybeUrl !== '' && $maybeUrl[0] === '/' && !self::isRemoteUrl( $maybeUrl ) ) {
                        return true;
                }
index c800c36..ddea695 100644 (file)
@@ -33,6 +33,11 @@ class IRCColourfulRCFeedFormatter implements RCFeedFormatter {
                global $wgUseRCPatrol, $wgUseNPPatrol, $wgLocalInterwikis,
                        $wgCanonicalServer, $wgScript;
                $attribs = $rc->getAttributes();
+               if ( $attribs['rc_type'] == RC_CATEGORIZE ) {
+                       // Don't send RC_CATEGORIZE events to IRC feed (T127360)
+                       return null;
+               }
+
                if ( $attribs['rc_type'] == RC_LOG ) {
                        // Don't use SpecialPage::getTitleFor, backwards compatibility with
                        // IRC API which expects "Log".
index 2318c87..fe9304f 100644 (file)
@@ -253,14 +253,24 @@ class ExtensionProcessor implements Processor {
                        ? $info['ResourceFileModulePaths']
                        : false;
                if ( isset( $defaultPaths['localBasePath'] ) ) {
-                       $defaultPaths['localBasePath'] = "$dir/{$defaultPaths['localBasePath']}";
+                       if ( $defaultPaths['localBasePath'] === '' ) {
+                               // Avoid double slashes (e.g. /extensions/Example//path)
+                               $defaultPaths['localBasePath'] = $dir;
+                       } else {
+                               $defaultPaths['localBasePath'] = "$dir/{$defaultPaths['localBasePath']}";
+                       }
                }
 
                foreach ( [ 'ResourceModules', 'ResourceModuleSkinStyles' ] as $setting ) {
                        if ( isset( $info[$setting] ) ) {
                                foreach ( $info[$setting] as $name => $data ) {
                                        if ( isset( $data['localBasePath'] ) ) {
-                                               $data['localBasePath'] = "$dir/{$data['localBasePath']}";
+                                               if ( $data['localBasePath'] === '' ) {
+                                                       // Avoid double slashes (e.g. /extensions/Example//path)
+                                                       $data['localBasePath'] = $dir;
+                                               } else {
+                                                       $data['localBasePath'] = "$dir/{$data['localBasePath']}";
+                                               }
                                        }
                                        if ( $defaultPaths ) {
                                                $data += $defaultPaths;
index 5573ec7..0abec1b 100644 (file)
@@ -358,19 +358,21 @@ final class SessionManager implements SessionManagerInterface {
 
                // Try the local user from the slave DB
                $localId = User::idFromName( $user->getName() );
+               $flags = 0;
 
                // Fetch the user ID from the master, so that we don't try to create the user
                // when they already exist, due to replication lag
                // @codeCoverageIgnoreStart
                if ( !$localId && wfGetLB()->getReaderIndex() != 0 ) {
                        $localId = User::idFromName( $user->getName(), User::READ_LATEST );
+                       $flags = User::READ_LATEST;
                }
                // @codeCoverageIgnoreEnd
 
                if ( $localId ) {
                        // User exists after all.
                        $user->setId( $localId );
-                       $user->loadFromId();
+                       $user->loadFromId( $flags );
                        return false;
                }
 
@@ -475,12 +477,21 @@ final class SessionManager implements SessionManagerInterface {
                        $status = $user->addToDatabase();
                        if ( !$status->isOK() ) {
                                // @codeCoverageIgnoreStart
-                               $logger->error( __METHOD__ . ': failed with message ' . $status->getWikiText(),
-                                       [
-                                               'username' => $userName,
-                               ] );
-                               $user->setId( 0 );
-                               $user->loadFromId();
+                               // double-check for a race condition (T70012)
+                               $id = User::idFromName( $user->getName(), User::READ_LATEST );
+                               if ( $id ) {
+                                       $logger->info( __METHOD__ . ': tried to autocreate existing user',
+                                               [
+                                                       'username' => $userName,
+                                               ] );
+                               } else {
+                                       $logger->error( __METHOD__ . ': failed with message ' . $status->getWikiText(),
+                                               [
+                                                       'username' => $userName,
+                                               ] );
+                               }
+                               $user->setId( $id );
+                               $user->loadFromId( User::READ_LATEST );
                                return false;
                                // @codeCoverageIgnoreEnd
                        }
index 11357fb..37f769e 100644 (file)
@@ -346,6 +346,24 @@ class SpecialBotPasswords extends FormSpecialPage {
                $out->addReturnTo( $this->getPageTitle() );
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return [];
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index d1569c1..d64697d 100644 (file)
        "uncategorizedtemplates": "Uncategorised templates",
        "sessionfailure": "There seems to be a problem with your login session;\nthis action has been cancelled as a precaution against session hijacking.\nGo back to the previous page, reload that page and then try again.",
        "blockiptext": "Use the form below to block write access from a specific IP address or username.\nThis should be done only to prevent vandalism, and in accordance with [[{{MediaWiki:Policy-url}}|policy]].\nFill in a specific reason below (for example, citing particular pages that were vandalised).",
+       "blocklogentry": "blocked [[$1]] with an expiry time of $2 $3",
+       "blockedtext": "<strong>Your username or IP address has been blocked.</strong>\n\nThe block was made by $1.\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiry of block: $6\n* Intended blockee: $7\n\nYou can contact $1 or another [[{{MediaWiki:Grouppage-sysop}}|administrator]] to discuss the block.\nYou cannot use the \"email this user\" feature unless a valid email address is specified in your [[Special:Preferences|account preferences]] and you have not been blocked from using it.\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
+       "autoblockedtext": "Your IP address has been automatically blocked because it was used by another user, who was blocked by $1.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiry of block: $6\n* Intended blockee: $7\n\nYou may contact $1 or one of the other [[{{MediaWiki:Grouppage-sysop}}|administrators]] to discuss the block.\n\nNote that you may not use the \"email this user\" feature unless you have a valid email address registered in your [[Special:Preferences|user preferences]] and you have not been blocked from using it.\n\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
+       "reblock-logentry": "changed block settings for [[$1]] with an expiry time of $2 $3",
+       "ipbexpiry": "Expiry:",
        "ipbreason-dropdown": "*Common block reasons\n** Inserting false information\n** Removing content from pages\n** Spamming links to external sites\n** Inserting nonsense/gibberish into pages\n** Intimidating behaviour/harassment\n** Abusing multiple accounts\n** Unacceptable username",
        "proxyblockreason": "Your IP address has been blocked because it is an open proxy.\nPlease contact your Internet service provider or technical support of your organisation and inform them of this serious security problem.",
        "movecategorypage-warning": "<strong>Warning:</strong> You are about to move a category page. Please note that only the page will be moved and any pages in the old category will <em>not</em> be recategorised into the new one.",
        "version-license-not-found": "No detailed licence information was found for this extension.",
        "version-credits-summary": "We would like to recognise the following persons for their contribution to [[Special:Version|MediaWiki]].",
        "version-license-info": "MediaWiki is free software; you can redistribute it and/or modify it under the terms of the GNU General Public Licence as published by the Free Software Foundation; either version 2 of the Licence, or (at your option) any later version.\n\nMediaWiki 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 Licence for more details.\n\nYou should have received [{{SERVER}}{{SCRIPTPATH}}/COPYING a copy of the GNU General Public Licence] along with this programme; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA or [//www.gnu.org/licenses/old-licenses/gpl-2.0.html read it online].",
-       "feedback-error1": "Error: Unrecognised result from API"
+       "feedback-error1": "Error: Unrecognised result from API",
+       "logentry-block-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} with an expiry time of $5 $6",
+       "logentry-block-reblock": "$1 {{GENDER:$2|changed}} block settings for {{GENDER:$4|$3}} with an expiry time of $5 $6",
+       "logentry-suppress-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} with an expiry time of $5 $6",
+       "logentry-suppress-reblock": "$1 {{GENDER:$2|changed}} block settings for {{GENDER:$4|$3}} with an expiry time of $5 $6",
+       "protect_expiry_invalid": "Expiry time is invalid.",
+       "protect_expiry_old": "Expiry time is in the past.",
+       "protect-existing-expiry": "Existing expiry time: $3, $2",
+       "protect-existing-expiry-infinity": "Existing expiry time: infinite"
 }
index 5e9b186..eb48dd6 100644 (file)
        "subject-preview": "Subject preview:",
        "previewerrortext": "An error occurred while attempting to preview your changes.",
        "blockedtitle": "User is blocked",
-       "blockedtext": "<strong>Your username or IP address has been blocked.</strong>\n\nThe block was made by $1.\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiry of block: $6\n* Intended blockee: $7\n\nYou can contact $1 or another [[{{MediaWiki:Grouppage-sysop}}|administrator]] to discuss the block.\nYou cannot use the \"email this user\" feature unless a valid email address is specified in your [[Special:Preferences|account preferences]] and you have not been blocked from using it.\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
-       "autoblockedtext": "Your IP address has been automatically blocked because it was used by another user, who was blocked by $1.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiry of block: $6\n* Intended blockee: $7\n\nYou may contact $1 or one of the other [[{{MediaWiki:Grouppage-sysop}}|administrators]] to discuss the block.\n\nNote that you may not use the \"email this user\" feature unless you have a valid email address registered in your [[Special:Preferences|user preferences]] and you have not been blocked from using it.\n\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
+       "blockedtext": "<strong>Your username or IP address has been blocked.</strong>\n\nThe block was made by $1.\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou can contact $1 or another [[{{MediaWiki:Grouppage-sysop}}|administrator]] to discuss the block.\nYou cannot use the \"email this user\" feature unless a valid email address is specified in your [[Special:Preferences|account preferences]] and you have not been blocked from using it.\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
+       "autoblockedtext": "Your IP address has been automatically blocked because it was used by another user, who was blocked by $1.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou may contact $1 or one of the other [[{{MediaWiki:Grouppage-sysop}}|administrators]] to discuss the block.\n\nNote that you may not use the \"email this user\" feature unless you have a valid email address registered in your [[Special:Preferences|user preferences]] and you have not been blocked from using it.\n\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
        "blockednoreason": "no reason given",
        "whitelistedittext": "Please $1 to edit pages.",
        "confirmedittext": "You must confirm your email address before editing pages.\nPlease set and validate your email address through your [[Special:Preferences|user preferences]].",
        "protect-legend": "Confirm protection",
        "protectcomment": "Reason:",
        "protectexpiry": "Expires:",
-       "protect_expiry_invalid": "Expiry time is invalid.",
-       "protect_expiry_old": "Expiry time is in the past.",
+       "protect_expiry_invalid": "Expiration time is invalid.",
+       "protect_expiry_old": "Expiration time is in the past.",
        "protect-unchain-permissions": "Unlock further protect options",
        "protect-text": "Here you may view and change the protection level for the page <strong>$1</strong>.",
        "protect-locked-blocked": "You cannot change protection levels while blocked.\nHere are the current settings for the page <strong>$1</strong>:",
        "protect-cantedit": "You cannot change the protection levels of this page because you do not have permission to edit it.",
        "protect-othertime": "Other time:",
        "protect-othertime-op": "other time",
-       "protect-existing-expiry": "Existing expiry time: $3, $2",
-       "protect-existing-expiry-infinity": "Existing expiry time: infinite",
+       "protect-existing-expiry": "Existing expiration time: $3, $2",
+       "protect-existing-expiry-infinity": "Existing expiration time: infinite",
        "protect-otherreason": "Other/additional reason:",
        "protect-otherreason-op": "Other reason",
        "protect-dropdown": "*Common protection reasons\n** Excessive vandalism\n** Excessive spamming\n** Counter-productive edit warring\n** High traffic page",
        "blockip-legend": "Block user",
        "blockiptext": "Use the form below to block write access from a specific IP address or username.\nThis should be done only to prevent vandalism, and in accordance with [[{{MediaWiki:Policy-url}}|policy]].\nFill in a specific reason below (for example, citing particular pages that were vandalized).\nYou can block IP ranges using the [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] syntax; the largest allowed range is /$1 for IPv4 and /$2 for IPv6.",
        "ipaddressorusername": "IP address or username:",
-       "ipbexpiry": "Expiry:",
+       "ipbexpiry": "Expiration:",
        "ipbreason": "Reason:",
        "ipbreason-dropdown": "*Common block reasons\n** Inserting false information\n** Removing content from pages\n** Spamming links to external sites\n** Inserting nonsense/gibberish into pages\n** Intimidating behavior/harassment\n** Abusing multiple accounts\n** Unacceptable username",
        "ipb-hardblock": "Prevent logged-in users from editing from this IP address",
        "blocklogpage": "Block log",
        "blocklog-showlog": "This user has been blocked previously.\nThe block log is provided below for reference:",
        "blocklog-showsuppresslog": "This user has been blocked and hidden previously.\nThe suppress log is provided below for reference:",
-       "blocklogentry": "blocked [[$1]] with an expiry time of $2 $3",
-       "reblock-logentry": "changed block settings for [[$1]] with an expiry time of $2 $3",
+       "blocklogentry": "blocked [[$1]] with an expiration time of $2 $3",
+       "reblock-logentry": "changed block settings for [[$1]] with an expiration time of $2 $3",
        "blocklogtext": "This is a log of user blocking and unblocking actions.\nAutomatically blocked IP addresses are not listed.\nSee the [[Special:BlockList|block list]] for the list of currently operational bans and blocks.",
        "unblocklogentry": "unblocked $1",
        "block-log-flags-anononly": "anonymous users only",
        "revdelete-uname-unhid": "username unhidden",
        "revdelete-restricted": "applied restrictions to administrators",
        "revdelete-unrestricted": "removed restrictions for administrators",
-       "logentry-block-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} with an expiry time of $5 $6",
+       "logentry-block-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} with an expiration time of $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$2|unblocked}} {{GENDER:$4|$3}}",
-       "logentry-block-reblock": "$1 {{GENDER:$2|changed}} block settings for {{GENDER:$4|$3}} with an expiry time of $5 $6",
-       "logentry-suppress-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} with an expiry time of $5 $6",
-       "logentry-suppress-reblock": "$1 {{GENDER:$2|changed}} block settings for {{GENDER:$4|$3}} with an expiry time of $5 $6",
+       "logentry-block-reblock": "$1 {{GENDER:$2|changed}} block settings for {{GENDER:$4|$3}} with an expiration time of $5 $6",
+       "logentry-suppress-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} with an expiration time of $5 $6",
+       "logentry-suppress-reblock": "$1 {{GENDER:$2|changed}} block settings for {{GENDER:$4|$3}} with an expiration time of $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|imported}} $3 by file upload",
        "logentry-import-upload-details": "$1 {{GENDER:$2|imported}} $3 by file upload ($4 {{PLURAL:$4|revision|revisions}})",
        "logentry-import-interwiki": "$1 {{GENDER:$2|imported}} $3 from another wiki",
index 730119e..ab83e1a 100644 (file)
                        $( '<label>' )
                                .text( mw.msg( 'powersearch-togglelabel' ) )
                ).append(
-                       $( '<input type="button" />' )
+                       $( '<input>' ).attr( 'type', 'button' )
                                .attr( 'id', 'mw-search-toggleall' )
                                .prop( 'value', mw.msg( 'powersearch-toggleall' ) )
                                .click( function () {
                                        $checkboxes.prop( 'checked', true );
                                } )
                ).append(
-                       $( '<input type="button" />' )
+                       $( '<input>' ).attr( 'type', 'button' )
                                .attr( 'id', 'mw-search-togglenone' )
                                .prop( 'value', mw.msg( 'powersearch-togglenone' ) )
                                .click( function () {
index d44e5d7..b0fbf51 100644 (file)
@@ -75,8 +75,7 @@
                        background-position: center center;
                        background-origin: border-box;
                        background-repeat: no-repeat;
-                       .background-size( @checkboxSize - 0.2em, @checkboxSize - 0.2em );
-                       background-size: 0 0;
+                       .background-size( 0, 0 );
                        .box-sizing( border-box );
                        position: absolute;
                        // align the checkbox to middle of the text
@@ -93,7 +92,7 @@
 
                // when the input is checked, style the label pseudo before element that followed as a checked checkbox
                &:checked + label::before {
-                       background-size: 100% 100%;
+                       .background-size( 100%, 100% );
                }
 
                &:active + label::before {
index 448390a..53c22b4 100644 (file)
@@ -67,8 +67,7 @@
                        background-origin: border-box;
                        background-position: center center;
                        background-repeat: no-repeat;
-                       .background-size( @radioSize, @radioSize );
-                       background-size: 0 0;
+                       .background-size( 0, 0 );
                        .box-sizing( border-box );
                        position: absolute;
                        left: 0;
@@ -81,7 +80,7 @@
 
                // when the input is checked, style the label pseudo before element that followed as a checked radio
                &:checked + label::before {
-                       background-size: 100% 100%;
+                       .background-size( 100%, 100% );
                }
 
                &:active + label::before {
index df148c7..7f36137 100644 (file)
                                        if ( spec.intercalarySize ) {
                                                $.each( spec.intercalarySize, reduceFunc );
                                        }
-                                       $field = $( '<input type="text">' )
+                                       $field = $( '<input>' ).attr( 'type', 'text' )
                                                .attr( {
                                                        tabindex: disabled ? -1 : 0,
                                                        size: spec.size,
         * @private
         */
        mw.widgets.datetime.DateTimeInputWidget.prototype.getInputElement = function () {
-               return $( '<input type="hidden" />' );
+               return $( '<input>' ).attr( 'type', 'hidden' );
        };
 
        /**
index 3e03502..cba580b 100644 (file)
         * @protected
         */
        mw.widgets.DateInputWidget.prototype.getInputElement = function () {
-               return $( '<input type="hidden">' );
+               return $( '<input>' ).attr( 'type', 'hidden' );
        };
 
        /**
index 47d80d6..437ddec 100644 (file)
@@ -61,7 +61,7 @@
         * @return {jQuery}
         */
        function getHiddenInput( name, val ) {
-               return $( '<input type="hidden" />' )
+               return $( '<input>' ).attr( 'type', 'hidden' )
                        .attr( 'name', name )
                        .val( val );
        }
index 8a3784c..170e124 100644 (file)
@@ -41,7 +41,7 @@
         * @cfg {string} [apiUrl] api.php URL if the feedback page is on another wiki
         * @cfg {string} [dialogTitleMessageKey="feedback-dialog-title"] Message key for the
         *  title of the dialog box
-        * @cfg {mw.Uri|string} [bugsLink="//phabricator.wikimedia.org/maniphest/task/create/"] URL where
+        * @cfg {mw.Uri|string} [bugsLink="//phabricator.wikimedia.org/maniphest/task/edit/form/1/"] URL where
         *  bugs can be posted
         * @cfg {mw.Uri|string} [bugsListLink="//phabricator.wikimedia.org/maniphest/query/advanced"] URL
         *  where bugs can be listed
@@ -61,7 +61,7 @@
                this.messagePosterPromise = mw.messagePoster.factory.create( this.feedbackPageTitle, config.apiUrl );
 
                // Links
-               this.bugsTaskSubmissionLink = config.bugsLink || '//phabricator.wikimedia.org/maniphest/task/create/';
+               this.bugsTaskSubmissionLink = config.bugsLink || '//phabricator.wikimedia.org/maniphest/task/edit/form/1/';
                this.bugsTaskListLink = config.bugsListLink || '//phabricator.wikimedia.org/maniphest/query/advanced';
 
                // Terms of use
index 6c0a923..8902ecd 100644 (file)
@@ -155,7 +155,7 @@ class CSSMinTest extends MediaWikiTestCase {
         * @cover CSSMin::isRemoteUrl
         */
        public function testIsRemoteUrl( $expect, $url ) {
-               $this->assertEquals( CSSMin::isRemoteUrl( $url ), $expect );
+               $this->assertEquals( CSSMinTestable::isRemoteUrl( $url ), $expect );
        }
 
        public static function provideIsLocalUrls() {
@@ -172,7 +172,7 @@ class CSSMinTest extends MediaWikiTestCase {
         * @cover CSSMin::isLocalUrl
         */
        public function testIsLocalUrl( $expect, $url ) {
-               $this->assertEquals( CSSMin::isLocalUrl( $url ), $expect );
+               $this->assertEquals( CSSMinTestable::isLocalUrl( $url ), $expect );
        }
 
        public static function provideRemapRemappingCases() {
@@ -443,3 +443,13 @@ class CSSMinTest extends MediaWikiTestCase {
                ];
        }
 }
+
+class CSSMinTestable extends CSSMin {
+       // Make some protected methods public
+       public static function isRemoteUrl( $maybeUrl ) {
+               return parent::isRemoteUrl( $maybeUrl );
+       }
+       public static function isLocalUrl( $maybeUrl ) {
+               return parent::isLocalUrl( $maybeUrl );
+       }
+}
index 8102437..4158ea2 100644 (file)
@@ -25,7 +25,8 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                        ],
                                ],
                                [
-                                       'text' => 'Sysop blocked Logtestuser with an expiry time of indefinite (anonymous users only)',
+                                       'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite'
+                                               . ' (anonymous users only)',
                                        'api' => [
                                                'duration' => 'infinite',
                                                'flags' => [ 'anononly' ],
@@ -50,7 +51,8 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                ],
                                [
                                        'legacy' => true,
-                                       'text' => 'Sysop blocked Logtestuser with an expiry time of indefinite (anonymous users only)',
+                                       'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite'
+                                               . ' (anonymous users only)',
                                        'api' => [
                                                'duration' => 'infinite',
                                                'flags' => [ 'anononly' ],
@@ -74,7 +76,7 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                ],
                                [
                                        'legacy' => true,
-                                       'text' => 'Sysop blocked Logtestuser with an expiry time of indefinite',
+                                       'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite',
                                        'api' => [
                                                'duration' => 'infinite',
                                                'flags' => [],
@@ -96,7 +98,7 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                ],
                                [
                                        'legacy' => true,
-                                       'text' => 'Sysop blocked Logtestuser with an expiry time of indefinite',
+                                       'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite',
                                        'api' => [
                                                'duration' => 'infinite',
                                                'flags' => [],
@@ -136,7 +138,7 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                        ],
                                ],
                                [
-                                       'text' => 'Sysop changed block settings for Logtestuser with an expiry time of'
+                                       'text' => 'Sysop changed block settings for Logtestuser with an expiration time of'
                                                . ' indefinite (anonymous users only)',
                                        'api' => [
                                                'duration' => 'infinite',
@@ -162,7 +164,7 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                ],
                                [
                                        'legacy' => true,
-                                       'text' => 'Sysop changed block settings for Logtestuser with an expiry time of'
+                                       'text' => 'Sysop changed block settings for Logtestuser with an expiration time of'
                                                . ' indefinite (anonymous users only)',
                                        'api' => [
                                                'duration' => 'infinite',
@@ -187,7 +189,7 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                ],
                                [
                                        'legacy' => true,
-                                       'text' => 'Sysop changed block settings for Logtestuser with an expiry time of indefinite',
+                                       'text' => 'Sysop changed block settings for Logtestuser with an expiration time of indefinite',
                                        'api' => [
                                                'duration' => 'infinite',
                                                'flags' => [],
@@ -261,7 +263,8 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                        ],
                                ],
                                [
-                                       'text' => 'Sysop blocked Logtestuser with an expiry time of indefinite (anonymous users only)',
+                                       'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite'
+                                               . ' (anonymous users only)',
                                        'api' => [
                                                'duration' => 'infinite',
                                                'flags' => [ 'anononly' ],
@@ -286,7 +289,8 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                ],
                                [
                                        'legacy' => true,
-                                       'text' => 'Sysop blocked Logtestuser with an expiry time of indefinite (anonymous users only)',
+                                       'text' => 'Sysop blocked Logtestuser with an expiration time of indefinite'
+                                               . ' (anonymous users only)',
                                        'api' => [
                                                'duration' => 'infinite',
                                                'flags' => [ 'anononly' ],
@@ -326,7 +330,7 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                        ],
                                ],
                                [
-                                       'text' => 'Sysop changed block settings for Logtestuser with an expiry time of'
+                                       'text' => 'Sysop changed block settings for Logtestuser with an expiration time of'
                                                . ' indefinite (anonymous users only)',
                                        'api' => [
                                                'duration' => 'infinite',
@@ -352,7 +356,7 @@ class BlockLogFormatterTest extends LogFormatterTestCase {
                                ],
                                [
                                        'legacy' => true,
-                                       'text' => 'Sysop changed block settings for Logtestuser with an expiry time of'
+                                       'text' => 'Sysop changed block settings for Logtestuser with an expiration time of'
                                                . ' indefinite (anonymous users only)',
                                        'api' => [
                                                'duration' => 'infinite',
index 7f84e33..35aca48 100644 (file)
@@ -218,7 +218,7 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
        }
 
        public static function provideExtractResourceLoaderModules() {
-               $dir = __DIR__ . '/FooBar/';
+               $dir = __DIR__ . '/FooBar';
                return [
                        // Generic module with localBasePath/remoteExtPath specified
                        [
@@ -285,7 +285,7 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
                                                ],
                                                'test.bar' => [
                                                        'styles' => 'bar.js',
-                                                       'localBasePath' => $dir . 'subdir',
+                                                       'localBasePath' => "$dir/subdir",
                                                        'remoteExtPath' => 'FooBar/subdir',
                                                ],
                                                'test.class' => [
index 0cb9cc8..81c116c 100644 (file)
 
        byteLimitTest( {
                description: 'Plain text input',
-               $input: $( '<input type="text"/>' ),
+               $input: $( '<input>' ).attr( 'type', 'text' ),
                sample: simpleSample,
                expected: simpleSample
        } );
 
        byteLimitTest( {
                description: 'Plain text input. Calling byteLimit with no parameters and no maxlength attribute (bug 36310)',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                        .byteLimit(),
                sample: simpleSample,
                expected: simpleSample
@@ -81,7 +81,7 @@
 
        byteLimitTest( {
                description: 'Limit using the maxlength attribute',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                        .attr( 'maxlength', '10' )
                        .byteLimit(),
                sample: simpleSample,
@@ -90,7 +90,7 @@
 
        byteLimitTest( {
                description: 'Limit using a custom value',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                        .byteLimit( 10 ),
                sample: simpleSample,
                expected: '1234567890'
@@ -98,7 +98,7 @@
 
        byteLimitTest( {
                description: 'Limit using a custom value, overriding maxlength attribute',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                        .attr( 'maxlength', '10' )
                        .byteLimit( 15 ),
                sample: simpleSample,
 
        byteLimitTest( {
                description: 'Limit using a custom value (multibyte)',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                        .byteLimit( 14 ),
                sample: mbSample,
                expected: '1234567890' + U_20AC + '1'
 
        byteLimitTest( {
                description: 'Limit using a custom value (multibyte) overlapping a byte',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                        .byteLimit( 12 ),
                sample: mbSample,
                expected: '1234567890' + '12'
 
        byteLimitTest( {
                description: 'Pass the limit and a callback as input filter',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                        .byteLimit( 6, function ( val ) {
                                var title = mw.Title.newFromText( String( val ) );
                                // Return without namespace prefix
 
        byteLimitTest( {
                description: 'Limit using the maxlength attribute and pass a callback as input filter',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                        .attr( 'maxlength', '6' )
                        .byteLimit( function ( val ) {
                                var title = mw.Title.newFromText( String( val ) );
 
        byteLimitTest( {
                description: 'Pass the limit and a callback as input filter',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                        .byteLimit( 6, function ( val ) {
                                var title = mw.Title.newFromText( String( val ) );
                                // Return without namespace prefix
 
        byteLimitTest( {
                description: 'Input filter that increases the length',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                .byteLimit( 10, function ( text ) {
                        return 'prefix' + text;
                } ),
        // Regression tests for bug 41450
        byteLimitTest( {
                description: 'Input filter of which the base exceeds the limit',
-               $input: $( '<input type="text"/>' )
+               $input: $( '<input>' ).attr( 'type', 'text' )
                .byteLimit( 3, function ( text ) {
                        return 'prefix' + text;
                } ),
        QUnit.test( 'Confirm properties and attributes set', 4, function ( assert ) {
                var $el, $elA, $elB;
 
-               $el = $( '<input type="text"/>' )
+               $el = $( '<input>' ).attr( 'type', 'text' )
                        .attr( 'maxlength', '7' )
                        .appendTo( '#qunit-fixture' )
                        .byteLimit();
 
                assert.strictEqual( $el.attr( 'maxlength' ), '7', 'maxlength attribute unchanged for simple limit' );
 
-               $el = $( '<input type="text"/>' )
+               $el = $( '<input>' ).attr( 'type', 'text' )
                        .attr( 'maxlength', '7' )
                        .appendTo( '#qunit-fixture' )
                        .byteLimit( 12 );
 
                assert.strictEqual( $el.attr( 'maxlength' ), '12', 'maxlength attribute updated for custom limit' );
 
-               $el = $( '<input type="text"/>' )
+               $el = $( '<input>' ).attr( 'type', 'text' )
                        .attr( 'maxlength', '7' )
                        .appendTo( '#qunit-fixture' )
                        .byteLimit( 12, function ( val ) {
 
                assert.strictEqual( $el.attr( 'maxlength' ), undefined, 'maxlength attribute removed for limit with callback' );
 
-               $elA = $( '<input type="text"/>' )
+               $elA = $( '<input>' ).attr( 'type', 'text' )
                        .addClass( 'mw-test-byteLimit-foo' )
                        .attr( 'maxlength', '7' )
                        .appendTo( '#qunit-fixture' );
 
-               $elB = $( '<input type="text"/>' )
+               $elB = $( '<input>' ).attr( 'type', 'text' )
                        .addClass( 'mw-test-byteLimit-foo' )
                        .attr( 'maxlength', '12' )
                        .appendTo( '#qunit-fixture' );
 
                // Use a new <input /> because the bug only occurs on the first time
                // the limit it reached (bug 40850)
-               $el = $( '<input type="text"/>' )
+               $el = $( '<input>' ).attr( 'type', 'text' )
                        .appendTo( '#qunit-fixture' )
                        .byteLimit( 3 )
                        .val( 'abc' ).trigger( 'change' )
 
                assert.strictEqual( $el.val(), 'abc', 'Trim from the insertion point (at 0), not the end' );
 
-               $el = $( '<input type="text"/>' )
+               $el = $( '<input>' ).attr( 'type', 'text' )
                        .appendTo( '#qunit-fixture' )
                        .byteLimit( 3 )
                        .val( 'abc' ).trigger( 'change' )