Merge "specialpage: Remove unused QueryPage::doFeed()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 26 Mar 2019 02:34:11 +0000 (02:34 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 26 Mar 2019 02:34:11 +0000 (02:34 +0000)
37 files changed:
.gitignore
.phpcs.xml
HISTORY
RELEASE-NOTES-1.33
autoload.php
includes/Block.php
includes/DefaultSettings.php
includes/ForeignResourceManager.php
includes/GlobalFunctions.php
includes/Linker.php
includes/Title.php
includes/actions/pagers/HistoryPager.php
includes/changetags/ChangeTags.php
includes/db/DatabaseOracle.php
includes/db/MWLBFactory.php
includes/libs/rdbms/lbfactory/LBFactory.php
includes/libs/rdbms/lbfactory/LBFactoryMulti.php
includes/objectcache/ObjectCache.php
includes/poolcounter/PoolCounter.php
includes/poolcounter/PoolCounterNull.php [new file with mode: 0644]
includes/rcfeed/RedisPubSubFeedEngine.php
includes/registration/ExtensionProcessor.php
includes/registration/Processor.php
includes/specialpage/QueryPage.php
maintenance/dictionary/mediawiki.dic
maintenance/dumpTextPass.php
maintenance/manageForeignResources.php [new file with mode: 0644]
maintenance/resources/foreign-resources.yaml [deleted file]
maintenance/resources/manageForeignResources.php [deleted file]
resources/Resources.php
resources/lib/foreign-resources.yaml [new file with mode: 0644]
resources/src/mediawiki.feedback/feedback.css
resources/src/mediawiki.feedback/feedback.js
resources/src/mediawiki.feedback/images/spinner.gif [deleted file]
resources/src/mediawiki.widgets.visibleLengthLimit/mediawiki.widgets.visibleLengthLimit.js
tests/phpunit/includes/cache/MessageCacheTest.php
tests/selenium/specs/page.js

index def5a08..8cacb1e 100644 (file)
@@ -50,6 +50,7 @@ sftp-config.json
 # Building & testing
 npm-debug.log
 node_modules/
+/resources/lib/.foreign
 /tests/phpunit/phpunit.phar
 /tests/selenium/log
 .eslintcache
index 6934648..d1e54a7 100644 (file)
                <exclude-pattern>*/includes/parser/Preprocessor_Hash\.php</exclude-pattern>
                <exclude-pattern>*/includes/parser/Preprocessor\.php</exclude-pattern>
                <exclude-pattern>*/includes/PathRouter\.php</exclude-pattern>
-               <exclude-pattern>*/includes/poolcounter/PoolCounter\.php</exclude-pattern>
                <exclude-pattern>*/includes/PrefixSearch\.php</exclude-pattern>
                <exclude-pattern>*/includes/profiler/SectionProfiler\.php</exclude-pattern>
                <exclude-pattern>*/includes/search/SearchEngine\.php</exclude-pattern>
diff --git a/HISTORY b/HISTORY
index a926069..e8a3692 100644 (file)
--- a/HISTORY
+++ b/HISTORY
@@ -4785,6 +4785,11 @@ of files that are no longer available follows.
 
 = MediaWiki 1.23 =
 
+== MediaWiki 1.23.17 ==
+
+=== Changes since 1.23.16 === <!--T:69-->
+* Fix syntax errors introduced in 1.23.16 when running PHP 5.3.
+
 == MediaWiki 1.23.16 ==
 This is a security and maintenance release of the MediaWiki 1.23 branch.
 
@@ -7044,6 +7049,52 @@ changes to languages because of Bugzilla reports.
 
 == MediaWiki 1.19 ==
 
+== MediaWiki 1.19.24 ==
+
+This is a security and maintenance release of the MediaWiki 1.19 branch.
+
+=== Changes since 1.19.23 ===
+
+* ({{bug|T85848}}, {{bug|T71210}}) SECURITY: Don't parse XMP blocks that
+contain XML entities, to prevent various DoS attacks.
+* ({{bug|T88310}}) SECURITY: Always expand xml entities when checking SVG's.
+* ({{bug|T73394}}) SECURITY: Escape > in Html::expandAttributes to prevent XSS.
+* ({{bug|T85855}}) SECURITY: Don't execute another user's CSS or JS on preview.
+* ({{bug|T85349}}, {{bug|T85850}}, {{bug|T86711}}) SECURITY: Multiple issues
+fixed in SVG filtering to prevent XSS and protect viewer's privacy.
+
+== MediaWiki 1.19.23 ==
+
+This is a security and maintenance release of the MediaWiki 1.19 branch.
+
+=== Changes since 1.19.22 ===
+
+* (bug T76686) [SECURITY] thumb.php outputs wikitext message as raw HTML, which
+could lead to xss. Permission to edit MediaWiki namespace is required to
+exploit this.
+* (bug T74222) The original patch for T74222 was reverted as unnecessary.
+* Add missing $ in front of variable in OutputPage.php
+
+== MediaWiki 1.19.22 ==
+
+This is a security and maintenance release of the MediaWiki 1.19 branch.
+
+=== Changes since 1.19.21 ===
+
+* ({{bug|66776}}, {{bug|71478}}) SECURITY:  User PleaseStand reported a way to
+inject code into API clients that used format=php to process pages that
+underwent flash policy mangling. This was fixed along with improving how the
+mangling was done for format=json, and allowing sites to disable the mangling
+using $wgMangleFlashPolicy.
+* ({{bug|72222}}) SECURITY: Do not show log action when the entry is revdeleted
+with DELETED_ACTION. NOTICE: this may be reverted in a future release pending a
+public RFC about the desired functionality. This issue was reported by user
+Bawolff.
+* ({{bug|71621}}) Make allowing site-wide styles on restricted special pages a
+config option.
+* $wgMangleFlashPolicy was added to make MediaWiki's mangling of anything that
+might be a flash policy directive configurable.
+
 == MediaWiki 1.19.21 ==
 This is a maintenance release of the MediaWiki 1.19 branch.
 
@@ -7618,6 +7669,20 @@ changes to languages because of Bugzilla reports.
 
 == MediaWiki 1.18 ==
 
+== MediaWiki 1.18.6 ==
+2012-11-29
+
+This is a maintenance and security release of the MediaWiki 1.18 branch
+
+=== Changes since 1.18.5 ===
+* ([[bugzilla:40995|bug 40995]]) Prevent session fixation in Special:UserLogin
+(CVE-2012-5391)
+* ([[bugzilla:41400|bug 41400]]) Prevent linker regex from exceeding PCRE
+backtrack limit
+* Localisation updates
+* Increase permitted runtime for testParserTest
+* ([[bugzilla:36179|bug 36179]]) Unquote 'null' for PostgreSQL.
+
 == MediaWiki 1.18.5 ==
 2012-08-30
 
@@ -11983,9 +12048,143 @@ Other changes in this release:
   the page
 * list=exturlusage in "list all links" mode can now filter by protocol
 
+== MediaWiki 1.12 ==
+
+== MediaWiki 1.12.4 ==
 
+February 7, 2009
 
