Merge "Don't double wrap rollback links"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 23 Jul 2019 21:13:46 +0000 (21:13 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 23 Jul 2019 21:13:46 +0000 (21:13 +0000)
73 files changed:
RELEASE-NOTES-1.34
composer.json
includes/DefaultSettings.php
includes/auth/LocalPasswordPrimaryAuthenticationProvider.php
includes/export/XmlDumpWriter.php
includes/filebackend/lockmanager/LockManagerGroup.php
includes/filerepo/ForeignAPIRepo.php
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/CachedBagOStuff.php
includes/libs/objectcache/IStoreKeyEncoder.php
includes/libs/objectcache/MediumSpecificBagOStuff.php
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/objectcache/ReplicatedBagOStuff.php
includes/libs/objectcache/WANObjectCache.php
includes/shell/Command.php
languages/classes/LanguageTr.php
languages/i18n/bg.json
languages/i18n/fa.json
languages/i18n/he.json
languages/i18n/tyv.json
languages/messages/MessagesHe.php
maintenance/cleanupSpam.php
maintenance/clearInterwikiCache.php
maintenance/copyJobQueue.php
maintenance/generateSitemap.php
maintenance/includes/TextPassDumper.php
maintenance/storage/recompressTracked.php
maintenance/userOptions.php
resources/lib/foreign-resources.yaml
resources/lib/ooui/History.md
resources/lib/ooui/oojs-ui-apex.js
resources/lib/ooui/oojs-ui-core-apex.css
resources/lib/ooui/oojs-ui-core-wikimediaui.css
resources/lib/ooui/oojs-ui-core.js
resources/lib/ooui/oojs-ui-toolbars-apex.css
resources/lib/ooui/oojs-ui-toolbars-wikimediaui.css
resources/lib/ooui/oojs-ui-toolbars.js
resources/lib/ooui/oojs-ui-widgets-apex.css
resources/lib/ooui/oojs-ui-widgets-wikimediaui.css
resources/lib/ooui/oojs-ui-widgets.js
resources/lib/ooui/oojs-ui-wikimediaui.js
resources/lib/ooui/oojs-ui-windows-apex.css
resources/lib/ooui/oojs-ui-windows-wikimediaui.css
resources/lib/ooui/oojs-ui-windows.js
resources/lib/ooui/themes/apex/icons-alerts.json
resources/lib/ooui/themes/apex/icons-user.json
resources/lib/ooui/themes/wikimediaui/icons-alerts.json
resources/lib/ooui/themes/wikimediaui/icons-user.json
resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-invert.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-invert.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-progressive.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-progressive.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/search-invert.png
resources/lib/ooui/themes/wikimediaui/images/icons/search-invert.svg
resources/lib/ooui/themes/wikimediaui/images/icons/search-progressive.png
resources/lib/ooui/themes/wikimediaui/images/icons/search-progressive.svg
resources/lib/ooui/themes/wikimediaui/images/icons/search.png
resources/lib/ooui/themes/wikimediaui/images/icons/search.svg
resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-invert.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-invert.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-progressive.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-progressive.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline.svg [new file with mode: 0644]
tests/phpunit/includes/auth/LocalPasswordPrimaryAuthenticationProviderTest.php
tests/phpunit/includes/libs/objectcache/CachedBagOStuffTest.php
tests/phpunit/includes/libs/objectcache/MultiWriteBagOStuffTest.php
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/includes/watcheditem/WatchedItemStoreUnitTest.php
tests/phpunit/maintenance/backup_PageTest.php
tests/phpunit/unit/includes/objectcache/RedisBagOStuffTest.php

index 32a858b..7a24818 100644 (file)
@@ -70,6 +70,8 @@ For notes on 1.33.x and older releases, see HISTORY.
 * $wgDebugPrintHttpHeaders - The default of including HTTP headers in the
   debug log channel is no longer configurable. The debug log itself remains
   configurable via $wgDebugLogFile.
+* $wgPasswordSalt – This setting, used for migrating exceptionally old, insecure
+  password setups and deprecated since 1.24, is now removed.
 
 === New user-facing features in 1.34 ===
 * Special:Mute has been added as a quick way for users to block unwanted emails
@@ -82,6 +84,8 @@ For notes on 1.33.x and older releases, see HISTORY.
   strings like "5 days ago" instead of "5 days 13 hours ago".
 * (T220163) Added SpecialMuteModifyFormFields hook to allow extensions
   to add fields to Special:Mute.
+* (T100896) Skin authors can define custom OOUI themes using OOUIThemePaths.
+  See <https://www.mediawiki.org/wiki/OOUI/Themes> for details.
 
 === External library changes in 1.34 ===
 
@@ -90,7 +94,7 @@ For notes on 1.33.x and older releases, see HISTORY.
 
 ==== Changed external libraries ====
 * Updated Mustache from 1.0.0 to v3.0.1.
-* Updated OOUI from v0.31.3 to v0.33.2.
+* Updated OOUI from v0.31.3 to v0.33.4.
 * Updated composer/semver from 1.4.2 to 1.5.0.
 * Updated composer/spdx-licenses from 1.4.0 to 1.5.1 (dev-only).
 * Updated mediawiki/codesniffer from 25.0.0 to 26.0.0 (dev-only).
index dc6d091..98e7ebf 100644 (file)
@@ -27,7 +27,7 @@
                "ext-xml": "*",
                "guzzlehttp/guzzle": "6.3.3",
                "liuggio/statsd-php-client": "1.0.18",
-               "oojs/oojs-ui": "0.33.3",
+               "oojs/oojs-ui": "0.33.4",
                "pear/mail": "1.4.1",
                "pear/mail_mime": "1.10.2",
                "pear/net_smtp": "1.8.1",
index 107c546..6a1f7b5 100644 (file)
@@ -4710,12 +4710,6 @@ $wgRemoveCredentialsBlacklist = [
        \MediaWiki\Auth\PasswordAuthenticationRequest::class,
 ];
 
-/**
- * For compatibility with old installations set to false
- * @deprecated since 1.24 will be removed in future
- */
-$wgPasswordSalt = true;
-
 /**
  * Specifies the minimal length of a user password. If set to 0, empty pass-
  * words are allowed.
index 7d02a82..aebfb22 100644 (file)
@@ -113,11 +113,7 @@ class LocalPasswordPrimaryAuthenticationProvider
                // Check for *really* old password hashes that don't even have a type
                // The old hash format was just an md5 hex hash, with no type information
                if ( preg_match( '/^[0-9a-f]{32}$/', $row->user_password ) ) {
-                       if ( $this->config->get( 'PasswordSalt' ) ) {
-                               $row->user_password = ":B:{$row->user_id}:{$row->user_password}";
-                       } else {
-                               $row->user_password = ":A:{$row->user_password}";
-                       }
+                       $row->user_password = ":B:{$row->user_id}:{$row->user_password}";
                }
 
                $status = $this->checkPasswordValidity( $username, $req->password );
index 8535564..f34b3bd 100644 (file)
@@ -291,6 +291,34 @@ class XmlDumpWriter {
                return MediaWikiServices::getInstance()->getBlobStore();
        }
 
+       /**
+        * Invokes the given method on the given object, catching and logging any storage related
+        * exceptions.
+        *
+        * @param object $obj
+        * @param string $method
+        * @param array $args
+        * @param string $warning The warning to output in case of a storage related exception.
+        *
+        * @return mixed Returns the method's return value,
+        *         or null in case of a storage related exception.
+        * @throws Exception
+        */
+       private function invokeLenient( $obj, $method, $args = [], $warning ) {
+               try {
+                       return call_user_func_array( [ $obj, $method ], $args );
+               } catch ( SuppressedDataException $ex ) {
+                       return null;
+               } catch ( Exception $ex ) {
+                       if ( $ex instanceof MWException || $ex instanceof RuntimeException ) {
+                               MWDebug::warning( $warning . ': ' . $ex->getMessage() );
+                               return null;
+                       } else {
+                               throw $ex;
+                       }
+               }
+       }
+
        /**
         * Dumps a "<revision>" section on the output stream, with
         * data filled in from the given database row.
@@ -354,14 +382,28 @@ class XmlDumpWriter {
                if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                        $out .= "      <sha1/>\n";
                } else {
-                       $out .= "      " . Xml::element( 'sha1', null, strval( $rev->getSha1() ) ) . "\n";
+                       $sha1 = $this->invokeLenient(
+                               $rev,
+                               'getSha1',
+                               [],
+                               'failed to determine sha1 for revision ' . $rev->getId()
+                       );
+                       $out .= "      " . Xml::element( 'sha1', null, strval( $sha1 ) ) . "\n";
                }
 
                // Avoid PHP 7.1 warning from passing $this by reference
                $writer = $this;
                $text = '';
                if ( $contentMode === self::WRITE_CONTENT ) {
-                       $text = $rev->getContent( SlotRecord::MAIN, RevisionRecord::RAW );
+                       /** @var Content $content */
+                       $content = $this->invokeLenient(
+                               $rev,
+                               'getContent',
+                               [ SlotRecord::MAIN, RevisionRecord::RAW ],
+                               'Failed to load main slot content of revision ' . $rev->getId()
+                       );
+
+                       $text = $content ? $content->serialize() : '';
                }
                Hooks::run( 'XmlDumpWriterWriteRevision', [ &$writer, &$out, $row, $text, $rev ] );
 
