Merge "RCFilters: Convert RL modules to packageFiles"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sun, 10 Feb 2019 01:19:26 +0000 (01:19 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sun, 10 Feb 2019 01:19:26 +0000 (01:19 +0000)
30 files changed:
RELEASE-NOTES-1.33
autoload.php
includes/DefaultSettings.php
includes/DummyLinker.php
includes/EventRelayerGroup.php
includes/Linker.php
includes/MediaWiki.php
includes/Setup.php
includes/Title.php
includes/cache/LinkCache.php
includes/content/JsonContent.php
includes/exception/MWExceptionHandler.php
includes/mail/EmailNotification.php
includes/page/ImagePage.php
includes/search/SearchEngine.php
includes/session/SessionManager.php
includes/site/SiteSQLStore.php [deleted file]
includes/user/User.php
maintenance/resources/foreign-resources.yaml
resources/lib/jquery.client/AUTHORS.txt
resources/lib/jquery.client/jquery.client.js
resources/src/mediawiki.ForeignStructuredUpload.BookletLayout/BookletLayout.js
resources/src/mediawiki.language/mediawiki.language.js
resources/src/mediawiki.language/mediawiki.language.specialCharacters.js
resources/src/mediawiki.libs.jpegmeta/export.js
resources/src/mediawiki.special.upload/upload.js
resources/src/mediawiki.user.js
resources/src/startup/mediawiki.js
tests/phpunit/includes/TitleTest.php
tests/selenium/wdio-mediawiki/RunJobs.js

index 4bd50c6..299cd94 100644 (file)
@@ -66,6 +66,7 @@ production.
   * The deprecated IPSet\IPSet alias was removed, Wikimedia\IPSet must be
     used instead.
 * Updated qunitjs from 2.6.2 to 2.9.1.
+* Updated jquery-client from 2.0.1 to 2.0.2.
 
 ==== Removed external libraries ====
 
@@ -195,6 +196,48 @@ because of Phabricator reports.
 * Class SquidUpdate, deprecated in 1.27, was removed.
 * Title->getSquidURLs(), deprecated in 1.27, was removed. Instead, use
   Title->getCdnUrls().
+* Title::escapeFragmentForURL(), deprecated in 1.30, was removed. Use
+  Sanitizer::escapeIdForLink() or escapeIdForExternalInterwiki() instead.
+* Title->canTalk(), deprecated in 1.30, was removed. Instead, use
+  Title->canHaveTalkPage().
+* Title's methods for site and user page related to CSS and JS, deprecated in
+  1.31, were removed:
+  * Title->isCssOrJsPage() — Use Title->isSiteConfigPage()
+  * Title->isCssJsSubpage() – Use Title->isUserConfigPage()
+  * Title->getSkinFromCssJsSubpage() – Use Title->getSkinFromConfigSubpage()
+  * Title->isCssSubpage() – Use Title->isUserCssConfigPage()
+  * Title->isJsSubpage() – Use Title->isUserJsConfigPage()
+* SiteSQLStore, deprecated in 1.27 and whose only method, ::newInstance(),
+  would return the global SiteStore instance, has been removed. You can get to
+  this via MediaWiki\MediaWikiServices::getInstance()->getSiteStore() directly.
+* Linker::formatSize, deprecated in 1.28, has been removed (with DummyLinker's).
+  Instead, use Language->formatSize() with the relevant Language object.
+* Linker::formatTemplates, deprecated in 1.28, has been removed (along with the
+  version in DummyLinker). You can use TemplatesOnThisPageFormatter directly.
+* EventRelayerGroup::singleton(), deprecated in 1.27, has been removed. You can
+  use MediaWikiServices::getInstance()->getEventRelayerGroup() directly.
+* LinkCache->addLink(), deprecated in 1.27, has been removed. It is thought to
+  be unused, and is distinct from OutputPage->addLink(), which remains.
+* JsonContent->getJsonData(), deprecated in 1.25, has been removed. Instead, use
+  JsonContent->getData().
+* MWExceptionHandler::getLogId(), deprecated in 1.27, has been removed, as the
+  exception ID is the same as the request ID, from WebRequest::getRequestId().
+* SearchEngine::getNearMatchResultSet(), deprecated in 1.27, has been removed.
+  You can use SearchEngine::getNearMatcher() instead.
+* EmailNotification::updateWatchlistTimestamp, deprecated in 1.27, has been
+  removed. Instead, use WatchedItemStore::updateNotificationTimestamp directly.
+* User::getGroupName() and ::getGroupMember(), both deprecated in 1.29, have
+  been removed. Instead, please use UserGroupMembership::getGroupName() and
+  UserGroupMembership::getGroupMemberName().
+* Backwards compatibility for setting wgSessionsInObjectCache to false or using
+  wgSessionHandler, both of which were deprecated in 1.27 with the introduction
+  of SessionManager, has been removed.
+* SessionManager::autoCreateUser, deprecated in 1.27, has been removed. Use
+  MediaWiki\Auth\AuthManager::autoCreateUser instead.
+* The mw.libs.jpegmeta property, deprecated in 1.31, was removed.
+  Use require( 'mediawiki.libs.jpegmeta' ) instead.
+* The mw.user.stickyRandomId() method, deprecated in 1.32, was removed.
+  Use mw.user.getPageviewToken() instead.
 
 === Deprecations in 1.33 ===
 * The configuration option $wgUseESI has been deprecated, and is expected
@@ -239,10 +282,11 @@ because of Phabricator reports.
   use the new extension registration key 'QUnitTestModule'.
 * (T213426) The jquery.throttle-debounce module has been deprecated. JavaScript
   code that needs this behaviour should use OO.ui.debounce/throttle.
+* The mw.language.specialCharacters property from the
+  'mediawiki.language.specialCharacters' module has been deprecated.
+  Use require( 'mediawiki.language.specialCharacters' ) instead.
 
 === Other changes in 1.33 ===
-* (T208871) The hard-coded Google search form on the database error page was
-  removed.
 * (T201747) Html::openElement() warns if given an element name with a space
   in it.
 
index 37d113b..471fdd7 100644 (file)
@@ -1330,7 +1330,6 @@ $wgAutoloadLocalClasses = [
        'SiteImporter' => __DIR__ . '/includes/site/SiteImporter.php',
        'SiteList' => __DIR__ . '/includes/site/SiteList.php',
        'SiteLookup' => __DIR__ . '/includes/site/SiteLookup.php',
-       'SiteSQLStore' => __DIR__ . '/includes/site/SiteSQLStore.php',
        'SiteStats' => __DIR__ . '/includes/SiteStats.php',
        'SiteStatsInit' => __DIR__ . '/includes/SiteStatsInit.php',
        'SiteStatsUpdate' => __DIR__ . '/includes/deferred/SiteStatsUpdate.php',
index e6b44ed..dc8f1e8 100644 (file)
@@ -2512,21 +2512,11 @@ $wgMainStash = 'db-replicated';
  */
 $wgParserCacheExpireTime = 86400;
 
-/**
- * @deprecated since 1.27, session data is always stored in object cache.
- */
-$wgSessionsInObjectCache = true;
-
 /**
  * The expiry time to use for session storage, in seconds.
  */
 $wgObjectCacheSessionExpiry = 3600;
 
-/**
- * @deprecated since 1.27, MediaWiki\Session\SessionManager doesn't use PHP session storage.
- */
-$wgSessionHandler = null;
-
 /**
  * Whether to use PHP session handling ($_SESSION and session_*() functions)
  *
index ba1233e..e46c45e 100644 (file)
@@ -403,38 +403,10 @@ class DummyLinker {
                );
        }
 
-       /**
-        * @deprecated since 1.28, use TemplatesOnThisPageFormatter directly
-        */
-       public function formatTemplates(
-               $templates,
-               $preview = false,
-               $section = false,
-               $more = null
-       ) {
-               wfDeprecated( __METHOD__, '1.28' );
-
-               return Linker::formatTemplates(
-                       $templates,
-                       $preview,
-                       $section,
-                       $more
-               );
-       }
-
        public function formatHiddenCategories( $hiddencats ) {
                return Linker::formatHiddenCategories( $hiddencats );
        }
 
-       /**
-        * @deprecated since 1.28, use Language::formatSize() directly
-        */
-       public function formatSize( $size ) {
-               wfDeprecated( __METHOD__, '1.28' );
-
-               return Linker::formatSize( $size );
-       }
-
        public function titleAttrib( $name, $options = null, array $msgParams = [] ) {
                return Linker::titleAttrib(
                        $name,
index 95d11d9..091a5ca 100644 (file)
@@ -18,8 +18,6 @@
  * @file
  */
 
-use MediaWiki\MediaWikiServices;
-
 /**
  * Factory class for spawning EventRelayer objects using configuration
  *
@@ -39,15 +37,6 @@ class EventRelayerGroup {
                $this->configByChannel = $config;
        }
 
-       /**
-        * @deprecated since 1.27 Use MediaWikiServices::getInstance()->getEventRelayerGroup()
-        * @return EventRelayerGroup
-        */
-       public static function singleton() {
-               wfDeprecated( __METHOD__, '1.27' );
-               return MediaWikiServices::getInstance()->getEventRelayerGroup();
-       }
-
        /**
         * @param string $channel
         * @return EventRelayer Relayer instance that handles the given channel
index d1434f8..cc1df39 100644 (file)
@@ -1895,47 +1895,6 @@ class Linker {
                }
        }
 
-       /**
-        * @deprecated since 1.28, use TemplatesOnThisPageFormatter directly
-        *
-        * Returns HTML for the "templates used on this page" list.
-        *
-        * Make an HTML list of templates, and then add a "More..." link at
-        * the bottom. If $more is null, do not add a "More..." link. If $more
-        * is a Title, make a link to that title and use it. If $more is a string,
-        * directly paste it in as the link (escaping needs to be done manually).
-        * Finally, if $more is a Message, call toString().
-        *
-        * @since 1.16.3. $more added in 1.21
-        * @param Title[] $templates Array of templates
-        * @param bool $preview Whether this is for a preview
-        * @param bool $section Whether this is for a section edit
-        * @param Title|Message|string|null $more An escaped link for "More..." of the templates
-        * @return string HTML output
-        */
-       public static function formatTemplates( $templates, $preview = false,
-               $section = false, $more = null
-       ) {
-               wfDeprecated( __METHOD__, '1.28' );
-
-               $type = false;
-               if ( $preview ) {
-                       $type = 'preview';
-               } elseif ( $section ) {
-                       $type = 'section';
-               }
-
-               if ( $more instanceof Message ) {
-                       $more = $more->toString();
-               }
-
-               $formatter = new TemplatesOnThisPageFormatter(
-                       RequestContext::getMain(),
-                       MediaWikiServices::getInstance()->getLinkRenderer()
-               );
-               return $formatter->format( $templates, $type, $more );
-       }
-
        /**
         * Returns HTML for the "hidden categories on this page" list.
         *
@@ -1963,23 +1922,6 @@ class Linker {
                return $outText;
        }
 
-       /**
-        * @deprecated since 1.28, use Language::formatSize() directly
-        *
-        * Format a size in bytes for output, using an appropriate
-        * unit (B, KB, MB or GB) according to the magnitude in question
-        *
-        * @since 1.16.3
-        * @param int $size Size to format
-        * @return string
-        */
-       public static function formatSize( $size ) {
-               wfDeprecated( __METHOD__, '1.28' );
-
-               global $wgLang;
-               return htmlspecialchars( $wgLang->formatSize( $size ) );
-       }
-
        /**
         * Given the id of an interface element, constructs the appropriate title
         * attribute from the system messages.  (Note, this is usually the id but
index f5a954d..43512e1 100644 (file)
@@ -569,8 +569,11 @@ class MediaWiki {
        }
 
        /**
-        * This function commits all DB changes as needed before
-        * the user can receive a response (in case commit fails)
+        * This function commits all DB and session changes as needed *before* the
+        * client can receive a response (in case DB commit fails) and thus also before
+        * the response can trigger a subsequent related request by the client
+        *
+        * If there is a significant amount of content to flush, it can be done in $postCommitWork
         *
         * @param IContextSource $context
         * @param callable|null $postCommitWork [default: null]
@@ -598,6 +601,8 @@ class MediaWiki {
                // Run updates that need to block the user or affect output (this is the last chance)
                DeferredUpdates::doUpdates( 'enqueue', DeferredUpdates::PRESEND );
                wfDebug( __METHOD__ . ': pre-send deferred updates completed' );
+               // T214471: persist the session to avoid race conditions on subsequent requests
+               $request->getSession()->save();
 
                // Should the client return, their request should observe the new ChronologyProtector
                // DB positions. This request might be on a foreign wiki domain, so synchronously update
index 516937e..b4b6ce6 100644 (file)
@@ -580,21 +580,6 @@ if ( $wgMaximalPasswordLength !== false ) {
        $wgPasswordPolicy['policies']['default']['MaximalPasswordLength'] = $wgMaximalPasswordLength;
 }
 
-// Backwards compatibility warning
-if ( !$wgSessionsInObjectCache ) {
-       wfDeprecated( '$wgSessionsInObjectCache = false', '1.27' );
-       if ( $wgSessionHandler ) {
-               wfDeprecated( '$wgSessionsHandler', '1.27' );
-       }
-       $cacheType = get_class( ObjectCache::getInstance( $wgSessionCacheType ) );
-       wfDebugLog(
-               'caches',
-               "Session data will be stored in \"$cacheType\" cache with " .
-                       "expiry $wgObjectCacheSessionExpiry seconds"
-       );
-}
-$wgSessionsInObjectCache = true;
-
 if ( $wgPHPSessionHandling !== 'enable' &&
        $wgPHPSessionHandling !== 'warn' &&
        $wgPHPSessionHandling !== 'disable'
index c2d04c3..849707e 100644 (file)
@@ -767,23 +767,6 @@ class Title implements LinkTarget, IDBAccessObject {
                return $name;
        }
 
-       /**
-        * Escape a text fragment, say from a link, for a URL
-        *
-        * @deprecated since 1.30, use Sanitizer::escapeIdForLink() or escapeIdForExternalInterwiki()
-        *
-        * @param string $fragment Containing a URL or link fragment (after the "#")
-        * @return string Escaped string
-        */
-       static function escapeFragmentForURL( $fragment ) {
-               wfDeprecated( __METHOD__, '1.30' );
-               # Note that we don't urlencode the fragment.  urlencoded Unicode
-               # fragments appear not to work in IE (at least up to 7) or in at least
-               # one version of Opera 9.x.  The W3C validator, for one, doesn't seem
-               # to care if they aren't encoded.
-               return Sanitizer::escapeId( $fragment, 'noninitial' );
-       }
-
        /**
         * Callback for usort() to do title sorts by (namespace, title)
         *
@@ -1071,17 +1054,6 @@ class Title implements LinkTarget, IDBAccessObject {
                        getNsText( MWNamespace::getTalk( $this->mNamespace ) );
        }
 
-       /**
-        * Can this title have a corresponding talk page?
-        *
-        * @deprecated since 1.30, use canHaveTalkPage() instead.
-        *
-        * @return bool True if this title either is a talk page or can have a talk page associated.
-        */
-       public function canTalk() {
-               return $this->canHaveTalkPage();
-       }
-
        /**
         * Can this title have a corresponding talk page?
         *
@@ -1308,17 +1280,6 @@ class Title implements LinkTarget, IDBAccessObject {
                );
        }
 
-       /**
-        * @return bool
-        * @deprecated Since 1.31; use ::isSiteConfigPage() instead (which also checks for JSON pages)
-        */
-       public function isCssOrJsPage() {
-               wfDeprecated( __METHOD__, '1.31' );
-               return ( NS_MEDIAWIKI == $this->mNamespace
-                               && ( $this->hasContentModel( CONTENT_MODEL_CSS )
-                                       || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
-       }
-
        /**
         * Is this a "config" (.css, .json, or .js) sub-page of a user page?
         *
@@ -1333,17 +1294,6 @@ class Title implements LinkTarget, IDBAccessObject {
                );
        }
 
-       /**
-        * @return bool
-        * @deprecated Since 1.31; use ::isUserConfigPage() instead (which also checks for JSON pages)
-        */
-       public function isCssJsSubpage() {
-               wfDeprecated( __METHOD__, '1.31' );
-               return ( NS_USER == $this->mNamespace && $this->isSubpage()
-                               && ( $this->hasContentModel( CONTENT_MODEL_CSS )
-                                       || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
-       }
-
        /**
         * Trim down a .css, .json, or .js subpage title to get the corresponding skin name
         *
@@ -1360,15 +1310,6 @@ class Title implements LinkTarget, IDBAccessObject {
                return substr( $subpage, 0, $lastdot );
        }
 
-       /**
-        * @deprecated Since 1.31; use ::getSkinFromConfigSubpage() instead
-        * @return string Containing skin name from .css, .json, or .js subpage title
-        */
-       public function getSkinFromCssJsSubpage() {
-               wfDeprecated( __METHOD__, '1.31' );
-               return $this->getSkinFromConfigSubpage();
-       }
-
        /**
         * Is this a CSS "config" sub-page of a user page?
         *
@@ -1383,15 +1324,6 @@ class Title implements LinkTarget, IDBAccessObject {
                );
        }
 
-       /**
-        * @deprecated Since 1.31; use ::isUserCssConfigPage()
-        * @return bool
-        */
-       public function isCssSubpage() {
-               wfDeprecated( __METHOD__, '1.31' );
-               return $this->isUserCssConfigPage();
-       }
-
        /**
         * Is this a JSON "config" sub-page of a user page?
         *
@@ -1420,15 +1352,6 @@ class Title implements LinkTarget, IDBAccessObject {
                );
        }
 
-       /**
-        * @deprecated Since 1.31; use ::isUserJsConfigPage()
-        * @return bool
-        */
-       public function isJsSubpage() {
-               wfDeprecated( __METHOD__, '1.31' );
-               return $this->isUserJsConfigPage();
-       }
-
        /**
         * Is this a sitewide CSS "config" page?
         *
index b9944a8..b3dc004 100644 (file)
@@ -189,22 +189,6 @@ class LinkCache {
                $this->goodLinks->clear( $dbkey );
        }
 
-       /**
-        * Add a title to the link cache, return the page_id or zero if non-existent
-        *
-        * @deprecated since 1.27, unused
-        * @param string $title Prefixed DB key
-        * @return int Page ID or zero
-        */
-       public function addLink( $title ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               $nt = Title::newFromDBkey( $title );
-               if ( !$nt ) {
-                       return 0;
-               }
-               return $this->addLinkObj( $nt );
-       }
-
        /**
         * Fields that LinkCache needs to select
         *
index 0f8a9a9..2cd1fb3 100644 (file)
@@ -28,17 +28,6 @@ class JsonContent extends TextContent {
                parent::__construct( $text, $modelId );
        }
 
-       /**
-        * Decodes the JSON into a PHP associative array.
-        *
-        * @deprecated since 1.25 Use getData instead.
-        * @return array|null
-        */
-       public function getJsonData() {
-               wfDeprecated( __METHOD__, '1.25' );
-               return FormatJson::decode( $this->getText(), true );
-       }
-
        /**
         * Decodes the JSON string.
         *
index 951498e..6e3fa79 100644 (file)
@@ -462,22 +462,6 @@ TXT;
                }, $trace );
        }
 
-       /**
-        * Get the ID for this exception.
-        *
-        * The ID is saved so that one can match the one output to the user (when
-        * $wgShowExceptionDetails is set to false), to the entry in the debug log.
-        *
-        * @since 1.22
-        * @deprecated since 1.27: Exception IDs are synonymous with request IDs.
-        * @param Exception|Throwable $e
-        * @return string
-        */
-       public static function getLogId( $e ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               return WebRequest::getRequestId();
-       }
-
        /**
         * If the exception occurred in the course of responding to a request,
         * returns the requested URL. Otherwise, returns false.
index 76a7760..4d1b855 100644 (file)
@@ -24,7 +24,6 @@
  * @author Luke Welling lwelling@wikimedia.org
  */
 
-use MediaWiki\Linker\LinkTarget;
 use MediaWiki\MediaWikiServices;
 
 /**
@@ -89,33 +88,6 @@ class EmailNotification {
                return $this->pageStatus;
        }
 
-       /**
-        * @deprecated since 1.27 use WatchedItemStore::updateNotificationTimestamp directly
-        *
-        * @param User $editor The editor that triggered the update.  Their notification
-        *  timestamp will not be updated(they have already seen it)
-        * @param LinkTarget $linkTarget The link target of the title to update timestamps for
-        * @param string $timestamp Set the update timestamp to this value
-        *
-        * @return int[] Array of user IDs
-        */
-       public static function updateWatchlistTimestamp(
-               User $editor,
-               LinkTarget $linkTarget,
-               $timestamp
-       ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               $config = RequestContext::getMain()->getConfig();
-               if ( !$config->get( 'EnotifWatchlist' ) && !$config->get( 'ShowUpdatedMarker' ) ) {
-                       return [];
-               }
-               return MediaWikiServices::getInstance()->getWatchedItemStore()->updateNotificationTimestamp(
-                       $editor,
-                       $linkTarget,
-                       $timestamp
-               );
-       }
-
        /**
         * Send emails corresponding to the user $editor editing the page $title.
         *
index 76b2de0..66804bc 100644 (file)
@@ -357,7 +357,7 @@ class ImagePage extends Article {
                                # image
                                # "Download high res version" link below the image
                                # $msgsize = $this->getContext()->msg( 'file-info-size', $width_orig, $height_orig,
-                               #   Linker::formatSize( $this->displayImg->getSize() ), $mime )->escaped();
+                               #   Language::formatSize( $this->displayImg->getSize() ), $mime )->escaped();
                                # We'll show a thumbnail of this image
                                if ( $width > $maxWidth || $height > $maxHeight || $this->displayImg->isVectorized() ) {
                                        list( $width, $height ) = $this->getDisplayWidthHeight(
index a0100ca..0b29dd7 100644 (file)
@@ -279,18 +279,6 @@ abstract class SearchEngine {
                return static::defaultNearMatcher()->getNearMatch( $searchterm );
        }
 
-       /**
-        * Do a near match (see SearchEngine::getNearMatch) and wrap it into a
-        * SearchResultSet.
-        * @deprecated since 1.27; Use SearchEngine::getNearMatcher()
-        * @param string $searchterm
-        * @return SearchResultSet
-        */
-       public static function getNearMatchResultSet( $searchterm ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               return static::defaultNearMatcher()->getNearMatchResultSet( $searchterm );
-       }
-
        /**
         * Get chars legal for search
         * NOTE: usage as static is deprecated and preserved only as BC measure
index ceb9ceb..385cc35 100644 (file)
@@ -377,23 +377,6 @@ final class SessionManager implements SessionManagerInterface {
         * @{
         */
 
-       /**
-        * Auto-create the given user, if necessary
-        * @private Don't call this yourself. Let Setup.php do it for you at the right time.
-        * @deprecated since 1.27, use MediaWiki\Auth\AuthManager::autoCreateUser instead
-        * @param User $user User to auto-create
-        * @return bool Success
-        * @codeCoverageIgnore
-        */
-       public static function autoCreateUser( User $user ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               return \MediaWiki\Auth\AuthManager::singleton()->autoCreateUser(
-                       $user,
-                       \MediaWiki\Auth\AuthManager::AUTOCREATE_SOURCE_SESSION,
-                       false
-               )->isGood();
-       }
-
        /**
         * Prevent future sessions for the user
         *
diff --git a/includes/site/SiteSQLStore.php b/includes/site/SiteSQLStore.php
deleted file mode 100644 (file)
index b106d11..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-/**
- * Dummy class for accessing the global SiteStore instance.
- *
- * 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
- *
- * @since 1.21
- *
- * @file
- * @ingroup Site
- *
- * @license GPL-2.0-or-later
- * @author Daniel Kinzler
- */
-class SiteSQLStore {
-
-       /**
-        * Returns the global SiteStore instance. This is a relict of the first implementation
-        * of SiteStore, and is kept around for compatibility.
-        *
-        * @note This does not return an instance of SiteSQLStore!
-        *
-        * @since 1.21
-        * @deprecated since 1.27 use MediaWikiServices::getSiteStore()
-        *             or MediaWikiServices::getSiteLookup() instead.
-        *
-        * @param null $sitesTable IGNORED
-        * @param BagOStuff|null $cache IGNORED
-        *
-        * @return SiteStore
-        */
-       public static function newInstance( $sitesTable = null, BagOStuff $cache = null ) {
-               wfDeprecated( __METHOD__, '1.27' );
-               if ( $sitesTable !== null ) {
-                       throw new InvalidArgumentException(
-                               __METHOD__ . ': $sitesTable parameter is unused and must be null'
-                       );
-               }
-
-               // NOTE: we silently ignore $cache for now, since some existing callers
-               // specify it. If we break compatibility with them, we could just as
-               // well just remove this class.
-
-               return \MediaWiki\MediaWikiServices::getInstance()->getSiteStore();
-       }
-
-}
index f5eee34..f4e2e48 100644 (file)
@@ -5124,31 +5124,6 @@ class User implements IDBAccessObject, UserIdentity {
                return true;
        }
 
-       /**
-        * Get the localized descriptive name for a group, if it exists
-        * @deprecated since 1.29 Use UserGroupMembership::getGroupName instead
-        *
-        * @param string $group Internal group name
-        * @return string Localized descriptive group name
-        */
-       public static function getGroupName( $group ) {
-               wfDeprecated( __METHOD__, '1.29' );
-               return UserGroupMembership::getGroupName( $group );
-       }
-
-       /**
-        * Get the localized descriptive name for a member of a group, if it exists
-        * @deprecated since 1.29 Use UserGroupMembership::getGroupMemberName instead
-        *
-        * @param string $group Internal group name
-        * @param string $username Username for gender (since 1.19)
-        * @return string Localized name for group member
-        */
-       public static function getGroupMember( $group, $username = '#' ) {
-               wfDeprecated( __METHOD__, '1.29' );
-               return UserGroupMembership::getGroupMemberName( $group, $username );
-       }
-
        /**
         * Return the set of defined explicit groups.
         * The implicit groups (by default *, 'user' and 'autoconfirmed')
index ee74bb3..7004102 100644 (file)
@@ -69,8 +69,8 @@ jquery:
 
 jquery.client:
   type: tar
-  src: https://registry.npmjs.org/jquery-client/-/jquery-client-2.0.1.tgz
-  integrity: sha256-tizJojJ55YYdKh67Zj/ho/9IAkDDA2UGKpcNvzn96Zs=
+  src: https://registry.npmjs.org/jquery-client/-/jquery-client-2.0.2.tgz
+  integrity: sha256-8c8nBbBykHEMc4I7ksdKJvvw/P7WkaC2X46RTPdz/pw=
   dest:
     package/AUTHORS.txt:
     package/jquery.client.js:
index 9f186ca..1061a7d 100644 (file)
@@ -1,9 +1,10 @@
-Trevor Parscal <trevorparscal@gmail.com>
-Timo Tijhof <krinklemail@gmail.com>
-Roan Kattouw <roan.kattouw@gmail.com>
-Derk-Jan Hartman <hartman.wiki@gmail.com>
+Alexander Monk <krenair@gmail.com>
 Bartosz Dziewoński <matma.rex@gmail.com>
-Rob Moen <rmoen@wikimedia.org>
+Brion Vibber <brion@wikimedia.org>
+Derk-Jan Hartman <hartman@videolan.org>
 Ed Sanders <esanders@wikimedia.org>
-Alex Monk <krenair@gmail.com>
 James D. Forrester <jforrester@wikimedia.org>
+Roan Kattouw <roan.kattouw@gmail.com>
+Rob Moen <rmoen@mediawiki.org>
+Timo Tijhof <krinklemail@gmail.com>
+Trevor Parscal <trevorparscal@gmail.com>
index cfe2d29..79f6174 100644 (file)
@@ -1,8 +1,8 @@
 /*!
- * jQuery Client v2.0.1
+ * jQuery Client v2.0.2
  * https://www.mediawiki.org/wiki/JQuery_Client
  *
- * Copyright 2010-2015 jquery-client maintainers and other contributors.
+ * Copyright 2010-2019 jquery-client maintainers and other contributors.
  * Released under the MIT license
  * http://jquery-client.mit-license.org
  */
@@ -13,7 +13,7 @@
  * @class jQuery.client
  * @singleton
  */
-( function ( $ ) {
+( function () {
 
        /**
         * @private
@@ -51,6 +51,7 @@
                                return profileCache[ nav.userAgent + '|' + nav.platform ];
                        }
 
+                       // eslint-disable-next-line vars-on-top
                        var
                                versionNumber,
                                key = nav.userAgent + '|' + nav.platform,
                                        [ 'Minefield', 'Firefox' ],
                                        // This helps keep different versions consistent
                                        [ 'Navigator', 'Netscape' ],
-                                       // This prevents version extraction issues, otherwise translation would happen later
+                                       // This prevents version extraction issues,
+                                       // otherwise translation would happen later
                                        [ 'PLAYSTATION 3', 'PS3' ]
                                ],
-                               // Strings which precede a version number in a user agent string - combined and used as
-                               // match 1 in version detection
+                               // Strings which precede a version number in a user agent string - combined and
+                               // used as match 1 in version detection
                                versionPrefixes = [
                                        'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'netscape6', 'opera', 'version', 'konqueror',
                                        'lynx', 'msie', 'safari', 'ps3', 'android'
                                ],
-                               // Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number
+                               // Used as matches 2, 3 and 4 in version extraction - 3 is used as actual
+                               // version number
                                versionSuffix = '(\\/|\\;?\\s|)([a-z0-9\\.\\+]*?)(\\;|dev|rel|\\)|\\s|$)',
                                // Names of known browsers
                                names = [
                                // Translations for conforming operating system names
                                platformTranslations = [ [ 'sunos', 'solaris' ], [ 'wow64', 'win' ] ],
 
-                               /**
-                                * Performs multiple replacements on a string
-                                * @ignore
-                                */
+                               // Performs multiple replacements on a string
                                translate = function ( source, translations ) {
                                        var i;
                                        for ( i = 0; i < translations.length; i++ ) {
                                platform = uk,
                                version = x;
 
-                       if ( match = new RegExp( '(' + wildUserAgents.join( '|' ) + ')' ).exec( ua ) ) {
-                               // Takes a userAgent string and translates given text into something we can more easily work with
+                       if ( ( match = new RegExp( '(' + wildUserAgents.join( '|' ) + ')' ).exec( ua ) ) ) {
+                               // Takes a userAgent string and translates given text into something we can more
+                               // easily work with
                                ua = translate( ua, userAgentTranslations );
                        }
                        // Everything will be in lowercase from now on
                        ua = ua.toLowerCase();
 
+                       // Firefox Mobile: Remove 'Android' identifier so it matches to 'Firefox' instead
+                       if ( ua.match( /android/ ) && ua.match( /firefox/ ) ) {
+                               ua = ua.replace( new RegExp( 'android' + versionSuffix ), '' );
+                       }
+
                        // Extraction
 
-                       if ( match = new RegExp( '(' + names.join( '|' ) + ')' ).exec( ua ) ) {
+                       if ( ( match = new RegExp( '(' + names.join( '|' ) + ')' ).exec( ua ) ) ) {
                                name = translate( match[ 1 ], nameTranslations );
                        }
-                       if ( match = new RegExp( '(' + layouts.join( '|' ) + ')' ).exec( ua ) ) {
+                       if ( ( match = new RegExp( '(' + layouts.join( '|' ) + ')' ).exec( ua ) ) ) {
                                layout = translate( match[ 1 ], layoutTranslations );
                        }
-                       if ( match = new RegExp( '(' + layoutVersions.join( '|' ) + ')\\/(\\d+)' ).exec( ua ) ) {
+                       if ( ( match = new RegExp( '(' + layoutVersions.join( '|' ) + ')\\/(\\d+)' ).exec( ua ) ) ) {
                                layoutversion = parseInt( match[ 2 ], 10 );
                        }
-                       if ( match = new RegExp( '(' + platforms.join( '|' ) + ')' ).exec( nav.platform.toLowerCase() ) ) {
+                       if ( ( match = new RegExp( '(' + platforms.join( '|' ) + ')' ).exec( nav.platform.toLowerCase() ) ) ) {
                                platform = translate( match[ 1 ], platformTranslations );
                        }
-                       if ( match = new RegExp( '(' + versionPrefixes.join( '|' ) + ')' + versionSuffix ).exec( ua ) ) {
+                       if ( ( match = new RegExp( '(' + versionPrefixes.join( '|' ) + ')' + versionSuffix ).exec( ua ) ) ) {
                                version = match[ 3 ];
                        }
 
                                layoutversion = parseInt( match[ 1 ], 10 );
                        }
                        // And Amazon Silk's lies about being Android on mobile or Safari on desktop
-                       if ( match = ua.match( /\bsilk\/([0-9.\-_]*)/ ) ) {
+                       if ( ( match = ua.match( /\bsilk\/([0-9.\-_]*)/ ) ) ) {
                                if ( match[ 1 ] ) {
                                        name = 'silk';
                                        version = match[ 1 ];
                        versionNumber = parseFloat( version, 10 ) || 0.0;
 
                        // Caching
-
-                       return profileCache[ key ] = {
+                       profileCache[ key ] = {
                                name: name,
                                layout: layout,
                                layoutVersion: layoutversion,
                                versionBase: ( version !== x ? Math.floor( versionNumber ).toString() : x ),
                                versionNumber: versionNumber
                        };
+
+                       return profileCache[ key ];
                },
 
                /**
                 *
                 * @param {Object} map Browser support map
                 * @param {Object} [profile] A client-profile object
-                * @param {boolean} [exactMatchOnly=false] Only return true if the browser is matched, otherwise
-                * returns true if the browser is not found.
+                * @param {boolean} [exactMatchOnly=false] Only return true if the browser is matched,
+                *  otherwise returns true if the browser is not found.
                 *
                 * @return {boolean} The current browser is in the support map
                 */
                test: function ( map, profile, exactMatchOnly ) {
-                       /* eslint-disable no-eval */
-
                        var conditions, dir, i, op, val, j, pieceVersion, pieceVal, compare;
                        profile = $.isPlainObject( profile ) ? profile : $.client.profile();
                        if ( map.ltr && map.rtl ) {
-                               dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr';
+                               dir = $( document.body ).is( '.rtl' ) ? 'rtl' : 'ltr';
                                map = map[ dir ];
                        }
-                       // Check over each browser condition to determine if we are running in a compatible client
+                       // Check over each browser condition to determine if we are running in a
+                       // compatible client
                        if ( typeof map !== 'object' || map[ profile.name ] === undefined ) {
                                // Not found, return true if exactMatchOnly not set, false otherwise
                                return !exactMatchOnly;
                                op = conditions[ i ][ 0 ];
                                val = conditions[ i ][ 1 ];
                                if ( typeof val === 'string' ) {
-                                       // Perform a component-wise comparison of versions, similar to PHP's version_compare
-                                       // but simpler. '1.11' is larger than '1.2'.
+                                       // Perform a component-wise comparison of versions, similar to
+                                       // PHP's version_compare but simpler. '1.11' is larger than '1.2'.
                                        pieceVersion = profile.version.toString().split( '.' );
                                        pieceVal = val.split( '.' );
                                        // Extend with zeroes to equal length
                                                }
                                        }
                                        // compare will be -1, 0 or 1, depending on comparison result
+                                       // eslint-disable-next-line no-eval
                                        if ( !( eval( String( compare + op + '0' ) ) ) ) {
                                                return false;
                                        }
                                } else if ( typeof val === 'number' ) {
+                                       // eslint-disable-next-line no-eval
                                        if ( !( eval( 'profile.versionNumber' + op + val ) ) ) {
                                                return false;
                                        }
                        return true;
                }
        };
-}( jQuery ) );
+}() );
index 9974e2b..24806b5 100644 (file)
                        fileReader = new FileReader();
                        fileReader.onload = function () {
                                var fileStr, arr, i, metadata,
-                                       jpegmeta = mw.loader.require( 'mediawiki.libs.jpegmeta' );
+                                       jpegmeta = require( 'mediawiki.libs.jpegmeta' );
 
                                if ( typeof fileReader.result === 'string' ) {
                                        fileStr = fileReader.result;
index 8fed695..277034b 100644 (file)
                        return text;
                },
 
-               setSpecialCharacters: function ( data ) {
-                       this.specialCharacters = data;
-               },
-
                /**
                 * Formats language tags according the BCP 47 standard.
                 * See LanguageCode::bcp47 for the PHP implementation.
index ba8a233..6674adb 100644 (file)
@@ -1,5 +1,9 @@
 ( function () {
        var specialCharacters = require( './specialcharacters.json' );
-       mw.language.setSpecialCharacters( specialCharacters );
+       // Deprecated since 1.33
+       mw.log.deprecate( mw.language, 'specialCharacters', specialCharacters,
+               'Use require( \'mediawiki.language.specialCharacters\' ) instead',
+               'mw.language.specialCharacters'
+       );
        module.exports = specialCharacters;
 }() );
index a28bd8f..849e8f2 100644 (file)
@@ -1,12 +1,6 @@
 /* global JpegMeta */
-( function () {
 
-       // Export as module
-       module.exports = function ( fileReaderResult, fileName ) {
-               return new JpegMeta.JpegFile( fileReaderResult, fileName );
-       };
-
-       // Back-compat: Also expose via mw.lib
-       // @deprecated since 1.31
-       mw.log.deprecate( mw.libs, 'jpegmeta', module.exports );
-}() );
+// Export as module
+module.exports = function ( fileReaderResult, fileName ) {
+       return new JpegMeta.JpegFile( fileReaderResult, fileName );
+};
index 8abb8f2..77ca848 100644 (file)
                                };
                                img.src = dataURL;
                        }, mw.config.get( 'wgFileCanRotate' ) ? function ( data ) {
-                               var jpegmeta = mw.loader.require( 'mediawiki.libs.jpegmeta' );
+                               var jpegmeta = require( 'mediawiki.libs.jpegmeta' );
                                try {
                                        meta = jpegmeta( data, file.fileName );
                                        // eslint-disable-next-line no-underscore-dangle, camelcase
index e41ed58..d9b1227 100644 (file)
                }
        } );
 
-       /**
-        * @method stickyRandomId
-        * @deprecated since 1.32 use getPageviewToken instead
-        */
-       mw.log.deprecate( mw.user, 'stickyRandomId', mw.user.getPageviewToken, 'Please use getPageviewToken instead' );
-
 }() );
index 8b2aa29..967c529 100644 (file)
@@ -13,8 +13,7 @@
        'use strict';
 
        var mw, StringSet, log,
-               hasOwn = Object.prototype.hasOwnProperty,
-               trackQueue = [];
+               hasOwn = Object.prototype.hasOwnProperty;
 
        /**
         * FNV132 hash function
         * @return {string} hash as an seven-character base 36 string
         */
        function fnv132( str ) {
-               /* eslint-disable no-bitwise */
                var hash = 0x811C9DC5,
                        i;
 
+               /* eslint-disable no-bitwise */
                for ( i = 0; i < str.length; i++ ) {
                        hash += ( hash << 1 ) + ( hash << 4 ) + ( hash << 7 ) + ( hash << 8 ) + ( hash << 24 );
                        hash ^= str.charCodeAt( i );
@@ -42,9 +41,9 @@
                while ( hash.length < 7 ) {
                        hash = '0' + hash;
                }
+               /* eslint-enable no-bitwise */
 
                return hash;
-               /* eslint-enable no-bitwise */
        }
 
        function defineFallbacks() {
        function logError( topic, data ) {
                var msg,
                        e = data.exception,
-                       source = data.source,
-                       module = data.module,
                        console = window.console;
 
                if ( console && console.log ) {
-                       msg = ( e ? 'Exception' : 'Error' ) + ' in ' + source;
-                       if ( module ) {
-                               msg += ' in module ' + module;
-                       }
-                       msg += ( e ? ':' : '.' );
+                       msg = ( e ? 'Exception' : 'Error' ) +
+                               ' in ' + data.source +
+                               ( data.module ? ' in module ' + data.module : '' ) +
+                               ( e ? ':' : '.' );
+
                        console.log( msg );
 
                        // If we have an exception object, log it to the warning channel to trigger
                        this.set = function ( selection, value ) {
                                var s;
                                if ( arguments.length > 1 ) {
-                                       if ( typeof selection !== 'string' ) {
-                                               return false;
+                                       if ( typeof selection === 'string' ) {
+                                               setGlobalMapValue( this, selection, value );
+                                               return true;
                                        }
-                                       setGlobalMapValue( this, selection, value );
-                                       return true;
-                               }
-                               if ( typeof selection === 'object' ) {
+                               } else if ( typeof selection === 'object' ) {
                                        for ( s in selection ) {
                                                setGlobalMapValue( this, s, selection[ s ] );
                                        }
                        var s;
                        // Use `arguments.length` because `undefined` is also a valid value.
                        if ( arguments.length > 1 ) {
-                               if ( typeof selection !== 'string' ) {
-                                       return false;
+                               // Set one key
+                               if ( typeof selection === 'string' ) {
+                                       this.values[ selection ] = value;
+                                       return true;
                                }
-                               this.values[ selection ] = value;
-                               return true;
-                       }
-                       if ( typeof selection === 'object' ) {
+                       } else if ( typeof selection === 'object' ) {
+                               // Set multiple keys
                                for ( s in selection ) {
                                        this.values[ s ] = selection[ s ];
                                }
                 * @param {string} key Name of property to create in `obj`
                 * @param {Mixed} val The value this property should return when accessed
                 * @param {string} [msg] Optional text to include in the deprecation message
-                * @param {string} [logName=key] Optional custom name for the feature.
-                *  This is used instead of `key` in the message and `mw.deprecate` tracking.
+                * @param {string} [logName] Name for the feature for logging and tracking
+                *  purposes. Except for properties of the window object, tracking is only
+                *  enabled if logName is set.
                 */
                log.deprecate = function ( obj, key, val, msg, logName ) {
                        var stacks;
                        function maybeLog() {
-                               var name,
+                               var name = logName || key,
                                        trace = new Error().stack;
                                if ( !stacks ) {
                                        stacks = new StringSet();
                                }
                                if ( !stacks.has( trace ) ) {
                                        stacks.add( trace );
-                                       name = logName || key;
-                                       mw.track( 'mw.deprecate', name );
+                                       if ( logName || obj === window ) {
+                                               mw.track( 'mw.deprecate', name );
+                                       }
                                        mw.log.warn(
-                                               'Use of "' + name + '" is deprecated.' + ( msg ? ( ' ' + msg ) : '' )
+                                               'Use of "' + name + '" is deprecated.' + ( msg ? ' ' + msg : '' )
                                        );
                                }
                        }
        mw = {
                redefineFallbacksForTest: function () {
                        if ( !window.QUnit ) {
-                               throw new Error( 'Reset not allowed outside unit tests' );
+                               throw new Error( 'Not allowed' );
                        }
                        defineFallbacks();
                },
                 * @return {number} Current time
                 */
                now: function () {
-                       // Optimisation: Define the shortcut on first call, not at module definition.
+                       // Optimisation: Make startup initialisation faster by defining the
+                       // shortcut on first call, not at module definition.
                        var perf = window.performance,
                                navStart = perf && perf.timing && perf.timing.navigationStart;
 
                        // Define the relevant shortcut
-                       mw.now = navStart && typeof perf.now === 'function' ?
+                       mw.now = navStart && perf.now ?
                                function () { return navStart + perf.now(); } :
                                Date.now;
 
                /**
                 * List of all analytic events emitted so far.
                 *
+                * Exposed only for use by mediawiki.base.
+                *
                 * @private
                 * @property {Array}
                 */
-               trackQueue: trackQueue,
+               trackQueue: [],
 
                track: function ( topic, data ) {
-                       trackQueue.push( { topic: topic, timeStamp: mw.now(), data: data } );
-                       // The base module extends this method to fire events here
+                       mw.trackQueue.push( { topic: topic, timeStamp: mw.now(), data: data } );
+                       // This method is extended by mediawiki.base to also fire events.
                },
 
                /**
                                                        i -= 1;
                                                        try {
                                                                if ( failed && job.error ) {
-                                                                       job.error( new Error( 'Module has failed dependencies' ), job.dependencies );
+                                                                       job.error( new Error( 'Failed dependencies' ), job.dependencies );
                                                                } else if ( !failed && job.ready ) {
                                                                        job.ready();
                                                                }
 
                                // Add base modules
                                if ( baseModules.indexOf( module ) === -1 ) {
-                                       baseModules.forEach( function ( baseModule ) {
-                                               if ( resolved.indexOf( baseModule ) === -1 ) {
-                                                       resolved.push( baseModule );
+                                       for ( i = 0; i < baseModules.length; i++ ) {
+                                               if ( resolved.indexOf( baseModules[ i ] ) === -1 ) {
+                                                       resolved.push( baseModules[ i ] );
                                                }
-                                       } );
+                                       }
                                }
 
                                // Tracks down dependencies
                                                // these as the server will deny them anyway (T101806).
                                                if ( registry[ module ].group === 'private' ) {
                                                        setAndPropagate( module, 'error' );
-                                                       return;
+                                               } else {
+                                                       queue.push( module );
                                                }
-                                               queue.push( module );
                                        }
                                } );
 
                                                        } else {
                                                                mainScript = script.files[ script.main ];
                                                                if ( typeof mainScript !== 'function' ) {
-                                                                       throw new Error( 'Main script file ' + script.main + ' in module ' + module +
-                                                                               'must be of type function, is of type ' + typeof mainScript );
+                                                                       throw new Error( 'Main file ' + script.main + ' in module ' + module +
+                                                                               ' must be of type function, found ' + typeof mainScript );
                                                                }
                                                                // jQuery parameters are not passed for multi-file modules
                                                                mainScript(
                                                                return;
                                                        }
                                                        // Unknown type
-                                                       throw new Error( 'invalid type for external url, must be text/css or text/javascript. not ' + type );
+                                                       throw new Error( 'type must be text/css or text/javascript, found ' + type );
                                                }
                                                // Called with single module
                                                modules = [ modules ];
                                        // Only ready modules can be required
                                        if ( state !== 'ready' ) {
                                                // Module may've forgotten to declare a dependency
-                                               throw new Error( 'Module "' + moduleName + '" is not loaded.' );
+                                               throw new Error( 'Module "' + moduleName + '" is not loaded' );
                                        }
 
                                        return registry[ moduleName ].module.exports;
                                                        this.stats.hits++;
                                                        return this.items[ key ];
                                                }
+
                                                this.stats.misses++;
                                                return false;
                                        },
                                                                !Array.isArray( descriptor.script )
                                                        ) {
                                                                encodedScript = '{' +
-                                                                       '"main":' + JSON.stringify( descriptor.script.main ) + ',' +
-                                                                       '"files":{' +
+                                                                       'main:' + JSON.stringify( descriptor.script.main ) + ',' +
+                                                                       'files:{' +
                                                                        Object.keys( descriptor.script.files ).map( function ( key ) {
                                                                                var value = descriptor.script.files[ key ];
                                                                                return JSON.stringify( key ) + ':' +
index f36fbfd..9c1d5af 100644 (file)
@@ -730,18 +730,6 @@ class TitleTest extends MediaWikiTestCase {
                $this->assertSame( $expected, $actual, $title->getPrefixedDBkey() );
        }
 
-       /**
-        * @dataProvider provideCanHaveTalkPage
-        * @covers Title::canTalk
-        *
-        * @param Title $title
-        * @param bool $expected
-        */
-       public function testCanTalk( Title $title, $expected ) {
-               $actual = $title->canTalk();
-               $this->assertSame( $expected, $actual, $title->getPrefixedDBkey() );
-       }
-
        public static function provideGetTalkPage_good() {
                return [
                        [ Title::makeTitle( NS_MAIN, 'Test' ), Title::makeTitle( NS_TALK, 'Test' ) ],
index 50ac601..070ad56 100644 (file)
@@ -1,6 +1,43 @@
 const MWBot = require( 'mwbot' ),
        Page = require( './Page' ),
-       FRONTPAGE_REQUESTS_MAX_RUNS = 10; // (arbitrary) safe-guard against endless execution
+       MAINPAGE_REQUESTS_MAX_RUNS = 10; // (arbitrary) safe-guard against endless execution
+
+function getJobCount() {
+       let bot = new MWBot( {
+               apiUrl: `${browser.options.baseUrl}/api.php`
+       } );
+       return bot.request( {
+               action: 'query',
+               meta: 'siteinfo',
+               siprop: 'statistics'
+       } ).then( ( response ) => {
+               return response.query.statistics.jobs;
+       } );
+}
+
+function log( message ) {
+       process.stdout.write( `RunJobs ${message}\n` );
+}
+
+function runThroughMainPageRequests( runCount = 1 ) {
+       let page = new Page();
+       log( `through requests to the main page (run ${runCount}).` );
+
+       page.openTitle( '' );
+
+       return getJobCount().then( ( jobCount ) => {
+               if ( jobCount === 0 ) {
+                       log( 'found no more queued jobs.' );
+                       return;
+               }
+               log( `detected ${jobCount} more queued job(s).` );
+               if ( runCount >= MAINPAGE_REQUESTS_MAX_RUNS ) {
+                       log( 'stopping requests to the main page due to reached limit.' );
+                       return;
+               }
+               return runThroughMainPageRequests( ++runCount );
+       } );
+}
 
 /**
  * Trigger the execution of jobs
@@ -26,48 +63,9 @@ class RunJobs {
 
        static run() {
                browser.call( () => {
-                       return this.runThroughFrontPageRequests();
+                       return runThroughMainPageRequests();
                } );
        }
-
-       static getJobCount() {
-               let bot = new MWBot( {
-                       apiUrl: `${browser.options.baseUrl}/api.php`
-               } );
-               return new Promise( ( resolve ) => {
-                       return bot.request( {
-                               action: 'query',
-                               meta: 'siteinfo',
-                               siprop: 'statistics'
-                       } ).then( ( response ) => {
-                               resolve( response.query.statistics.jobs );
-                       } );
-               } );
-       }
-
-       static runThroughFrontPageRequests( runCount = 1 ) {
-               let page = new Page();
-               this.log( `through requests to the front page (run ${runCount}).` );
-
-               page.openTitle( '' );
-
-               return this.getJobCount().then( ( jobCount ) => {
-                       if ( jobCount === 0 ) {
-                               this.log( 'found no more queued jobs.' );
-                               return;
-                       }
-                       this.log( `detected ${jobCount} more queued job(s).` );
-                       if ( runCount >= FRONTPAGE_REQUESTS_MAX_RUNS ) {
-                               this.log( 'stopping requests to the front page due to reached limit.' );
-                               return;
-                       }
-                       return this.runThroughFrontPageRequests( ++runCount );
-               } );
-       }
-
-       static log( message ) {
-               process.stdout.write( `RunJobs ${message}\n` );
-       }
 }
 
 module.exports = RunJobs;