Merge "includes: Replace implicit Bugzilla bug numbers with Phab ones"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 28 Feb 2017 00:51:57 +0000 (00:51 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 28 Feb 2017 00:51:57 +0000 (00:51 +0000)
14 files changed:
1  2 
includes/DefaultSettings.php
includes/EditPage.php
includes/FeedUtils.php
includes/Linker.php
includes/Setup.php
includes/WatchedItemQueryService.php
includes/WebStart.php
includes/objectcache/SqlBagOStuff.php
includes/page/Article.php
includes/page/ImagePage.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/user/User.php

@@@ -1337,7 -1337,7 +1337,7 @@@ $wgXMLMimeTypes = 
   * to reduce disk usage, limits can only be selected from a list.
   * The user preference is saved as an array offset in the database, by default
   * the offset is set with $wgDefaultUserOptions['imagesize']. Make sure you
-  * change it if you alter the array (see bug 8858).
+  * change it if you alter the array (see T10858).
   * This is the list of settings the user can choose from:
   */
  $wgImageLimits = [
@@@ -3357,7 -3357,7 +3357,7 @@@ $wgDisableOutputCompression = false
   *
   * Currently this appears to work fine in all browsers, but it's disabled by
   * default because it normalizes id's a bit too aggressively, breaking preexisting
-  * content (particularly Cite).  See bug 27733, bug 27694, bug 27474.
+  * content (particularly Cite).  See T29733, T29694, T29474.
   */
  $wgExperimentalHtmlIds = false;
  
@@@ -4081,7 -4081,7 +4081,7 @@@ $wgMaxRedirects = 1
   * Attempting to create a redirect to any of the pages in this array
   * will make the redirect fail.
   * Userlogout is hard-coded, so it does not need to be listed here.
-  * (bug 10569) Disallow Mypage and Mytalk as well.
+  * (T12569) Disallow Mypage and Mytalk as well.
   *
   * As of now, this only checks special pages. Redirects to pages in
   * other namespaces cannot be invalidated by this variable.
@@@ -5682,7 -5682,7 +5682,7 @@@ $wgRateLimits = 
  ];
  
  /**
 - * Array of IPs which should be excluded from rate limits.
 + * Array of IPs / CIDR ranges which should be excluded from rate limits.
   * This may be useful for whitelisting NAT gateways for conferences, etc.
   */
  $wgRateLimitsExcludedIPs = [];
@@@ -8372,7 -8372,7 +8372,7 @@@ $wgPagePropsHaveSortkey = true
  /**
   * Port where you have HTTPS running
   * Supports HTTPS on non-standard ports
-  * @see bug 65184
+  * @see T67184
   * @since 1.24
   */
  $wgHttpsPort = 443;
diff --combined includes/EditPage.php
@@@ -993,7 -993,7 +993,7 @@@ class EditPage 
                        $this->recreate = false;
  
                        // When creating a new section, we can preload a section title by passing it as the
-                       // preloadtitle parameter in the URL (Bug 13100)
+                       // preloadtitle parameter in the URL (T15100)
                        if ( $this->section == 'new' && $request->getVal( 'preloadtitle' ) ) {
                                $this->sectiontitle = $request->getVal( 'preloadtitle' );
                                // Once wpSummary isn't being use for setting section titles, we should delete this.
                $revision = $this->mArticle->getRevisionFetched();
                if ( $revision === null ) {
                        if ( !$this->contentModel ) {
 -                              $this->contentModel = $this->getTitle()->getContentModel();
 +                              throw new RuntimeException( 'EditPage contentModel was false' );
                        }
                        $handler = ContentHandler::getForModelID( $this->contentModel );
  
  
                if ( $content === false || $content === null ) {
                        if ( !$this->contentModel ) {
 -                              $this->contentModel = $this->getTitle()->getContentModel();
 +                              throw new RuntimeException( 'EditPage contentModel was false' );
                        }
                        $handler = ContentHandler::getForModelID( $this->contentModel );
  
  
                        // Don't save a new page if it's blank or if it's a MediaWiki:
                        // message with content equivalent to default (allow empty pages
-                       // in this case to disable messages, see bug 50124)
+                       // in this case to disable messages, see T52124)
                        $defaultMessageText = $this->mTitle->getDefaultMessageText();
                        if ( $this->mTitle->getNamespace() === NS_MEDIAWIKI && $defaultMessageText !== false ) {
                                $defaultText = $defaultMessageText;
                }
                # Give a notice if the user is editing a deleted/moved page...
                if ( !$this->mTitle->exists() ) {
 +                      $dbr = wfGetDB( DB_REPLICA );
 +
                        LogEventsList::showLogExtract( $wgOut, [ 'delete', 'move' ], $this->mTitle,
                                '',
                                [
                                        'lim' => 10,
 -                                      'conds' => [ "log_action != 'revision'" ],
 +                                      'conds' => [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ],
                                        'showIfEmpty' => false,
                                        'msgKey' => [ 'recreate-moveddeleted-warn' ]
                                ]
                if ( $this->hasPresetSummary ) {
                        // If a summary has been preset using &summary= we don't want to prompt for
                        // a different summary. Only prompt for a summary if the summary is blanked.
-                       // (Bug 17416)
+                       // (T19416)
                        $this->autoSumm = md5( '' );
                }
  
diff --combined includes/FeedUtils.php
@@@ -129,6 -129,13 +129,6 @@@ class FeedUtils 
                }
  
                if ( $oldid ) {
 -
 -                      # $diffText = $de->getDiff( wfMessage( 'revisionasof',
 -                      #       $wgLang->timeanddate( $timestamp ),
 -                      #       $wgLang->date( $timestamp ),
 -                      #       $wgLang->time( $timestamp ) )->text(),
 -                      #       wfMessage( 'currentrev' )->text() );
 -
                        $diffText = '';
                        // Don't bother generating the diff if we won't be able to show it
                        if ( $wgFeedDiffCutoff > 0 ) {
  
                        if ( $html === null ) {
  
-                               // Omit large new page diffs, bug 29110
+                               // Omit large new page diffs, T31110
                                // Also use diff link for non-textual content
                                $diffText = self::getDiffLink( $title, $newid );
                        } else {
diff --combined includes/Linker.php
@@@ -590,7 -590,7 +590,7 @@@ class Linker 
  
                # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
                # So we don't need to pass it here in $query. However, the URL for the
-               # zoom icon still needs it, so we make a unique query for it. See bug 14771
+               # zoom icon still needs it, so we make a unique query for it. See T16771
                $url = $title->getLocalURL( $query );
                if ( $page ) {
                        $url = wfAppendQuery( $url, [ 'page' => $page ] );
                        if ( $altUserName === false ) {
                                $altUserName = IP::prettifyIP( $userName );
                        }
-                       $classes .= ' mw-anonuserlink'; // Separate link class for anons (bug 43179)
+                       $classes .= ' mw-anonuserlink'; // Separate link class for anons (T45179)
                } else {
                        $page = Title::makeTitle( NS_USER, $userName );
                }
                if ( $userId ) {
                        // check if the user has an edit
                        $attribs = [];
 +                      $attribs['class'] = 'mw-usertoollinks-contribs';
                        if ( $redContribsWhenNoEdits ) {
                                if ( intval( $edits ) === 0 && $edits !== 0 ) {
                                        $user = User::newFromId( $userId );
                                        $edits = $user->getEditCount();
                                }
                                if ( $edits === 0 ) {
 -                                      $attribs['class'] = 'new';
 +                                      $attribs['class'] .= ' new';
                                }
                        }
                        $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
         */
        public static function userTalkLink( $userId, $userText ) {
                $userTalkPage = Title::makeTitle( NS_USER_TALK, $userText );
 -              $userTalkLink = self::link( $userTalkPage, wfMessage( 'talkpagelinktext' )->escaped() );
 +              $moreLinkAttribs['class'] = 'mw-usertoollinks-talk';
 +              $userTalkLink = self::link( $userTalkPage,
 +                                              wfMessage( 'talkpagelinktext' )->escaped(),
 +                                              $moreLinkAttribs );
                return $userTalkLink;
        }
  
         */
        public static function blockLink( $userId, $userText ) {
                $blockPage = SpecialPage::getTitleFor( 'Block', $userText );
 -              $blockLink = self::link( $blockPage, wfMessage( 'blocklink' )->escaped() );
 +              $moreLinkAttribs['class'] = 'mw-usertoollinks-block';
 +              $blockLink = self::link( $blockPage,
 +                                       wfMessage( 'blocklink' )->escaped(),
 +                                       $moreLinkAttribs );
                return $blockLink;
        }
  
         */
        public static function emailLink( $userId, $userText ) {
                $emailPage = SpecialPage::getTitleFor( 'Emailuser', $userText );
 -              $emailLink = self::link( $emailPage, wfMessage( 'emaillink' )->escaped() );
 +              $moreLinkAttribs['class'] = 'mw-usertoollinks-mail';
 +              $emailLink = self::link( $emailPage,
 +                                       wfMessage( 'emaillink' )->escaped(),
 +                                       $moreLinkAttribs );
                return $emailLink;
        }
  
        ) {
                # Sanitize text a bit:
                $comment = str_replace( "\n", " ", $comment );
-               # Allow HTML entities (for bug 13815)
+               # Allow HTML entities (for T15815)
                $comment = Sanitizer::escapeHtmlAllowEntities( $comment );
  
                # Render autocomments and make links:
                                                $section = str_replace( '[[', '', $section );
                                                $section = str_replace( ']]', '', $section );
  
-                                               $section = Sanitizer::normalizeSectionNameWhitespace( $section ); # bug 22784
+                                               $section = Sanitizer::normalizeSectionNameWhitespace( $section ); # T24784
                                                if ( $local ) {
                                                        $sectionTitle = Title::newFromText( '#' . $section );
                                                } else {
                        } else {
                                $suffix = '';
                        }
-                       # bug 7425
+                       # T9425
                        $target = trim( $target );
                        # Look at the first character
                        if ( $target != '' && $target[0] === '/' ) {
  
                if ( $context->getRequest()->getBool( 'bot' ) ) {
                        $query['bot'] = '1';
-                       $query['hidediff'] = '1'; // bug 15999
+                       $query['hidediff'] = '1'; // T17999
                }
  
                $disableRollbackEditCount = false;
diff --combined includes/Setup.php
@@@ -329,7 -329,7 +329,7 @@@ if ( $wgEnableEmail ) 
        $wgUseEnotif = $wgEnotifUserTalk || $wgEnotifWatchlist;
  } else {
        // Disable all other email settings automatically if $wgEnableEmail
-       // is set to false. - bug 63678
+       // is set to false. - T65678
        $wgAllowHTMLEmail = false;
        $wgEmailAuthentication = false; // do not require auth if you're not sending email anyway
        $wgEnableUserEmail = false;
@@@ -521,6 -521,35 +521,6 @@@ if ( $wgSharedDB && $wgSharedTables ) 
  // is complete.
  define( 'MW_SERVICE_BOOTSTRAP_COMPLETE', 1 );
  
 -// Install a header callback to prevent caching of responses with cookies (T127993)
 -if ( !$wgCommandLineMode ) {
 -      header_register_callback( function () {
 -              $headers = [];
 -              foreach ( headers_list() as $header ) {
 -                      list( $name, $value ) = explode( ':', $header, 2 );
 -                      $headers[strtolower( trim( $name ) )][] = trim( $value );
 -              }
 -
 -              if ( isset( $headers['set-cookie'] ) ) {
 -                      $cacheControl = isset( $headers['cache-control'] )
 -                              ? implode( ', ', $headers['cache-control'] )
 -                              : '';
 -
 -                      if ( !preg_match( '/(?:^|,)\s*(?:private|no-cache|no-store)\s*(?:$|,)/i', $cacheControl ) ) {
 -                              header( 'Expires: Thu, 01 Jan 1970 00:00:00 GMT' );
 -                              header( 'Cache-Control: private, max-age=0, s-maxage=0' );
 -                              MediaWiki\Logger\LoggerFactory::getInstance( 'cache-cookies' )->warning(
 -                                      'Cookies set on {url} with Cache-Control "{cache-control}"', [
 -                                              'url' => WebRequest::getGlobalRequestURL(),
 -                                              'cookies' => $headers['set-cookie'],
 -                                              'cache-control' => $cacheControl ?: '<not set>',
 -                                      ]
 -                              );
 -                      }
 -              }
 -      } );
 -}
 -
  MWExceptionHandler::installHandler();
  
  require_once "$IP/includes/compat/normal/UtfNormalUtil.php";
@@@ -2,7 -2,6 +2,7 @@@
  
  use MediaWiki\Linker\LinkTarget;
  use Wikimedia\Assert\Assert;
 +use Wikimedia\Rdbms\LoadBalancer;
  
  /**
   * Class performing complex database queries related to WatchedItems.
@@@ -402,7 -401,7 +402,7 @@@ class WatchedItemQueryService 
                if ( !isset( $options['start'] ) && !isset( $options['end'] ) ) {
                        if ( $db->getType() === 'mysql' ) {
                                // This is an index optimization for mysql
 -                              $conds[] = "rc_timestamp > ''";
 +                              $conds[] = 'rc_timestamp > ' . $db->addQuotes( '' );
                        }
                }
  
                        $conds[] = 'rc_user_text != ' . $db->addQuotes( $options['notByUser'] );
                }
  
-               // Avoid brute force searches (bug 17342)
+               // Avoid brute force searches (T19342)
                $bitmask = 0;
                if ( !$user->isAllowed( 'deletedhistory' ) ) {
                        $bitmask = Revision::DELETED_USER;
diff --combined includes/WebStart.php
@@@ -30,7 -30,7 +30,7 @@@ if ( ini_get( 'mbstring.func_overload' 
        die( 'MediaWiki does not support installations where mbstring.func_overload is non-zero.' );
  }
  
- # bug 15461: Make IE8 turn off content sniffing. Everybody else should ignore this
+ # T17461: Make IE8 turn off content sniffing. Everybody else should ignore this
  # We're adding it here so that it's *always* set, even for alternate entry
  # points and when $wgOut gets disabled or overridden.
  header( 'X-Content-Type-Options: nosniff' );
@@@ -104,9 -104,6 +104,9 @@@ if ( !interface_exists( 'Psr\Log\Logger
        die( 1 );
  }
  
 +# Install a header callback
 +MediaWiki\HeaderCallback::register();
 +
  if ( defined( 'MW_CONFIG_CALLBACK' ) ) {
        # Use a callback function to configure MediaWiki
        call_user_func( MW_CONFIG_CALLBACK );
@@@ -24,7 -24,6 +24,7 @@@
  use \MediaWiki\MediaWikiServices;
  use \Wikimedia\WaitConditionLoop;
  use \Wikimedia\Rdbms\TransactionProfiler;
 +use Wikimedia\Rdbms\LoadBalancer;
  
  /**
   * Class to store objects in the database
@@@ -405,7 -404,7 +405,7 @@@ class SqlBagOStuff extends BagOStuff 
                                $exptime = $this->convertExpiry( $exptime );
                                $encExpiry = $db->timestamp( $exptime );
                        }
-                       // (bug 24425) use a replace if the db supports it instead of
+                       // (T26425) use a replace if the db supports it instead of
                        // delete/insert to avoid clashes with conflicting keynames
                        $db->update(
                                $tableName,
                                ], __METHOD__, 'IGNORE' );
  
                        if ( $db->affectedRows() == 0 ) {
-                               // Race condition. See bug 28611
+                               // Race condition. See T30611
                                $newValue = null;
                        }
                } catch ( DBError $e ) {
@@@ -726,7 -726,7 +726,7 @@@ class Article implements Page 
  
                $ns = $this->getTitle()->getNamespace();
  
-               # Don't index user and user talk pages for blocked users (bug 11443)
+               # Don't index user and user talk pages for blocked users (T13443)
                if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) {
                        $specificTarget = null;
                        $vagueTarget = null;
                }
  
                if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
-                       # (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
+                       # (T16900) site config can override user-defined __INDEX__ or __NOINDEX__
                        $policy = array_merge(
                                $policy,
                                self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
                $loggedIn = $this->getContext()->getUser()->isLoggedIn();
                if ( $loggedIn || $cache->get( $key ) ) {
                        $logTypes = [ 'delete', 'move' ];
 -                      $conds = [ "log_action != 'revision'" ];
 +
 +                      $dbr = wfGetDB( DB_REPLICA );
 +
 +                      $conds = [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ];
                        // Give extensions a chance to hide their (unrelated) log entries
                        Hooks::run( 'Article::MissingArticleConditions', [ &$conds, $logTypes ] );
                        LogEventsList::showLogExtract(
@@@ -213,7 -213,7 +213,7 @@@ class ImagePage extends Article 
                }
  
                $out->addModuleStyles( [
-                       'filepage', // always show the local local Filepage.css, bug 29277
+                       'filepage', // always show the local local Filepage.css, T31277
                        'mediawiki.action.view.filepage', // Add MediaWiki styles for a file page
                ] );
        }
                                // this will get messy.
                                // The dirmark, however, must not be immediately adjacent
                                // to the filename, because it can get copied with it.
-                               // See bug 25277.
+                               // See T27277.
                                // @codingStandardsIgnoreStart Ignore long line
                                $out->addWikiText( <<<EOT
  <div class="fullMedia"><span class="dangerousLink">{$medialink}</span> $dirmark<span class="fileInfo">$longDesc</span></div>
@@@ -585,8 -585,6 +585,8 @@@ EO
                } else {
                        # Image does not exist
                        if ( !$this->getId() ) {
 +                              $dbr = wfGetDB( DB_REPLICA );
 +
                                # No article exists either
                                # Show deletion log to be consistent with normal articles
                                LogEventsList::showLogExtract(
                                        $this->getTitle()->getPrefixedText(),
                                        '',
                                        [ 'lim' => 10,
 -                                              'conds' => [ "log_action != 'revision'" ],
 +                                              'conds' => [ 'log_action != ' . $dbr->addQuotes( 'revision' ) ],
                                                'showIfEmpty' => false,
                                                'msgKey' => [ 'moveddeleted-notice' ]
                                        ]
@@@ -714,7 -714,7 +714,7 @@@ class ResourceLoader implements LoggerA
                        $module = $this->getModule( $name );
                        if ( $module ) {
                                // Do not allow private modules to be loaded from the web.
-                               // This is a security issue, see bug 34907.
+                               // This is a security issue, see T36907.
                                if ( $module->getGroup() === 'private' ) {
                                        $this->logger->debug( "Request for private module '$name' denied" );
                                        $this->errors[] = "Cannot show private module \"$name\"";
         * @return void
         */
        protected function sendResponseHeaders( ResourceLoaderContext $context, $etag, $errors ) {
 +              \MediaWiki\HeaderCallback::warnIfHeadersSent();
                $rlMaxage = $this->config->get( 'ResourceLoaderMaxage' );
                // Use a short cache expiry so that updates propagate to clients quickly, if:
                // - No version specified (shared resources, e.g. stylesheets)
@@@ -1217,7 -1216,7 +1217,7 @@@ MESSAGE
                        $styles = (array)$styles;
                        foreach ( $styles as $style ) {
                                $style = trim( $style );
-                               // Don't output an empty "@media print { }" block (bug 40498)
+                               // Don't output an empty "@media print { }" block (T42498)
                                if ( $style !== '' ) {
                                        // Transform the media type based on request params and config
                                        // The way that this relies on $wgRequest to propagate request params is slightly evil
         */
        public function getLessCompiler( $extraVars = [] ) {
                // When called from the installer, it is possible that a required PHP extension
-               // is missing (at least for now; see bug 47564). If this is the case, throw an
+               // is missing (at least for now; see T49564). If this is the case, throw an
                // exception (caught by the installer) to prevent a fatal error later on.
                if ( !class_exists( 'Less_Parser' ) ) {
                        throw new MWException( 'MediaWiki requires the less.php parser' );
@@@ -461,28 -461,13 +461,28 @@@ abstract class ResourceLoaderModule imp
         * @param array $localFileRefs List of files
         */
        protected function saveFileDependencies( ResourceLoaderContext $context, $localFileRefs ) {
 -              // Normalise array
 -              $localFileRefs = array_values( array_unique( $localFileRefs ) );
 -              sort( $localFileRefs );
  
                try {
 +                      // Related bugs and performance considerations:
 +                      // 1. Don't needlessly change the database value with the same list in a
 +                      //    different order or with duplicates.
 +                      // 2. Use relative paths to avoid ghost entries when $IP changes. (T111481)
 +                      // 3. Don't needlessly replace the database with the same value
 +                      //    just because $IP changed (e.g. when upgrading a wiki).
 +                      // 4. Don't create an endless replace loop on every request for this
 +                      //    module when '../' is used anywhere. Even though both are expanded
 +                      //    (one expanded by getFileDependencies from the DB, the other is
 +                      //    still raw as originally read by RL), the latter has not
 +                      //    been normalized yet.
 +
 +                      // Normalise
 +                      $localFileRefs = array_values( array_unique( $localFileRefs ) );
 +                      sort( $localFileRefs );
 +                      $localPaths = self::getRelativePaths( $localFileRefs );
 +
 +                      $storedPaths = self::getRelativePaths( $this->getFileDependencies( $context ) );
                        // If the list has been modified since last time we cached it, update the cache
 -                      if ( $localFileRefs !== $this->getFileDependencies( $context ) ) {
 +                      if ( $localPaths !== $storedPaths ) {
                                $vary = $context->getSkin() . '|' . $context->getLanguage();
                                $cache = ObjectCache::getLocalClusterInstance();
                                $key = $cache->makeKey( __METHOD__, $this->getName(), $vary );
                                        return; // T124649; avoid write slams
                                }
  
 -                              // Use relative paths to avoid ghost entries when $IP changes (T111481)
 -                              $deps = FormatJson::encode( self::getRelativePaths( $localFileRefs ) );
 +                              $deps = FormatJson::encode( $localPaths );
                                $dbw = wfGetDB( DB_MASTER );
                                $dbw->upsert( 'module_deps',
                                        [
                                        && substr( rtrim( $scripts ), -1 ) !== ';'
                                ) {
                                        // Append semicolon to prevent weird bugs caused by files not
-                                       // terminating their statements right (bug 27054)
+                                       // terminating their statements right (T29054)
                                        $scripts .= ";\n";
                                }
                        }
                if ( $context->shouldIncludeStyles() ) {
                        $styles = [];
                        // Don't create empty stylesheets like [ '' => '' ] for modules
-                       // that don't *have* any stylesheets (bug 38024).
+                       // that don't *have* any stylesheets (T40024).
                        $stylePairs = $this->getStyles( $context );
                        if ( count( $stylePairs ) ) {
                                // If we are in debug mode without &only= set, we'll want to return an array of URLs
@@@ -268,7 -268,7 +268,7 @@@ class ResourceLoaderWikiModule extends 
                        return true;
                }
  
-               // Bug 68488: For other modules (i.e. ones that are called in cached html output) only check
+               // T70488: For other modules (i.e. ones that are called in cached html output) only check
                // page existance. This ensures that, if some pages in a module are temporarily blanked,
                // we don't end omit the module's script or link tag on some pages.
                return count( $revisions ) === 0;
                        }
                }
  
 +              if ( !$wikiModules ) {
 +                      // Nothing to preload
 +                      return;
 +              }
 +
                $pageNames = array_keys( $allPages );
                sort( $pageNames );
                $hash = sha1( implode( '|', $pageNames ) );
diff --combined includes/user/User.php
@@@ -949,7 -949,7 +949,7 @@@ class User implements IDBAccessObject 
  
                // Ensure that the username isn't longer than 235 bytes, so that
                // (at least for the builtin skins) user javascript and css files
-               // will work. (bug 23080)
+               // will work. (T25080)
                if ( strlen( $name ) > 235 ) {
                        wfDebugLog( 'username', __METHOD__ .
                                ": '$name' invalid due to length" );
                }
  
                // Clean up name according to title rules,
-               // but only when validation is requested (bug 12654)
+               // but only when validation is requested (T14654)
                $t = ( $validate !== false ) ?
                        Title::newFromText( $name, NS_USER ) : Title::makeTitle( NS_USER, $name );
                // Check for invalid titles
                        }
                }
  
-               // (bug 23343) Apply IP blocks to the contents of XFF headers, if enabled
+               // (T25343) Apply IP blocks to the contents of XFF headers, if enabled
                if ( !$block instanceof Block
                        && $wgApplyIpBlocksToXff
                        && $ip !== null
                $found = false;
                // @todo FIXME: IPv6 ???  (https://bugs.php.net/bug.php?id=33170)
                if ( IP::isIPv4( $ip ) ) {
-                       // Reverse IP, bug 21255
+                       // Reverse IP, T23255
                        $ipReversed = implode( '.', array_reverse( explode( '.', $ip ) ) );
  
                        foreach ( (array)$bases as $base ) {
         */
        public function isPingLimitable() {
                global $wgRateLimitsExcludedIPs;
 -              if ( in_array( $this->getRequest()->getIP(), $wgRateLimitsExcludedIPs ) ) {
 +              if ( IP::isInRanges( $this->getRequest()->getIP(), $wgRateLimitsExcludedIPs ) ) {
                        // No other good way currently to disable rate limits
                        // for specific IPs. :P
                        // But this is a crappy hack and should die.
                // user_talk page; it's cleared one page view later in WikiPage::doViewUpdates().
        }
  
 +      /**
 +       * Compute experienced level based on edit count and registration date.
 +       *
 +       * @return string 'newcomer', 'learner', or 'experienced'
 +       */
 +      public function getExperienceLevel() {
 +              global $wgLearnerEdits,
 +                         $wgExperiencedUserEdits,
 +                         $wgLearnerMemberSince,
 +                         $wgExperiencedUserMemberSince;
 +
 +              if ( $this->isAnon() ) {
 +                      return false;
 +              }
 +
 +              $editCount = $this->getEditCount();
 +              $registration = $this->getRegistration();
 +              $now = time();
 +              $learnerRegistration = wfTimestamp( TS_MW, $now - $wgLearnerMemberSince * 86400 );
 +              $experiencedRegistration = wfTimestamp( TS_MW, $now - $wgExperiencedUserMemberSince * 86400 );
 +
 +              if (
 +                      $editCount < $wgLearnerEdits ||
 +                      $registration > $learnerRegistration
 +              ) {
 +                      return 'newcomer';
 +              } elseif (
 +                      $editCount > $wgExperiencedUserEdits &&
 +                      $registration <= $experiencedRegistration
 +              ) {
 +                      return 'experienced';
 +              } else {
 +                      return 'learner';
 +              }
 +      }
 +
        /**
         * Set a cookie on the user's client. Wrapper for
         * WebResponse::setCookie
         *   }
         *   // do something with $user...
         *
-        * However, this was vulnerable to a race condition (bug 16020). By
+        * However, this was vulnerable to a race condition (T18020). By
         * initialising the user object if the user exists, we aim to support this
         * calling sequence as far as possible.
         *
                        return $this->mBlock;
                }
  
-               # bug 13611: if the IP address the user is trying to create an account from is
+               # T15611: if the IP address the user is trying to create an account from is
                # blocked with createaccount disabled, prevent new account creation there even
                # when the user is logged in
                if ( $this->mBlockedFromCreateAccount === false && !$this->isAllowed( 'ipblock-exempt' ) ) {
         * @note Since these URLs get dropped directly into emails, using the
         * short English names avoids insanely long URL-encoded links, which
         * also sometimes can get corrupted in some browsers/mailers
-        * (bug 6957 with Gmail and Internet Explorer).
+        * (T8957 with Gmail and Internet Explorer).
         *
         * @param string $page Special page
         * @param string $token Token
                # Note that the pattern requirement will always be satisfied if the
                # input is empty, so we need required in all cases.
  
-               # @todo FIXME: Bug 23769: This needs to not claim the password is required
+               # @todo FIXME: T25769: This needs to not claim the password is required
                # if e-mail confirmation is being used.  Since HTML5 input validation
                # is b0rked anyway in some browsers, just return nothing.  When it's
                # re-enabled, fix this code to not output required for e-mail