-== MediaWiki 1.12 ==
+A number of cross-site scripting (XSS) security vulnerabilities were discovered
+in the web-based installer (config/index.php). These vulnerabilities all
+require a live installer -- once the installer has been used to install a wiki,
+it is deactivated.
+
+Note that cross-site scripting vulnerabilities can be used to attack any
+website in the same cookie domain. So if you have an uninstalled copy of
+MediaWiki on the same site as an active web service, MediaWiki could be used to
+attack the active service.
+
+If you are hosting an old copy of MediaWiki that you have never installed, you
+are advised to remove it from the web.
+
+== MediaWiki 1.12.3 ==
+
+* Fixed packaging/distribution error. Many files were missing from the
+distributed tarball.
+
+== MediaWiki 1.12.2 ==
+
+David Remahl of Apple's Product Security team has identified a number of
+security issues in previous releases of MediaWiki. Subsequent analysis by the
+MediaWiki development team expanded the scope of these vulnerabilities. The
+issues with a significant impact are as follows:
+
+* A local script injection vulnerability affecting Internet Explorer clients
+for all MediaWiki installations with uploads enabled. [CVE-2008-5250]
+* A local script injection vulnerability affecting clients with SVG scripting
+capability (such as Firefox 1.5+), for all MediaWiki installations with SVG
+uploads enabled. [CVE-2008-5250]
+* A CSRF vulnerability affecting the Special:Import feature, for all MediaWiki
+installations since the feature was introduced in 1.3.0. [CVE-2008-5252]
+
+A local script injection vulnerability allows an attacker with a wiki account
+to steal another user's login session, and to act as that user on the wiki. The
+attacker uploads a malicious script file, and tricks the victim into executing
+it.
+
+CSRF vulnerabilities allow an attacker to act as an authorised user on the
+wiki, but unlike an XSS vulnerability, the attacker can only act as the user in
+a specific and restricted way. The present CSRF vulnerability allows pages to
+be edited, with forged revision histories. Like an XSS vulnerability, the
+authorised user must visit the malicious web page to activate the attack.
+
+These three vulnerabilities are all fixed in this release.
+
+David Remahl also reminded us of some security-related configuration issues:
+
+* By default, MediaWiki stores a backup of deleted images in the images/deleted
+directory. If you do not want these images to be publically accessible, make
+sure this directory is not accessible from the web. MediaWiki takes some steps
+to avoid leaking these images, but these measures are not perfect.
+* Set display_errors=off in your php.ini to avoid path disclosure via PHP fatal
+errors. This is the default on most shared web hosts.
+* Enabling MediaWiki's debugging features, such as $wgShowExceptionDetails, may
+lead to path disclosure.
+
+Other changes in this release:
+
+* Avoid fatal error in profileinfo.php when not configured.
+* Add a .htaccess to deleted images directory for additional protection against
+exposure of deleted files with known SHA-1 hashes on default installations.
+* Avoid streaming uploaded files to the user via index.php. This allows
+security-conscious users to serve uploaded files via a different domain, and
+thus client-side scripts executed from that domain cannot access the login
+cookies. Affects Special:Undelete, img_auth.php and thumb.php.
+* When streaming files via index.php, use the MIME type detected from the file
+extension, not from the data. This reduces the XSS attack surface.
+* Blacklist redirects via Special:Filepath. Such redirects exacerbate any XSS
+vulnerabilities involving uploads of files containing scripts.
+* Internationalisation updates.
+
+== MediaWiki 1.12.1 ==
+
+Changes since 1.12.0:
+* (bug [[bugzilla:13522|13522]]) Fix fatal error in Parser::extractTagsAndParams
+* (bug [[bugzilla:12077|12077]]) Fix HTML nesting for TOC
+* (bug [[bugzilla:13532|13532]]) Use proper timestamp call when reverting images
+* (bug [[bugzilla:13649|13649]], [[bugzilla:14084|14084]]) Bad call to
+wfTimestamp()
+* (bug [[bugzilla:13770|13770]]) Use Preprocessor_Hash by default to avoid
+missing DOM module errors
+* (bug [[bugzilla:13442|13442]]) API: Missing pages in prop=langlinks and
+prop=extlinks are now handled properly.
+* (bug [[bugzilla:13482|13482]]) API: Disabled search types handled properly
+* (bug [[bugzilla:13836|13836]]) API: Fixed fatal errors resulting from
+combining iiprop=metadata  with format=xml
+* (bug [[bugzilla:11633|11633]]) API: Explicitly convert redirect titles to
+strings due to PHP's very weak typing on array keys.
+* API: Fixing main page display in meta=siteinfo
+* (bug [[bugzilla:11719|11719]]) API: Remove trailing blanks in YAML output.
+* (bug [[bugzilla:13718|13718]]) API: Return the proper continue parameter for
+cmsort=timestamp
+* Security: Work around misconfiguration by requiring strict comparisons for
+in_array in User::isAllowed().
+* Security: Fixed XSS vulnerability in useskin parameter.
+
+== MediaWiki 1.12.0 ==
+
+This is the quarterly branch release of [[MediaWiki]] for Winter 2008.
+
+MediaWiki is now using a "continuous integration" development model with
+quarterly snapshot releases. The latest development code is always kept "ready
+to run", and in fact runs our own sites on [[wikipedia:|Wikipedia]].
+
+Release branches will continue to receive security updates for about a year
+from first release, but nonessential bugfixes and feature developments will be
+made on the development trunk and appear in the next quarterly release.
+
+Those wishing to use the latest code instead of a branch release can obtain it
+from source control: [[Download from SVN]].
+
+Changes since 1.12.0rc1:
+*(bug [[bugzilla:13359|13359]]) Double-escaping in [[Special:Allpages]].
+*Localization updates.
+
+== MediaWiki 1.12.0rc1 ==
+
+This is a release candidate of the Winter 2008 quarterly snapshot release of
+[[MediaWiki]].
+
+MediaWiki is now using a "continuous integration" development model with
+quarterly snapshot releases. The latest development code is always kept "ready
+to run", and in fact runs our own sites on [[wikipedia:|Wikipedia]].
+
+Release branches will continue to receive security updates for about a year
+from first release, but nonessential bugfixes and feature developments will be
+made on the development trunk and appear in the next quarterly release.
+
+Those wishing to use the latest code instead of a branch release can obtain it
+from source control: [[Download from SVN]].
 
 This is the Winter 2007 quarterly release.
 
@@ -12539,6 +12738,76 @@ Full API documentation is available at https://www.mediawiki.org/wiki/API
 
 == MediaWiki 1.11 ==
 
+== MediaWiki 1.11.2 ==
+
+March 2, 2008
+
+This is a security release of the Fall 2007 snapshot release of MediaWiki.
+Possible cross-site information leaks using the callback parameter for
+JSON-formatted results in the API are prevented by dropping user credentials.
+
+MediaWiki release versions prior to 1.11 are not vulnerable, as they do not
+include the callback feature which allows client-side JavaScript on other sites
+to reach API data.
+
+Changes in this release:
+
+* User credentials are dropped for API JSON requests using a callback
+* Edit tokens are not reported for API JSON requests using a callback
+
+== MediaWiki 1.11.1 ==
+
+January 23, 2008
+
+This is a security and bugfix release of the Fall 2007 snapshot release of
+ MediaWiki. A potential XSS injection vector affecting api.php only for
+ Microsoft Internet Explorer users has been closed.
+
+Changes in this release:
+* (bug [[bugzilla:11450|11450]]) Fix creation of objectcache table on upgrade
+* (bug [[bugzilla:11462|11462]]) Fix typo in LanguageGetSpecialPageAliases hook
+name
+* Fix regression in LinkBatch.php breaking PHP 5.0
+* Security fix for API on MSIE
+
+To work around the vulnerability without upgrading, you may disable the API if
+you don't need it:
+:[[Manual:$wgEnableAPI|$wgEnableAPI]] = false;
+
+Not vulnerable versions:
+* 1.12 or later
+* 1.11 >= 1.11.1
+* 1.10 >= 1.10.3
+* 1.9 >= 1.9.5
+* 1.8 any version (if $wgEnableAPI has been left off)
+
+Vulnerable versions:
+* 1.11 <= 1.11.0rc1
+* 1.10 <= 1.10.2
+* 1.9 <= 1.9.4
+* 1.8 any version (if $wgEnableAPI has been switched on)
+
+MediaWiki 1.7 and below are not affected as they do not include the API
+functionality, however the BotQuery extension is similarly vulnerable unless
+updated to the latest SVN version.
+
+== MediaWiki 1.11.0 ==
+
+September 10, 2007
+
+This is the Fall 2007 snapshot release of MediaWiki.
+
+MediaWiki is now using a "continuous integration" development model with
+quarterly snapshot releases. The latest development code is always kept "ready
+to run", and in fact runs our own sites on Wikipedia.
+
+Release branches will continue to receive security updates for about a year
+from first release, but nonessential bugfixes and feature developments will be
+made on the development trunk and appear in the next quarterly release.
+
+Those wishing to use the latest code instead of a branch release can obtain it
+from source control: [[Download from SVN]]
+
 This is the Summer 2007 branch release of MediaWiki.
 
 MediaWiki is now using a "continuous integration" development model with
@@ -12552,6 +12821,33 @@ will be made on the development trunk and appear in the next quarterly release.
 Those wishing to use the latest code instead of a branch release can obtain
 it from source control: https://www.mediawiki.org/wiki/Download_from_SVN
 
+== Changes since 1.11.0rc1 ==
+
+A possible HTML/XSS injection vector in the API pretty-printing mode has been
+found and fixed.
+
+The vulnerability may be worked around in an unfixed version by simply
+disabling the API interface if it is not in use, by adding this to
+[[Manual:LocalSettings.php|LocalSettings.php]]:<br />
+<code>[[Manual:$wgEnableAPI|$wgEnableAPI]] = false;</code> <br />
+(This is the default setting in 1.8.x.)
+
+Not vulnerable versions:
+* 1.11 >= 1.11.0
+* 1.10 >= 1.10.2
+* 1.9 >= 1.9.4
+* 1.8 >= 1.8.5
+
+Vulnerable versions:
+* 1.11 <= 1.11.0rc1
+* 1.10 <= 1.10.1
+* 1.9 <= 1.9.3
+* 1.8 <= 1.8.4 (if [[Manual:$wgEnableAPI|$wgEnableAPI]] has been switched on)
+
+MediaWiki 1.7 and below are not affected as they do not include the faulty
+function, however the [[Extension:BotQuery|BotQuery extension]] is similarly
+vulnerable unless updated to the latest SVN version.
+
 == Configuration changes since 1.10 ==
 
 * $wgThumbUpright - Adjust width of upright images when parameter 'upright' is
@@ -12560,7 +12856,8 @@ it from source control: https://www.mediawiki.org/wiki/Download_from_SVN
   usergroups
 * $wgEnotifImpersonal, $wgEnotifUseJobQ - Bulk mail options for large sites
 * $wgShowHostnames - Expose server host names through the API and HTML comments
-* $wgSaveDeletedFiles has been removed, the feature is now enabled unconditionally
+* $wgSaveDeletedFiles has been removed, the feature is now enabled
+unconditionally
 
 == New features since 1.10 ==
 
index 93d3253..72a468b 100644 (file)
@@ -84,6 +84,8 @@ For notes on 1.32.x and older releases, see HISTORY.
   is no longer a problem, because the code now ensures the timestamp is always
   higher than the previous one. The writes are guarded with CAS logic (check
   and set), which prevents updates that would overlap.
+* $wgDBmysql5 (T196185) - This experimental setting, deprecated in 1.31, has
+  been removed.
 
 === New user-facing features in 1.33 ===
 * (T96041) __EXPECTUNUSEDCATEGORY__ on a category page causes the category