@@ -410,37 +452,38 @@ class XmlDumpWriter {
 
                $textAttributes = [
                        'xml:space' => 'preserve',
-                       'bytes' => $slot->getSize(),
+                       'bytes' => $this->invokeLenient(
+                               $slot,
+                               'getSize',
+                               [],
+                               'failed to determine size for slot ' . $slot->getRole() . ' of revision '
+                               . $slot->getRevision()
+                       ) ?: '0'
                ];
 
                if ( $isV11 ) {
-                       $textAttributes['sha1'] = $slot->getSha1();
+                       $textAttributes['sha1'] = $this->invokeLenient(
+                               $slot,
+                               'getSha1',
+                               [],
+                               'failed to determine sha1 for slot ' . $slot->getRole() . ' of revision '
+                               . $slot->getRevision()
+                       ) ?: '';
                }
 
                if ( $contentMode === self::WRITE_CONTENT ) {
-                       try {
-                               // write <text> tag
-                               $out .= $this->writeText( $slot->getContent(), $textAttributes, $indent );
-                       } catch ( SuppressedDataException $ex ) {
-                               // NOTE: this shouldn't happen, since the caller is supposed to have checked
-                               // for suppressed content!
-                               // write <text> placeholder tag
-                               $textAttributes['deleted'] = 'deleted';
+                       $content = $this->invokeLenient(
+                               $slot,
+                               'getContent',
+                               [],
+                               'failed to load content for slot ' . $slot->getRole() . ' of revision '
+                               . $slot->getRevision()
+                       );
+
+                       if ( $content === null ) {
                                $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
-                       }
-                       catch ( Exception $ex ) {
-                               if ( $ex instanceof MWException || $ex instanceof RuntimeException ) {
-                                       // there's no provision in the schema for an attribute that will let
-                                       // the user know this element was unavailable due to error; an empty
-                                       // tag is the best we can do
-                                       $out .= $indent . Xml::element( 'text' ) . "\n";
-                                       wfLogWarning(
-                                               'failed to load content slot ' . $slot->getRole() . ' for revision '
-                                               . $slot->getRevision() . "\n"
-                                       );
-                               } else {
-                                       throw $ex;
-                               }
+                       } else {
+                               $out .= $this->writeText( $content, $textAttributes, $indent );
                        }
                } elseif ( $contentMode === self::WRITE_STUB_DELETED ) {
                        // write <text> placeholder tag
@@ -455,14 +498,21 @@ class XmlDumpWriter {
                        // Output the numerical text ID if possible, for backwards compatibility.
                        // Note that this is currently the ONLY reason we have a BlobStore here at all.
                        // When removing this line, check whether the BlobStore has become unused.
-                       $textId = $this->getBlobStore()->getTextIdFromAddress( $slot->getAddress() );
+                       try {
+                               // NOTE: this will only work for addresses of the form "tt:12345".
+                               // If we want to support other kinds of addresses in the future,
+                               // we will have to silently ignore failures here.
+                               // For now, this fails for "tt:0", which is present in the WMF production
+                               // database of of Juli 2019, due to data corruption.
+                               $textId = $this->getBlobStore()->getTextIdFromAddress( $slot->getAddress() );
+                       } catch ( InvalidArgumentException $ex ) {
+                               MWDebug::warning( 'Bad content address for slot ' . $slot->getRole()
+                                       . ' of revision ' . $slot->getRevision() . ': ' . $ex->getMessage() );
+                               $textId = 0;
+                       }
+
                        if ( $textId ) {
                                $textAttributes['id'] = $textId;
-                       } elseif ( !$isV11 ) {
-                               throw new InvalidArgumentException(
-                                       'Cannot produce stubs for non-text-table content blobs with schema version '
-                                       . $this->schemaVersion
-                               );
                        }
 
                        $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
index 43f6010..957af3e 100644 (file)
@@ -121,10 +121,13 @@ class LockManagerGroup {
                        $config = $this->managers[$name]['config'];
                        if ( $class === DBLockManager::class ) {
                                $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
-                               $lb = $lbFactory->newMainLB( $config['domain'] );
-                               $dbw = $lb->getLazyConnectionRef( DB_MASTER, [], $config['domain'] );
-
-                               $config['dbServers']['localDBMaster'] = $dbw;
+                               $lb = $lbFactory->getMainLB( $config['domain'] );
+                               $config['dbServers']['localDBMaster'] = $lb->getLazyConnectionRef(
+                                       DB_MASTER,
+                                       [],
+                                       $config['domain'],
+                                       $lb::CONN_TRX_AUTOCOMMIT
+                               );
                                $config['srvCache'] = ObjectCache::getLocalServerInstance( 'hash' );
                        }
                        $config['logger'] = LoggerFactory::getInstance( 'LockManager' );
index 8ff8143..314c4c3 100644 (file)
@@ -580,7 +580,7 @@ class ForeignAPIRepo extends FileRepo {
 
                                return $html;
                        },
-                       [ 'pcTTL' => WANObjectCache::TTL_PROC_LONG ]
+                       [ 'pcGroup' => 'http-get:3', 'pcTTL' => WANObjectCache::TTL_PROC_LONG ]
                );
        }
 
index 906e955..8c99532 100644 (file)
@@ -440,20 +440,20 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         *
         * @since 1.27
         * @param string $class Key class
-        * @param string|null $component [optional] Key component (starting with a key collection name)
-        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @param string ...$components Key components (starting with a key collection name)
+        * @return string Colon-delimited list of $keyspace followed by escaped components
         */
-       abstract public function makeGlobalKey( $class, $component = null );
+       abstract public function makeGlobalKey( $class, ...$components );
 
        /**
         * Make a cache key, scoped to this instance's keyspace.
         *
         * @since 1.27
         * @param string $class Key class
-        * @param string|null $component [optional] Key component (starting with a key collection name)
-        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @param string ...$components Key components (starting with a key collection name)
+        * @return string Colon-delimited list of $keyspace followed by escaped components
         */
-       abstract public function makeKey( $class, $component = null );
+       abstract public function makeKey( $class, ...$components );
 
        /**
         * @param int $flag ATTR_* class constant
index c97f56e..0ab26c9 100644 (file)
@@ -145,15 +145,15 @@ class CachedBagOStuff extends BagOStuff {
        }
 
        public function makeKeyInternal( $keyspace, $args ) {
-               return $this->backend->makeKeyInternal( ...func_get_args() );
+               return $this->backend->makeKeyInternal( $keyspace, $args );
        }
 
-       public function makeKey( $class, $component = null ) {
-               return $this->backend->makeKey( ...func_get_args() );
+       public function makeKey( $class, ...$components ) {
+               return $this->backend->makeKey( $class, ...$components );
        }
 
-       public function makeGlobalKey( $class, $component = null ) {
-               return $this->backend->makeGlobalKey( ...func_get_args() );
+       public function makeGlobalKey( $class, ...$components ) {
+               return $this->backend->makeGlobalKey( $class, ...$components );
        }
 
        public function getLastError() {
index da0686e..59e1c05 100644 (file)
@@ -11,17 +11,17 @@ interface IStoreKeyEncoder {
         * Make a global cache key.
         *
         * @param string $class Key class
-        * @param string|null $component [optional] Key component (starting with a key collection name)
-        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @param string ...$components Key components (starting with a key collection name)
+        * @return string Colon-delimited list of $keyspace followed by escaped components
         */
-       public function makeGlobalKey( $class, $component = null );
+       public function makeGlobalKey( $class, ...$components );
 
        /**
         * Make a cache key, scoped to this instance's keyspace.
         *
         * @param string $class Key class
-        * @param string|null $component [optional] Key component (starting with a key collection name)
-        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @param string ...$components Key components (starting with a key collection name)
+        * @return string Colon-delimited list of $keyspace followed by escaped components
         */
-       public function makeKey( $class, $component = null );
+       public function makeKey( $class, ...$components );
 }
index fb088c8..e742432 100644 (file)
@@ -858,11 +858,11 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
         * Make a global cache key.
         *
         * @param string $class Key class
-        * @param string|null $component [optional] Key component (starting with a key collection name)
-        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @param string ...$components Key components (starting with a key collection name)
+        * @return string Colon-delimited list of $keyspace followed by escaped components
         * @since 1.27
         */
-       public function makeGlobalKey( $class, $component = null ) {
+       public function makeGlobalKey( $class, ...$components ) {
                return $this->makeKeyInternal( 'global', func_get_args() );
        }
 
@@ -870,11 +870,11 @@ abstract class MediumSpecificBagOStuff extends BagOStuff {
         * Make a cache key, scoped to this instance's keyspace.
         *
         * @param string $class Key class
-        * @param string|null $component [optional] Key component (starting with a key collection name)
-        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @param string ...$components Key components (starting with a key collection name)
+        * @return string Colon-delimited list of $keyspace followed by escaped components
         * @since 1.27
         */
-       public function makeKey( $class, $component = null ) {
+       public function makeKey( $class, ...$components ) {
                return $this->makeKeyInternal( $this->keyspace, func_get_args() );
        }
 
index 6e9f387..d150880 100644 (file)
@@ -350,14 +350,14 @@ class MultiWriteBagOStuff extends BagOStuff {
        }
 
        public function makeKeyInternal( $keyspace, $args ) {
-               return $this->caches[0]->makeKeyInternal( ...func_get_args() );
+               return $this->caches[0]->makeKeyInternal( $keyspace, $args );
        }
 
-       public function makeKey( $class, $component = null ) {
+       public function makeKey( $class, ...$components ) {
                return $this->caches[0]->makeKey( ...func_get_args() );
        }
 
-       public function makeGlobalKey( $class, $component = null ) {
+       public function makeGlobalKey( $class, ...$components ) {
                return $this->caches[0]->makeGlobalKey( ...func_get_args() );
        }
 
index e49fa10..504d515 100644 (file)
@@ -162,11 +162,11 @@ class ReplicatedBagOStuff extends BagOStuff {
                return $this->writeStore->makeKeyInternal( ...func_get_args() );
        }
 
-       public function makeKey( $class, $component = null ) {
+       public function makeKey( $class, ...$components ) {
                return $this->writeStore->makeKey( ...func_get_args() );
        }
 
-       public function makeGlobalKey( $class, $component = null ) {
+       public function makeGlobalKey( $class, ...$components ) {
                return $this->writeStore->makeGlobalKey( ...func_get_args() );
        }
 
index 2a8da89..69edb11 100644 (file)
@@ -1885,22 +1885,22 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
        /**
         * @see BagOStuff::makeKey()
         * @param string $class Key class
-        * @param string|null $component [optional] Key component (starting with a key collection name)
-        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @param string ...$components Key components (starting with a key collection name)
+        * @return string Colon-delimited list of $keyspace followed by escaped components
         * @since 1.27
         */
-       public function makeKey( $class, $component = null ) {
+       public function makeKey( $class, ...$components ) {
                return $this->cache->makeKey( ...func_get_args() );
        }
 
        /**
         * @see BagOStuff::makeGlobalKey()
         * @param string $class Key class
-        * @param string|null $component [optional] Key component (starting with a key collection name)
-        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @param string ...$components Key components (starting with a key collection name)
+        * @return string Colon-delimited list of $keyspace followed by escaped components
         * @since 1.27
         */
-       public function makeGlobalKey( $class, $component = null ) {
+       public function makeGlobalKey( $class, ...$components ) {
                return $this->cache->makeGlobalKey( ...func_get_args() );
        }
 
index 7742075..4ba7868 100644 (file)
@@ -446,8 +446,9 @@ class Command {
                        // stream_select parameter names are from the POV of us being able to do the operation;
                        // proc_open desriptor types are from the POV of the process doing it.
                        // So $writePipes is passed as the $read parameter and $readPipes as $write.
-                       // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
-                       $numReadyPipes = @stream_select( $writePipes, $readPipes, $emptyArray, $timeout );
+                       AtEase::suppressWarnings();
+                       $numReadyPipes = stream_select( $writePipes, $readPipes, $emptyArray, $timeout );
+                       AtEase::restoreWarnings();
                        if ( $numReadyPipes === false ) {
                                $error = error_get_last();
                                if ( strncmp( $error['message'], $eintrMessage, strlen( $eintrMessage ) ) == 0 ) {
index 49ee88a..03790fa 100644 (file)
 /**
  * Turkish (Türkçe)
  *
- * Turkish has two different i, one with a dot and another without a dot. They
- * are totally different letters in this language, so we have to override the
+ * The Turkish language, like other Turkic languages, distinguishes
+ * a dotted letter 'i' from a dotless letter 'ı' (U+0131 LATIN SMALL LETTER DOTLESS I).
+ * In these languages, each has an equivalent uppercase mapping:
+ * ı (U+0131 LATIN SMALL LETTER DOTLESS I) -> I (U+0049 LATIN CAPITAL LETTER I),
+ * i (U+0069 LATIN SMALL LETTER I) -> İ (U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE).
+ *
+ * Unicode CaseFolding.txt defines this case as type 'T', a special case for Turkic languages:
+ * tr and az. PHP 7.3 parser ignores this special cases. so we have to override the
  * ucfirst and lcfirst methods.
+ *
  * See https://en.wikipedia.org/wiki/Dotted_and_dotless_I and T30040
  * @ingroup Language
  */
 class LanguageTr extends Language {
 
+       private $uc = [ 'I', 'İ' ];
+       private $lc = [ 'ı', 'i' ];
+
        /**
         * @param string $string
         * @return string
         */
        public function ucfirst( $string ) {
-               if ( strlen( $string ) && $string[0] == 'i' ) {
-                       return 'İ' . substr( $string, 1 );
+               $first = mb_substr( $string, 0, 1 );
+               if ( in_array( $first, $this->lc ) ) {
+                       $first = str_replace( $this->lc, $this->uc, $first );
+                       return $first . mb_substr( $string, 1 );
                }
                return parent::ucfirst( $string );
        }
@@ -48,8 +60,10 @@ class LanguageTr extends Language {
         * @return mixed|string
         */
        function lcfirst( $string ) {
-               if ( strlen( $string ) && $string[0] == 'I' ) {
-                       return 'ı' . substr( $string, 1 );
+               $first = mb_substr( $string, 0, 1 );
+               if ( in_array( $first, $this->uc ) ) {
+                       $first = str_replace( $this->uc, $this->lc, $first );
+                       return $first . mb_substr( $string, 1 );
                }
                return parent::lcfirst( $string );
        }
index e65bbd8..4a525c7 100644 (file)
        "backend-fail-maxsize": "Файлът „$1“ не може да бъде съхранен, тъй като размерът му надвишава {{PLURAL:$2|един байт|$2 байт}}.",
        "backend-fail-connect": "Не е възможно свързването към бекенда за съхранение „$1“.",
        "backend-fail-internal": "Възникна неизвестна грешка в бекенда за съхранение „$1“.",
+       "backend-fail-contenttype": "Не може да бъде определен типът на съдържанието на този файл, за да бъде съхранен в „$1“.",
        "zip-file-open-error": "Възникна грешка при отваряне на файла за проверка на ZIP.",
        "zip-wrong-format": "Указаният файл не е ZIP файл.",
        "zip-bad": "Файлът е повреден или е нечетим ZIP файл.\nСигурността му не може да бъде проверена.",
        "uploadstash-errclear": "Изчистването на файловете беше неуспешно.",
        "uploadstash-refresh": "Обновяване на списъка с файлове",
        "uploadstash-thumbnail": "преглед на миниатюра",
+       "uploadstash-bad-path": "Пътят не съществува.",
+       "uploadstash-bad-path-invalid": "Пътят не е валиден.",
        "uploadstash-bad-path-unknown-type": "Неизвестен тип „$1“.",
+       "uploadstash-bad-path-unrecognized-thumb-name": "Неразпознато име на миниатюрата.",
+       "uploadstash-bad-path-bad-format": "Ключът „$1“ не е в подходящ формат.",
+       "uploadstash-file-not-found-missing-content-type": "Липсващо заглавие за типа на съдържание.",
+       "uploadstash-file-not-found-not-exists": "Не може да бъде намерен пътят или файлът не е обикновен.",
        "uploadstash-wrong-owner": "Файлът ($1) не принадлежи на текущия потребител.",
        "uploadstash-no-such-key": "Няма такъв ключ ($1), не може да бъде премахнат.",
        "uploadstash-no-extension": "Разширението е нулево.",
        "blocklist-userblocks": "Скриване блокирането на потребителски сметки",
        "blocklist-tempblocks": "Скриване на временни блокирания",
        "blocklist-addressblocks": "Скриване на отделни блокирания на IP адреси",
+       "blocklist-type-opt-all": "Всички",
        "blocklist-type-opt-sitewide": "За всички уикита",
        "blocklist-type-opt-partial": "Частично",
        "blocklist-rangeblocks": "Скриване на блокиранията по IP диапазон",
        "blocklink": "блокиране",
        "unblocklink": "отблокиране",
        "change-blocklink": "промяна на параметрите на блокирането",
+       "empty-username": "(недостъпно потребителско име)",
        "contribslink": "приноси",
        "emaillink": "изпращане на е-писмо",
        "autoblocker": "Бяхте блокиран автоматично, тъй като неотдавна IP-адресът Ви е бил ползван от блокирания в момента потребител „[[User:$1|$1]]“.\nПричината за блокирането на „$1“ е: „$2“.",
        "limitreport-ppgeneratednodes-value": "$1/$2",
        "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|байт|байта}}",
        "limitreport-templateargumentsize-value": "$1/$2 {{PLURAL:$2|байт|байта}}",
+       "limitreport-expansiondepth": "Най-голяма дълбочина на разгръщане",
        "limitreport-expansiondepth-value": "$1/$2",
        "limitreport-expensivefunctioncount-value": "$1/$2",
        "expandtemplates": "Разгръщане на шаблони",
        "authmanager-provider-password": "Удостоверяване с парола",
        "authmanager-provider-temporarypassword": "Временна парола",
        "authprovider-confirmlink-option": "$1 ($2)",
+       "authprovider-confirmlink-success-line": "$1: Успешно свързано.",
        "authprovider-confirmlink-failed-line": "$1: $2",
+       "authprovider-confirmlink-failed": "Свързването на сметката не е напълно успешно: $1",
+       "authprovider-confirmlink-ok-help": "Продължаване след показване на съобщения за неуспешно свързване.",
        "authprovider-resetpass-skip-label": "Пропускане",
        "authform-newtoken": "Липсва маркер. $1",
        "authform-notoken": "Липсва маркер",
index ed2f698..8d4fd7d 100644 (file)
        "revdelete-unsuppress": "حذف محدودیت‌ها در بازبینی‌های ترمیم‌شده",
        "revdelete-log": "دلیل:",
        "revdelete-submit": "اعمال بر {{PLURAL:$1|نسخهٔ|نسخه‌های}} انتخاب شده",
-       "revdelete-success": "پیدایی نسخه، روزآمد شد.",
+       "revdelete-success": "پیدایی نسخه روزآمد شد.",
        "revdelete-failure": "'''پیدایی نسخه‌ها قابل به روز کردن نیست:'''\n$1",
        "logdelete-success": "تغییر پیدایی مورد انجام شد.",
        "logdelete-failure": "'''پیدایی سیاهه‌ها قابل تنظیم نیست:'''\n$1",
index 08db1b6..79f0e30 100644 (file)
        "systemblockedtext": "שם המשתמש או כתובת ה־IP שלך נחסמו באופן אוטומטי על־ידי תוכנת מדיה־ויקי.\nהסיבה שניתנה לחסימה היא:\n\n:<em>$2</em>\n\n* תחילת החסימה: $8\n* פקיעת החסימה: $6\n* החסימה שבוצעה: $7\n\nכתובת ה־IP הנוכחית שלך היא $3.\nיש לציין את כל הפרטים הללו בכל פנייה לבירור החסימה.",
        "blockednoreason": "לא ניתנה סיבה",
        "blockedtext-composite": "<strong>שם המשתמש או כתובת ה־IP שלך נחסמו.</strong>\n\nהסיבה שניתנה לכך היא:\n\n:<em>$2</em>.\n\n* תחילת החסימה: $8\n* פקיעת החסימה הארוכה ביותר: $6\n\n* $5\n\nכתובת ה־IP הנוכחית שלך היא $3.\nיש לציין את כל הפרטים הללו בכל פנייה לבירור החסימה.",
+       "blockedtext-composite-ids": "מזהי החסימות הרלוונטיים: $1 (גם כתובת ה־IP שלך יכולה להיות ברשימה השחורה)",
+       "blockedtext-composite-no-ids": "כתובת ה־IP שלך מופיעה במספר רשימות שחורות",
        "blockedtext-composite-reason": "הופעלו מספר חסימות על חשבון המשתמש שלך או על כתובת ה־IP שלך (או על שניהם)",
        "whitelistedittext": "נדרשת $1 כדי לערוך דפים.",
        "confirmedittext": "יש לאמת את כתובת הדוא\"ל לפני עריכת דפים.\nנא להגדיר ולאמת את כתובת הדוא\"ל שלך באמצעות [[Special:Preferences|העדפות המשתמש]] שלך.",
        "right-editmyusercss": "עריכת קובצי CSS של המשתמש עצמו",
        "right-editmyuserjson": "עריכת קובצי JSON של המשתמש עצמו",
        "right-editmyuserjs": "עריכת קובצי JavaScript של המשתמש עצמו",
+       "right-editmyuserjsredirect": "עריכת דפי JavaScript שלך שהם הפניות",
        "right-viewmywatchlist": "צפייה ברשימת המעקב של המשתמש עצמו",
        "right-editmywatchlist": "עריכת רשימת המעקב של המשתמש עצמו. מספר פעולות יוסיפו דפים גם ללא הרשאה זו.",
        "right-viewmyprivateinfo": "צפייה במידע הפרטי של המשתמש עצמו (כגון: כתובת דוא\"ל, שם אמיתי)",
        "action-editmyusercss": "לערוך קובצי CSS של עצמך",
        "action-editmyuserjson": "לערוך קובצי JSON של עצמך",
        "action-editmyuserjs": "לערוך קובצי JavaScript של עצמך",
+       "action-editmyuserjsredirect": "לערוך את דפי ה־JavaScript שלך שהם הפניות",
        "action-viewsuppressed": "לצפות בגרסאות שהוסתרו מכל המשתמשים",
        "action-hideuser": "לחסום שם משתמש תוך הסתרתו מהציבור",
        "action-ipblock-exempt": "לעקוף חסימות של כתובות IP, חסימות אוטומטיות וחסימות טווחים",
        "specialmute-label-mute-email": "השתקת הודעות דואר אלקטרוני מהמשתמש הזה",
        "specialmute-header": "נא לבחור את העדפות ההשתקה שלך עבור המשתמש <b>{{BIDI:[[User:$1|$1]]}}</b>.",
        "specialmute-error-invalid-user": "שם המשתמש המבוקש לא נמצא.",
+       "specialmute-error-no-options": "אפשרויות ההשתקה אינן זמינות. ייתכן שזה קורה כי: לא עשית אימות כתובת דואר אלקטרוני או שמנהל הוויקי כיבה את אפשרויות הדואר האלקטרוני או את הרשימה השחורה של הדואר האלקטרוני עבור הוויקי הזה.",
        "specialmute-email-footer": "כדי לנהל את העדפות קבלת הדואר האלקטרוני שנשלח על־ידי המשתמש {{BIDI:$2}}, באפשרותך לבקר בדף <$1>.",
        "specialmute-login-required": "נדרשת כניסה לחשבון כדי לשנות את העדפות ההשתקה שלך.",
        "mute-preferences": "העדפות השתקה",
        "passwordpolicies-policy-passwordnotinlargeblacklist": "הסיסמה לא יכולה להיות ברשימת 100,000 הסיסמאות הנפוצות ביותר.",
        "passwordpolicies-policyflag-forcechange": "לדרוש שינוי בעת כניסה לחשבון",
        "passwordpolicies-policyflag-suggestchangeonlogin": "להציע שינוי בעת כניסה לחשבון",
+       "mycustomjsredirectprotected": "אין לך הרשאה לערוך את דף ה־JavaScript הזה כי זאת הפניה ואינה מצביעה לדף בתוך מרחב המשתמש שלך.",
        "easydeflate-invaliddeflate": "התוכן שהועבר אינו דחוס כנדרש",
        "unprotected-js": "מסיבות אבטחה, לא ניתן לטעון JavaScript מדפים שאינם מוגנים. ניתן ליצור סקריפטי JavaScript רק במרחב השם \"מדיה ויקי:\" או בדפי משנה של דף המשתמש.",
        "userlogout-continue": "האם ברצונך לצאת מהחשבון?"
index 9c6550d..75d6b2e 100644 (file)
        "changepassword": "Чажыт сөстү өскертири",
        "resetpass_text": "<!-- Маңаа сөзүглелди немерелээри -->",
        "resetpass_header": "Чажыт сөстү катап чогаадып кылыры",
-       "oldpassword": "Эгри чажыт сөзүңер:",
+       "oldpassword": "Эрги уруң (чажыт сөс):",
        "newpassword": "Чаа чажыт сөзүңер:",
        "retypenew": "Чажыт сөзүңерни катап бижиңер:",
        "resetpass_submit": "Чажыт сөстү чоогадып кылыр база кирер.",
        "page_first": "бирги",
        "page_last": "сөөлгү",
        "histlegend": "Версиялар шилиири: деңнээр дээн арыныңар версияларын имнеңеш, бээр базыптыңар '''{{int:compare-submit}}'''.<br />\nТайылбыр: '''({{int:cur}})''' — амгы версиядан ылгавыр; '''({{int:last}})''' — эрткен версиядан ылгавыр;  '''{{int:minoreditletter}}''' — биче өскерилгелер.",
-       "history-fieldset-title": "Ð\9aаÑ\80алааÑ\80Ñ\8b Ñ\82өөгүзү",
+       "history-fieldset-title": "ЭдилгелеÑ\80ни Ñ\88Ò¯Ò¯Ñ\80",
        "history-show-deleted": "Чүгле казыттынган",
        "histfirst": "Эң эрги",
        "histlast": "Эң чаа",
        "unusedcategories": "Ажыглаваан бөлүктер",
        "unusedimages": "Ажыглаваан файлдар",
        "wantedcategories": "Күзээринге бөлүктер",
-       "wantedpages": "Күзээрүнге арыннар",
+       "wantedpages": "Күзээн арыннар",
        "mostlinked": "Эң холбаалар арыннар",
        "mostlinkedcategories": "Эң холбаалар бөлүктер",
        "mostlinkedtemplates": "Эң холбаалар майыктар",
        "duration-years": "$1 {{PLURAL:$1|чыл|чыл}}",
        "duration-decades": "$1 {{PLURAL:$1|1=он хонук|он хонук}}",
        "duration-centuries": "$1 {{PLURAL:$1|1=чүс чыл|чүс чыл}}",
-       "mw-widgets-abandonedit-title": "Бүзүрелдиг-дир бе?"
+       "mw-widgets-abandonedit-title": "Бүзүрелдиг-дир бе?",
+       "mw-widgets-dateinput-no-date": "Ай-хүн шилитинмээн",
+       "date-range-to": "Ай-хүнге чедир:"
 }
index f3d4f97..06295d7 100644 (file)
@@ -48,22 +48,27 @@ $specialPageAliases = [
        'ApiHelp'                   => [ 'עזרת_API' ],
        'ApiSandbox'                => [ 'ארגז_חול_של_API' ],
        'Ancientpages'              => [ 'דפים_מוזנחים' ],
+       'AutoblockList'             => [ 'חסימות_אוטומטיות', 'רשימת_חסימות_אוטומטיות' ],
        'Badtitle'                  => [ 'כותרת_שגויה' ],
        'Blankpage'                 => [ 'דף_ריק' ],
        'Block'                     => [ 'חסימה', 'חסימת_כתובת', 'חסימת_משתמש' ],
        'Booksources'               => [ 'משאבי_ספרות', 'משאבי_ספרות_חיצוניים' ],
+       'BotPasswords'              => [ 'סיסמאות_בוט' ],
        'BrokenRedirects'           => [ 'הפניות_לא_תקינות', 'הפניות_שבורות' ],
        'Categories'                => [ 'קטגוריות', 'רשימת_קטגוריות' ],
+       'ChangeContentModel'        => [ 'שינוי_מודל_התוכן' ],
+       'ChangeCredentials'         => [ 'שינוי_נתוני_ההזדהות' ],
        'ChangeEmail'               => [ 'שינוי_דואר_אלקטרוני', 'שינוי_דוא"ל' ],
        'ChangePassword'            => [ 'שינוי_סיסמה' ],
        'ComparePages'              => [ 'השוואת_דפים' ],
        'Confirmemail'              => [ 'אימות_כתובת_דואר' ],
        'Contributions'             => [ 'תרומות', 'תרומות_המשתמש' ],
-       'CreateAccount'             => [ 'הרשמה_לחשבון' ],
+       'CreateAccount'             => [ 'הרשמה_לחשבון', 'יצירת_חשבון' ],
        'Deadendpages'              => [ 'דפים_ללא_קישורים' ],
        'DeletedContributions'      => [ 'תרומות_מחוקות' ],
        'Diff'                      => [ 'הבדלים', 'הבדל' ],
        'DoubleRedirects'           => [ 'הפניות_כפולות' ],
+       'EditTags'                  => [ 'עריכת_תגיות' ],
        'EditWatchlist'             => [ 'עריכת_רשימת_המעקב' ],
        'Emailuser'                 => [ 'שליחת_דואר_למשתמש' ],
        'ExpandTemplates'           => [ 'פריסת_תבניות' ],
@@ -71,15 +76,18 @@ $specialPageAliases = [
        'Fewestrevisions'           => [ 'הגרסאות_המעטות_ביותר', 'הדפים_בעלי_מספר_העריכות_הנמוך_ביותר' ],
        'FileDuplicateSearch'       => [ 'חיפוש_קבצים_כפולים' ],
        'Filepath'                  => [ 'נתיב_לקובץ' ],
+       'GoToInterwiki'             => [ 'מעבר_לאתר_אחר' ],
        'Import'                    => [ 'ייבוא', 'ייבוא_דפים' ],
        'Invalidateemail'           => [ 'ביטול_דואר' ],
        'JavaScriptTest'            => [ 'בדיקת_JavaScript' ],
        'BlockList'                 => [ 'רשימת_חסומים', 'רשימת_משתמשים_חסומים', 'משתמשים_חסומים' ],
        'LinkSearch'                => [ 'חיפוש_קישורים_חיצוניים' ],
+       'LinkAccounts'              => [ 'קישור_חשבונות' ],
        'Listadmins'                => [ 'רשימת_מפעילים' ],
        'Listbots'                  => [ 'רשימת_בוטים' ],
        'Listfiles'                 => [ 'רשימת_קבצים', 'רשימת_תמונות', 'קבצים', 'תמונות' ],
        'Listgrouprights'           => [ 'רשימת_הרשאות_לקבוצה' ],
+       'Listgrants'                => [ 'רשימת_זיכיונות', 'זיכיונות' ],
        'Listredirects'             => [ 'רשימת_הפניות', 'הפניות' ],
        'ListDuplicatedFiles'       => [ 'רשימת_קבצים_כפולים' ],
        'Listusers'                 => [ 'רשימת_משתמשים', 'משתמשים' ],
@@ -106,7 +114,9 @@ $specialPageAliases = [
        'Newimages'                 => [ 'קבצים_חדשים', 'תמונות_חדשות', 'גלריית_קבצים_חדשים', 'גלריית_תמונות_חדשות' ],
        'Newpages'                  => [ 'דפים_חדשים' ],
        'PagesWithProp'             => [ 'דפים_עם_מאפיינים', 'דפים_לפי_מאפיינים' ],
+       'PageData'                  => [ 'מידע_על_הדף' ],
        'PageLanguage'              => [ 'שפת_הדף' ],
+       'PasswordPolicies'          => [ 'מדיניות_הסיסמאות' ],
        'PasswordReset'             => [ 'איפוס_סיסמה' ],
        'PermanentLink'             => [ 'קישור_קבוע' ],
        'Preferences'               => [ 'העדפות', 'ההעדפות_שלי' ],
@@ -116,9 +126,11 @@ $specialPageAliases = [
        'Randompage'                => [ 'אקראי', 'דף_אקראי' ],
        'RandomInCategory'          => [ 'דף_אקראי_בקטגוריה' ],
        'Randomredirect'            => [ 'הפניה_אקראית' ],
+       'Randomrootpage'            => [ 'דף_בסיס_אקראי' ],
        'Recentchanges'             => [ 'שינויים_אחרונים' ],
        'Recentchangeslinked'       => [ 'שינויים_בדפים_המקושרים' ],
        'Redirect'                  => [ 'הפניה' ],
+       'RemoveCredentials'         => [ 'הסרת_נתוני_ההזדהות' ],
        'ResetTokens'               => [ 'איפוס_אסימונים' ],
        'Revisiondelete'            => [ 'מחיקת_ושחזור_גרסאות' ],
        'RunJobs'                   => [ 'הרצת_משימות' ],
@@ -134,6 +146,7 @@ $specialPageAliases = [
        'Uncategorizedpages'        => [ 'דפים_חסרי_קטגוריה' ],
        'Uncategorizedtemplates'    => [ 'תבניות_חסרות_קטגוריות' ],
        'Undelete'                  => [ 'צפייה_בדפים_מחוקים' ],
+       'UnlinkAccounts'            => [ 'ביטול_הקישור_בין_חשבונות' ],
        'Unlockdb'                  => [ 'שחרור_בסיס_הנתונים' ],
        'Unusedcategories'          => [ 'קטגוריות_שאינן_בשימוש' ],
        'Unusedimages'              => [ 'קבצים_שאינם_בשימוש', 'תמונות_שאינן_בשימוש' ],
index 13f3cfa..4cc52a4 100644 (file)
@@ -69,9 +69,9 @@ class CleanupSpam extends Maintenance {
                        // Clean up spam on all wikis
                        $this->output( "Finding spam on " . count( $wgLocalDatabases ) . " wikis\n" );
                        $found = false;
-                       foreach ( $wgLocalDatabases as $wikiID ) {
+                       foreach ( $wgLocalDatabases as $wikiId ) {
                                /** @var Database $dbr */
-                               $dbr = $this->getDB( DB_REPLICA, [], $wikiID );
+                               $dbr = $this->getDB( DB_REPLICA, [], $wikiId );
 
                                foreach ( $protConds as $conds ) {
                                        $count = $dbr->selectField(
@@ -84,9 +84,9 @@ class CleanupSpam extends Maintenance {
                                                $found = true;
                                                $cmd = wfShellWikiCmd(
                                                        "$IP/maintenance/cleanupSpam.php",
-                                                       [ '--wiki', $wikiID, $spec ]
+                                                       [ '--wiki', $wikiId, $spec ]
                                                );
-                                               passthru( "$cmd | sed 's/^/$wikiID:  /'" );
+                                               passthru( "$cmd | sed 's/^/$wikiId:  /'" );
                                        }
                                }
                        }
index 8579f0f..45c66ae 100644 (file)
@@ -44,10 +44,10 @@ class ClearInterwikiCache extends Maintenance {
                        $prefixes[] = $row->iw_prefix;
                }
 
-               foreach ( $wgLocalDatabases as $db ) {
-                       $this->output( "$db..." );
+               foreach ( $wgLocalDatabases as $wikiId ) {
+                       $this->output( "$wikiId..." );
                        foreach ( $prefixes as $prefix ) {
-                               $wgMemc->delete( "$db:interwiki:$prefix" );
+                               $wgMemc->delete( "$wikiId:interwiki:$prefix" );
                        }
                        $this->output( "done\n" );
                }
index dc70e9c..0e96142 100644 (file)
@@ -57,8 +57,9 @@ class CopyJobQueue extends Maintenance {
                        ? JobQueueGroup::singleton()->getQueueTypes()
                        : [ $this->getOption( 'type' ) ];
 
+               $dbDomain = WikiMap::getCurrentWikiDbDomain()->getId();
                foreach ( $types as $type ) {
-                       $baseConfig = [ 'type' => $type, 'wiki' => wfWikiID() ];
+                       $baseConfig = [ 'type' => $type, 'domain' => $dbDomain ];
                        $src = JobQueue::factory( $baseConfig + $wgJobQueueMigrationConfig[$srcKey] );
                        $dst = JobQueue::factory( $baseConfig + $wgJobQueueMigrationConfig[$dstKey] );
 
index 05dd0d0..aef45bf 100644 (file)
@@ -188,18 +188,20 @@ class GenerateSitemap extends Maintenance {
                        $this->fatalError( "Can not create directory $fspath." );
                }
 
+               $dbDomain = WikiMap::getCurrentWikiDbDomain()->getId();
                $this->fspath = realpath( $fspath ) . DIRECTORY_SEPARATOR;
                $this->urlpath = $this->getOption( 'urlpath', "" );
                if ( $this->urlpath !== "" && substr( $this->urlpath, -1 ) !== '/' ) {
                        $this->urlpath .= '/';
                }
-               $this->identifier = $this->getOption( 'identifier', wfWikiID() );
+               $this->identifier = $this->getOption( 'identifier', $dbDomain );
                $this->compress = $this->getOption( 'compress', 'yes' ) !== 'no';
                $this->skipRedirects = $this->hasOption( 'skip-redirects' );
                $this->dbr = $this->getDB( DB_REPLICA );
                $this->generateNamespaces();
                $this->timestamp = wfTimestamp( TS_ISO_8601, wfTimestampNow() );
-               $this->findex = fopen( "{$this->fspath}sitemap-index-{$this->identifier}.xml", 'wb' );
+               $encIdentifier = rawurlencode( $this->identifier );
+               $this->findex = fopen( "{$this->fspath}sitemap-index-{$encIdentifier}.xml", 'wb' );
                $this->main();
        }
 
index c6738bc..f9b3951 100644 (file)
@@ -759,6 +759,7 @@ TEXT
        function openSpawn() {
                global $IP;
 
+               $wiki = WikiMap::getWikiIdFromDbDomain( WikiMap::getCurrentWikiDbDomain() );
                if ( count( $this->php ) == 2 ) {
                        $mwscriptpath = $this->php[1];
                } else {
@@ -771,14 +772,14 @@ TEXT
                                                $this->php[0],
                                                $mwscriptpath,
                                                "fetchText.php",
-                                               '--wiki', wfWikiID() ] ) );
+                                               '--wiki', $wiki ] ) );
                } else {
                        $cmd = implode( " ",
                                array_map( [ Shell::class, 'escape' ],
                                        [
                                                $this->php[0],
                                                "$IP/maintenance/fetchText.php",
-                                               '--wiki', wfWikiID() ] ) );
+                                               '--wiki', $wiki ] ) );
                }
                $spec = [
                        0 => [ "pipe", "r" ],
index 8a8f4d8..6fd53cc 100644 (file)
@@ -218,6 +218,8 @@ class RecompressTracked {
         * writing are all slow.
         */
        function startReplicaProcs() {
+               $wiki = WikiMap::getWikiIdFromDbDomain( WikiMap::getCurrentWikiDbDomain() );
+
                $cmd = 'php ' . Shell::escape( __FILE__ );
                foreach ( self::$cmdLineOptionMap as $cmdOption => $classOption ) {
                        if ( $cmdOption == 'replica-id' ) {
@@ -229,7 +231,7 @@ class RecompressTracked {
                        }
                }
                $cmd .= ' --child' .
-                       ' --wiki ' . Shell::escape( wfWikiID() ) .
+                       ' --wiki ' . Shell::escape( $wiki ) .
                        ' ' . Shell::escape( ...$this->destClusters );
 
                $this->replicaPipes = $this->replicaProcs = [];
index 4c9dcb4..98f1c24 100644 (file)
@@ -107,16 +107,14 @@ The new option is NOT validated.' );
 
                                $userValue = $user->getOption( $option );
                                if ( $userValue <> $defaultOptions[$option] ) {
-                                       // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
-                                       @$ret[$option][$userValue]++;
+                                       $ret[$option][$userValue] = ( $ret[$option][$userValue] ?? 0 ) + 1;
                                }
                        } else {
 
                                foreach ( $defaultOptions as $name => $defaultValue ) {
                                        $userValue = $user->getOption( $name );
                                        if ( $userValue != $defaultValue ) {
-                                               // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
-                                               @$ret[$name][$userValue]++;
+                                               $ret[$option][$userValue] = ( $ret[$option][$userValue] ?? 0 ) + 1;
                                        }
                                }
                        }
index 5d18fdf..09998da 100644 (file)
@@ -249,8 +249,8 @@ oojs-router:
 
 ooui:
   type: tar
-  src: https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.33.3.tgz
-  integrity: sha384-eM78ktDU9DG7WIjxnAHWUsPa9VHCaltqLya4afg0C10gd+c5c5q9NJSnNdFgy76J
+  src: https://registry.npmjs.org/oojs-ui/-/oojs-ui-0.33.4.tgz
+  integrity: sha384-ZqQ9VxkRqt444Xthv89HNZB1PyM1SmNz+7gnC2HfaXUtBh+coJdXv7JxlaBjAouq
 
   dest:
     # Main stuff
index 60ef70c..c913a1d 100644 (file)
@@ -1,4 +1,11 @@
 # OOUI Release History
+## v0.33.4 / 2019-07-22
+### Styles
+* Frameless buttons should feature hover and active states (Volker E.)
+* Revert "WikimediaUI theme: Apply primary flag to ButtonWidget (frameless)" (Volker E.)
+* icons: Add 'bellOutline' and 'userAvatarOutline' and amend 'search' (Volker E.)
+
+
 ## v0.33.3 / 2019-07-16
 ### Styles
 * MessageWidget: Apply `bold` only to inline message types (Volker E.)
index b7f3f10..2a1984d 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:36Z
+ * Date: 2019-07-23T03:23:32Z
  */
 ( function ( OO ) {
 
index 5fe630e..e664153 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:43Z
+ * Date: 2019-07-23T03:23:40Z
  */
 .oo-ui-element-hidden {
   display: none !important;
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
   color: #000;
 }
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:hover {
+  background-color: #fafafa;
+  color: #000;
+}
+.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:active {
+  background-color: #ddd;
+  color: #000;
+}
 .oo-ui-buttonElement-frameless.oo-ui-labelElement:first-child,
 .oo-ui-buttonElement-frameless.oo-ui-iconElement:first-child {
   margin-left: -0.3125em;
index ccbda55..2b45f4e 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:43Z
+ * Date: 2019-07-23T03:23:40Z
  */
 .oo-ui-element-hidden {
   display: none !important;
   color: #222;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
+  background-color: #f8f9fa;
+  color: #000;
+}
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active {
+  background-color: #eaecf0;
   color: #444;
 }
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-iconElement > .oo-ui-buttonElement-button:focus,
   color: #b32424;
   box-shadow: none;
 }
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
-  color: #fff;
-  background-color: #36c;
-  border-color: #36c;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover {
-  background-color: #447ff5;
-  border-color: #447ff5;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:active:focus,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-popupToolGroup-active > .oo-ui-buttonElement-button {
-  color: #fff;
-  background-color: #2a4b8d;
-  border-color: #2a4b8d;
-  box-shadow: none;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus {
-  border-color: #36c;
-  box-shadow: inset 0 0 0 1px #36c, inset 0 0 0 2px #fff;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
-  color: #fff;
-  background-color: #d33;
-  border-color: #d33;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:hover {
-  background-color: #ff4242;
-  border-color: #ff4242;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:active,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:active:focus,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-buttonElement-active > .oo-ui-buttonElement-button,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-popupToolGroup-active > .oo-ui-buttonElement-button {
-  color: #fff;
-  background-color: #b32424;
-  border-color: #b32424;
-  box-shadow: none;
-}
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:focus {
-  border-color: #d33;
-  box-shadow: inset 0 0 0 1px #d33, inset 0 0 0 2px #fff;
-}
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled[class*='oo-ui-flaggedElement'] > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon,
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled[class*='oo-ui-flaggedElement'] > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
   opacity: 1;
   padding-top: 1.42857143em;
   padding-right: 0;
 }
+.oo-ui-fieldLayout .oo-ui-fieldLayout-help .oo-ui-buttonElement-button:hover,
+.oo-ui-fieldLayout .oo-ui-fieldLayout-help .oo-ui-buttonElement-button:active {
+  background-color: transparent;
+}
 .oo-ui-fieldLayout.oo-ui-fieldLayout-align-top > .oo-ui-fieldLayout-body > .oo-ui-inline-help {
   margin-top: 0.28571429em;
 }
@@ -788,6 +751,10 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout {
   padding-top: 1.42857143em;
   padding-right: 0;
 }
+.oo-ui-fieldsetLayout .oo-ui-fieldsetLayout-help .oo-ui-buttonElement-button:hover,
+.oo-ui-fieldsetLayout .oo-ui-fieldsetLayout-help .oo-ui-buttonElement-button:active {
+  background-color: transparent;
+}
 
 .oo-ui-formLayout + .oo-ui-fieldsetLayout,
 .oo-ui-formLayout + .oo-ui-formLayout {
index 213540f..a0bc401 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:36Z
+ * Date: 2019-07-23T03:23:32Z
  */
 ( function ( OO ) {
 
index d1db776..7b84493 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:43Z
+ * Date: 2019-07-23T03:23:40Z
  */
 .oo-ui-tool > .oo-ui-tool-link > .oo-ui-tool-checkIcon {
   display: none;
index cf0245d..63a3784 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:43Z
+ * Date: 2019-07-23T03:23:40Z
  */
 .oo-ui-tool {
   -webkit-box-sizing: border-box;
index d2a22ff..02b796d 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:36Z
+ * Date: 2019-07-23T03:23:32Z
  */
 ( function ( OO ) {
 
index e9845f3..eefee7b 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:43Z
+ * Date: 2019-07-23T03:23:40Z
  */
 .oo-ui-draggableElement-handle:not( .oo-ui-draggableElement-undraggable ).oo-ui-widget {
   cursor: move;
index 8305107..fb59c44 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:43Z
+ * Date: 2019-07-23T03:23:40Z
  */
 .oo-ui-draggableElement-handle:not( .oo-ui-draggableElement-undraggable ).oo-ui-widget {
   cursor: move;
index 722f6dd..1f5ca65 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:36Z
+ * Date: 2019-07-23T03:23:32Z
  */
 ( function ( OO ) {
 
index 56f7ea4..7a67cce 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:36Z
+ * Date: 2019-07-23T03:23:32Z
  */
 ( function ( OO ) {
 
index b54b053..f0c4b64 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:43Z
+ * Date: 2019-07-23T03:23:40Z
  */
 
 .oo-ui-window {
index a80f293..617599d 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:43Z
+ * Date: 2019-07-23T03:23:40Z
  */
 
 .oo-ui-window {
index de7e45d..d6f9989 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOUI v0.33.3
+ * OOUI v0.33.4
  * https://www.mediawiki.org/wiki/OOUI
  *
  * Copyright 2011–2019 OOUI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2019-07-16T21:33:36Z
+ * Date: 2019-07-23T03:23:32Z
  */
 ( function ( OO ) {
 
index 8e440cb..c8111a2 100644 (file)
@@ -8,6 +8,9 @@
                "bell": {
                        "file": "../wikimediaui/images/icons/bell.svg"
                },
+               "bellOutline": {
+                       "file": "../wikimediaui/images/icons/bellOutline.svg"
+               },
                "error": {
                        "file": "../wikimediaui/images/icons/error.svg"
                },
index 89b38b5..e13bb1d 100644 (file)
@@ -8,6 +8,9 @@
                "userAvatar": {
                        "file": "../wikimediaui/images/icons/userAvatar.svg"
                },
+               "userAvatarOutline": {
+                       "file": "../wikimediaui/images/icons/userAvatarOutline.svg"
+               },
                "userTalk": {
                        "file": {
                                "ltr": "../wikimediaui/images/icons/userTalk-ltr.svg",
index 2b315c8..627c6ef 100644 (file)
@@ -34,6 +34,9 @@
                "bell": {
                        "file": "images/icons/bell.svg"
                },
+               "bellOutline": {
+                       "file": "images/icons/bellOutline.svg"
+               },
                "error": {
                        "file": "images/icons/error.svg",
                        "variants": [
index 0e4fe16..7266d34 100644 (file)
@@ -31,6 +31,9 @@
                "userAvatar": {
                        "file": "images/icons/userAvatar.svg"
                },
+               "userAvatarOutline": {
+                       "file": "images/icons/userAvatarOutline.svg"
+               },
                "userTalk": {
                        "file": {
                                "ltr": "images/icons/userTalk-ltr.svg",
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-invert.png b/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-invert.png
new file mode 100644 (file)
index 0000000..65a31fb
Binary files /dev/null and b/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-invert.png differ
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-invert.svg b/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-invert.svg
new file mode 100644 (file)
index 0000000..b1b7c8a
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>bell</title><path fill="#fff" fill-rule="evenodd" d="M11.5 2.19C14.09 2.86 16 5.2 16 8v6l2 2v1H2v-1l2-2V8c0-2.8 1.91-5.14 4.5-5.81V1.5C8.5.67 9.17 0 10 0s1.5.67 1.5 1.5v.69zM10 4C7.79 4 6 5.79 6 8v7h8V8c0-2.21-1.79-4-4-4zM8 18h4c0 1.1-.9 2-2 2s-2-.9-2-2z"/></svg>
\ No newline at end of file
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-progressive.png b/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-progressive.png
new file mode 100644 (file)
index 0000000..984c582
Binary files /dev/null and b/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-progressive.png differ
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-progressive.svg b/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline-progressive.svg
new file mode 100644 (file)
index 0000000..4525006
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>bell</title><path fill="#36c" fill-rule="evenodd" d="M11.5 2.19C14.09 2.86 16 5.2 16 8v6l2 2v1H2v-1l2-2V8c0-2.8 1.91-5.14 4.5-5.81V1.5C8.5.67 9.17 0 10 0s1.5.67 1.5 1.5v.69zM10 4C7.79 4 6 5.79 6 8v7h8V8c0-2.21-1.79-4-4-4zM8 18h4c0 1.1-.9 2-2 2s-2-.9-2-2z"/></svg>
\ No newline at end of file
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline.png b/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline.png
new file mode 100644 (file)
index 0000000..c5ac8fb
Binary files /dev/null and b/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline.png differ
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline.svg b/resources/lib/ooui/themes/wikimediaui/images/icons/bellOutline.svg
new file mode 100644 (file)
index 0000000..3d6a721
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>bell</title><path fill-rule="evenodd" d="M11.5 2.19C14.09 2.86 16 5.2 16 8v6l2 2v1H2v-1l2-2V8c0-2.8 1.91-5.14 4.5-5.81V1.5C8.5.67 9.17 0 10 0s1.5.67 1.5 1.5v.69zM10 4C7.79 4 6 5.79 6 8v7h8V8c0-2.21-1.79-4-4-4zM8 18h4c0 1.1-.9 2-2 2s-2-.9-2-2z"/></svg>
\ No newline at end of file
index 91dc0c4..75a663c 100644 (file)
Binary files a/resources/lib/ooui/themes/wikimediaui/images/icons/search-invert.png and b/resources/lib/ooui/themes/wikimediaui/images/icons/search-invert.png differ
index 16c3438..f736c95 100644 (file)
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>search</title><path fill="#fff" d="M19 17l-5.15-5.15a7 7 0 1 0-2 2L17 19zM3.5 8A4.5 4.5 0 1 1 8 12.5 4.5 4.5 0 0 1 3.5 8z"/></svg>
\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>search</title><path fill="#fff" d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46A7.432 7.432 0 0 1 7.5 15C3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/></svg>
\ No newline at end of file
index e410eca..5644489 100644 (file)
Binary files a/resources/lib/ooui/themes/wikimediaui/images/icons/search-progressive.png and b/resources/lib/ooui/themes/wikimediaui/images/icons/search-progressive.png differ
index 789999d..4c5ed6f 100644 (file)
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>search</title><path fill="#36c" d="M19 17l-5.15-5.15a7 7 0 1 0-2 2L17 19zM3.5 8A4.5 4.5 0 1 1 8 12.5 4.5 4.5 0 0 1 3.5 8z"/></svg>
\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>search</title><path fill="#36c" d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46A7.432 7.432 0 0 1 7.5 15C3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/></svg>
\ No newline at end of file
index fe88ca2..e633273 100644 (file)
Binary files a/resources/lib/ooui/themes/wikimediaui/images/icons/search.png and b/resources/lib/ooui/themes/wikimediaui/images/icons/search.png differ
index 293f53f..b304da6 100644 (file)
@@ -1 +1 @@
-<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>search</title><path d="M19 17l-5.15-5.15a7 7 0 1 0-2 2L17 19zM3.5 8A4.5 4.5 0 1 1 8 12.5 4.5 4.5 0 0 1 3.5 8z"/></svg>
\ No newline at end of file
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>search</title><path d="M7.5 13c3.04 0 5.5-2.46 5.5-5.5S10.54 2 7.5 2 2 4.46 2 7.5 4.46 13 7.5 13zm4.55.46A7.432 7.432 0 0 1 7.5 15C3.36 15 0 11.64 0 7.5S3.36 0 7.5 0C11.64 0 15 3.36 15 7.5c0 1.71-.57 3.29-1.54 4.55l6.49 6.49-1.41 1.41-6.49-6.49z"/></svg>
\ No newline at end of file
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-invert.png b/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-invert.png
new file mode 100644 (file)
index 0000000..b5689de
Binary files /dev/null and b/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-invert.png differ
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-invert.svg b/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-invert.svg
new file mode 100644 (file)
index 0000000..3db6aca
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>user avatar</title><path fill="#fff" d="M10 8c1.7 0 3.06-1.35 3.06-3S11.7 2 10 2 6.94 3.35 6.94 5 8.3 8 10 8zm0 2c-2.8 0-5.06-2.24-5.06-5S7.2 0 10 0s5.06 2.24 5.06 5-2.26 5-5.06 5zm-7 8h14v-1.33c0-1.75-2.31-3.56-7-3.56s-7 1.81-7 3.56V18zm7-6.89c6.66 0 9 3.33 9 5.56V20H1v-3.33c0-2.23 2.34-5.56 9-5.56z"/></svg>
\ No newline at end of file
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-progressive.png b/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-progressive.png
new file mode 100644 (file)
index 0000000..5bb95db
Binary files /dev/null and b/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-progressive.png differ
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-progressive.svg b/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-progressive.svg
new file mode 100644 (file)
index 0000000..c14e3e7
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>user avatar</title><path fill="#36c" d="M10 8c1.7 0 3.06-1.35 3.06-3S11.7 2 10 2 6.94 3.35 6.94 5 8.3 8 10 8zm0 2c-2.8 0-5.06-2.24-5.06-5S7.2 0 10 0s5.06 2.24 5.06 5-2.26 5-5.06 5zm-7 8h14v-1.33c0-1.75-2.31-3.56-7-3.56s-7 1.81-7 3.56V18zm7-6.89c6.66 0 9 3.33 9 5.56V20H1v-3.33c0-2.23 2.34-5.56 9-5.56z"/></svg>
\ No newline at end of file
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline.png b/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline.png
new file mode 100644 (file)
index 0000000..cd9b4fe
Binary files /dev/null and b/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline.png differ
diff --git a/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline.svg b/resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline.svg
new file mode 100644 (file)
index 0000000..1f0d41d
--- /dev/null
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><title>user avatar</title><path d="M10 8c1.7 0 3.06-1.35 3.06-3S11.7 2 10 2 6.94 3.35 6.94 5 8.3 8 10 8zm0 2c-2.8 0-5.06-2.24-5.06-5S7.2 0 10 0s5.06 2.24 5.06 5-2.26 5-5.06 5zm-7 8h14v-1.33c0-1.75-2.31-3.56-7-3.56s-7 1.81-7 3.56V18zm7-6.89c6.66 0 9 3.33 9 5.56V20H1v-3.33c0-2.23 2.34-5.56 9-5.56z"/></svg>
\ No newline at end of file
index 6d831f6..4f875ce 100644 (file)
@@ -336,15 +336,6 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                );
 
                // Correct handling of really old password hashes
-               $this->config->set( 'PasswordSalt', false );
-               $password = md5( 'FooBar' );
-               $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => $userName ] );
-               $req->password = 'FooBar';
-               $this->assertEquals(
-                       AuthenticationResponse::newPass( $userName ),
-                       $provider->beginPrimaryAuthentication( $reqs )
-               );
-
                $this->config->set( 'PasswordSalt', true );
                $password = md5( "$id-" . md5( 'FooBar' ) );
                $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => $userName ] );
index f953319..8c53873 100644 (file)
@@ -125,6 +125,12 @@ class CachedBagOStuffTest extends PHPUnit\Framework\TestCase {
         * @covers CachedBagOStuff::makeKey
         */
        public function testMakeKey() {
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       // This works fine on HHVM (and verified by integration tests), but due to
+                       // a bug in HHVM's Reflection, PHPUnit 4 fails to create a mock (T228563)
+                       $this->markTestSkipped( 'HHVM Reflection buggy' );
+               }
+
                $backend = $this->getMockBuilder( HashBagOStuff::class )
                        ->setMethods( [ 'makeKey' ] )
                        ->getMock();
@@ -145,6 +151,10 @@ class CachedBagOStuffTest extends PHPUnit\Framework\TestCase {
         * @covers CachedBagOStuff::makeGlobalKey
         */
        public function testMakeGlobalKey() {
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       $this->markTestSkipped( 'HHVM Reflection buggy' );
+               }
+
                $backend = $this->getMockBuilder( HashBagOStuff::class )
                        ->setMethods( [ 'makeGlobalKey' ] )
                        ->getMock();
index 9f88474..dc49a13 100644 (file)
@@ -107,6 +107,10 @@ class MultiWriteBagOStuffTest extends MediaWikiTestCase {
         * @covers MultiWriteBagOStuff::makeKey
         */
        public function testMakeKey() {
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       $this->markTestSkipped( 'HHVM Reflection buggy' );
+               }
+
                $cache1 = $this->getMockBuilder( HashBagOStuff::class )
                        ->setMethods( [ 'makeKey' ] )->getMock();
                $cache1->expects( $this->once() )->method( 'makeKey' )
@@ -124,6 +128,10 @@ class MultiWriteBagOStuffTest extends MediaWikiTestCase {
         * @covers MultiWriteBagOStuff::makeGlobalKey
         */
        public function testMakeGlobalKey() {
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       $this->markTestSkipped( 'HHVM Reflection buggy' );
+               }
+
                $cache1 = $this->getMockBuilder( HashBagOStuff::class )
                        ->setMethods( [ 'makeGlobalKey' ] )->getMock();
                $cache1->expects( $this->once() )->method( 'makeGlobalKey' )
index 76bfea2..9e4a5d7 100644 (file)
@@ -1865,6 +1865,10 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
         * @covers WANObjectCache::makeKey
         */
        public function testMakeKey() {
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       $this->markTestSkipped( 'HHVM Reflection buggy' );
+               }
+
                $backend = $this->getMockBuilder( HashBagOStuff::class )
                        ->setMethods( [ 'makeKey' ] )->getMock();
                $backend->expects( $this->once() )->method( 'makeKey' )
@@ -1881,6 +1885,10 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
         * @covers WANObjectCache::makeGlobalKey
         */
        public function testMakeGlobalKey() {
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       $this->markTestSkipped( 'HHVM Reflection buggy' );
+               }
+
                $backend = $this->getMockBuilder( HashBagOStuff::class )
                        ->setMethods( [ 'makeGlobalKey' ] )->getMock();
                $backend->expects( $this->once() )->method( 'makeGlobalKey' )
index 72db766..9616672 100644 (file)
@@ -86,6 +86,10 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
         * @return PHPUnit_Framework_MockObject_MockObject|HashBagOStuff
         */
        private function getMockCache() {
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       $this->markTestSkipped( 'HHVM Reflection buggy' );
+               }
+
                $mock = $this->getMockBuilder( HashBagOStuff::class )
                        ->disableOriginalConstructor()
                        ->setMethods( [ 'get', 'set', 'delete', 'makeKey' ] )
index 7a78e52..54a362e 100644 (file)
@@ -6,6 +6,7 @@ use DumpBackup;
 use Exception;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Revision\RevisionRecord;
+use MediaWiki\Revision\SlotRecord;
 use MediaWikiTestCase;
 use MWException;
 use RequestContext;
@@ -28,13 +29,14 @@ class BackupDumperPageTest extends DumpTestCase {
 
        // We'll add several pages, revision and texts. The following variables hold the
        // corresponding ids.
-       private $pageId1, $pageId2, $pageId3, $pageId4;
-       private $pageTitle1, $pageTitle2, $pageTitle3, $pageTitle4;
+       private $pageId1, $pageId2, $pageId3, $pageId4, $pageId5;
+       private $pageTitle1, $pageTitle2, $pageTitle3, $pageTitle4, $pageTitle5;
        private $revId1_1, $textId1_1;
        private $revId2_1, $textId2_1, $revId2_2, $textId2_2;
        private $revId2_3, $textId2_3, $revId2_4, $textId2_4;
        private $revId3_1, $textId3_1, $revId3_2, $textId3_2;
        private $revId4_1, $textId4_1;
+       private $revId5_1, $textId5_1;
        private $namespace, $talk_namespace;
 
        /**
@@ -106,6 +108,15 @@ class BackupDumperPageTest extends DumpTestCase {
                                "Talk about BackupDumperTestP1 Text1",
                                "Talk BackupDumperTestP1 Summary1" );
                        $this->pageId4 = $page->getId();
+
+                       $this->pageTitle5 = Title::newFromText( 'BackupDumperTestP5' );
+                       $page = WikiPage::factory( $this->pageTitle5 );
+                       list( $this->revId5_1, $this->textId5_1 ) = $this->addRevision( $page,
+                               "BackupDumperTestP5 Text1",
+                               "BackupDumperTestP5 Summary1" );
+                       $this->pageId5 = $page->getId();
+
+                       $this->corruptRevisionData( $page->getRevision()->getRevisionRecord() );
                } catch ( Exception $e ) {
                        // We'd love to pass $e directly. However, ... see
                        // documentation of exceptionFromAddDBData in
@@ -114,6 +125,39 @@ class BackupDumperPageTest extends DumpTestCase {
                }
        }
 
+       /**
+        * Corrupt the information about the given revision in the database.
+        *
+        * @param RevisionRecord $revision
+        */
+       private function corruptRevisionData( RevisionRecord $revision ) {
+               global $wgMultiContentRevisionSchemaMigrationStage;
+
+               if ( ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) ) {
+                       $this->db->update(
+                               'revision',
+                               [
+                                       'rev_text_id' => 0,
+                                       'rev_sha1' => '',
+                                       'rev_len' => '0',
+                               ],
+                               [ 'rev_id' => $revision->getId() ]
+                       );
+               }
+
+               if ( ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) ) {
+                       $this->db->update(
+                               'content',
+                               [
+                                       'content_address' => 'tt:0',
+                                       'content_sha1' => '',
+                                       'content_size' => '0',
+                               ],
+                               [ 'content_id' => $revision->getSlot( SlotRecord::MAIN )->getContentId() ]
+                       );
+               }
+       }
+
        protected function setUp() {
                parent::setUp();
 
@@ -201,11 +245,14 @@ class BackupDumperPageTest extends DumpTestCase {
                $dumper = $this->newDumpBackup(
                        [ '--full', '--quiet', '--output', 'file:' . $fname, '--schema-version', $schemaVersion ],
                        $this->pageId1,
-                       $this->pageId4 + 1
+                       $this->pageId5 + 1
                );
 
-               // Performing the dump
+               // Performing the dump. Suppress warnings, since we want to test
+               // accessing broken revision data (page 5).
+               $this->setMwGlobals( 'wgDevelopmentWarnings', false );
                $dumper->execute();
+               $this->setMwGlobals( 'wgDevelopmentWarnings', true );
 
                // Checking the dumped data
                $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
@@ -295,6 +342,26 @@ class BackupDumperPageTest extends DumpTestCase {
                );
                $asserter->assertPageEnd();
 
+               // Page 5 (broken revision data)
+               $asserter->assertPageStart(
+                       $this->pageId5,
+                       $this->namespace,
+                       $this->pageTitle5->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId5_1,
+                       "BackupDumperTestP5 Summary1",
+                       null,
+                       0,
+                       "",
+                       false,
+                       false,
+                       CONTENT_MODEL_WIKITEXT,
+                       CONTENT_FORMAT_WIKITEXT,
+                       $schemaVersion
+               );
+               $asserter->assertPageEnd();
+
                $asserter->assertDumpEnd();
 
                // FIXME: add multi-slot test case!
@@ -317,11 +384,14 @@ class BackupDumperPageTest extends DumpTestCase {
                                '--schema-version', $schemaVersion,
                        ],
                        $this->pageId1,
-                       $this->pageId4 + 1
+                       $this->pageId5 + 1
                );
 
-               // Performing the dump
+               // Performing the dump. Suppress warnings, since we want to test
+               // accessing broken revision data (page 5).
+               $this->setMwGlobals( 'wgDevelopmentWarnings', false );
                $dumper->execute();
+               $this->setMwGlobals( 'wgDevelopmentWarnings', true );
 
                // Checking the dumped data
                $this->assertDumpSchema( $fname, $this->getXmlSchemaPath( $schemaVersion ) );
@@ -404,6 +474,21 @@ class BackupDumperPageTest extends DumpTestCase {
                );
                $asserter->assertPageEnd();
 
+               // Page 5 (broken revision data)
+               $asserter->assertPageStart(
+                       $this->pageId5,
+                       $this->namespace,
+                       $this->pageTitle5->getPrefixedText()
+               );
+               $asserter->assertRevision(
+                       $this->revId5_1,
+                       "BackupDumperTestP5 Summary1",
+                       null,
+                       0,
+                       ""
+               );
+               $asserter->assertPageEnd();
+
                $asserter->assertDumpEnd();
        }
 
index e35e373..7233b86 100644 (file)
@@ -12,6 +12,11 @@ class RedisBagOStuffTest extends MediaWikiUnitTestCase {
 
        protected function setUp() {
                parent::setUp();
+
+               if ( defined( 'HHVM_VERSION' ) ) {
+                       $this->markTestSkipped( 'HHVM Reflection buggy' );
+               }
+
                $cache = $this->getMockBuilder( RedisBagOStuff::class )
                        ->disableOriginalConstructor()
                        ->getMock();