index bbfe251..528b7fe 100644 (file)
@@ -851,7 +851,7 @@ $wgAutoloadLocalClasses = [
        'Maintenance' => __DIR__ . '/maintenance/Maintenance.php',
        'MakeTestEdits' => __DIR__ . '/maintenance/makeTestEdits.php',
        'MalformedTitleException' => __DIR__ . '/includes/title/MalformedTitleException.php',
-       'ManageForeignResources' => __DIR__ . '/maintenance/resources/manageForeignResources.php',
+       'ManageForeignResources' => __DIR__ . '/maintenance/manageForeignResources.php',
        'ManageJobs' => __DIR__ . '/maintenance/manageJobs.php',
        'ManualLogEntry' => __DIR__ . '/includes/logging/LogEntry.php',
        'MapCacheLRU' => __DIR__ . '/includes/libs/MapCacheLRU.php',
@@ -1108,10 +1108,10 @@ $wgAutoloadLocalClasses = [
        'PhpXmlBugTester' => __DIR__ . '/includes/installer/PhpBugTests.php',
        'Pingback' => __DIR__ . '/includes/Pingback.php',
        'PoolCounter' => __DIR__ . '/includes/poolcounter/PoolCounter.php',
+       'PoolCounterNull' => __DIR__ . '/includes/poolcounter/PoolCounterNull.php',
        'PoolCounterRedis' => __DIR__ . '/includes/poolcounter/PoolCounterRedis.php',
        'PoolCounterWork' => __DIR__ . '/includes/poolcounter/PoolCounterWork.php',
        'PoolCounterWorkViaCallback' => __DIR__ . '/includes/poolcounter/PoolCounterWorkViaCallback.php',
-       'PoolCounter_Stub' => __DIR__ . '/includes/poolcounter/PoolCounter.php',
        'PoolWorkArticleView' => __DIR__ . '/includes/poolcounter/PoolWorkArticleView.php',
        'PopulateArchiveRevId' => __DIR__ . '/maintenance/populateArchiveRevId.php',
        'PopulateBacklinkNamespace' => __DIR__ . '/maintenance/populateBacklinkNamespace.php',
index 7e32f7e..060eebd 100644 (file)
@@ -166,7 +166,7 @@ class Block {
                }
 
                $this->setReason( $options['reason'] );
-               $this->mTimestamp = wfTimestamp( TS_MW, $options['timestamp'] );
+               $this->setTimestamp( wfTimestamp( TS_MW, $options['timestamp'] ) );
                $this->setExpiry( wfGetDB( DB_REPLICA )->decodeExpiry( $options['expiry'] ) );
 
                # Boolean settings
@@ -471,7 +471,7 @@ class Block {
                        $row->ipb_by, $row->ipb_by_text, $row->ipb_by_actor ?? null
                ) );
 
-               $this->mTimestamp = wfTimestamp( TS_MW, $row->ipb_timestamp );
+               $this->setTimestamp( wfTimestamp( TS_MW, $row->ipb_timestamp ) );
                $this->mAuto = $row->ipb_auto;
                $this->setHideName( $row->ipb_deleted );
                $this->mId = (int)$row->ipb_id;
@@ -904,7 +904,7 @@ class Block {
                                ->inContentLanguage()->plain()
                );
                $timestamp = wfTimestampNow();
-               $autoblock->mTimestamp = $timestamp;
+               $autoblock->setTimestamp( $timestamp );
                $autoblock->mAuto = 1;
                $autoblock->isCreateAccountBlocked( $this->isCreateAccountBlocked() );
                # Continue suppressing the name if needed
@@ -975,7 +975,7 @@ class Block {
         */
        public function updateTimestamp() {
                if ( $this->mAuto ) {
-                       $this->mTimestamp = wfTimestamp();
+                       $this->setTimestamp( wfTimestamp() );
                        $this->setExpiry( self::getAutoblockExpiry( $this->getTimestamp() ) );
 
                        $dbw = wfGetDB( DB_MASTER );
@@ -1036,10 +1036,7 @@ class Block {
         * @return int (0 for foreign users)
         */
        public function getBy() {
-               $blocker = $this->getBlocker();
-               return ( $blocker instanceof User )
-                       ? $blocker->getId()
-                       : 0;
+               return $this->getBlocker()->getId();
        }
 
        /**
@@ -1048,10 +1045,7 @@ class Block {
         * @return string
         */
        public function getByName() {
-               $blocker = $this->getBlocker();
-               return ( $blocker instanceof User )
-                       ? $blocker->getName()
-                       : (string)$blocker; // username
+               return $this->getBlocker()->getName();
        }
 
        /**
index 3afa593..7a645a6 100644 (file)
@@ -2114,26 +2114,6 @@ $wgDBerrorLog = false;
  */
 $wgDBerrorLogTZ = false;
 
-/**
- * Set to true to engage MySQL 4.1/5.0 charset-related features;
- * for now will just cause sending of 'SET NAMES=utf8' on connect.
- *
- * @warning THIS IS EXPERIMENTAL!
- *
- * May break if you're not using the table defs from mysql5/tables.sql.
- * May break if you're upgrading an existing wiki if set differently.
- * Broken symptoms likely to include incorrect behavior with page titles,
- * usernames, comments etc containing non-ASCII characters.
- * Might also cause failures on the object cache and other things.
- *
- * Even correct usage may cause failures with Unicode supplementary
- * characters (those not in the Basic Multilingual Plane) unless MySQL
- * has enhanced their Unicode support.
- *
- * @deprecated since 1.31
- */
-$wgDBmysql5 = false;
-
 /**
  * Set true to enable Oracle DCRP (supported from 11gR1 onward)
  *
index e0d088a..9fd1e4f 100644 (file)
@@ -30,10 +30,12 @@ class ForeignResourceManager {
        private $registryFile;
        private $libDir;
        private $tmpParentDir;
+       private $cacheDir;
        private $infoPrinter;
        private $errorPrinter;
        private $verbosePrinter;
        private $action;
+       private $registry;
 
        /**
         * @param string $registryFile Path to YAML file
@@ -60,8 +62,11 @@ class ForeignResourceManager {
 
                // Use a temporary directory under the destination directory instead
                // of wfTempDir() because PHP's rename() does not work across file
-               // systems, as the user's /tmp and $IP may be on different filesystems.
-               $this->tmpParentDir = "{$this->libDir}/.tmp";
+               // systems, and the user's /tmp and $IP may be on different filesystems.
+               $this->tmpParentDir = "{$this->libDir}/.foreign/tmp";
+
+               $cacheHome = getenv( 'XDG_CACHE_HOME' ) ? realpath( getenv( 'XDG_CACHE_HOME' ) ) : false;
+               $this->cacheDir = $cacheHome ? "$cacheHome/mw-foreign" : "{$this->libDir}/.foreign/cache";
        }
 
        /**
@@ -69,18 +74,24 @@ class ForeignResourceManager {
         * @throws Exception
         */
        public function run( $action, $module ) {
-               if ( !in_array( $action, [ 'update', 'verify', 'make-sri' ] ) ) {
-                       throw new Exception( 'Invalid action parameter.' );
+               $actions = [ 'update', 'verify', 'make-sri' ];
+               if ( !in_array( $action, $actions ) ) {
+                       $this->error( "Invalid action.\n\nMust be one of " . implode( ', ', $actions ) . '.' );
+                       return false;
                }
                $this->action = $action;
 
-               $registry = $this->parseBasicYaml( file_get_contents( $this->registryFile ) );
+               $this->registry = $this->parseBasicYaml( file_get_contents( $this->registryFile ) );
                if ( $module === 'all' ) {
-                       $modules = $registry;
-               } elseif ( isset( $registry[ $module ] ) ) {
-                       $modules = [ $module => $registry[ $module ] ];
+                       $modules = $this->registry;
+               } elseif ( isset( $this->registry[ $module ] ) ) {
+                       $modules = [ $module => $this->registry[ $module ] ];
                } else {
-                       throw new Exception( 'Unknown module name.' );
+                       $this->error( "Unknown module name.\n\nMust be one of:\n" .
+                               wordwrap( implode( ', ', array_keys( $this->registry ) ), 80 ) .
+                               '.'
+                       );
+                       return false;
                }
 
                foreach ( $modules as $moduleName => $info ) {
@@ -121,8 +132,8 @@ class ForeignResourceManager {
                        }
                }
 
-               $this->cleanUp();
                $this->output( "\nDone!\n" );
+               $this->cleanUp();
                if ( $this->hasErrors ) {
                        // The verify mode should check all modules/files and fail after, not during.
                        return false;
@@ -131,7 +142,29 @@ class ForeignResourceManager {
                return true;
        }
 
+       private function cacheKey( $src, $integrity ) {
+               $key = basename( $src ) . '_' . substr( $integrity, -12 );
+               $key = preg_replace( '/[.\/+?=_-]+/', '_', $key );
+               return rtrim( $key, '_' );
+       }
+
+       /** @return string|false */
+       private function cacheGet( $key ) {
+               return Wikimedia\quietCall( 'file_get_contents', "{$this->cacheDir}/$key.data" );
+       }
+
+       private function cacheSet( $key, $data ) {
+               wfMkdirParents( $this->cacheDir );
+               file_put_contents( "{$this->cacheDir}/$key.data", $data, LOCK_EX );
+       }
+
        private function fetch( $src, $integrity ) {
+               $key = $this->cacheKey( $src, $integrity );
+               $data = $this->cacheGet( $key );
+               if ( $data ) {
+                       return $data;
+               }
+
                $req = MWHttpRequest::factory( $src, [ 'method' => 'GET', 'followRedirects' => false ] );
                if ( !$req->execute()->isOK() ) {
                        throw new Exception( "Failed to download resource at {$src}" );
@@ -144,6 +177,7 @@ class ForeignResourceManager {
                $actualIntegrity = $algo . '-' . base64_encode( hash( $algo, $data, true ) );
                if ( $integrity === $actualIntegrity ) {
                        $this->verbose( "... passed integrity check for {$src}\n" );
+                       $this->cacheSet( $key, $data );
                } else {
                        if ( $this->action === 'make-sri' ) {
                                $this->output( "Integrity for {$src}\n\tintegrity: ${actualIntegrity}\n" );
@@ -271,6 +305,23 @@ class ForeignResourceManager {
 
        private function cleanUp() {
                wfRecursiveRemoveDir( $this->tmpParentDir );
+
+               // Prune the cache of files we don't recognise.
+               $knownKeys = [];
+               foreach ( $this->registry as $info ) {
+                       if ( $info['type'] === 'file' || $info['type'] === 'tar' ) {
+                               $knownKeys[] = $this->cacheKey( $info['src'], $info['integrity'] );
+                       } elseif ( $info['type'] === 'multi-file' ) {
+                               foreach ( $info['files'] as $file ) {
+                                       $knownKeys[] = $this->cacheKey( $file['src'], $file['integrity'] );
+                               }
+                       }
+               }
+               foreach ( glob( "{$this->cacheDir}/*" ) as $cacheFile ) {
+                       if ( !in_array( basename( $cacheFile, '.data' ), $knownKeys ) ) {
+                               unlink( $cacheFile );
+                       }
+               }
        }
 
        /**
index 319bf63..55b78ac 100644 (file)
@@ -334,6 +334,7 @@ function wfUrlencode( $s ) {
        static $needle;
 
        if ( is_null( $s ) ) {
+               // Reset $needle for testing.
                $needle = null;
                return '';
        }
index decc13c..ec3b245 100644 (file)
@@ -1077,16 +1077,18 @@ class Linker {
         * @since 1.16.3
         * @param Revision $rev
         * @param bool $isPublic Show only if all users can see it
+        * @param bool $useParentheses (optional) Wrap comments in parentheses where needed
         * @return string HTML
         */
-       public static function revUserTools( $rev, $isPublic = false ) {
+       public static function revUserTools( $rev, $isPublic = false, $useParentheses = true ) {
                if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
                        $link = wfMessage( 'rev-deleted-user' )->escaped();
                } elseif ( $rev->userCan( Revision::DELETED_USER ) ) {
                        $userId = $rev->getUser( Revision::FOR_THIS_USER );
                        $userText = $rev->getUserText( Revision::FOR_THIS_USER );
                        $link = self::userLink( $userId, $userText )
-                               . self::userToolLinks( $userId, $userText );
+                               . self::userToolLinks( $userId, $userText, false, 0, null,
+                                       $useParentheses );
                } else {
                        $link = wfMessage( 'rev-deleted-user' )->escaped();
                }
@@ -1532,9 +1534,8 @@ class Linker {
                        $stxt = wfMessage( 'historyempty' )->escaped();
                } else {
                        $stxt = wfMessage( 'nbytes' )->numParams( $size )->escaped();
-                       $stxt = wfMessage( 'parentheses' )->rawParams( $stxt )->escaped();
                }
-               return "<span class=\"history-size\">$stxt</span>";
+               return "<span class=\"history-size mw-diff-bytes\">$stxt</span>";
        }
 
        /**
index d8aeb62..0f45839 100644 (file)
@@ -3727,6 +3727,7 @@ class Title implements LinkTarget, IDBAccessObject {
                // @todo: get rid of secureAndSplit, refactor parsing code.
                // @note: getTitleParser() returns a TitleParser implementation which does not have a
                //        splitTitleString method, but the only implementation (MediaWikiTitleCodec) does
+               /** @var MediaWikiTitleCodec $titleCodec */
                $titleCodec = MediaWikiServices::getInstance()->getTitleParser();
                // MalformedTitleException can be thrown here
                $parts = $titleCodec->splitTitleString( $this->mDbkeyform, $this->mDefaultNamespace );
index d3a32d0..9e4080d 100644 (file)
@@ -310,11 +310,12 @@ class HistoryPager extends ReverseChronologicalPager {
 
                $curlink = $this->curLink( $rev, $latest );
                $lastlink = $this->lastLink( $rev, $next );
-               $curLastlinks = $curlink . $this->historyPage->message['pipe-separator'] . $lastlink;
+               $curLastlinks = Html::rawElement( 'span', [], $curlink ) .
+                       Html::rawElement( 'span', [], $lastlink );
                $histLinks = Html::rawElement(
                        'span',
-                       [ 'class' => 'mw-history-histlinks' ],
-                       $this->msg( 'parentheses' )->rawParams( $curLastlinks )->escaped()
+                       [ 'class' => 'mw-history-histlinks mw-changeslist-links' ],
+                       $curLastlinks
                );
 
                $diffButtons = $this->diffButtons( $rev, $firstInList );
@@ -362,7 +363,7 @@ class HistoryPager extends ReverseChronologicalPager {
                $s .= " $link";
                $s .= $dirmark;
                $s .= " <span class='history-user'>" .
-                       Linker::revUserTools( $rev, true ) . "</span>";
+                       Linker::revUserTools( $rev, true, false ) . "</span>";
                $s .= $dirmark;
 
                if ( $rev->isMinor() ) {
@@ -374,12 +375,12 @@ class HistoryPager extends ReverseChronologicalPager {
                        # Size is always public data
                        $prevSize = $this->parentLens[$row->rev_parent_id] ?? 0;
                        $sDiff = ChangesList::showCharacterDifference( $prevSize, $rev->getSize() );
-                       $fSize = Linker::formatRevisionSize( $rev->getSize() );
-                       $s .= ' <span class="mw-changeslist-separator">. .</span> ' . "$fSize $sDiff";
+                       $fSize = Linker::formatRevisionSize( $rev->getSize(), false );
+                       $s .= ' <span class="mw-changeslist-separator"></span> ' . "$fSize $sDiff";
                }
 
                # Text following the character difference is added just before running hooks
-               $s2 = Linker::revComment( $rev, false, true );
+               $s2 = Linker::revComment( $rev, false, true, false );
 
                if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) {
                        $s2 .= ' <span class="updatedmarker">' . $this->msg( 'updatedmarker' )->escaped() . '</span>';
@@ -427,7 +428,11 @@ class HistoryPager extends ReverseChronologicalPager {
                Hooks::run( 'HistoryRevisionTools', [ $rev, &$tools, $prevRev, $user ] );
 
                if ( $tools ) {
-                       $s2 .= ' ' . $this->msg( 'parentheses' )->rawParams( $lang->pipeList( $tools ) )->escaped();
+                       $s2 .= ' ' . Html::openElement( 'span', [ 'class' => 'mw-changeslist-links' ] );
+                       foreach ( $tools as $tool ) {
+                               $s2 .= Html::rawElement( 'span', [], $tool );
+                       }
+                       $s2 .= Html::closeElement( 'span' );
                }
 
                # Tags
@@ -443,7 +448,7 @@ class HistoryPager extends ReverseChronologicalPager {
 
                # Include separator between character difference and following text
                if ( $s2 !== '' ) {
-                       $s .= ' <span class="mw-changeslist-separator">. .</span> ' . $s2;
+                       $s .= ' <span class="mw-changeslist-separator"></span> ' . $s2;
                }
 
                $attribs = [ 'data-mw-revid' => $rev->getId() ];
index 00eed14..3a93e57 100644 (file)
@@ -766,7 +766,7 @@ class ChangeTags {
                                        // Return nothing.
                                        $conds[] = '0';
                                        break;
-                               };
+                               }
                        }
 
                        if ( $filterTagIds !== [] ) {
index 16bde4b..bc3873d 100644 (file)
@@ -700,14 +700,6 @@ class DatabaseOracle extends Database {
                return new Blob( $b );
        }
 
-       function decodeBlob( $b ) {
-               if ( $b instanceof Blob ) {
-                       $b = $b->fetch();
-               }
-
-               return $b;
-       }
-
        function unionQueries( $sqls, $all ) {
                $glue = ' UNION ALL ';
 
@@ -1350,10 +1342,6 @@ class DatabaseOracle extends Database {
                return 'BITOR(' . $fieldLeft . ', ' . $fieldRight . ')';
        }
 
-       function getServer() {
-               return $this->server;
-       }
-
        public function buildGroupConcatField(
                $delim, $table, $field, $conds = '', $join_conds = []
        ) {
index 6ed693e..9851460 100644 (file)
@@ -122,7 +122,6 @@ abstract class MWLBFactory {
                                                'tablePrefix' => $mainConfig->get( 'DBprefix' ),
                                                'flags' => DBO_DEFAULT,
                                                'sqlMode' => $mainConfig->get( 'SQLMode' ),
-                                               'utf8Mode' => $mainConfig->get( 'DBmysql5' )
                                        ];
 
                                        $lbConf['servers'][$i] = $server;
@@ -142,7 +141,6 @@ abstract class MWLBFactory {
                                        'load' => 1,
                                        'flags' => $flags,
                                        'sqlMode' => $mainConfig->get( 'SQLMode' ),
-                                       'utf8Mode' => $mainConfig->get( 'DBmysql5' )
                                ];
                                if ( in_array( $server['type'], $typesWithSchema, true ) ) {
                                        $server += [ 'schema' => $mainConfig->get( 'DBmwschema' ) ];
@@ -168,7 +166,6 @@ abstract class MWLBFactory {
                                        $lbConf['serverTemplate']['schema'] = $mainConfig->get( 'DBmwschema' );
                                }
                                $lbConf['serverTemplate']['sqlMode'] = $mainConfig->get( 'SQLMode' );
-                               $lbConf['serverTemplate']['utf8Mode'] = $mainConfig->get( 'DBmysql5' );
                        }
                }
 
index 007ac20..3a8f2e1 100644 (file)
@@ -654,7 +654,7 @@ abstract class LBFactory implements ILBFactory {
        }
 
        public function closeAll() {
-               $this->forEachLBCallMethod( 'closeAll', [] );
+               $this->forEachLBCallMethod( 'closeAll' );
        }
 
        public function setAgentName( $agent ) {
index 189ceee..aec99f4 100644 (file)
@@ -89,9 +89,6 @@ class LBFactoryMulti extends LBFactory {
         */
        private $readOnlyBySection = [];
 
-       /** @var array Load balancer factory configuration */
-       private $conf;
-
        /** @var LoadBalancer[] */
        private $mainLBs = [];
 
@@ -166,7 +163,6 @@ class LBFactoryMulti extends LBFactory {
        public function __construct( array $conf ) {
                parent::__construct( $conf );
 
-               $this->conf = $conf;
                $required = [ 'sectionsByDB', 'sectionLoads', 'serverTemplate' ];
                $optional = [ 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName',
                        'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer',
index dc8b146..fed0854 100644 (file)
@@ -238,7 +238,6 @@ class ObjectCache {
                global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType;
                $candidates = [ $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType ];
                foreach ( $candidates as $candidate ) {
-                       $cache = false;
                        if ( $candidate !== CACHE_NONE && $candidate !== CACHE_ANYTHING ) {
                                $cache = self::getInstance( $candidate );
                                // CACHE_ACCEL might default to nothing if no APCu
index ba0b4cb..060faec 100644 (file)
@@ -39,7 +39,7 @@
  * that start with "nowait:". However, only 0 timeouts (non-blocking requests)
  * can be used with "nowait:" keys.
  *
- * By default PoolCounter_Stub is used, which provides no locking. You
+ * By default PoolCounterNull is used, which provides no locking. You
  * can get a useful one in the PoolCounter extension.
  */
 abstract class PoolCounter {
@@ -111,7 +111,7 @@ abstract class PoolCounter {
        public static function factory( $type, $key ) {
                global $wgPoolCounterConf;
                if ( !isset( $wgPoolCounterConf[$type] ) ) {
-                       return new PoolCounter_Stub;
+                       return new PoolCounterNull;
                }
                $conf = $wgPoolCounterConf[$type];
                $class = $conf['class'];
@@ -208,23 +208,3 @@ abstract class PoolCounter {
                return $type . ':' . ( hexdec( substr( sha1( $key ), 0, 4 ) ) % $slots );
        }
 }
-
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PoolCounter_Stub extends PoolCounter {
-
-       public function __construct() {
-               /* No parameters needed */
-       }
-
-       public function acquireForMe() {
-               return Status::newGood( PoolCounter::LOCKED );
-       }
-
-       public function acquireForAnyone() {
-               return Status::newGood( PoolCounter::LOCKED );
-       }
-
-       public function release() {
-               return Status::newGood( PoolCounter::RELEASED );
-       }
-}
diff --git a/includes/poolcounter/PoolCounterNull.php b/includes/poolcounter/PoolCounterNull.php
new file mode 100644 (file)
index 0000000..95a5057
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Provides of semaphore semantics for restricting the number
+ * of workers that may be concurrently performing the same task.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * A default PoolCounter, which provides no locking.
+ */
+class PoolCounterNull extends PoolCounter {
+
+       public function __construct() {
+               /* No parameters needed */
+       }
+
+       public function acquireForMe() {
+               return Status::newGood( PoolCounter::LOCKED );
+       }
+
+       public function acquireForAnyone() {
+               return Status::newGood( PoolCounter::LOCKED );
+       }
+
+       public function release() {
+               return Status::newGood( PoolCounter::RELEASED );
+       }
+}
index 8a3aa0c..c954df1 100644 (file)
@@ -37,7 +37,7 @@
  *
  * @since 1.22
  */
-class RedisPubSubFeedEngine extends RCFeedEngine {
+class RedisPubSubFeedEngine extends FormattedRCFeed {
 
        /**
         * @see FormattedRCFeed::send
@@ -68,8 +68,8 @@ class RedisPubSubFeedEngine extends RCFeedEngine {
                if ( $conn !== false ) {
                        $conn->publish( $channel, $line );
                        return true;
-               } else {
-                       return false;
                }
+
+               return false;
        }
 }
index 1d3fd86..b474ddc 100644 (file)
@@ -189,7 +189,6 @@ class ExtensionProcessor implements Processor {
         * @param string $path
         * @param array $info
         * @param int $version manifest_version for info
-        * @return array
         */
        public function extractInfo( $path, array $info, $version ) {
                $dir = dirname( $path );
index 636d3b3..68ba413 100644 (file)
@@ -17,7 +17,6 @@ interface Processor {
         * @param string $path Absolute path of JSON file
         * @param array $info
         * @param int $version manifest_version for info
-        * @return array "credits" information to store
         */
        public function extractInfo( $path, array $info, $version );
 
index 2c954e8..b88479a 100644 (file)
@@ -46,13 +46,18 @@ abstract class QueryPage extends SpecialPage {
         * The number of rows returned by the query. Reading this variable
         * only makes sense in functions that are run after the query has been
         * done, such as preprocessResults() and formatRow().
+        *
+        * @var int
         */
        protected $numRows;
 
+       /**
+        * @var string|null
+        */
        protected $cachedTimestamp = null;
 
        /**
-        * Whether to show prev/next links
+        * @var bool Whether to show prev/next links
         */
        protected $shownavigation = true;
 
@@ -62,7 +67,8 @@ abstract class QueryPage extends SpecialPage {
         *
         * DO NOT CHANGE THIS LIST without testing that
         * maintenance/updateSpecialPages.php still works.
-        * @return array
+        *
+        * @return string[][]
         */
        public static function getPages() {
                static $qp = null;
@@ -166,7 +172,7 @@ abstract class QueryPage extends SpecialPage {
         * Subclasses return an array of fields to order by here. Don't append
         * DESC to the field names, that'll be done automatically if
         * sortDescending() returns true.
-        * @return array
+        * @return string[]
         * @since 1.18
         */
        function getOrderFields() {
@@ -378,7 +384,7 @@ abstract class QueryPage extends SpecialPage {
 
        /**
         * Get a DB connection to be used for slow recache queries
-        * @return IDatabase
+        * @return \Wikimedia\Rdbms\Database
         */
        function getRecacheDB() {
                return wfGetDB( DB_REPLICA, [ $this->getName(), 'QueryPage::recache', 'vslow' ] );
@@ -500,6 +506,9 @@ abstract class QueryPage extends SpecialPage {
                return [ 'value' ];
        }
 
+       /**
+        * @return string
+        */
        public function getCachedTimestamp() {
                if ( is_null( $this->cachedTimestamp ) ) {
                        $dbr = wfGetDB( DB_REPLICA );
index fc17a3d..45457f5 100644 (file)
@@ -2425,7 +2425,6 @@ mkdir
 mms
 mobile
 mobileformat
-mobilelanding
 mobileview
 modified
 modifiedarticleprotection
index f515df7..61c63e9 100644 (file)
@@ -839,6 +839,7 @@ TEXT
                if ( $newAddress === false ) {
                        return false;
                }
+               $newAddress = trim( $newAddress );
                if ( strpos( $newAddress, ':' ) === false ) {
                        $newAddress = SqlBlobStore::makeAddressFromTextId( intval( $newAddress ) );
                }
diff --git a/maintenance/manageForeignResources.php b/maintenance/manageForeignResources.php
new file mode 100644 (file)
index 0000000..54554b8
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+
+require_once __DIR__ . '/Maintenance.php';
+
+/**
+ * Manage foreign resources registered with ResourceLoader.
+ *
+ * @ingroup Maintenance
+ * @since 1.32
+ */
+class ManageForeignResources extends Maintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->addDescription( <<<TEXT
+Manage foreign resources registered with ResourceLoader.
+
+This helps developers with downloading, verifying, and updating local copies of upstream
+libraries registered as ResourceLoader modules. See resources/lib/foreign-resources.yaml.
+
+Use the "update" action to download urls specified in foreign-resources.yaml, and unpack
+them to the resources directory. This will also verify them against the integrity hashes.
+
+Use the "verify" action to verify the files currently in the resources directory match
+what "update" would replace them with. This is effectively a dry-run and will not change
+any module resources on disk.
+
+Use the "make-sri" action to compute an integrity hash for upstreams that do not publish
+one themselves. Add or update the urls foreign-resources.yaml as needed, but omit (or
+leave empty) the "integrity" key. Then, run the "make-sri" action for the module and
+copy the integrity into the file. Then, you can use "verify" or "update" normally.
+TEXT
+               );
+               $this->addArg( 'action', 'One of "update", "verify" or "make-sri"', true );
+               $this->addArg( 'module', 'Name of a single module (Default: all)', false );
+               $this->addOption( 'verbose', 'Be verbose', false, false, 'v' );
+       }
+
+       /**
+        * @return bool
+        * @throws Exception
+        */
+       public function execute() {
+               global $IP;
+               $frm = new ForeignResourceManager(
+                        "{$IP}/resources/lib/foreign-resources.yaml",
+                        "{$IP}/resources/lib",
+                       function ( $text ) {
+                               $this->output( $text );
+                       },
+                       function ( $text ) {
+                               $this->error( $text );
+                       },
+                       function ( $text ) {
+                               if ( $this->hasOption( 'verbose' ) ) {
+                                       $this->output( $text );
+                               }
+                       }
+               );
+
+               $action = $this->getArg( 0 );
+               $module = $this->getArg( 1, 'all' );
+               return $frm->run( $action, $module );
+       }
+}
+
+$maintClass = ManageForeignResources::class;
+require_once RUN_MAINTENANCE_IF_MAIN;
diff --git a/maintenance/resources/foreign-resources.yaml b/maintenance/resources/foreign-resources.yaml
deleted file mode 100644 (file)
index d4458aa..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-### Format of this file
-#
-# The top-level keys are directory names (under resources/lib/).
-# They should match module names (as registered in Resources.php), but there are exceptions.
-# Each top-level key holds a resource descriptor that must have one of
-# the following `type` values:
-#
-# - `tar`: For tarball archive (may be gzip-compressed).
-# - `file: For a plain file.
-# - `multi-file`: For multiple plain files.
-#
-### Type tar
-#
-# The `src` and `integrity` keys are required.
-#
-# * `src`: Full URL to the remote resource.
-# * `integrity`: Cryptographic hash (integrity metadata format per <https://www.w3.org/TR/SRI/>).
-# * `dest`: An object mapping paths to files or directory from the remote resource to a destination
-#    in the module directory. The value of key in dest may be omitted, which will extract the key
-#    directly to the module directory.
-#
-### Type file
-#
-# The `src` and `integrity` keys are required.
-#
-# * `src`: Full URL to the remote resource.
-# * `integrity`: Cryptographic hash (integrity metadata format per <https://www.w3.org/TR/SRI/>).
-# * `dest`: The name of the file in the module directory. Default: Basename of URL.
-#
-### Type multi-file
-#
-# The `files` key is required.
-#
-# * `files`: An object mapping destination paths to an object containing `src` and `integrity`
-#    keys.
-
-CLDRPluralRuleParser:
-  type: file
-  src: https://raw.githubusercontent.com/santhoshtr/CLDRPluralRuleParser/0dda851/src/CLDRPluralRuleParser.js
-  integrity: sha384-M4taeYYG2+9Ob1/La16iO+zlRRmBV5lBR3xUKkQT6kfkJ0aLbCi6yc0RYI1BDzdh
-
-easy-deflate:
-  type: multi-file
-  files:
-    deflate.js:
-      src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/deflate.js
-      integrity: sha384-sHnZLDSWMUhA2w9ygkzCK8YFvoh/fQKY6lXMbvmrYzjuNURiLB0DZFCDNMpGyZ77
-    easydeflate.js:
-      src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/easydeflate.js
-      integrity: sha384-EwPfP2RMkDPa1HkzQsXgzTsy1KEjcIzQPA1HDS/JPHjvEMvVUsCxWwm1oXql/jk2
-    inflate.js:
-      src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/inflate.js
-      integrity: sha384-hMg44Hw424mUYvmzKl0JT4J8UU/1YYhTiGRtR0YX/MXNLK9qWTK0d62FBCDGxmxw
-    README.md:
-      src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/README.md
-      integrity: sha384-6kwcfCLivvqXBZy2ATyya+mTVWLk3eaQyBdC6tbpBtkygnBrM2SNkq3jz/l7IkvP
-
-html5shiv:
-  type: file
-  src: https://raw.githubusercontent.com/aFarkas/html5shiv/3.7.3/src/html5shiv.js
-  integrity: sha384-RPXhaTf22QktT8KTwZ6bUz/C+7CnccaIw5W/y/t0FW5WSDGj3wc3YtRIJC0w47in
-
-jquery:
-  type: file
-  src: https://code.jquery.com/jquery-3.3.1.js
-  # Integrity from link modals https://code.jquery.com/jquery/
-  integrity: sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=
-  dest: jquery.js
-
-jquery.client:
-  type: tar
-  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:
-    package/LICENSE-MIT:
-    package/README.md:
-
-jquery.cookie:
-  type: multi-file
-  files:
-    jquery.cookie.js:
-      src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/jquery.cookie.js
-      integrity: sha384-Xxq63E9KDgzUJ6WPNPqVeOtRIwZyx6y9DzEwY2u6LYKSnWrjSoGtWSKmTindYBf2
-    MIT-LICENSE.txt:
-      src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/MIT-LICENSE.txt
-      integrity: sha384-zYsGf3KJ7S0AhOICjcoh0kkn7aGZlzYUXXX5xz8dwR9KjLMM+/JPR2g/jVOGGeId
-    CHANGELOG.md:
-      src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/CHANGELOG.md
-      integrity: sha384-SQOHhLc7PHxHDQpGE/zv9XfXKL0A7OBu8kuyVDnHVp+zSoWyRw4xUJ+LSm5ql4kS
-
-jquery.form:
-  type: file
-  src: https://raw.githubusercontent.com/jquery-form/form/ff80d9ddf4/jquery.form.js
-  integrity: sha384-h4G2CrcSbixzMvrrK259cNBYaL/vS1D4+KdUN9NJDzQnTU1bQ6Avluget+Id13M7
-  dest: jquery.form.js
-
-jquery.fullscreen:
-  type: file
-  src: https://raw.githubusercontent.com/theopolisme/jquery-fullscreen/v2.1.0/jquery.fullscreen.js
-  integrity: sha384-G4KPs2d99tgcsyUnJ3eeZ1r2hEKDwZfc4+/xowL/LIemq2VVwEE8HpVAWt4WYNLR
-  dest: jquery.fullscreen.js
-
-jquery.hoverIntent:
-  type: file
-  src: https://raw.githubusercontent.com/briancherne/jquery-hoverIntent/823603fdac/jquery.hoverIntent.js
-  integrity: sha384-lca0haN0hqFGGh2aYUhtAgX9dhVHfQnTADH4svDeM6gcXnL7aFGeAi1NYwipDMyS
-  dest: jquery.hoverIntent.js
-
-jquery.jStorage:
-  type: file
-  src: https://raw.githubusercontent.com/andris9/jStorage/v0.4.12/jstorage.js
-  integrity: sha384-geMeN8k803kPp6cqRL4VNfuSM1L8DcbKRk0St/KHJzxgpX9S0y9FA6HxA/JgucrJ
-  dest: jstorage.js
-
-jquery.throttle-debounce:
-  type: file
-  src: https://raw.githubusercontent.com/cowboy/jquery-throttle-debounce/v1.1/jquery.ba-throttle-debounce.js
-  integrity: sha384-ULOy4DbAghrCqRcrTJLXOY9e4gDpWh0BeEf6xMSL0VtNudXWggcb6AmrVrl4KDAP
-  dest: jquery.ba-throttle-debounce.js
-
-moment:
-  type: tar
-  src: https://codeload.github.com/moment/moment/tar.gz/2.24.0
-  integrity: sha384-2/I9rfqkN8AAgh5wOXXphuo827uV7lMmOodrCfIvqC6W6JKKiDGOwd+lE3e8R0yz
-  dest:
-    moment-2.24.0/moment.js:
-    moment-2.24.0/CHANGELOG.md:
-    moment-2.24.0/README.md:
-    moment-2.24.0/LICENSE:
-    moment-2.24.0/locale/*.js: locale
-
-mustache:
-  type: multi-file
-  files:
-    mustache.js:
-      src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/mustache.js
-      integrity: sha384-k2UYqmzoiq/qgIzZvcYBxbXQW4YdPAsXDOTkHTGb9TCZ9sjCkyT4TlaUN0wQRkql
-    LICENSE:
-      src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/LICENSE
-      integrity: sha384-MYVwXwula9+YkyXexOJVZ0v0DaVvG22uX57mNq5Di+7u8OH9EG9q3yuXkp1Iehiq
-
-oojs:
-  type: tar
-  src: https://registry.npmjs.org/oojs/-/oojs-2.2.2.tgz
-  integrity: sha256-ebgQW2EGrSkBCnDJBGqDpsBDjA3PMN/M8U5DyLHt9mw=
-  dest:
-    package/dist/oojs.jquery.js:
-    package/AUTHORS.txt:
-    package/LICENSE-MIT:
-    package/README.md:
-
-oojs-router:
-  type: tar
-  src: https://registry.npmjs.org/oojs-router/-/oojs-router-0.2.0.tgz
-  integrity: sha384-VngYqdQ3vTDMXbm4e4FUZCCGos7fB0Jkr9V+kBL5MElprK1h0yQZOzBNnMHtSJS/
-  dest:
-    package/dist/oojs-router.js:
-    package/LICENSE:
-    package/AUTHORS.txt:
-    package/History.md:
-
-ooui:
-  type: tar
-  src: https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.31.1.tgz
-  integrity: sha384-M9KdU6u02zSKCVczcw6YJmSvFLhdeagNg9CPhizYVqrybL8bamrF5u6YfrFGEyiv
-  dest:
-    # Main stuff
-    package/dist/oojs-ui-core.js{,.map.json}:
-    package/dist/oojs-ui-core-{wikimediaui,apex}.css:
-    package/dist/oojs-ui-widgets.js{,.map.json}:
-    package/dist/oojs-ui-widgets-{wikimediaui,apex}.css:
-    package/dist/oojs-ui-toolbars.js{,.map.json}:
-    package/dist/oojs-ui-toolbars-{wikimediaui,apex}.css:
-    package/dist/oojs-ui-windows.js{,.map.json}:
-    package/dist/oojs-ui-windows-{wikimediaui,apex}.css:
-    package/dist/oojs-ui-{wikimediaui,apex}.js{,.map.json}:
-    package/dist/i18n:
-    package/dist/images:
-    # WikimediaUI theme
-    package/dist/themes/wikimediaui/images/icons/*.{svg,png}: themes/wikimediaui/images/icons
-    package/dist/themes/wikimediaui/images/indicators/*.{svg,png}: themes/wikimediaui/images/indicators
-    package/dist/themes/wikimediaui/images/textures/*.{gif,svg}: themes/wikimediaui/images/textures
-    package/src/themes/wikimediaui/*.json: themes/wikimediaui
-    package/dist/wikimedia-ui-base.less:
-    # Apex theme (icons, indicators, and textures)
-    package/src/themes/apex/*.json: themes/apex
-    # Misc stuff
-    package/dist/AUTHORS.txt:
-    package/dist/History.md:
-    package/dist/LICENSE-MIT:
-    package/dist/README.md:
-
-qunitjs:
-  type: multi-file
-  # Integrity from link modals at https://code.jquery.com/qunit/
-  files:
-    qunit.js:
-      src: http://code.jquery.com/qunit/qunit-2.9.1.js
-      integrity: sha256-eNccBdxd8zReziWcVjEsPeyJDi3LKMYnzMXyDv8bzsU=
-    qunit.css:
-      src: https://code.jquery.com/qunit/qunit-2.9.1.css
-      integrity: sha256-SSS7o92V7wzcIFg3qnJL9mc4msePaT4klbxtuSGvVVo=
-
-sinonjs:
-  type: file
-  src: https://sinonjs.org/releases/sinon-1.17.7.js
-  integrity: sha384-wR63Jwy75KqwBfzCmXd6gYws6uj3qV/XMAybzXrkEYGYG3AQ58ZWwr1fVpkHa5e8
-  dest: sinon.js
diff --git a/maintenance/resources/manageForeignResources.php b/maintenance/resources/manageForeignResources.php
deleted file mode 100644 (file)
index 6de82c0..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Maintenance
- */
-
-require_once __DIR__ . '/../Maintenance.php';
-
-/**
- * Manage foreign resources registered with ResourceLoader.
- *
- * @ingroup Maintenance
- * @since 1.32
- */
-class ManageForeignResources extends Maintenance {
-       public function __construct() {
-               parent::__construct();
-               $this->addDescription( <<<TEXT
-Manage foreign resources registered with ResourceLoader.
-
-This helps developers to download, verify and update local copies of upstream
-libraries registered as ResourceLoader modules. See also foreign-resources.yaml.
-
-For sources that don't publish an integrity hash, omit "integrity" (or leave empty)
-and run the "make-sri" action to compute the missing hashes.
-
-This script runs in dry-run mode by default. Use --update to actually change,
-remove, or add files to resources/lib/.
-TEXT
-               );
-               $this->addArg( 'action', 'One of "update", "verify" or "make-sri"', true );
-               $this->addArg( 'module', 'Name of a single module (Default: all)', false );
-               $this->addOption( 'verbose', 'Be verbose', false, false, 'v' );
-       }
-
-       /**
-        * @return bool
-        * @throws Exception
-        */
-       public function execute() {
-               global $IP;
-               $frm = new ForeignResourceManager(
-                        __DIR__ . '/foreign-resources.yaml',
-                        "{$IP}/resources/lib",
-                       function ( $text ) {
-                               $this->output( $text );
-                       },
-                       function ( $text ) {
-                               $this->error( $text );
-                       },
-                       function ( $text ) {
-                               if ( $this->hasOption( 'verbose' ) ) {
-                                       $this->output( $text );
-                               }
-                       }
-               );
-
-               $action = $this->getArg( 0 );
-               $module = $this->getArg( 1, 'all' );
-               return $frm->run( $action, $module );
-       }
-}
-
-$maintClass = ManageForeignResources::class;
-require_once RUN_MAINTENANCE_IF_MAIN;
index 5e5f308..b2d0ad2 100644 (file)
@@ -592,6 +592,7 @@ return [
                'group' => 'jquery.ui',
        ],
        'jquery.ui.spinner' => [
+               'deprecated' => 'Please use "jquery.spinner" instead.',
                'scripts' => 'resources/lib/jquery.ui/jquery.ui.spinner.js',
                'dependencies' => [
                        'jquery.ui.core',
diff --git a/resources/lib/foreign-resources.yaml b/resources/lib/foreign-resources.yaml
new file mode 100644 (file)
index 0000000..f862850
--- /dev/null
@@ -0,0 +1,259 @@
+# ## Format of this file
+#
+# The top-level keys in this file correspond with directories under resources/lib/.
+# These in turn are registered as module bundles in Resources.php.
+#
+# ## How to install an foreign resource
+#
+# 1. Add or update the url(s) for the upstream module to this YAML file.
+#
+#    Look at other modules for examples. To install a module from npm,
+#    we use the tarball distribution from npmjs.org. This is the same as what
+#    the npm CLI uses. For example, to install jquery-client@9.2.0, use:
+#    <https://registry.npmjs.org/jquery-client/-/jquery-client-9.2.0.tgz>.
+#
+# 2. If the upstream maintainers publish an integrity hash, set that as well.
+#    Otherwise, use manageForeignResources.php to compute the integrity hash.
+#
+#    Run `php manageForeignResources.php make-sri "my module name"`
+#
+#    This will download the specified file(s) and print their integrity hashes,
+#    already formatted in YAML, ready for copying to this file.
+#
+# 3. Last but not least, decide where files go.
+#
+#    If you specified a direct url to JavaScript or CSS file, this step is
+#    optional. See the corresponding documentation section below for more
+#    information and examples for "dest" keys. Once you've set any "dest" keys,
+#    run `php manageForeignResources.php update "my module name"`.
+#
+# ## Package formats
+#
+# Each top-level key must use one of these types:
+#
+# - `file`: For a plain file.
+# - `multi-file`: For multiple plain files.
+# - `tar`: For a tarball archive (may be compressed).
+#
+# ### The "file" type
+#
+# * `src`: Full URL to the remote resource.
+# * `integrity`: Cryptographic hash (integrity metadata format per <https://www.w3.org/TR/SRI/>).
+# * `dest`: [optional] The file name to use in the module directory. Default: Basename of URL.
+#
+# For example, the following would produce resources/lib/mymodule/x.js:
+#
+#     mymodule:
+#       type: file
+#       src: https://mymodule.example/1.2.3/x.js
+#       integrity: sha384-Je+NE+saisQuoi
+#
+# ### The "multi-file" type
+#
+# * `files`: An object mapping destination paths to `src` and `integrity` keys.
+#
+# For example:
+#
+#     mymodule:
+#       type: multi-file
+#       files:
+#         x.js:
+#           src: https://mymodule.example/1.2.3/x.js
+#           integrity: sha384-Je+NE+saisQuoi
+#         x.css:
+#           src: https://mymodule.example/1.2.3/x.css
+#           integrity: sha384-Je+NE+saisQuoi
+#
+# ### The "tar" type
+#
+# * `src`: Full URL to the remote resource.
+# * `integrity`: Cryptographic hash (integrity metadata format per <https://www.w3.org/TR/SRI/>).
+# * `dest`: [optional] The default is to extract all files from the package.
+#    To only extract some of the files or directories, use "dest" to specify
+#    files, directories, and/or glob patterns. You can use a site like https://unpkg.com/
+#    to easily inspect an npm package, like <https://unpkg.com/jquery-client@2.0.2/>.
+#
+# For example:
+#
+#     mymodule:
+#       type: tar
+#       src: https://registry.npmjs.org/jquery-client/-/jquery-client-9.2.0.tgz
+#       integrity: sha384-Je+NE+saisQuoi
+#       dest:
+#         package/dist/x.js:
+#         package/dist/i18n:
+#         package/dist/style/*.css:
+#
+# The would extract the "x.js" file, the "i18n" directory (recursive),
+# and any "*.css" files from the "style" directory.
+#
+
+CLDRPluralRuleParser:
+  type: file
+  src: https://raw.githubusercontent.com/santhoshtr/CLDRPluralRuleParser/0dda851/src/CLDRPluralRuleParser.js
+  integrity: sha384-M4taeYYG2+9Ob1/La16iO+zlRRmBV5lBR3xUKkQT6kfkJ0aLbCi6yc0RYI1BDzdh
+
+easy-deflate:
+  type: multi-file
+  files:
+    deflate.js:
+      src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/deflate.js
+      integrity: sha384-sHnZLDSWMUhA2w9ygkzCK8YFvoh/fQKY6lXMbvmrYzjuNURiLB0DZFCDNMpGyZ77
+    easydeflate.js:
+      src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/easydeflate.js
+      integrity: sha384-EwPfP2RMkDPa1HkzQsXgzTsy1KEjcIzQPA1HDS/JPHjvEMvVUsCxWwm1oXql/jk2
+    inflate.js:
+      src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/inflate.js
+      integrity: sha384-hMg44Hw424mUYvmzKl0JT4J8UU/1YYhTiGRtR0YX/MXNLK9qWTK0d62FBCDGxmxw
+    README.md:
+      src: https://raw.githubusercontent.com/edg2s/Easy-Deflate/7a6056e5302f6f385ff2efa60afda45b4ad81e51/README.md
+      integrity: sha384-6kwcfCLivvqXBZy2ATyya+mTVWLk3eaQyBdC6tbpBtkygnBrM2SNkq3jz/l7IkvP
+
+html5shiv:
+  type: file
+  src: https://raw.githubusercontent.com/aFarkas/html5shiv/3.7.3/src/html5shiv.js
+  integrity: sha384-RPXhaTf22QktT8KTwZ6bUz/C+7CnccaIw5W/y/t0FW5WSDGj3wc3YtRIJC0w47in
+
+jquery:
+  type: file
+  src: https://code.jquery.com/jquery-3.3.1.js
+  # Integrity from link modals https://code.jquery.com/jquery/
+  integrity: sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=
+  dest: jquery.js
+
+jquery.client:
+  type: tar
+  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:
+    package/LICENSE-MIT:
+    package/README.md:
+
+jquery.cookie:
+  type: multi-file
+  files:
+    jquery.cookie.js:
+      src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/jquery.cookie.js
+      integrity: sha384-Xxq63E9KDgzUJ6WPNPqVeOtRIwZyx6y9DzEwY2u6LYKSnWrjSoGtWSKmTindYBf2
+    MIT-LICENSE.txt:
+      src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/MIT-LICENSE.txt
+      integrity: sha384-zYsGf3KJ7S0AhOICjcoh0kkn7aGZlzYUXXX5xz8dwR9KjLMM+/JPR2g/jVOGGeId
+    CHANGELOG.md:
+      src: https://raw.githubusercontent.com/carhartl/jquery-cookie/v1.3.1/CHANGELOG.md
+      integrity: sha384-SQOHhLc7PHxHDQpGE/zv9XfXKL0A7OBu8kuyVDnHVp+zSoWyRw4xUJ+LSm5ql4kS
+
+jquery.form:
+  type: file
+  src: https://raw.githubusercontent.com/jquery-form/form/ff80d9ddf4/jquery.form.js
+  integrity: sha384-h4G2CrcSbixzMvrrK259cNBYaL/vS1D4+KdUN9NJDzQnTU1bQ6Avluget+Id13M7
+
+jquery.fullscreen:
+  type: file
+  src: https://raw.githubusercontent.com/theopolisme/jquery-fullscreen/v2.1.0/jquery.fullscreen.js
+  integrity: sha384-G4KPs2d99tgcsyUnJ3eeZ1r2hEKDwZfc4+/xowL/LIemq2VVwEE8HpVAWt4WYNLR
+
+jquery.hoverIntent:
+  type: file
+  src: https://raw.githubusercontent.com/briancherne/jquery-hoverIntent/823603fdac/jquery.hoverIntent.js
+  integrity: sha384-lca0haN0hqFGGh2aYUhtAgX9dhVHfQnTADH4svDeM6gcXnL7aFGeAi1NYwipDMyS
+
+jquery.jStorage:
+  type: file
+  src: https://raw.githubusercontent.com/andris9/jStorage/v0.4.12/jstorage.js
+  integrity: sha384-geMeN8k803kPp6cqRL4VNfuSM1L8DcbKRk0St/KHJzxgpX9S0y9FA6HxA/JgucrJ
+
+jquery.throttle-debounce:
+  type: file
+  src: https://raw.githubusercontent.com/cowboy/jquery-throttle-debounce/v1.1/jquery.ba-throttle-debounce.js
+  integrity: sha384-ULOy4DbAghrCqRcrTJLXOY9e4gDpWh0BeEf6xMSL0VtNudXWggcb6AmrVrl4KDAP
+
+moment:
+  type: tar
+  src: https://codeload.github.com/moment/moment/tar.gz/2.24.0
+  integrity: sha384-2/I9rfqkN8AAgh5wOXXphuo827uV7lMmOodrCfIvqC6W6JKKiDGOwd+lE3e8R0yz
+  dest:
+    moment-2.24.0/moment.js:
+    moment-2.24.0/CHANGELOG.md:
+    moment-2.24.0/README.md:
+    moment-2.24.0/LICENSE:
+    moment-2.24.0/locale/*.js: locale
+
+mustache:
+  type: multi-file
+  files:
+    mustache.js:
+      src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/mustache.js
+      integrity: sha384-k2UYqmzoiq/qgIzZvcYBxbXQW4YdPAsXDOTkHTGb9TCZ9sjCkyT4TlaUN0wQRkql
+    LICENSE:
+      src: https://raw.githubusercontent.com/janl/mustache.js/v1.0.0/LICENSE
+      integrity: sha384-MYVwXwula9+YkyXexOJVZ0v0DaVvG22uX57mNq5Di+7u8OH9EG9q3yuXkp1Iehiq
+
+oojs:
+  type: tar
+  src: https://registry.npmjs.org/oojs/-/oojs-2.2.2.tgz
+  integrity: sha256-ebgQW2EGrSkBCnDJBGqDpsBDjA3PMN/M8U5DyLHt9mw=
+  dest:
+    package/dist/oojs.jquery.js:
+    package/AUTHORS.txt:
+    package/LICENSE-MIT:
+    package/README.md:
+
+oojs-router:
+  type: tar
+  src: https://registry.npmjs.org/oojs-router/-/oojs-router-0.2.0.tgz
+  integrity: sha384-VngYqdQ3vTDMXbm4e4FUZCCGos7fB0Jkr9V+kBL5MElprK1h0yQZOzBNnMHtSJS/
+  dest:
+    package/dist/oojs-router.js:
+    package/LICENSE:
+    package/AUTHORS.txt:
+    package/History.md:
+
+ooui:
+  type: tar
+  src: https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.31.1.tgz
+  integrity: sha384-M9KdU6u02zSKCVczcw6YJmSvFLhdeagNg9CPhizYVqrybL8bamrF5u6YfrFGEyiv
+  dest:
+    # Main stuff
+    package/dist/oojs-ui-core.js{,.map.json}:
+    package/dist/oojs-ui-core-{wikimediaui,apex}.css:
+    package/dist/oojs-ui-widgets.js{,.map.json}:
+    package/dist/oojs-ui-widgets-{wikimediaui,apex}.css:
+    package/dist/oojs-ui-toolbars.js{,.map.json}:
+    package/dist/oojs-ui-toolbars-{wikimediaui,apex}.css:
+    package/dist/oojs-ui-windows.js{,.map.json}:
+    package/dist/oojs-ui-windows-{wikimediaui,apex}.css:
+    package/dist/oojs-ui-{wikimediaui,apex}.js{,.map.json}:
+    package/dist/i18n:
+    package/dist/images:
+    # WikimediaUI theme
+    package/dist/themes/wikimediaui/images/icons/*.{svg,png}: themes/wikimediaui/images/icons
+    package/dist/themes/wikimediaui/images/indicators/*.{svg,png}: themes/wikimediaui/images/indicators
+    package/dist/themes/wikimediaui/images/textures/*.{gif,svg}: themes/wikimediaui/images/textures
+    package/src/themes/wikimediaui/*.json: themes/wikimediaui
+    package/dist/wikimedia-ui-base.less:
+    # Apex theme (icons, indicators, and textures)
+    package/src/themes/apex/*.json: themes/apex
+    # Misc stuff
+    package/dist/AUTHORS.txt:
+    package/dist/History.md:
+    package/dist/LICENSE-MIT:
+    package/dist/README.md:
+
+qunitjs:
+  type: multi-file
+  # Integrity from link modals at https://code.jquery.com/qunit/
+  files:
+    qunit.js:
+      src: http://code.jquery.com/qunit/qunit-2.9.1.js
+      integrity: sha256-eNccBdxd8zReziWcVjEsPeyJDi3LKMYnzMXyDv8bzsU=
+    qunit.css:
+      src: https://code.jquery.com/qunit/qunit-2.9.1.css
+      integrity: sha256-SSS7o92V7wzcIFg3qnJL9mc4msePaT4klbxtuSGvVVo=
+
+sinonjs:
+  type: file
+  src: https://sinonjs.org/releases/sinon-1.17.7.js
+  integrity: sha384-wR63Jwy75KqwBfzCmXd6gYws6uj3qV/XMAybzXrkEYGYG3AQ58ZWwr1fVpkHa5e8
+  dest: sinon.js
index 92e1d04..c2d4835 100644 (file)
@@ -1,13 +1,3 @@
-.feedback-spinner {
-       display: inline-block;
-       zoom: 1;
-       *display: inline; /* IE7 and below */ /* stylelint-disable declaration-block-no-duplicate-properties */
-       /* @embed */
-       background: url( images/spinner.gif );
-       width: 18px;
-       height: 18px;
-}
-
 .mw-feedbackDialog-welcome-message,
 .mw-feedbackDialog-feedback-terms {
        line-height: 1.4;
index 5b73e7c..3ffc496 100644 (file)
                        padded: true
                } );
 
-               this.$spinner = $( '<div>' )
-                       .addClass( 'feedback-spinner' );
-
                // Feedback form
                this.feedbackMessageLabel = new OO.ui.LabelWidget( {
                        classes: [ 'mw-feedbackDialog-welcome-message' ]
diff --git a/resources/src/mediawiki.feedback/images/spinner.gif b/resources/src/mediawiki.feedback/images/spinner.gif
deleted file mode 100644 (file)
index aed0ea4..0000000
Binary files a/resources/src/mediawiki.feedback/images/spinner.gif and /dev/null differ
index 63da95a..453bf03 100644 (file)
         *
         * @param {OO.ui.TextInputWidget} textInputWidget Text input widget
         * @param {number} [limit] Byte limit, defaults to $input's maxlength
+        * @param {Function} [filterFunction] Function to call on the string before assessing the length.
         */
-       mw.widgets.visibleByteLimit = function ( textInputWidget, limit ) {
+       mw.widgets.visibleByteLimit = function ( textInputWidget, limit, filterFunction ) {
                limit = limit || +textInputWidget.$input.attr( 'maxlength' );
+               if ( !filterFunction || typeof filterFunction !== 'function' ) {
+                       filterFunction = undefined;
+               }
 
                function updateCount() {
-                       var remaining = limit - byteLength( textInputWidget.getValue() );
+                       var value = textInputWidget.getValue(),
+                               remaining;
+                       if ( filterFunction ) {
+                               value = filterFunction( value );
+                       }
+                       remaining = limit - byteLength( value );
                        if ( remaining > 99 ) {
                                remaining = '';
                        } else {
@@ -32,7 +41,7 @@
                updateCount();
 
                // Actually enforce limit
-               textInputWidget.$input.byteLimit( limit );
+               textInputWidget.$input.byteLimit( limit, filterFunction );
        };
 
        /**
         * Uses jQuery#codePointLimit to enforce the limit.
         *
         * @param {OO.ui.TextInputWidget} textInputWidget Text input widget
-        * @param {number} [limit] Byte limit, defaults to $input's maxlength
+        * @param {number} [limit] Code point limit, defaults to $input's maxlength
+        * @param {Function} [filterFunction] Function to call on the string before assessing the length.
         */
-       mw.widgets.visibleCodePointLimit = function ( textInputWidget, limit ) {
+       mw.widgets.visibleCodePointLimit = function ( textInputWidget, limit, filterFunction ) {
                limit = limit || +textInputWidget.$input.attr( 'maxlength' );
+               if ( !filterFunction || typeof filterFunction !== 'function' ) {
+                       filterFunction = undefined;
+               }
 
                function updateCount() {
-                       var remaining = limit - codePointLength( textInputWidget.getValue() );
+                       var value = textInputWidget.getValue(),
+                               remaining;
+                       if ( filterFunction ) {
+                               value = filterFunction( value );
+                       }
+                       remaining = limit - codePointLength( value );
                        if ( remaining > 99 ) {
                                remaining = '';
                        } else {
@@ -60,7 +78,7 @@
                updateCount();
 
                // Actually enforce limit
-               textInputWidget.$input.codePointLimit( limit );
+               textInputWidget.$input.codePointLimit( limit, filterFunction );
        };
 
 }() );
index d8330ef..b03a309 100644 (file)
@@ -223,8 +223,6 @@ class MessageCacheTest extends MediaWikiLangTestCase {
 
        /**
         * Regression test for T218918
-        * @group Broken
-        * @fixme Disabled per https://phabricator.wikimedia.org/T219042
         */
        public function testLoadFromDB_fetchLatestRevision() {
                // Create three revisions of the same message page.
@@ -239,7 +237,7 @@ class MessageCacheTest extends MediaWikiLangTestCase {
                $importRevision = new WikiRevision( new HashConfig() );
                $importRevision->setTitle( $r3->getTitle() );
                $importRevision->setComment( 'Imported edit' );
-               $importRevision->setTimestamp( '19991122334455' );
+               $importRevision->setTimestamp( '19991122001122' );
                $importRevision->setText( 'IMPORTED OLD TEST' );
                $importRevision->setUsername( 'Alan Smithee' );
 
index d35843b..80e12cd 100644 (file)
@@ -91,7 +91,7 @@ describe( 'Page', function () {
 
                // check
                HistoryPage.open( name );
-               assert.strictEqual( HistoryPage.comment.getText(), `(Created or updated page with "${content}")` );
+               assert.strictEqual( HistoryPage.comment.getText(), `Created or updated page with "${content}"` );
        } );
 
        it( 'should be deletable', function () {